Home

GDI Colors

Overview

The color is one the most fundamental objects that enhances the aesthetic appearance of an object. The color is a non-spatial object that is added to an object to modify some of its visual aspects. The MFC library, combined with the Win32 API, provides various actions you can use to take advantage of the various aspects of colors.

Three numeric values are used to create a color. Each one of these values is 8 bits. The first number is called red. The second is called green. The third is called blue:

Bits
Red 
7 6 5 4 3 2 1 0
Green 
7 6 5 4 3 2 1 0
Blue 
7 6 5 4 3 2 1 0

Converted to decimal, each one of these numbers would produce:

27 + 26 + 25 + 24 + 23 + 22 + 21 + 20 

= 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1

= 255

Therefore, each number can have a value that ranges from 0 to 255 in the decimal system.
These three numbers are combined to produce a single number as follows:

Color 
23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Blue Green Red
Value

Converted to decimal, this number has a value of 255 * 255 * 255 = 16581375. This means that we can have approximately 16 million colors available. The question that comes to mind is how we use these colors, to produce what effect.

You computer monitor has a surface that resembles a series of tinny horizontal and vertical lines. The intersection of a one horizontal line and a vertical line is called a pixel. This pixel holds, carries, or displays one color.

As the pixels close to each other have different colors, the effect is a wonderful distortion that creates an aesthetic picture. It is by changing the colors of pixels that you produce the effect of color variances seen on pictures and other graphics.

The Color as a Data Type

Microsoft Windows considers that a color is a 32-bit numeric value. Therefore, a color is actually a combination of 32 bits:

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Reserved Red Green Blue

The bits of the most significant byte (the left byte) are reserved for the operating system's internal use and must be set to 0. Based on this, each color is characterized by its combination of a red, a green, and a blue values.

When all three numbers have their lowest value, which is 0, the color is called black. When the numbers are at their highest value, which is 255, the color is called white. Not all color combinations have a name. In fact, in MS Windows programming, the names are very limited (but this has nothing to do with the operating system). For this reason, colors are rarely called by a name and usually, a name would depend on who is using it. Nevertheless, there are popular names that most people recognize. Examples are Black, White, Red, Green, Blue, Yellow. Except for black and white, each color can assume different variations. For example, when all three numbers have the same value but neither 0 nor 255, the color is called Gray and there are more than 250 possible combinations. Sometimes the combination is called Silver (each value is 192) or Gray (values=128).

The 32-bit numeric value used to characterize a color is defined by the COLORREF data type in Microsoft Windows programming. It can be used to declare a color value. Here is an example:

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

	switch(Msg)
	{
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

When, or after, declaring such a variable, you can initialize it with a 32-bit decimal value. Here is an example:

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
			       WPARAM wParam, LPARAM lParam)
{
	COLORREF NewColor = 16711935;

	switch(Msg)
	{
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

Although the above number (16711935) is a legitimate color value, it does not mean much. To create a color value, the Win32 API provides the RGB macro. Its syntax is:

COLORREF RGB(BYTE byRed, BYTE byGreen, BYTE byBlue);

The RGB macro behaves like a function and allows you to pass three numeric values separated by a comma. Each value must be between 0 and 255. Therefore, the above initialization can be done as follows:

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
			       WPARAM wParam, LPARAM lParam)
{
	COLORREF NewColor = RGB(255, 0, 255);

	switch(Msg)
	{
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

Whether a color was initialized with a 32-bit integer or using the RGB macro, if you want to retrieve the red, green, and blue values of a color, you can use the GetRValue(), the GetGValue(), of the GetBValue() macros to extract the value of each. The syntaxes of these macros are:

BYTE GetRValue(DWORD rgb);
BYTE GetGValue(DWORD rgb);
BYTE GetBValue(DWORD rgb);

Each macro takes a 32-bit value as argument, rgb:

  • The GetRValue() macro returns the red value of the rgb number

  • The GetGValue() macro returns the green value of the rgb number

  • The GetBValue() macro returns the blue value of the rgb number.

Color Palettes

Device independence is the ability for an application to draw its intended figures, text, shapes, and display colors regardless of the device on which the drawing is performed. One way to take care of this is to manage colors at the operating system level so that Microsoft Windows can select the right color to render an object or portion of it. In some cases, a device, such as a monitor or a printer, may need to take care of the coloring details of the jobs it is asked to perform.

A color palette is a list of colors that a device can display. Based on this, one device may be able to handle only two colors; such is the case for a black and white printer. Another device could be able to use more colors than that. To control this situation, Microsoft Windows keeps track of the color palette of each device installed on the computer.

There are two types of color palettes. The default color palette is a list of colors that the operating system would use on a device unless notified otherwise. There are typically 20 reserved colors as default. A logical palette is a palette that an application creates for a specific device context.

Coloring a Pixel

As mentioned above, a pixel is the real object that holds a color. Although it is so small, you can access a pixel and change its color. The pixels are stored in an array of [x][y] values. In fact, when you try accessing a pixel, you would be asked to provide a color for it. To change the color of a pixel, you can call the SetPixel() function. Its syntax is:

COLORREF SetPixel(HDC hdc, int X, int Y, COLORREF crColor);

The pixel you want to access is defined by its X and Y coordinates. The color you want to specify is the crColor argument.

Drawing Text

By default, the device context is able to draw text using a font pre-selected, known as the System Font. To draw text, you can use the TextOut() method. Its syntax is:

BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString);

To use this funuction, you must specify where the text would start. This location is determined from the (0, 0) origin to the nXStart and to the nYStart values. The text to display is the lpString string. The cbString value is the length of the text. 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[] = "GDISuite";
    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 Accessories and Tools",
                          WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          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;

	switch(Msg)
	{
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);
		TextOut(hDC, 50, 42, "Johnny Carson", 13);
		EndPaint(hWnd, &Ps);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}
Result of the TextOut() function


If you want to control the color used to draw the text, use the SetTextColor() method whose syntax is:

COLORREF SetTextColor(HDC hdc, COLORREF crColor);

The crColor argument can be provided as a COLORREF variable or by calling the RGB macro. is an example:

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC hDC;
         PAINTSTRUCT Ps;
	COLORREF clrRedish = RGB(255, 25, 2);
	
	switch(Msg)
	{
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);
		SetTextColor(hDC, clrRedish);
		TextOut(hDC, 50, 42, "Johnny Carson", 13);
		EndPaint(hWnd, &Ps);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}
Using the SetTextColor() function to specify the color of text

As you will see from now on concerning the device context, once you change one of its characteristics, it stays there until you change it again. It is similar to picking a spoon and start eating. As long as you are eating, you can use the spoon you are holding and only that spoon. It you want to cut the meat, you must replace the spoon in your hand with a knife. In the same way, if you change the color of text and draw more than one line of text, all of them would use the same color. If you want to use a different color, you must change the color used by calling the SetTextColor() method again. Here is an example:

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC hDC;
         PAINTSTRUCT Ps;
	COLORREF clrRed  = RGB(255, 25, 5);
	COLORREF clrBlue = RGB(12, 25, 255);

	switch(Msg)
	{
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);
		SetTextColor(hDC, clrRed);
		TextOut(hDC, 50, 42, "Johnny Carson", 13);
		SetTextColor(hDC, clrBlue);
		TextOut(hDC, 50, 80, "The once king of late-night", 27);
		EndPaint(hWnd, &Ps);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

If you want to highlight the text, which is equivalent to changing its background, you can call the SetBkColor() function. Its syntax is:

COLORREF SetBkColor(HDC hdc, COLORREF crColor);

You must provide the color you want to use as the crColor argument. If this function succeeds, it changes the background of the next text that would be drawn and it returns the previous background color, which you can restore at a later time. Here is an example:

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC hDC;
         PAINTSTRUCT Ps;
	COLORREF clrRed  = RGB(255, 25, 5);
	COLORREF clrAqua = RGB(0, 255, 255);
	COLORREF clrNavy = RGB(0, 0, 128);

	switch(Msg)
	{
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);
		SetBkColor(hDC, RGB(255, 255, 255));
		SetTextColor(hDC, clrRed);
		TextOut(hDC, 50, 42, "Johnny Carson", 13);
		SetBkColor(hDC, clrNavy);
		SetTextColor(hDC, clrAqua);
		TextOut(hDC, 50, 80, "The once king of late-night", 27);
		EndPaint(hWnd, &Ps);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}

If you want to know the background color applied on a drawn string, you can call the GetBkColor() function. Its syntax is:

COLORREF GetBkColor(HDC hdc);

This function returns the color used to highlight the text, if the text is highlighted.

The highlighting of text is actually controlled by the SetBkMode() function whose syntax is:

int SetBkMode(HDC hdc, int iBkMode);

This function specifies whether the background color should be applied or not. This is set by the iBkMode argument. It can have one of two values. If it is:

  • OPAQUE: the background would be drawn using the crColor value of the SetBkColor() function

  • TRANSPARENT: the background would not be drawn

If you want to find out what background mode is applied to the string(s) drawn, you can call the GetBkMode() method. It is declared as follows:

int GetBkMode(HDC hdc);

You can also draw text and include it in a (colored) rectangle. This can be done using the ExtTextOut() function. Its syntax is:

BOOL ExtTextOut(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, 
                LPCTSTR lpString, UINT cbCount, CONST INT *lpDx);

The X and Y values specify the location of the first character of the text to be drawn.

The fuOptions argument holds a constant that determines how the rectangle will be drawn. It can be:

  • ETO_OPAQUE: in this case the color set by SetBkColor() would be used to fill the rectangle

  • ETO_CLIPPED: the color previously specified by SetBkColor() will only highlight the text

The lprc is a RECT or CRect rectangle that will be drawn behind the text.

The lpString value is the text to be drawn.

The cbCount is the number of characters of lpString.

The lpDx argument is an array of integers that specifies the amount of empty spaces that will be used between each combination of two characters. Unless you know what you are doing, pass this argument as 0, in which case the regular space used to separate characters would be used.

Here is an example:

LRESULT CALLBACK WindProcedure(HWND hWnd, UINT Msg,
                               WPARAM wParam, LPARAM lParam)
{
	HDC         hDC;
         PAINTSTRUCT Ps;
	COLORREF    clrBlue = RGB(25, 55, 200);
	RECT        Recto = { 20, 28, 188, 128 };
	COLORREF    clrAqua = RGB(128, 255, 255);

	switch(Msg)
	{
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &Ps);
		SetTextColor(hDC, clrBlue);
		SetBkColor(hDC, clrAqua);
		ExtTextOut(hDC, 50, 42, ETO_OPAQUE,
			  &Recto, "Johnny Carson", 13, NULL);
		EndPaint(hWnd, &Ps);
		break;
	default:
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}
	return 0;
}
Using the ExtTextOut() function

 

 

Previous Copyright © 2003 FunctionX, Inc. Next