In Windows, the Graphics Device Interface (GDI) displays graphics and formatted text on both the screen and the printer. One of the primary goals of GDI is to support a device-independent development environment. GDI provides several hundred functions for drawing points, lines, rectangles, polygons, ellipses, bitmaps, and text, as well as special objects such as pens and brushes used to control the appearance of graphical output. Pens define the style, thickness, and colour of the pixels drawn, while a brush determines the fill colour of a shape.
A device context always contains one pen, one brush, one font, and a series of values that control how the device context behaves. If an application requires a change to the device context, such as using a new pen, the new pen must first be created and then selected into the device context. Selecting a new graphics object does not alter any graphics that have already been drawn; it only affects subsequent drawing operations. Each time a new GDI object is created, it consumes Windows resources. It is therefore important to release GDI objects after they are no longer needed.
Creating Pens
Pens are created and referred to by using the handle type HPEN. In addition to a limited number of pre-supplied stock pens, the programmer can define user-defined pens using the API function CreatePen(). The prototype of this function is:
HPEN CreatePen(int iStyle,int cWidth,COLORREF color);
where –
iStyle – The pen style. It can be any one of the following values.
PS_SOLID – The pen is solid.
PS_DASH – The pen is dashed. This style is valid only when the pen width is one or less in device units.
PS_DOT – The pen is dotted. This style is valid only when the pen width is one or less in device units.
PS_DASHDOT – The pen has alternating dashes and dots. This style is valid only when the pen width is one or less in device units.
PS_DASHDOTDOT – The pen has alternating dashes and double dots. This style is valid only when the pen width is one or less in device units.
PS_NULL – The pen is invisible.
PS_INSIDEFRAME – The pen is solid. When this pen is used in any GDI drawing function that takes a bounding rectangle, the figure dimensions are shrunk so that it fits entirely in the bounding rectangle, taking into account the width of the pen. This applies only to geometric pens
cWidth – The width of the pen, in logical units. If nWidth is zero, the pen is a single pixel wide, regardless of the current transformation.
color – is a COLORREF that determines the pen colour.
If the function succeeds, the return value identifies a logical pen. If the function fails, the return value is NULL.
Creating a Brush
Brushes are used to fill closed graphical objects. A brush has a colour and style, and it can also be defined using a bitmap pattern. Brushes are created and referred to by using the handle type HBRUSH. In addition to the pre-created stock brushes, programmers can define custom brushes using the API function CreateSolidBrush(). The prototype for this function is:
HBRUSH CreateSolidBrush( COLORREF color);
Where color is a COLORREF value. If the function succeeds, the return value identifies a logical brush. If the function fails, the return value is NULL.
In addition to solid brushes, a programmer can create a pattern brush that fills the brush area with a bitmapped image and a hatchbrush that creates a specified hatch pattern and colour. The prototype for these two API functions are
HBRUSH CreatePatternBrush(NBITMAP hbmap);
Where hbmap is a handle to the bitmap used to create the logical brush. If the function succeeds, the return value identifies a logical brush. If the function fails, the return value is NULL.
HBRUSH CreateHatchBrush(int style,COLORREF color);
Where
style – is the hatch style of the brush and can be one of the following
HS_BDIAGONAL – 45-degree upward left-to-right hatch
HS_CROSS – Horizontal and vertical crosshatch
HS_DIAGCROSS – 45-degree crosshatch
HS_FDIAGONAL – 45-degree downward left-to-right hatch
HS_HORIZONTAL – Horizontal hatch
HS_VERTICAL – Vertical hatch
Color – is a COLORREF value.
If the function succeeds, the return value identifies a logical brush. If the function fails, the return value is NULL.
Selecting Objects
Before any graphics object can be used, it must be selected into the current device context (DC). The new object then replaces the previous graphics object of the same type. The SelectObject() API function prototype is:
HGDIOBJ SelectObject(HDC hdc,HGDIOBJ h);
where hdc refers to the device context and h is a handle to the object to be selected.
SelectObject() returns a handle to the previous object of the same type. This may be useful if the application needs to restore the previous selection later.
The following short code segment creates a new brush and then selects it into the current device context:
HBRUSH greenbrush;
Greenbrush=CreateSolidBrush(RGB(0,255,0));
SelectObject(hdc, Greenbush)
For further reading
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-selectobject
DeleteObject
The DeleteObject() function deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object and rendering the specified handle invalid. This is necessary because the system has only a finite amount of resources, and failure to release allocated objects reduces the amount of memory available to the system.
BOOL DeleteObject(HGDIOBJ hobject);
Where hobject is a handle to a logical pen, brush, font, bitmap, region, or palette. If the function succeeds, the return value is nonzero. If the specified handle is invalid the return value is zero.
Important Rule
A GDI object must not be deleted while it is still selected into a device context. The original object should first be restored using SelectObject(), and then the created object can be deleted.
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(0,0,255));
HPEN oldPen = (HPEN)SelectObject(hdc, hPen);
/* drawing operations */
SelectObject(hdc, oldPen);
DeleteObject(hPen);
Using Stock Objects
When a window creates its first display device context, it comes with a limited number of pre-created graphics objects known as stock objects. These stock objects include pens, brushes, fonts, and palettes. The API function GetStockObject() retrieves a handle to one of these stock objects. The prototype of this function is:
HGDIOBJ GetStockObject(int i);
Where the parameter i can be one of the following values: BLACK_BRUSH, DKGRAY_BRUSH ,DC_BRUSH ,GRAY_BRUSH ,HOLLOW_BRUSH ,LTGRAY_BRUSH ,NULL_BRUSH ,WHITE_BRUSH ,BLACK_PEN ,DC_PEN ,NULL_PEN ,WHITE_PEN, ANSI_FIXED_FONT ,ANSI_VAR_FONT ,DEVICE_DEFAULT_FONT ,DEFAULT_GUI_FONT ,OEM_FIXED_FONT ,SYSTEM_FONT ,SYSTEM_FIXED_FONT , DEFAULT_PALETTE
If the function succeeds, the return value is a handle to the requested logical object. If the function fails, the return value is NULL
Since stock objects are pre-created system resources there is no need to delete the object handle once they are no longer required.
Dealing with Colour Values
The Windows graphics system uses RGB additive colour mixing to display colours. A computer screen combines individual pixels to form an image, and each pixel consists of varying amounts of the primary colours red, green, and blue. The term additive colour mixing comes from the fact that every RGB colour is produced by combining different intensities of these three basic colours.
Since the Windows graphics system uses the 8-bit (0–255) RGB colour model, this results in 16,777,216 possible colours (256 × 256 × 256).
The Windows API uses the COLORREF type to represent an RGB value as a 32-bit value. The GDI provides several macros to combine RGB values into a COLORREF value and to extract the individual RGB components from a COLORREF.
//converts rgb to colourref value
COLORREF RGB(BYTE byRed, BYTE byGreen, BYTE byBlue);
//converts colourref value to RGB equivalent
int iRed = GetRValue(COLORREF rgb);
int iGreen = GetGValue(COLORREF rgb);
int iBlue = GetBValue(COLORREF rgb);