The Graphics Device Interface (GDI) is the Windows graphics subsystem used to draw graphics and formatted text on display devices such as monitors and printers. One of its primary objectives is to provide a device-independent programming environment, allowing applications to produce the same output on different types of devices without requiring device-specific code.
GDI provides several hundred functions for drawing points, lines, rectangles, polygons, ellipses, bitmaps, and text. It also provides graphics objects, such as pens, brushes, and fonts, which control the appearance of the output. A pen defines the colour, width, and style of lines and outlines, while a brush determines how enclosed shapes are filled.
A device context (DC) always has a pen, brush, font, and other graphics attributes selected into it. When an application needs to change one of these attributes, it must first create the required GDI object and then select it into the device context. Any drawing performed after the new object has been selected uses the new attributes. Previously drawn graphics are unaffected.
Each GDI object created by an application consumes Windows system resources. To avoid resource leaks, applications should delete GDI objects using DeleteObject() when they are no longer required. Before deleting a GDI object, the original object should first be reselected into the device context, as an object that is currently selected into a device context must not be deleted.
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 the RGB (Red, Green, Blue) additive colour model to represent colours. A computer display forms an image from millions of individual pixels, each of which is created by combining varying intensities of the three primary colours: red, green, and blue. The term additive colour refers to the way these coloured light sources are combined to produce different colours. When all three components are at their maximum intensity, the result is white; when all are zero, the result is black.
Each RGB component is represented by an 8-bit value ranging from 0 to 255. This provides 256 possible intensity levels for each colour component, resulting in a total of 16,777,216 possible colours (256 × 256 × 256), often referred to as 24-bit colour or True Color.
The Windows API represents an RGB colour using the COLORREF data type. A COLORREF is a 32-bit value in which the lower three bytes store the red, green, and blue intensity values, while the highest-order byte is reserved and normally set to zero.
The GDI provides the RGB() macro to combine separate red, green, and blue values into a COLORREF, together with the GetRValue(), GetGValue(), and GetBValue() macros to extract the individual colour components.
//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);