Home

Brushes

 

Introduction

A brush is a drawing tool used to fill out closed shapes or the interior of lines. Using a brush is like picking up a bucket of paint and pouring it somewhere. In the case of computer graphics, the area where you position the brush is called the brush origin. The color or pattern that the brush holds would be used to fill the whole area until the brush finds a limit set by some rule.

A brush can be characterized by its color (if used), its pattern used to fill the area, or a picture (bitmap) used as the brush.

Because there can be so many variations of brushes, there are different functions for the various possible types of brushes you would need. The easiest brush you can create is made of a color.

Solid Brushes

A brush is referred to as solid if it is made of a color simply used to fill a closed shape. To create a solid brush, call the CreateSolidBrush() function. Its syntax is:

HBRUSH CreateSolidBrush(COLORREF crColor);

The color to provide as the crColor argument follows the rules we reviewed for colors.

To use the newly created brush, you can select it into the device context by calling the SelectObject() function. Once this is done. Any closed shape you draw (ellipse, rectangle, polygon) would be filled with the color specified. After using the brush, you can delete it and restore the previous brush. Here is an example:

#include <windows.h>

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX  WndCls;
    static char szAppName[] = "ExoBrush";
    MSG         Msg;

    WndCls.cbSize        = sizeof(WndCls);
    WndCls.style         = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
    WndCls.lpfnWndProc   = WindProcedure;
    WndCls.cbClsExtra    = 0;
    WndCls.cbWndExtra    = 0;
    WndCls.hInstance     = hInstance;
    WndCls.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    WndCls.hCursor       = LoadCursor(NULL, IDC_ARROW);
    WndCls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    WndCls.lpszMenuName  = NULL;
    WndCls.lpszClassName = szAppName;
    WndCls.hIconSm       = LoadIcon(hInstance, IDI_APPLICATION);
    RegisterClassEx(&WndCls);

    CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                   szAppName, "GDI Brushes Fundamentals",
                   WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                   CW_USEDEFAULT, CW_USEDEFAULT, 420, 220,
                   NULL, NULL, hInstance, NULL);

    while( GetMessage(&Msg, NULL, 0, 0) )
    {
        TranslateMessage(&Msg);
        DispatchMessage( &Msg);
    }

    return static_cast<int>(Msg.wParam);
}

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC         hDC;
    PAINTSTRUCT Ps;
	HBRUSH      NewBrush;


	switch(Msg)
	{
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);

        NewBrush = CreateSolidBrush(RGB(250, 25, 5));

        SelectObject(hDC, NewBrush);
        Rectangle(hDC, 20, 20, 250, 125);
        DeleteObject(NewBrush);

		EndPaint(hWnd, &Ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

Once a brush has been selected, it would be used on all shapes that are drawn under it, until you delete or change it. Here is an example:

#include <windows.h>

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX  WndCls;
    static char szAppName[] = "ExoBrush";
    MSG         Msg;

    WndCls.cbSize        = sizeof(WndCls);
    WndCls.style         = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
    WndCls.lpfnWndProc   = WindProcedure;
    WndCls.cbClsExtra    = 0;
    WndCls.cbWndExtra    = 0;
    WndCls.hInstance     = hInstance;
    WndCls.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    WndCls.hCursor       = LoadCursor(NULL, IDC_ARROW);
    WndCls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    WndCls.lpszMenuName  = NULL;
    WndCls.lpszClassName = szAppName;
    WndCls.hIconSm       = LoadIcon(hInstance, IDI_APPLICATION);
    RegisterClassEx(&WndCls);

    CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                   szAppName, "GDI Brushes Fundamentals",
                   WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                   CW_USEDEFAULT, CW_USEDEFAULT, 400, 280,
                   NULL, NULL, hInstance, NULL);

    while( GetMessage(&Msg, NULL, 0, 0) )
    {
        TranslateMessage(&Msg);
        DispatchMessage( &Msg);
    }

    return static_cast<int>(Msg.wParam);
}

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC         hDC;
    PAINTSTRUCT Ps;
	HBRUSH      NewBrush;
    POINT       Pt[3];



	switch(Msg)
	{
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);

		NewBrush = CreateSolidBrush(RGB(255, 2, 5));
        SelectObject(hDC, NewBrush);

        // Top Triangle
        Pt[0].x = 125; Pt[0].y = 10;
        Pt[1].x =  95; Pt[1].y = 70;
        Pt[2].x = 155; Pt[2].y = 70;

        Polygon(hDC, Pt, 3);

        // Left Triangle
        Pt[0].x =  80; Pt[0].y =  80;
        Pt[1].x =  20; Pt[1].y = 110;
        Pt[2].x =  80; Pt[2].y = 140;

        Polygon(hDC, Pt, 3);

        // Bottom Triangle
        Pt[0].x =  95; Pt[0].y = 155;
        Pt[1].x = 125; Pt[1].y = 215;
        Pt[2].x = 155; Pt[2].y = 155;

        Polygon(hDC, Pt, 3);

        // Right Triangle
        Pt[0].x = 170; Pt[0].y =  80;
        Pt[1].x = 170; Pt[1].y = 140;
        Pt[2].x = 230; Pt[2].y = 110;

        Polygon(hDC, Pt, 3);

        DeleteObject(NewBrush);

		EndPaint(hWnd, &Ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

If you want to use a different brush, you should create a new one. Here is an example:

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC         hDC;
    PAINTSTRUCT Ps;
	HBRUSH BrushGreen  = CreateSolidBrush(RGB(0, 125, 5));
    HBRUSH BrushRed    = CreateSolidBrush(RGB(255, 2, 5));
    HBRUSH BrushYellow = CreateSolidBrush(RGB(250, 255, 5));
    HBRUSH BrushBlue   = CreateSolidBrush(RGB(0, 2, 255));

    POINT       Pt[3];

	switch(Msg)
	{
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);

        // Top Triangle
        Pt[0].x = 125; Pt[0].y = 10;
        Pt[1].x =  95; Pt[1].y = 70;
        Pt[2].x = 155; Pt[2].y = 70;

        SelectObject(hDC, BrushGreen);
        Polygon(hDC, Pt, 3);

        // Left Triangle
        Pt[0].x =  80; Pt[0].y =  80;
        Pt[1].x =  20; Pt[1].y = 110;
        Pt[2].x =  80; Pt[2].y = 140;

        SelectObject(hDC, BrushRed);
        Polygon(hDC, Pt, 3);

        // Bottom Triangle
        Pt[0].x =  95; Pt[0].y = 155;
        Pt[1].x = 125; Pt[1].y = 215;
        Pt[2].x = 155; Pt[2].y = 155;

        SelectObject(hDC, BrushYellow);
        Polygon(hDC, Pt, 3);

        // Right Triangle
        Pt[0].x = 170; Pt[0].y =  80;
        Pt[1].x = 170; Pt[1].y = 140;
        Pt[2].x = 230; Pt[2].y = 110;

        SelectObject(hDC, BrushBlue);
        Polygon(hDC, Pt, 3);

        DeleteObject(BrushGreen);
        DeleteObject(BrushRed);
        DeleteObject(BrushYellow);
        DeleteObject(BrushBlue);

		EndPaint(hWnd, &Ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}


Hatched Brushes

A hatch brush is one that uses a drawn hatch pattern to regularly fill an area. Microsoft Windows provides 6 preset patterns for such a brush. To create a hatched brush, you can call the CreateHatchBrush() function. Its syntax is:

HBRUSH CreateHatchBrush(int fnStyle, COLORREF clrref);

The fnStyle argument specifies the hatch style that must be used to fill the area. The possible values to use are HS_BDIAGONAL, HS_CROSS, HS_DIAGCROSS, HS_FDIAGONAL, HS_HORIZONTAL, or HS_VERTICAL.

The clrref argument specifies the color applied on the drawn pattern.

Here is an example:

#include <windows.h>

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX  WndCls;
    static char szAppName[] = "ExoBrush";
    MSG         Msg;

    WndCls.cbSize        = sizeof(WndCls);
    WndCls.style         = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
    WndCls.lpfnWndProc   = WindProcedure;
    WndCls.cbClsExtra    = 0;
    WndCls.cbWndExtra    = 0;
    WndCls.hInstance     = hInstance;
    WndCls.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    WndCls.hCursor       = LoadCursor(NULL, IDC_ARROW);
    WndCls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    WndCls.lpszMenuName  = NULL;
    WndCls.lpszClassName = szAppName;
    WndCls.hIconSm       = LoadIcon(hInstance, IDI_APPLICATION);
    RegisterClassEx(&WndCls);

    CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                   szAppName, "Hatch Brushes",
                   WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                   CW_USEDEFAULT, CW_USEDEFAULT, 520, 230,
                   NULL, NULL, hInstance, NULL);

    while( GetMessage(&Msg, NULL, 0, 0) )
    {
        TranslateMessage(&Msg);
        DispatchMessage( &Msg);
    }

    return static_cast<int>(Msg.wParam);
}

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
    HDC         hDC;
    PAINTSTRUCT Ps;
    HBRUSH      brBDiagonal  = CreateHatchBrush(HS_BDIAGONAL,  RGB(0, 0, 255));
    HBRUSH      brCross      = CreateHatchBrush(HS_CROSS, RGB(200, 0, 0));
    HBRUSH      brDiagCross  = CreateHatchBrush(HS_DIAGCROSS,  RGB(0, 128, 0));
    HBRUSH      brFDiagonal  = CreateHatchBrush(HS_FDIAGONAL,  RGB(0, 128, 192));
    HBRUSH      brHorizontal = CreateHatchBrush(HS_HORIZONTAL, RGB(255, 128, 0));
    HBRUSH      brVertical   = CreateHatchBrush(HS_VERTICAL,   RGB(255, 0, 255));

	switch(Msg)
	{
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);

        SelectObject(hDC, brBDiagonal);
        RoundRect(hDC, 20, 30, 160, 80, 10, 10);

        SelectObject(hDC, brFDiagonal);
        RoundRect(hDC, 180, 30, 320, 80, 10, 10);

        SelectObject(hDC, brDiagCross);
        RoundRect(hDC, 340, 30, 480, 80, 10, 10);

        SelectObject(hDC, brVertical);
        RoundRect(hDC, 20, 120, 160, 170, 10, 10);

        SelectObject(hDC, brHorizontal);
        RoundRect(hDC, 180, 120, 320, 170, 10, 10);

        SelectObject(hDC, brCross);
        RoundRect(hDC, 340, 120, 480, 170, 10, 10);

        SetTextColor(hDC, RGB(0, 0, 255));
        TextOut(hDC, 40, 10, "HS_BDIAGONAL", 12);
        SetTextColor(hDC, RGB(0, 128, 192));
        TextOut(hDC, 205, 10, "HS_FDIAGONAL", 12);
        SetTextColor(hDC, RGB(0, 128, 0));
        TextOut(hDC, 355, 10, "HS_DIAGCROSS", 12);
        SetTextColor(hDC, RGB(255, 0, 255));
        TextOut(hDC, 44, 100, "HS_VERTICAL", 11);
        SetTextColor(hDC, RGB(255, 128, 0));
        TextOut(hDC, 195, 100, "HS_HORIZONTAL", 13);
        SetTextColor(hDC, RGB(200, 0, 0));
        TextOut(hDC, 370, 100, "HS_CROSS", 8);

        DeleteObject(brBDiagonal);
        DeleteObject(brCross);
        DeleteObject(brDiagCross);
        DeleteObject(brFDiagonal);
        DeleteObject(brHorizontal);
        DeleteObject(brVertical);

		EndPaint(hWnd, &Ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

Patterned Brushes

A pattern brush is one that uses a bitmap or (small) picture to fill out an area. To create DDB bitmap, you can first create an array of WORD values. Then call the CreateBitmap() function to initialize it. As this makes the bitmap ready, call the CreatePatternBrush() function to initialize the brush. The syntax of this function is:

HBRUSH CreatePatternBrush(HBITMAP hbmp);

Once the brush has been defined, you can select it into a device context and use it as you see fit. For example, you can use it to fill a shape. Here is an example:

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC         hDC;
    PAINTSTRUCT Ps;
	HBITMAP     BmpBrush;
	HBRUSH		brBits;
	WORD		wBits[] = { 0x00, 0x22, 0x44, 0x88, 0x00, 0x22, 0x44, 0x88,
                            0x22, 0x44, 0x88, 0x00, 0x22, 0x44, 0x88, 0x00,
                            0x44, 0x88, 0x00, 0x22, 0x44, 0x88, 0x00, 0x22,
                            0x88, 0x00, 0x22, 0x44, 0x88, 0x00, 0x22, 0x44 };
	 
	switch(Msg)
	{
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);

        BmpBrush  = CreateBitmap(32, 32, 1, 1, wBits);
        brBits    = CreatePatternBrush(BmpBrush);

        SelectObject(hDC, brBits);

        Rectangle(hDC, 20, 20, 280, 180);

        DeleteObject(BmpBrush);

		EndPaint(hWnd, &Ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

Another technique you can use to create a pattern brush consists of using a bitmap resource. Before creating a pattern, you must first have a picture, which can be done by creating a bitmap. For example, imagine you create the following bitmap identified as IDB_PATTERN:

To create a brush based on a bitmap, you can first load the bitmap either using LoadBitmap() or CreateBitmap(). Once the bitmap is ready, call the CreatePatternBrush() function to initialize it. This will allow you to get an HBRUSH that you can then select into the device context and use it as you see fit.

Here is an example:

#include <windows.h>
#include "resource.h"

HINSTANCE hInst;
LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX  WndCls;
    static char szAppName[] = "ExoBrush";
    MSG         Msg;

	hInst      = hInstance;
    WndCls.cbSize        = sizeof(WndCls);
    WndCls.style         = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
    WndCls.lpfnWndProc   = WindProcedure;
    WndCls.cbClsExtra    = 0;
    WndCls.cbWndExtra    = 0;
    WndCls.hInstance     = hInst;
    WndCls.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    WndCls.hCursor       = LoadCursor(NULL, IDC_ARROW);
    WndCls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    WndCls.lpszMenuName  = NULL;
    WndCls.lpszClassName = szAppName;
    WndCls.hIconSm       = LoadIcon(hInstance, IDI_APPLICATION);
    RegisterClassEx(&WndCls);

    CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                   szAppName, "Pattern Brushes",
                   WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                   CW_USEDEFAULT, CW_USEDEFAULT, 500, 240,
                   NULL, NULL, hInstance, NULL);

    while( GetMessage(&Msg, NULL, 0, 0) )
    {
        TranslateMessage(&Msg);
        DispatchMessage( &Msg);
    }

    return static_cast<int>(Msg.wParam);
}

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC         hDC;
    PAINTSTRUCT Ps;
	HBITMAP     BmpBrush;
	HBRUSH		brPattern;
	HPEN        hPen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
	 
	switch(Msg)
	{
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);

        BmpBrush  = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_PATTERN));
        brPattern = CreatePatternBrush(BmpBrush);

		SelectObject(hDC, hPen);
        SelectObject(hDC, brPattern);

        Rectangle(hDC, 5, 3, 380, 280);

		DeleteObject(hPen);
        DeleteObject(BmpBrush);

		EndPaint(hWnd, &Ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

You can use this same approach to paint an area with a more sophisticated picture.

Logical Brushes

The Win32 library provides the LOGBRUSH structure that can be used to create a brush by specifying its characteristics. LOGBRUSH is defined as follows:

typedef struct tagLOGBRUSH { 
	UINT lbStyle; 
	COLORREF lbColor; 
	LONG lbHatch; 
} LOGBRUSH, *PLOGBRUSH;

The lbStyle member variable specifies the style applied on the brush.

The lbColor is specified as a COLORREF value.

The lbHatch value represents the hatch pattern used on the brush.

To use this structure, declare and initialize a LOGBRUSH variable. Once the variable is ready, you can pass it to the CreateBrushIndirect() function. Its syntax is:

HBRUSH CreateBrushIndirect(CONST LOGBRUSH *lplb);

The CreateBrushIndirect() function returns an HBRUSH value that you can select into the device context and use it as you see fit. Here is an example:

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC         hDC;
         PAINTSTRUCT Ps;
	HBRUSH      brLogBrush;
         LOGBRUSH    LogBrush;

	switch(Msg)
	{
	case WM_PAINT:
	    hDC = BeginPaint(hWnd, &Ps);

             LogBrush.lbStyle = BS_HATCHED;
             LogBrush.lbColor = RGB(255, 0, 255);
             LogBrush.lbHatch = HS_DIAGCROSS;

             brLogBrush = CreateBrushIndirect(&LogBrush);
             SelectObject(hDC, brLogBrush);

             Rectangle(hDC, 20, 12, 250, 175);
 
             DeleteObject(brLogBrush);
	    EndPaint(hWnd, &Ps);
	    break;
	case WM_DESTROY:
	    PostQuitMessage(WM_QUIT);
	    break;
	default:
	    return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    return 0;
}

 

 

Previous Copyright © 2003 FunctionX, Inc. Next