Windows Programming/GDI and Drawing

This page of the Windows Programming book is a stub. You can help by expanding it.


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[1].

Device Contexts edit

What's in? And their defaults:

GDI objects (one per type)Windows versionSet functionGet function
PenZero-width black pen (means 1 pixel width on any scaling)2SelectObject (SelectPen, SelectBrush, SelectFont)GetCurrentObject
BrushWhite solid brush2
FontSystem default font2
PaletteSystem default palette (Notes below)3SelectPalette
Color space?4SetColorSpace
Destination bitmap (not for Metafile contexts)depends on which function creates the DC2SelectObject (SelectBrush)
Clipping Regiondepends on which function creates the DC2GetClipRgn (GetClipBox)
Numbers
Mapping mode, scaling and offset1 unit = 1 pixel, origin = left/top2Get/Set MapMode, WindowOrgEx, WindowExtEx, ViewportOrgEx, ViewportExtEx
Transformation matrix (rotation, shearing, scaling, offset)Equality4SetWorldTransform, ModifyTransformGetWorldTransform
Text color, also used for black-and-white involving blt operationsblack2SetTextColorGetTextColor
Background color, used for text / hatch brush / dotted line background and some blt operationswhite2SetBkColorGetBkColor
Miter limit (clipping of sharp polygon corners)10.0F (ten times the line width)4SetMiterLimitGetMiterLimit
Brush origin (for aligning a pattern brush)0/02SetBrushOrgExGetBrushOrgEx
Enums and boolean switches
Enable advanced GDI functionsNo4SetGraphicsModeGetGraphicsMode
What's inside or outside a complex polygon (PolyFillMode)Alternate Rule2SetPolyFillModeGetPolyFillMode
Text alignmenttop/left2SetTextAlignGetTextAlign
Text / hatch brush / dotted line output with opaque background color or notwith background2SetBkModeGetBkMode
How bitmaps are stretched on blt operations?3SetStretchBltModeGetStretchBltMode
How pen / brush and surface are combined (boolean operations)Copy pen2SetROP2GetROP2
Something else
Path (Prepared lines)empty path4BeginPath, EndPath ...-
Context stackempty2SaveDCRestoreDC

To be continued ...

Brush object edit

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?

On creation, see LOGBRUSH structure Modification
Color (RGB color, can be dithered at realization) SetDCBrushColor
Style (solid, hatched, or bitmap) -
Bitmap (for bitmap style) -
Hidden fields on realization
Referencing device context (to track whether realization is still valid) UnrealizeObject
Device-dependent, possibly dithered bitmap, typically 8x8 size -

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 edit

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

Visible fields, see LOGPEN structure Windows version Modification
Width, in horizontal(!) logical units, 0 = 1 pixel 2 -
Style (solid or somehow dotted) 2 -
User dotting style (for user style selected) 4 -
Color 2 SetDCPenColor
Brush (Content: See above) 4 -
Geometric / Cosmetic flag 4 -
Hint how to draw line ends 4 -
Hint how to draw line joins 4 -
Hidden fields
Referencing device context (to track validity of other hidden fields) 2 UnrealizeObject?
Width in device pixels 2 -
Palette index of given color 2 -
Device-dependent bitmap for filling wide lines 2 -

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 edit

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 edit

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 edit

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) {
 ...
}

Metafiles edit

Next Chapter edit

  1. QuinnRadich. "Overview of the Windows Graphics Architecture - Win32 apps". learn.microsoft.com. Retrieved 2023-04-11.