The GDI
The GDI (Graphics Device Interface) is an interface for working with graphics. It is used to interact with graphic devices such as monitor, printer or a file. The GDI allows programmers to display data on a screen or printer without having to be concerned about the details of a particular device. The GDI insulates the programmer from the hardware. From the programmer's point of view, the GDI is a group of classes and methods for working with graphics. The GDI consists of 2D Vector Graphics, Fonts and Images. To begin drawing graphics, we must obtain a device context (DC) object.
Basic Graphics Primitives
Pixels
The simplest object in GDI is the pixel. It is a single spot on the monitor screen.
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Simple" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Simple"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 150, NULL, NULL, hInstance, NULL);
while( GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
static int w=250, h=150;
int x, y;
int i;
switch(msg)
{
case WM_SIZE:
GetClientRect(hwnd, &rect);
w = LOWORD(lParam) + 1;
h = HIWORD(lParam) + 1;
InvalidateRect(hwnd, &rect, TRUE);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
for (i = 0; i<1000; i++) {
x = (rand() % w) + 1;
y = (rand() % h) + 1;
SetPixel(hdc, x, y, RGB(255, 0, 0));
}
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example we display randomly 1000 red pixels on the client area of the window. If we resize the window, the pixels are redrawn.
Rectangle
To draw a rectangle, we use the Rectangle() function.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Rectangle" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Rectangle"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 200, NULL, NULL, hInstance, NULL);
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
Rectangle(hdc, 50, 50, 200, 100);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
The outline of the rectangle is drawn using the current pen. The background is drawn using the current brush.
BOOL Rectangle( HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect );
The rectangle is drawn using the Rectangle function. We draw the rectangle using two points. Top left point and bottom right point.
Pen
Pen is an elementary graphics object. It is used to draw lines, curves and outlines of rectangles, ellipses, polygons or other shapes.
HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);
The CreatePen() function creates a logical pen with a specified style, width and color.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Lines" );
wc.hInstance = hInstance ;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Lines"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 180, NULL, NULL, hInstance, NULL);
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HPEN holdPen;
HPEN hPen1;
HPEN hPen2;
HPEN hPen3;
HPEN hPen4;
HPEN hPen5;
switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hPen1 = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
hPen2 = CreatePen(PS_DASH, 1, RGB(0, 0, 0));
hPen3 = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
hPen4 = CreatePen(PS_DASHDOT, 1, RGB(0, 0, 0));
hPen5 = CreatePen(PS_DASHDOTDOT, 1, RGB(0, 0, 0));
holdPen = SelectObject(hdc, hPen1);
MoveToEx(hdc, 50, 30, NULL);
LineTo(hdc, 200, 30);
SelectObject(hdc, hPen2);
MoveToEx(hdc, 50, 50, NULL);
LineTo(hdc, 200, 50);
SelectObject(hdc, hPen2);
MoveToEx(hdc, 50, 70, NULL);
LineTo(hdc, 200, 70);
SelectObject(hdc, hPen3);
MoveToEx(hdc, 50, 90, NULL);
LineTo(hdc, 200, 90);
SelectObject(hdc, hPen4);
MoveToEx(hdc, 50, 110, NULL);
LineTo(hdc, 200, 110);
SelectObject(hdc, holdPen);
DeleteObject(hPen1);
DeleteObject(hPen2);
DeleteObject(hPen3);
DeleteObject(hPen4);
DeleteObject(hPen5);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example, we draw 5 different lines. Using 5 different pen styles.
SelectObject(hdc, hPen1);
To activate a pen, we call the SelectObject() function.
MoveToEx(hdc, 50, 30, NULL); LineTo(hdc, 200, 30);
To draw lines, we use MoveToEx() and LineTo() functions.
DeleteObject(hPen1); DeleteObject(hPen2); DeleteObject(hPen3); DeleteObject(hPen4); DeleteObject(hPen5);
In the end, we clean up resources.
Brush
Brush is an elementary graphics object. It is used to paint the background of graphics shapes, such as rectangles, ellipses or polygons. A brush can be a solid colour a hatch or a custom bitmap pattern.
In the following example, we create 4 rectangles filled with 4 different solid colors.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Brush" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Solid Brush"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 220, 240, NULL, NULL, hInstance, NULL);
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HPEN holdPen, hPen;
HBRUSH holdBrush, hBrush1, hBrush2, hBrush3, hBrush4;
switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
holdPen = SelectObject(hdc, hPen);
hBrush1 = CreateSolidBrush(RGB(121, 90, 0));
hBrush2 = CreateSolidBrush(RGB(240, 63, 19));
hBrush3 = CreateSolidBrush(RGB(240, 210, 18));
hBrush4 = CreateSolidBrush(RGB(9, 189, 21));
holdBrush = SelectObject(hdc, hBrush1);
Rectangle(hdc, 30, 30, 100, 100);
SelectObject(hdc, hBrush2);
Rectangle(hdc, 110, 30, 180, 100);
SelectObject(hdc, hBrush3);
Rectangle(hdc, 30, 110, 100, 180);
SelectObject(hdc, hBrush4);
Rectangle(hdc, 110, 110, 180, 180);
SelectObject(hdc, holdPen);
SelectObject(hdc, holdBrush);
DeleteObject(hPen);
DeleteObject(hBrush1);
DeleteObject(hBrush2);
DeleteObject(hBrush3);
DeleteObject(hBrush4);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
hBrush1 = CreateSolidBrush(RGB(121, 90, 0));
Here we create a solid color brush.
Hatch brushes
There are six predefined hatch brushes availabel. In our example, we show all of them.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Brush" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Hatch brush"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 220, NULL, NULL, hInstance, NULL);
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HPEN holdPen, hPen;
HBRUSH holdBrush, hBrush1, hBrush2, hBrush3, hBrush4, hBrush5, hBrush6;
switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
holdPen = SelectObject(hdc, hPen);
hBrush1 = CreateHatchBrush(HS_BDIAGONAL, RGB(0, 0, 0));
hBrush2 = CreateHatchBrush(HS_FDIAGONAL, RGB(0, 0, 0));
hBrush3 = CreateHatchBrush(HS_CROSS, RGB(0, 0, 0));
hBrush4 = CreateHatchBrush(HS_HORIZONTAL, RGB(0, 0, 0));
hBrush5 = CreateHatchBrush(HS_DIAGCROSS, RGB(0, 0, 0));
hBrush6 = CreateHatchBrush(HS_VERTICAL, RGB(0, 0, 0));
holdBrush = SelectObject(hdc, hBrush1);
Rectangle(hdc, 30, 30, 100, 80);
SelectObject(hdc, hBrush2);
Rectangle(hdc, 110, 30, 180, 80);
SelectObject(hdc, hBrush3);
Rectangle(hdc, 190, 30, 260, 80);
SelectObject(hdc, hBrush4);
Rectangle(hdc, 30, 110, 100, 160);
SelectObject(hdc, hBrush5);
Rectangle(hdc, 110, 110, 180, 160);
SelectObject(hdc, hBrush6);
Rectangle(hdc, 190, 110, 260, 160);
SelectObject(hdc, holdPen);
SelectObject(hdc, holdBrush);
DeleteObject(hPen);
DeleteObject(hBrush1);
DeleteObject(hBrush2);
DeleteObject(hBrush3);
DeleteObject(hBrush4);
DeleteObject(hBrush5);
DeleteObject(hBrush6);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
This example is very similar to the previous one. We only use a new function call CreateHatchBrush()
Shapes
Shapes are more sophisticated geometrical objects. We will draw various geometrical shapes in the following example.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Shapes" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Shapes"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 390, 230, NULL, NULL, hInstance, NULL);
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
const POINT polygon[10] = { 30, 145, 85, 165, 105, 110, 65, 125, 30, 105 };
const POINT bezier[4] = {280, 160, 320, 160, 325, 110, 350, 110};
switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
Ellipse(hdc, 30, 30, 120, 90);
RoundRect(hdc, 150, 30, 240, 90, 15, 20);
Chord(hdc, 270, 30, 360, 90, 270, 45, 360, 45);
Polygon(hdc, polygon, 5);
Rectangle(hdc, 150, 110, 230, 160);
PolyBezier(hdc, bezier, 4);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example, we have created an ellipse, a rounded rectangle, a chord, a polygon, a rectangle and a bezier curve.
Fonts
Text can be drawn on the window using various fonts. A font is a set of type characters of a particular typeface design and size. (answers.com) Various typefaces include Helvetica, Georgia, Times or Verdana.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT( "Sonnet 55" );
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
CreateWindow( wc.lpszClassName, TEXT("Sonnet 55"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 390, 350, NULL, NULL, hInstance, NULL);
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
DWORD color;
HFONT hFont, holdFont;
static TCHAR *verse1 = TEXT("Not marble, nor the gilded monuments");
static TCHAR *verse2 = TEXT("Of princes, shall outlive this powerful rhyme;");
static TCHAR *verse3 = TEXT("But you shall shine more bright in these contents");
static TCHAR *verse4 = TEXT("Than unswept stone, besmear'd with sluttish time.");
static TCHAR *verse5 = TEXT("When wasteful war shall statues overturn,");
static TCHAR *verse6 = TEXT("And broils root out the work of masonry,");
static TCHAR *verse7 = TEXT("Nor Mars his sword, nor war's quick fire shall burn");
static TCHAR *verse8 = TEXT("The living record of your memory.");
static TCHAR *verse9 = TEXT("'Gainst death, and all oblivious enmity");
static TCHAR *verse10 = TEXT("Shall you pace forth; your praise shall still find room ");
static TCHAR *verse11 = TEXT("Even in the eyes of all posterity");
static TCHAR *verse12 = TEXT("That wear this world out to the ending doom.");
static TCHAR *verse13 = TEXT("So, till the judgment that yourself arise,");
static TCHAR *verse14 = TEXT("You live in this, and dwell in lovers' eyes.");
switch(msg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
color = GetSysColor(COLOR_BTNFACE);
SetBkColor(hdc, color);
hFont = CreateFont(15, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0,
0, 0, 0, 0, TEXT("Georgia"));
holdFont = SelectObject(hdc, hFont);
TextOut(hdc, 50, 20, verse1, lstrlen(verse1));
TextOut(hdc, 50, 40, verse2, lstrlen(verse2));
TextOut(hdc, 50, 60, verse3, lstrlen(verse3));
TextOut(hdc, 50, 80, verse4, lstrlen(verse4));
TextOut(hdc, 50, 100, verse5, lstrlen(verse5));
TextOut(hdc, 50, 120, verse6, lstrlen(verse6));
TextOut(hdc, 50, 140, verse7, lstrlen(verse7));
TextOut(hdc, 50, 160, verse8, lstrlen(verse8));
TextOut(hdc, 50, 180, verse9, lstrlen(verse9));
TextOut(hdc, 50, 200, verse10, lstrlen(verse10));
TextOut(hdc, 50, 220, verse11, lstrlen(verse11));
TextOut(hdc, 50, 240, verse12, lstrlen(verse12));
TextOut(hdc, 50, 260, verse13, lstrlen(verse13));
TextOut(hdc, 50, 280, verse14, lstrlen(verse14));
SelectObject(hdc, holdFont);
DeleteObject(hFont);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
color = GetSysColor(COLOR_BTNFACE); SetBkColor(hdc, color);
By default, if we draw some text on the client area of the window, the background is set to white color. We can change this by setting the background color using the SetBkColor() function. I used the typical Windows gray color. The GetSysColor function is used to get the system colors used in buttons, title or backround of window controls.
hFont = CreateFont(15, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0,
0, 0, 0, 0, TEXT("Georgia"));
Here we create a font object. There are 14 parameters. We don't have to specify all of them. I used only the font size, font weight and fontface parameters.
TextOut(hdc, 50, 20, verse1, lstrlen(verse1));
The text is drawn onto the window using the TextOut() function.
In this part of the Winapi tutorial, we did some drawing.