Introduction
In this tip, we will learn how to draw a chessboard using the Windows GDI32. The goal of this tip is not only to show how to draw a chessboard, but also to give some knowledge about GDI32.
Background
This tip will assume that you are familiar with pure Win32 programming.
Windows GDI32
At first, we will know very little information about the GDI32 so it will be easier for a novice programmer to understand this post.
The GDI
GDI means Graphical Device Interface and it is used for drawing on Windows. Pen
, Brush
are GDI objects used for defining how the drawing will be look like. Font
is another GDI object used for describing how the text will appear.
The DC
DC means device context. Device context is a structure that defines a set of graphic objects. The graphic objects can be a Pen
, a Brush
or a Bitmap
.
The WM_PAINT
In Windows, all GDI drawings are usually done inside the WM_PAINT
message handler. Inside the WM_PAINT
event handler, an application can call BeginPaint
function of GDI32 to retrieve the display device context for the client area. After completing the drawing operations, the application calls the EndPaint
function of GDI32 to release the display device context.
Including the GDI32 Library into Project
For including GDI32 library header file, all you have to do is just add this simple line at the beginning of your C++ source file if you have not done this already:
#include <windows.h>
This will include not only the GDI functions prototype but also all the necessary Windows functions prototype into your source code that you will need to do Win32 GUI programming. In fact, the windows.h file itself actually includes all the necessary Windows library header files.
Do not forget to add 'Gdi32.Lib' import library into your project.
Drawing the Chessboard
For drawing a Chessboard, we will create a Bitmap
, will change the Bitmap
pixel data/color and then draw it later inside the WM_PAINT
event.
At first, we need to know how a bitmap can be created in GDI. Though GDI has got various functions for that, but here, we will use the CreateDIBSection
function of GDI32 library.
The MSDN library says:
The CreateDIBSection function creates a DIB that applications can write to directly. The function gives you a pointer to the location of the bitmap bit values. You can supply a handle to a file-mapping object that the function will use to create the bitmap, or you can let the system allocate the memory for the bitmap.
That means using this function, you can easily create Device-Independent Bitmap (DIB). This is the prototype of the function:
HBITMAP CreateDIBSection(
HDC hdc, CONST BITMAPINFO *pbmi, UINT iUsage, VOID **ppvBits, HANDLE hSection, DWORD dwOffset );
So let's create a 24-bit bitmap using the function. We will do this after creating the drawing window.
We can use the GetClientRect
function to get the size of the client area of the drawing window to use it as our bitmap size.
RECT rc;
::GetClientRect(hWnd, &rc);
int nBitmapWidth = rc.right;
int nBitmapHeight = rc.bottom;
As we can see, the CreateDIBSection
function takes a structure of the BITMAPINFO
type as its second parameter. So let's create the structure and fill-up its member variables using the necessary value.
const int nColorBit = 24;
BYTE* pPixel = NULL;
const int nHeaderSize = sizeof(BITMAPINFOHEADER) + 16;
BYTE* temp_buf = new BYTE[(size_t)nHeaderSize];
ZeroMemory(temp_buf, nHeaderSize);
BITMAPINFO* pInfo = (BITMAPINFO*)&temp_buf[0];
pInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pInfo->bmiHeader.biWidth = nBitmapWidth;
pInfo->bmiHeader.biHeight = nBitmapHeight;
pInfo->bmiHeader.biPlanes = 1;
pInfo->bmiHeader.biBitCount = (WORD)nColorBit;
nBitmapWidth
and nBitmapHeight
variables hold the bitmap
size though I have used the drawing window client size as their value but you may want to use your own value.
At this point, we have all the information that will be needed for CreateDIBSection
function parameters value. So call the function to create the bitmap
:
g_hbmp = ::CreateDIBSection(NULL, pInfo, DIB_RGB_COLORS, (VOID**) &pPixel, NULL, 0);
Now, we have a 24-bit bitmap
on memory and a pointer to its pixel data. The pPixel
variable is now pointed to bitmap pixel data.
Let's clear the data:
const int nPitch = 4 * ((nBitmapWidth * nColorBit + 31) / 32);
ZeroMemory(pPixel, nPitch * nBitmapHeight);
This code will make the bitmap
black.
Then loop through the bitmap
pixels:
for(int y = 0;y < nBoardSize;y++)
{
xOffset = 0;
for(int x = 0;x < nBoardSize;x++)
{
Index = yOffset + xOffset;
...
For checking whether it should be a black or white (the common two colors for a chessboard) block pixel, we will use a %
operator:
int nX = x / nBlockSize;
int nY = y / nBlockSize;
if ( ((nX + nY) % 2 == 0) )
{
pPixel[Index] = 255; pPixel[Index+1] = 255; pPixel[Index+2] = 255; }
else
{
pPixel[Index] = 0; pPixel[Index+1] = 0; pPixel[Index+2] = 0; }
Now, all that remains is to draw the bitmap
, and we will do it using BitBlt
function of GDI32 library inside the WM_PAINT
event:
HDC tmpDC = ::CreateCompatibleDC(NULL) ;
HGDIOBJ tmpOBJ = ::SelectObject(tmpDC, g_hbmp);
::BitBlt(hdc, 0, 0, nBitmapWidth, nBitmapHeight, tmpDC, 0, 0, SRCCOPY);
::SelectObject (tmpDC, tmpOBJ);
::DeleteDC(tmpDC);
g_hbmp
( global variable ) is the handle of the bitmap
that we have created using the CreateDIBSection
function.
Points of Interest
I think drawing a chessboard programmatically is more interesting than drawing it in any other way. You can easily change the size of blocks and its color during run-time. So I hope it will be interesting for those who want to create a chess game and also for those who want to learn more about GDI32.
Conclusion
You may think that I could simply use the SetPixel
function of GDI32 library inside the WM_PAINT
message for drawing the chessboard. But using the SetPixel
function, it will be very very slow and will take several seconds for every redraw.
So, you may not want to use this.
At last, I want to say that if you find anything wrong in the tip or if you want to say anything about this post, please comment to help me improve my tip.