GDI stands for Graphics Device Interface. It provides many functions for displaying graphics in your Windows application.
Note: in GDI calls screen position 0,0 is the top left of the window. If you need to work out the window width and height follow this link here: Window size
An example application showing these GDI functions can be downloaded here: WinGDIdemo.exe. In addition the source code for this demo can also be downloaded: WinGDIDemoCode.zip
This page contains notes on the following GDI capabilities:
To display some text you can use the TextOut function. In order to use this you need to obtain a handle to the device, an HDC and release it after use.
To obtain the handle you call: BeginPaint, it takes your window handle and a pointer to a PAINTSTRUCT that you have declared. This structure is just used by windows so you don't need to do anything apart from pass it on to functions.
The definition of the TextOut function is:
BOOL TextOut(HDC hdc, int x, int y, LPCSTR lpString, int cbString);
It takes the handle you were returned from BeginPaint, the x, y position on screen, a pointer to some text and the number of characters you want to display.
After displaying your text you must call EndPaint to release the handle.
E.g. to display 'Hello World' at screen position 10,10 you could do this:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
TextOut(hdc,10,10,"Hello World",11);
EndPaint(hWnd, &ps);
You can drawn single pixels to the screen using the following function:
SetPixel( HDC hdc, int X, int Y, COLORREF crColor);
Again it requires a handle to a device context (HDC) and a screen position. Additionally the colour you want to set the pixel to can be provided. This needs to be given in a Win32 type COLORREF. Fotunately we can use a macro (RGB) to convert from three Red, Green and Blue values to a COLORREF type. Each colour component can range from 0 to 255.
To display a red pixel at screen co-ordinates 100,100 we would write:
SetPixel(hdc, 100, 100, RGB(255,0,0));
There are a number of functions for drawing lines using GDI. You can use MoveTo and LineTo to position a pen on the screen and draw a line to another position. You can also use PolyLine, PolyPolyLine, Arc etc. To explore which ones are available look in the MSDN help. I will describe here the use of PolyLine.
PolyLine draws lines between points defined in an array you pass to the function. The function definition is:
Polyline( HDC hdc, CONST POINT *lppt, int cPoints);
This takes the device handle, an array of points and how many points there are in the array. It draws lines between each point. POINT is a structure with member variables x and y.
To draw a simple diagonal line we could do this:
POINT pntArray[2];
pntArray[0].x=10;
pntArray[0].y=10;
pntArray[1].x=100;
pntArray[1].y=100;
Polyline(hdc, pntArray, 2);
The colour and style of the line can be changed by using pens. Pens are described later on this page: Pens and Brushes
You can draw filled rectangles using FillRect and filled ellipses using Ellipse. Also, not covered here, you can draw filled polygons, pies, rounded rectangles etc. Again look in the MSDN help for details.
To draw a filled ellipse we can use:
BOOL Ellipse( HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect );
This function takes the device context and the screen positions of a rectangle that represents the bounding rectangle for the ellipse. The ellipse will be drawn to fit in this rectangle.
To draw a filled rectangle we can use:
FillRect( HDC hDC, CONST RECT *lprc, HBRUSH hbr);
This function takes the device context, a rectangle structure and a brush. The RECT structure contains member variables left, right, top and bottom. Brushes are described in the next section: Pens and Brushes.
GDI uses the concept of Pens and Brushes. A Pen defines the style and colour that pixels will be drawn in while a brush determines the fill colour of shapes. A number of the GDI drawing functions can be effected by the pen or brush chosen.
To create a pen we need to obtain a handle to one. This handle is named HPEN, we can keep hold of this handle during the lifetime of our program but must remember to delete it before exiting.
To create a pen we use the function:
HPEN CreatePen( int fnPenStyle, int nWidth, COLORREF crColor);
This function takes a style, a pen width and a colour. The style must be one of the predefined styles. The main ones are shown below:
PS_SOLID - Pen is solid.
PS_DASH - Pen is dashed.
PS_DOT - Pen is dotted.
PS_DASHDOT - Pen has alternating dashes and dots.
PS_DASHDOTDOT - Pen has alternating dashes and double dots.
PS_NULL - Pen is invisible.
So to create a solid green pen of 1 pixel width we would write:
HPEN greenPen=CreatePen(PS_SOLID, 1, RGB(0,255,0));
It is a good idea to create our pens (and brushes) in advance of drawing and then apply them as required. To apply a pen so future drawing commands use it we must select it. This is known as selecting it into the device context and is achieved by using SelectObject. One thing we must be aware of is that when we no longer want to use our pen we need to re-select the old pen. Luckily SelectObject returns the old pen. So to use our green pen to draw a line we may do this:
// Select our green pen into the device context and remember previous pen
HGDIOBJ oldPen=SelectObject(hdc,greenPen);
// Draw our line
Polyline(hdc, pntArray, 2);
// Select the old pen back into the device context
SelectObject(hdc,oldPen);
Creating and using Brushes is very similar to pens. We can use CreateSolidBrush to create a solid coloured brush or CreateBrushIndirect to create a brush with specific styles (like hatched) or even using a loaded bitmap. You can use a bitmap as a repeating pattern for your brush using CreatePatternBrush. Here I will describe CreateSolidBrush for the others please look int he MSDN help file.
HBRUSH CreateSolidBrush( COLORREF crColor)
This is a very simple function that takes the required colour and returns a handle to a brush. We can use it in much the same way as the pen. To create a blue brush we would write:
HBRUSH blueBrush=CreateSolidBrush(RGB(0,255,0));
To use it to fill a rectangle:
RECT rct;
rct.left=10;
rct.right=100;
rct.top=10;
rct.bottom=200;
FillRect(hdc, &rct, blueBrush);
When you create a window you can specify its size. However the size of the window does not always relate to the drawing area as there are menu bars and borders that also take up room. Additionally the user can simply alter the size of your window at any time. So we need a way of determining the current drawing area. We do this using the GetClientRect call. This returns a rectangle defining the area of the window your program (the client) can draw into. You must pass in the handle to your window and the address of a rectangle that will be filled by the function e.g.
RECT clientRect;
GetClientRect(hWnd,&clientRect);
As I mentioned previously you can only draw your window when Windows tells you to via a WM_PAINT message. Sometimes you would like to update your window whenever you want. To do this you have to tell Windows that your window is dirty and needs a redraw. You can do this by using InvalidateRect. This tells Windows your window is dirty and needs redrawing. Windows then sends you a WM_PAINT message telling you to draw. InvalidateRect takes three parameters, the handle to your window, the rectangular area of your window that needs to be redrawn (or NULL for the whole window) and a Boolean indicating if you wish the window to be cleared first. So the standard call for clearing the whole window is:
InvalidateRect(hWnd,NULL,TRUE);
There are many things you can draw using the GDI Windows API functions. Look in the help file for a comprehensive list and at the MSDN page here: GDI. There are functions for drawing rectangles, lines, ellipses etc. and pens and brushes can be used to define the colour and style of drawings.
For further reading I recommend the 'bible' of Windows API coding 'Programming Windows' by Petzold - more details can be found on the books page.