Article From:

Using an existing brush

Windows Three spare brushes (Stock Pen) are provided.BLACK_PEN(Black brushes)WHITE_PEN(White brushes)NULL_PEN(A brush that does not draw any graphics.

Calling the GetStockObject function can get the handle of the standby brush (HPEN), and the call SelectObject function can select the specified brush into the device environment and return the brush handle that was selected to the device environment before:

// Definition brush handle
HPEN hPen, hPrevPen;

// Handle the handle of a spare brush
hPen = GetStockObject(WHITE_PEN);

// The brush is selected into the device environment, and the function returns the handle of the brush selected before the device environment.
hPrevPen = SelectObject(hdc, hPen);

GDI Object usage rules

  • In the end, all GDI objects created by users should be deleted.
  • When a GDI object is selected into a valid device environment, it must not be deleted.
  • Cannot delete the standby object (Stock Object)

Create a brush

The CreatePen function can be invoked to create a brush, and the brush handle will be returned as a return value.

HPEN CreatePen(
    int fnPenStyle,    // Brush style (decided to draw solid line, dotted line or dot line).
    int nWidth,        // When the brush width is 0, it will be set to 1 pixels. The dotted line or dot line can only be 1 pixels, otherwise it will be set as a solid line.
    COLORREF crColor   // Brush color (COLORREF value can be specified through the RGB macro).

The CreatePenIndirect function can be invoked based on the LOGPEN (logical brush) structure to create a brush. The brush handle will be returned as a return value.

LOGPEN Structure:

typedef struct tagLOGPEN { 
    UINT     lopnStyle; // Brush style
    POINT    lopnWidth; // Brush width (Windows only uses the X field)
    COLORREF lopnColor; // stroke color

CreatePenIndirect Function:

HPEN CreatePenIndirect(
  CONST LOGPEN *lplgpn   // LOGPEN The address of the structure

Choose a brush

By calling the SelectObject function, you can select the brush you just created into the device environment and return the brush handle before you choose the device environment:

HGDIOBJ SelectObject(
    HDC hdc,          // Device environment handle
    HGDIOBJ hgdiobj   // GDI Object handle (this refers to a brush handle)

Delete Brush

By calling the DeleteObject function, you can delete the finished brush.

BOOL DeleteObject(
  HGDIOBJ hObject   // GDI Object handle (this refers to a brush handle)

Note: do not delete the current brush that has been selected into the device environment.

Get the created brush

The GetObject function can be called from the specified brush handle to get the values of the fields of the LOGPEN structure of this brush.

GetObject(hPen, sizeof(LOGPEN), (LPVOID)&logpen);

The GetCurrentObject function can be invoked to get the brush handle of the currently selected device environment:

hPen = GetCurentObject(hdc, OBJ_PEN);


The color of the gap is determined by the background pattern of the device environment and the background color, and the default background mode is OPAQUE (opaque), which is filled with the background color (the default is white).

Calling the SetBkColor function can change the background color of the Windows filled gap.

    HDC hdc,           // Device environment handle
    COLORREF crColor   // Background color value (COLORREF)

The GetBkColor function can be used to get the background color of the Windows filled void, and the function returns it as a return value.

    HDC hdc   // Device environment handle

Calling the SetBkMode function can set the background mode and set it to TRANSPARENT to prevent Windows from filling the gap.

int SetBkMode(
    HDC hdc,      // Device environment handle
    int iBkMode   // Background mode, optional QPAQUE (opaque) and TRANSPARENT (transparent) two modes.

Drawing pattern

Two element raster operation (ROP2, raster operation 2): when Windows draws a straight line, the pixel color of the brush and the pixel color of the target display surface are Boolean operation

By default, the drawing pattern is a simple copy of R2_COPYPEN, pixel.

  • R2_NOTCOPYPEN The pixel is replicated as the color of the brush
  • R2_BLACK Always draw black
  • R2_WHITE Always draw white
  • R2_NOP No operation
  • R2_NOT Get the color of the target back to get the color

The SetROP2 function can be invoked to set up a new drawing mode:

int SetROP2(
    HDC hdc,         // Device environment handle
    int fnDrawMode   // Drawing mode (a sign with R2 as a prefix)

Calling the GetROP2 function can get the current drawing mode and return it as a return value:

int GetROP2(
    HDC hdc   // Device environment handle

PENDEMO Sample program

#include <windows.h>
#include <math.h>

#define NUMS 1000
#define TWOPI 6.283185307179586476925286766559

LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

    HDC hdc;
    static LOGPEN logpen;
    static HPEN hRedPen;
    static HPEN hBluePen;
    static int cxClient, cyClient;
    POINT apt[NUMS];
    int i;

    switch (message) {

    case WM_CREATE:
        logpen.lopnColor = RGB(255, 0, 0);
        logpen.lopnStyle = PS_DASH;
        logpen.lopnWidth.x = 1;
        hRedPen = CreatePenIndirect(&logpen);

        logpen.lopnColor = RGB(0, 0, 255);
        logpen.lopnStyle = PS_INSIDEFRAME;
        logpen.lopnWidth.x = 3;
        hBluePen = CreatePenIndirect(&logpen);

        return 0;

    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);

        return 0;

    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        SelectObject(hdc, hRedPen);
        MoveToEx(hdc, 0, cyClient / 2, NULL);
        LineTo(hdc, cxClient, cyClient / 2);

        SelectObject(hdc, hBluePen);
        for (i = 0; i < NUMS; i++) {
            apt[i].x = i * cxClient / NUMS;
            apt[i].y = (int)(( 1 - sin(TWOPI * i / NUMS)) * cyClient / 2);

        SetTextAlign(hdc, TA_TOP | TA_RIGHT);
        TextOut(hdc, cxClient - 12, 12, TEXT("y  =  sin  x"), 12);
        TextOut(hdc, cxClient / 2 - 12, cyClient / 2 + 12, TEXT("( π,  0 )"), 9);
        TextOut(hdc, cxClient - 12, cyClient / 2 + 12, TEXT("( 2 π,  0 )"), 11);

        SetTextAlign(hdc, TA_TOP | TA_LEFT);
        TextOut(hdc, 12, cyClient / 2 + 12, TEXT("( 0,  0 )"), 9);

        PolyBezier(hdc, apt, NUMS);

        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        return 0;

    return DefWindowProc(hwnd, message, wParam, lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    LPCTSTR lpszClassName = TEXT("PenDemo");
    LPCTSTR lpszWindowName = TEXT("Pen Demo Program");

    WNDCLASS wndclass;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hInstance = hInstance;
    wndclass.lpfnWndProc = WindowProc;
    wndclass.lpszClassName = lpszClassName;
    wndclass.lpszMenuName = NULL; = CS_HREDRAW | CS_VREDRAW;

    if (!RegisterClass(&wndclass)) {
        MessageBox(NULL, TEXT("This program requires Windows NT!"), lpszWindowName, MB_ICONERROR);
        return 0;

    HWND hwnd = CreateWindow(

    ShowWindow(hwnd, nCmdShow);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {

    return msg.wParam;
Link of this Article: GDI brushes (9)

Leave a Reply

Your email address will not be published. Required fields are marked *