Painting and Drawing
14.1 Painting in a Window
The WM_PAINT message is sent when the system or another application makes a request to paint a portion of an application's window. The message is sent by the DispatchMessage function to a window procedure when the application obtains a WM_PAINT message from the message queue by using the GetMessage or PeekMessage functions.
A window receives this message through its WindowProc function. Windows always specifies the invalid area of any window in terms of a least bounding rectangle; hence, the entire window is not repainted.
- WM_PAINT message is generated by the system only when any part of the application window becomes invalid.
- The message should not be sent by an application directly.
- The DefWindowProc function validates the update region. It may also send WM_NCPAINT (nonclient area) and WM_ERASEBKGND (background) messages.
14.1.1 When to Draw in a Window
An application draws in a window at a variety of times: when first creating a window, resizing, moving from behind another window, minimizing/maximizing, and scrolling.
Ideally, an application carries out most of its drawing operations during the processing of WM_PAINT messages. In this case, the application retrieves a display device context (DC) by calling the BeginPaint function. If drawing at any other time, it calls GetDC or GetDCEx.
14.1.2 The WM_PAINT Message
Upon receiving WM_PAINT, an application calls BeginPaint to retrieve the device context. After completing drawing, it calls EndPaint to release the DC.
BeginPaint performs the following:
- Sets the clipping region of the DC to the intersection of the update region and visible region.
- Fills a PAINTSTRUCT structure with information about the update request.
- Sets the update region to NULL to prevent further WM_PAINT messages.
- Hides the caret to prevent drawing corruption.
14.1.3 Drawing Without the WM_PAINT Message
Useful for immediate feedback (e.g., dragging an object). The application uses GetDC, performs drawing, and then calls ReleaseDC. Usually, the window is not invalidated during this process; instead, the application manages the restoration of the previous state (often by inverting colors).
14.1.4 Window Background
The system sends WM_ERASEBKGND when BeginPaint is called. If the application passes this to DefWindowProc, the system erases the background using the class background brush (hbrBackground).
14.2 Window Coordinate System
The coordinate system is based on device units (pixels). The x-coordinates increase to the right; y-coordinates increase from top to bottom.
- Screen Coordinates: Origin (0,0) is the upper-left corner of the screen.
- Client Coordinates: Origin (0,0) is the upper-left corner of the window's client area.
Applications can map between these using the MapWindowPoints function.
14.3 Window Regions
- Visible Region: Defines the part of the window visible to the user (managed by the system).
- Clipping Region: Determines where the system permits drawing. It is the intersection of the visible region and the update region.
- WS_CLIPCHILDREN / WS_CLIPSIBLINGS: Styles that exclude child or sibling windows from the visible region.
14.4 Conditions: PAINT Message Sent
- Hidden part of window becomes visible.
- Window is resized (with CS_VREDRAW/CS_HREDRAW styles).
- Program scrolls its window.
- InvalidateRect or InvalidateRgn is called.
14.5 Conditions: PAINT Message MAY be Sent
- A dialog is dismissed.
- A drop-down menu disappears.
- A tooltip is displayed and then hidden.
14.6 Conditions: PAINT Message NEVER Sent
- An icon is dragged over the window.
- The mouse cursor is moved.
14.7 PAINT Reference
14.7.1 InvalidateRect Function
Used to make a window or part of it invalid.
BOOL InvalidateRect( HWND hWnd, // handle to window CONST RECT *lpRect, // rectangle coordinates BOOL bErase // erase state );
14.7.2 PAINTSTRUCT Structure
typedef struct tagPAINTSTRUCT { HDC hdc; // Handle to the Device context BOOL fErase; // Erase background if true RECT rcPaint; // Rectangle to the invalidate region BOOL fRestore; // Reserved BOOL fIncUpdate; // Reserved BYTE rgbReserved[32]; } PAINTSTRUCT;
14.8 Other GDI Text Output Functions
14.8.1 DrawText
Draws formatted text in a specified rectangle.
int DrawText( HDC hDC, // handle to DC LPCTSTR lpString, // text to draw int nCount, // text length LPRECT lpRect, // formatting dimensions UINT uFormat // text-drawing options );
| Value | Description |
|---|---|
| DT_BOTTOM | Justifies text to the bottom (used with DT_SINGLELINE). |
| DT_CENTER | Centers text horizontally. |
| DT_VCENTER | Centers text vertically (used with DT_SINGLELINE). |
| DT_WORDBREAK | Breaks words automatically at the edge of the rectangle. |
| DT_NOCLIP | Draws without clipping; slightly faster. |
| DT_SINGLELINE | Displays text on a single line only. |
14.8.2 TabbedTextOut
Writes a string expanding tabs to specified positions.
LONG TabbedTextOut( HDC hDC, int X, int Y, LPCTSTR lpString, int nCount, int nTabPositions, CONST LPINT lpnTabStopPositions, int nTabOrigin );
14.9 Primitive Shapes
Includes Lines, Curves, and filled shapes (Ellipse, Chord, Pie, Polygon, Rectangles).
14.9.1 Lines
Drawn using MoveToEx (sets starting point) and LineTo (draws to ending point).
BOOL LineTo( HDC hdc, int nXEnd, int nYEnd );
14.9.2 Rectangle
Outlined by the current pen and filled by the current brush.
BOOL Rectangle(HDC hdc, int nLeft, int nTop, int nRight, int nBottom);
14.10 Stock Objects
Pre-defined GDI objects in Windows are:
- Pens
- Brushes
- Fonts
- Palettes
14.10.1 GetStockObject Function
The GetStockObject function retrieves a handle to one of the stock pens, brushes, fonts, or palettes.
HGDIOBJ GetStockObject( int fnObject // stock object type );
fnObject: Specifies the type of stock object. This parameter can be one of the following values:
| Value | Meaning |
|---|---|
| BLACK_BRUSH | Black brush. |
| DKGRAY_BRUSH | Dark gray brush. |
| DC_BRUSH | Windows 2000/XP: Solid color brush. Default is white. Change with SetDCBrushColor. |
| GRAY_BRUSH | Gray brush. |
| HOLLOW_BRUSH | Hollow brush (equivalent to NULL_BRUSH). |
| LTGRAY_BRUSH | Light gray brush. |
| WHITE_BRUSH | White brush. |
| BLACK_PEN | Black pen. |
| DC_PEN | Windows 2000/XP: Solid pen color. Default is white. Change with SetDCPenColor. |
| WHITE_PEN | White pen. |
| ANSI_FIXED_FONT | Windows fixed-pitch (monospace) system font. |
| ANSI_VAR_FONT | Windows variable-pitch (proportional space) system font. |
| DEFAULT_GUI_FONT | Default font for UI objects (menus/dialogs). Usually MS Sans Serif or Tahoma. |
| SYSTEM_FONT | System font used to draw menus and controls. |
| DEFAULT_PALETTE | Default palette consisting of static colors in the system palette. |
Return Values: If the function succeeds, the return value is a handle to the requested logical object. If it fails, the return value is NULL.
Note: Use DKGRAY_BRUSH, GRAY_BRUSH, and LTGRAY_BRUSH only in windows with CS_HREDRAW and CS_VREDRAW styles to avoid pattern misalignment. It is not necessary to call DeleteObject to delete stock objects.
14.11 SelectObject
The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type.
HGDIOBJ SelectObject( HDC hdc, // handle to DC HGDIOBJ hgdiobj // handle to object );
| Object | Creation Functions |
|---|---|
| Bitmap | CreateBitmap, CreateCompatibleBitmap, CreateDIBSection (Memory DCs only). |
| Brush | CreateBrushIndirect, CreateSolidBrush. |
| Font | CreateFont, CreateFontIndirect. |
| Pen | CreatePen, CreatePenIndirect. |
| Region | CreatePolygonRgn, CreateRectRgn, CreateRectRgnIndirect. |
Return Values: If the function succeeds and the object is not a region, the return value is a handle to the object being replaced. An application should always replace a new object with the original, default object after it has finished drawing.
14.12 Example
// Select a dynamic pen color using stock objects SelectObject(hdc, GetStockObject(DC_PEN)); SetDCPenColor(hdc, RGB(0, 255, 0)); Rectangle(hdc, 0, 0, 20, 20); // Change color to blue SetDCPenColor(hdc, RGB(0, 0, 255)); Rectangle(hdc, 0, 0, 20, 20); // Example for Brush SelectObject(hdc, GetStockObject(DC_BRUSH)); SetDCBrushColor(hdc, 0x000000); // Black
Summary
In this lecture, we studied painting in windows, specifically the WM_PAINT message. This message is triggered when a region becomes invalidated. We also explored:
- Invalidation vs. Validation: A region receives paint messages until it is validated.
- Message Handling: The difference between sending (direct) and posting (queue) messages.
- GDI Objects: How to use Pens, Brushes, and Fonts to customize window appearance.
Tips
- Resource Management: All GDI objects (bitmaps, regions, fonts) create handles. These must be deleted using DeleteObject once they are no longer used to prevent memory leaks.
- Exception: You do not need to delete Stock Objects retrieved via GetStockObject.
- To experiment with different colors while working with painting and drawing concepts, you can use our Random Color Picker Tool to generate and preview random color values.
Exercises
- Create a round rectangular region and paint it using a custom-created hatched brush. Write text that is clipped specifically to that region.
- Implement logic where pressing the left mouse button inside a specific region triggers a MessageBox confirming the click location.
Would you like me to generate the C++ code for the "round rectangular region" exercise described above?