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
editWhat's in? And their defaults:
GDI objects (one per type) | Windows version | Set function | Get function | |
Pen | Zero-width black pen (means 1 pixel width on any scaling) | 2 | SelectObject (SelectPen, SelectBrush, SelectFont) | GetCurrentObject |
Brush | White solid brush | 2 | ||
Font | System default font | 2 | ||
Palette | System default palette (Notes below) | 3 | SelectPalette | |
Color space | ? | 4 | SetColorSpace | |
Destination bitmap (not for Metafile contexts) | depends on which function creates the DC | 2 | SelectObject (SelectBrush) | |
Clipping Region | depends on which function creates the DC | 2 | GetClipRgn (GetClipBox) | |
Numbers | ||||
Mapping mode, scaling and offset | 1 unit = 1 pixel, origin = left/top | 2 | Get/Set MapMode, WindowOrgEx, WindowExtEx, ViewportOrgEx, ViewportExtEx | |
Transformation matrix (rotation, shearing, scaling, offset) | Equality | 4 | SetWorldTransform, ModifyTransform | GetWorldTransform |
Text color, also used for black-and-white involving blt operations | black | 2 | SetTextColor | GetTextColor |
Background color, used for text / hatch brush / dotted line background and some blt operations | white | 2 | SetBkColor | GetBkColor |
Miter limit (clipping of sharp polygon corners) | 10.0F (ten times the line width) | 4 | SetMiterLimit | GetMiterLimit |
Brush origin (for aligning a pattern brush) | 0/0 | 2 | SetBrushOrgEx | GetBrushOrgEx |
Enums and boolean switches | ||||
Enable advanced GDI functions | No | 4 | SetGraphicsMode | GetGraphicsMode |
What's inside or outside a complex polygon (PolyFillMode) | Alternate Rule | 2 | SetPolyFillMode | GetPolyFillMode |
Text alignment | top/left | 2 | SetTextAlign | GetTextAlign |
Text / hatch brush / dotted line output with opaque background color or not | with background | 2 | SetBkMode | GetBkMode |
How bitmaps are stretched on blt operations | ? | 3 | SetStretchBltMode | GetStretchBltMode |
How pen / brush and surface are combined (boolean operations) | Copy pen | 2 | SetROP2 | GetROP2 |
Something else | ||||
Path (Prepared lines) | empty path | 4 | BeginPath, EndPath ... | - |
Context stack | empty | 2 | SaveDC | RestoreDC |
To be continued ...
Brush object
editWindows 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
editPens 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
editFonts 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
editIn 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
editBecause 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
editNext Chapter
edit- ↑ QuinnRadich. "Overview of the Windows Graphics Architecture - Win32 apps". learn.microsoft.com. Retrieved 2023-04-11.