Windows Programming/GDI and Drawing

This page will talk about graphics and drawing using the windows GDI libraries.

GDI was the original graphics API for Windows. However, the DirectX Graphics Infrastructure is now recommended for its improved use of GPU functions, alpha blending, and anti-aliasing.

Device Contexts
What's in? And their defaults: To be continued ...

Brush object
Windows uses brushes to paint colors and fill areas with predefined patterns. Brushes have a minimum size of 8X8 pixels and like pens, have three basic characteristic: size, pattern and color. With their 8X8 pixel minimum size,brushes are said to have a pattern, not a style as pens do. The pattern may be a solid color, hatched, diagonal or any other user definable combination, even a bitmap pattern.

case WM_PAINT: {    /* This will paint a red rectangle */ HDC holdBrush; HDC hdc = BeginPaint(hwnd, &ps); HBRUSH hBrush = CreateSolidBrush(RGB(255,0,0)); holdBrush = SelectObject(hdc, hBrush); Rectangle(hdc, 10, 10, 100, 100);

DeleteObject(hBrush); /* Memory management has been omitted for brevity */ EndPaint(hwnd, &ps); break; }

What's behind the brush object?

Note that a bitmap pattern brush behaves differently from a hatched brush, even when the bitmap looks like the hatch. Hatched brushes are transparent, whereas bitmap brushes are opaque. If a black-and-white bitmap is used, black is replaced by the Device Context's text color, and white is replaced by the background color. (This is exactly the BitBlt behaviour.) SetBkMode won't work. However, DC's ROP2 applies.

Pen object
Pens are used to create borders around shapes you have drawn.

Lines which result in widths larger than 1 device pixel (not logical units) are drawn as polygons with winding rule. Line colors are never dithered, except ExtCreatePen with a solid brush is used.

Font object
Fonts are for displaying text and symbols in various styles and sizes. Internal large differences exist for font management between Windows 2 (very basic), 3.1 (introducing TrueType), and 4+ (Unicode; world-transform rotation and mirroring; escapement can differ from rotation).

See LOGFONT structure for visible fields.

Hidden fields include
 * Referencing device context
 * Device-dependent bitmap font representation

Therefore, selecting a font into a Device Context (SelectObject) can be time-consuming, especially for large Asian fonts and large font sizes, to “paint” all the glyphs out of the TrueType template into the bitmap. Furthermore, as for all GDI objects, it's not a good idea to select one object from one to another context, because invalidating the hidden files can be a time-consuming process. Note that any GDI object cannot be selected into more that one DC.

Basic Drawing
In Windows, drawing is typically handled by the WM_PAINT message. The following is an example of how to draw a red square: case WM_PAINT: { 	PAINTSTRUCT ps; BeginPaint(hwnd, &ps); // RECT defines the upper-left and lower-right corners of a rectangle RECT rectangle = {50, 50, 250, 250}; HBRUSH hbr = CreateSolidBrush(RGB(125, 0, 0)); FillRect(ps.hdc, &rectangle, hbr); DeleteObject(hbr); EndPaint(hwnd, &ps); } break; Firstly, we create the PAINTSTRUCT variable ps. This is a data structure containing information about the painting operation. The next line calls BeginPaint. This initializes ps, then fills it with relevant information. For this example, we only need the hdc member of ps. This is a handle to our window's Device Context. Next, we create a rectangle. This holds the upper-left and lower-right coordinates we're going to paint this rectangle at. The coordinates are relative to the upper-left corner of the window's client area. We also have to create a brush, otherwise Windows won't know what color to paint the rectangle. Finally, we call FillRect, and pass the parameters ps.hdc, a pointer to rectangle, and hbr, our brush. This paints the rectangle directly to our window's device context, and from there it is painted on the screen. After every painting operation, it is necessary to clean up any GDI objects we use, in this case hbr and ps.

Advanced hint
Because Windows should respond to WM_PAINT and WM_PRINTCLIENT, a more general rule for writing a WM_PAINT handler is this: case WM_PRINTCLIENT: OnPaint((HDC)wParam, NULL); break;

case WM_PAINT: { PAINTSTRUCT ps; BeginPaint(hwnd, &ps); OnPaint(ps.hdc, &ps.rcPaint);	// It's a good idea to manage the update area. Other PAINTSTRUCT fields are of less usefulness. EndPaint(hwnd, &ps); }break;

// Somewhere else void OnPaint(HDC dc, RECT* rcUpdate) { ... }

Next Chapter

 * Dialog Boxes