# 13 Graphics Device Interfaces Graphics Device Interfaces (GDI) is an important part of a GUI system. Through GDI, the GUI application can execute graphics output on the screen or other display devices, including basic painting and text output. In this chapter and the two sequent chapters, we will describe in detail the important concepts of GDI, the methods of graphics programming and the main GDI functions of MiniGUI, and will illustrate the use of important functions with example. ## 13.1 Architecture of MiniGUI Graphics System ### 13.1.1 GAL and GDI In order to separate the bottom layer graphics device and the top layer graphics interface so as to increase the portability of the MiniGUI graphics system, MiniGUI introduces the concept of Graphics Abstract Layer (GAL). GAL defines a group of abstract interfaces, which do not depend on any special hardware, and all the top layer graphics operation are based on these abstract interfaces. The bottom layer code used to realize this abstract interface is called “graphics engine”, similar to the driver in an operating system. Using GAL, MiniGUI can run on may existed graphics function libraries, and can be readily port to other POSIX systems, only requiring to realize the new graphics engine according to our abstract layer interfaces. For example, in a system based on Linux, we can create general MiniGUI graphics engine based on Linux FrameBuffer driver. In fact, the native graphics engine included in MiniGUI 1.0.00 version is the graphics engine based on Linux FrameBuffer. Generally speaking, all the embedded systems based on Linux will provide FrameBuffer support so that the native graphics engine can be run on either a common PC or a special embedded system. ### 13.1.2 New GAL MiniGUI version 1.1.0 makes much improvement to GAL and GDI, introducing new GAL and GDI interfaces and functions. In the old GAL and GDI design, GAL can be considered as the graphics driver of GDI, and many graphics operations, for example drawing point, drawing line, filling rectangle, and bitmap operations, etc., are implemented through the corresponding function of GAL. The biggest problem of this design is GDI cannot be extended. For example, in order to add the ellipse drawing function, it is needed to realize the ellipse painting function in each engine. Moreover, it is the clipping region, which GDI manages, while GAL engine is based on clipping rectangle. This method also causes that GDI function cannot optimize the painting. Therefore, in the interface design of new GAL and GDI, we make restriction to GAL interface, and make many graphics input functions which are previous completed by GAL engine to be completed in top layer GDI functions. The function partition of New GAL (NEWGAL) and new GDI (NEWGDI) are as follow: - NEWGAL is responsible for initializing the video device, and managing the use of video memory; - NEWGAL is responsible for providing top layer GDI with linear video memory which is mapped into process address space, and other information such as palette; - NEWGAL is responsible for realizing fast bit block operation, including rectangle filling and blitting operation, etc., and using hardware acceleration function in possible cases; - NEWGDI function realizes advanced graphics function, including point, line, circle, ellipse, arc, spine curve, and further advanced logical pen and logical brush, and implements acceleration function by calling NEWGAL interface when it is necessary;; - Although some video devices also provide hardware support for the advanced graphics functions mentioned above, however, considering other factors, these hardware acceleration functions are not provided by NEWGAL interface, but are all realized by software.。 Thus, the main painting function realized by NEWGAL is limited to bit block operation, for example, rectangle filling and bit blitting operation; and other advanced graphics functions are all realized by NEWGDI functions. The interface of NEWGAL can effectively use video memory in video card, and sufficiently use the hardware acceleration function. As we know, current video cards commonly have more than 4MB video memory, and not all the video memory will be used in a common display mode. Therefore, NEWGAL engine can manage this unused video memory, and allocate it to the application. Thus, it is realized to save the use of system memory on one hand, and sufficiently use the acceleration function provided by video card so that it can perform fast bit block operation between different video memory areas, i.e. blitting, on the other hand. When top layer NEWGDI interface is creating a memory DC device, it will allocate memory from video memory, and will consider to use the system memory when if it is not successful. Thus, if NEWGAL engine provides hardware acceleration function, blitting operation (i.e., GDI function BitBlt) will be run in the fastest speed between two different DC devices. Further, if the hardware supports transparent or alpha blending function, the transparent or alpha blending blitting operation will also be run in the fastest speed. NEWGAL interface can automatically use these hardware acceleration functions according to the acceleration ability of the bottom layer engine. The hardware acceleration abilities currently supported mainly include: rectangle filling, normal blitting operation, transparent and alpha blending blitting operation, etc. Certainly, if the hardware does not support these acceleration functions, NEWGAL interface can also realize these functions by software. Currently, the video cards which provide above hardware acceleration function through NEWGAL and FrameBuffer include: Matrox, and 3DFX, etc. GDI interface based on NEWGAL are partially compatible with old GDI, but we provide some advanced functions based on NEWGAL. We will describe advanced GDI interfaces based on NEWGAL in Chapter 15. ## 13.2 Painting and Updating of a Window ### 13.2.1 When to Paint a Window? The application uses the window as the main output device, i.e., the MiniGUI application paints only within its window. MiniGUI manages the display output on the entire screen. If the window content should be repaint due to actions such as window movement, MiniGUI puts a flag to the area in the window to be updated, and then sends a MSG_PAINT message to the corresponding window. The application must perform necessary painting to update the window when receiving this message. If the window content changed is caused by the application itself, the application can make a flag to the window area to be updated, and generate a MSG_PAINT message. If it is needed to paint within a window, the application needs to get the device context handle of this window first. Most painting operations of the application are executed during handling MSG_PAINT. At this time, the application gets the device context handle by calling BeginPaint function. If a certain operation of the application is required to respond immediately, for example to handle the keyboard and mouse messages, it can execute painting immediately without waiting MSG_PAINT message. The application can get the device context handle by calling GetDC or GetClientDC when painting at other time. ### 13.2.2 MSG_PAINT Message Usually, the application executes the window painting when receiving MSG_PAINT message. If the change of the window influences the content in the client area, or the invalid region of the window is not NULL, MiniGUI will send a MSG_PAINT message to the corresponding window procedure. When receiving MSG_PAINT message, the application should call BeginPaint function to get the device context handle, and use it to call GDI functions to execute painting which is necessary for updating the client area. After finishing painting, the application should call EndPaint function to release the device context handle. BeginPaint function is used to complete the preparing work before painting the window. It first gets the device context of the window client area by calling GetClientDC function, and sets the clipping region of the device context to be the current invalid region of the window. Only those regions, which have, be changed need to be repainted, and any attempt to painting outside the clipping region will be clipped and will not be shown on the screen. In order not to influence the painting operation, BeginPaint function hides the caret. Finally, BeginPaint clears the invalid region of the window to prevent generating continually MSG_PAINT message, and then returns the gotten device context handle. lParam parameter of MSG_PAINT message is the pointer to the window invalid region, and the application can use the information of the window invalid region to optimize painting, for example, limiting painting within the window invalid region. If the application output is simple, you can paint in the whole window and ignoring the invalid region, and let MiniGUI clips the unnecessary painting outside the clipping region so that only the painting within the invalid region is visible. The application should call EndPaint function to end the whole painting process after finishing painting. The main work of EndPaint function is to call ReleaseDC function to release the device context gotten by GetClientDC function; in addition, it shows the caret hidden by BeginPaint function. ### 13.2.3 Valid and Invalid Region Updating region (invalid region) is referred to the region in the window, which is outdated or invalid and need to be repainted. MiniGUI generates MSG_PAINT message for the application according to the region needed to be updated, and the application can also generates MSG_PAINT message by setting invalid region. The application can use InvalidateRect function to invalidate a certain rectangular region of the window. The prototype of this function is as follows: ```cplusplus BOOL GUIAPI InvalidateRect (HWND hWnd, const RECT* prc, BOOL bEraseBkgnd) ``` The meaning of the arguments is as follows: ```cplusplus hWnd the handle of the window needed to be updated prc pointer to invalid rectangle bEraseBkgnd whether to clear the window background ``` InvalidateRect function adds the specified rectangle to the updating region. This function combines the specified rectangle and the previous updating region of the application window, and then posts a MSG_PAINT message to the message queue of this window. If bEraseBkgnd is TRUE, the application window will receive a MSG_ERASEBKGND message, and the window procedure can handle this message and automatically clear the window background. If the application does not handle MSG_ERASEBKGND message, but passes it to DefaultMainWinProc, the default handling of MSG_ERASEBKGND by MiniGUI is to erase the background with the background color of the window. The window background is referred to the color and style used to fill the client area before painting the window. The window background may cover the previous content in the client area of the window, and make the program output not disturbed by the existed content on the screen. lParam parameter of MSG_ERASEBKGND message includes a RECT structure pointer, indicating the rectangle, which should be erased. The application can use this parameter to paint the window background. After finishing painting, the application can directly return zero without calling DefaultMainWinProc for default message handling. The example related to handling MSG_ERASEBKGND message can be referred to the related sections of Chapter 3 of this guide. ## 13.3 Graphics Device Context ### 13.3.1 Abstraction of Graphics Device The application usually calls the painting primitives provided by the graphics system to paint on a graphics context. The context is an object, which notes the graphics properties used by painting primitives. These properties usually include: - Foreground color (pen), the pixel value or the image used when drawing lines. - Background color or filling bitmap (brush), the pixel value or image used by painting primitives when filling. - Painting mode, which describes how the foreground color and the exited screen color are combined. The usual option is to cover the existed screen content or execute “XOR” bit logical operation with the painting color and the screen color. XOR mode makes the painting object able to be reappeared through repainting. - Filling mode, which describes how the background color or image and the screen color are combined. The usual option is transparent, i.e. ignoring the background and reserving the existed screen content. - Color mask, which is a bitmap, used to determine the style of the influence on the screen pixel by the painting operation. - Pen style, the width, the cap shape, and the joint type when drawing line. - Font, which usually corresponds to a group of bitmaps for a certain character set, and is used by text output functions. Specifying the properties such as size, style, and character set usually chooses font. - Painting region, which is in concept a viewport with arbitrary size and position mapped to the window. Changing its origin can move the viewport. The system sometimes allows the viewport to be scaled. - Clipping region. A painting primitive is valid only when it outputs within the clipping region. The output outside the clipping region will be clipped. The clipping region is mainly used in repainting window, and consists of the invalid regions of the window. The application can adjust the clipping region. - Current position, for example, you can use painting primitives such as MoveTo and LineTo to draw a line. MiniGUI adopts the concept of graphics device context (DC) commonly used in GUI systems such as Windows and X Window. Each graphics device context defines a rectangular displaying output region and its related graphics properties in graphics output device or memory. When calling the graphics output function, an initialized graphics device context needs to be specified. That is to say, all the painting operations must work in a certain graphics device context. From the point view of a program, an initialized graphics device context defines a graphics device environment, determines some basic properties of the graphics operations on it thereafter, and keeps these properties until they are changed. These properties include: the line color, filling color, font color, font shape, and so on. However, from the point view of GUI system, the meanings presented by a graphics device context are more complex, and at least include the following contents: - Information of the device in which the device context is (display mode, color depth, and layout of video memory, etc.); - Information of the window presented by this device context and the clipping region of this window by other windows (called “global clipping region” in MiniGUI); - Basic operation functions of this context (point, line, polygon, filling, block operations, etc.), and its context information; - Local information set by the program (painting property, mapping relationship, and local clipping region, etc.). When you want to paint on a graphics output device (e.g. the monitor screen), you must first get a device context handle and take it as a parameter in GDI function to identify the graphics device context to be used when painting. The device context includes many current properties to determine how GDI function works on the device. These properties make that the parameter transferred to GDI function may only include the starting coordinate or size information and need not include the other information required for displaying an object, since this information is a part of the device context. When you want to change on of these properties, you can call a function which can change the device context property, and GDI function calling for this device context will use the changed property. The device context is actually a data structure managed internally in GDI. The device context is related to the specified displaying device. Some values in the device context are graphics properties. These properties define some special contents of the working status of some GDI painting functions. For example, for TextOut function, the property of the device context determines the text color, background color, the mapping manner of the x-coordinate and y-coordinate to the window client area, and the font used for displaying the text. When the program needs to paint, it must first get a device context handle. The device context handle is a value presenting a device context, and the GDI functions use this handle. ### 13.3.2 Getting and Releasing of Device Context In MiniGUI, all the functions related to painting need a device context. When the program needs to paint, it must first get a device context handle. When the program finishes painting, it must release the device context handle. The program must get and release the handle during handling a single message. That is to say, if the program gets a device context handle when handing a message, it must release this device context handle before it finishes handling this message and quits the window procedure function. One of the commonly used methods for getting and releasing the device context is through BeginPaint and EndPaint functions. The prototypes of these two functions are as follow (minigui/window.h): ```cplusplus HDC GUIAPI BeginPaint(HWND hWnd); void GUIAPI EndPaint(HWND hWnd, HDC hdc); ``` It should be noted that these two functions can only be called when handling MSG_PAINT message. Then handling of MSG_PAINT message has usually the following form: ```cplusplus MSG_PAINT: HDC hdc = BeginPaint (hWnd); /* use GDI functions to piant */ EndPaint (hWnd, hdc); return 0; } ``` BeginPaint takes the window handle hWnd according to the window procedure function as its argument, and returns a device context handle. Then GDI function can use this device context handle for graphics operations. In a typical graphics user interface environment (including MiniGUI), the application is usually paint text and graphics in the client area of the window. However, the graphics system does not ensure the painting content in the client area be kept all the time. If the client area of this program window is overlaid by another window, the graphics system will not reserve the content of the overlaid window region and leave repainting of the window to the application. When needing to recover some contents of the window, the graphics system usually informs the program to update this part of client area. MiniGUI informs the application to perform the painting operation of the window client area by sending MSG_PAINT message to the application. If program consider it is necessary to update the content of client area, it can generate a MSG_PAINT message on its own, so that client area is repainted。 Generally speaking, in the following case, window procedure will receive a MSG_PAINT message: - When the user moves or shows a window, MiniGUI sends MSG_PAINT message to the previously hidden window. - When the program uses InvalidateRect function to update the invalid region of the window, a MSG_PAINT message will be generated; - The program calls UpdateWindow function to redraw the window; - The dialog box or message box over a window is destroyed; - Pull down or popup menu is disappeared. In some cases, MiniGUI saves some overlaid displaying area, and recovers them when necessary, for example the mouse cursor moving. In usual cases, the window procedure function needs only to update a part of the client area. For example, a dialog box overlays only a part of the client area of a window; when the dialog box destroyed, redrawing of the part of the client area previously overlaid by the dialog box is needed. The part of the client area needed to be repainted called “invalid region”. MiniGUI gets the client area device context through GetClientDC in BeginPaint function, and then selects the current invalid region of the window to be the clipping region of the window. While EndPaint function clears the invalid region of the window, and release the device context. Because BeginPaint function selects the invalid region of the window to the device context, you can improve the handling efficiency of MSG_PAINT through some necessary optimizations. For example, if a certain program wants fill some rectangles in the window client area; it can handle as follows in MSG_PAINT function: ```cplusplus MSG_PAINT: { HDC hdc = BeginPaint (hWnd); for (j = 0; j < 10; j ++) { if (RectVisible (hdc, rcs + j)) { FillBox (hdc, rcs[j].left, rcs[j].top, rcs [j].right, rcs [j].bottom); } } EndPaint (hWnd, hdc); return 0; } ``` Thereby unnecessary redrawing operation can be avoided, and the painting efficiency is improved. The device context can be gotten and released through GetClientDC and ReleaseDC function. The device context gotten by GetDC is for the whole window, while the device context gotten GetClientDC is for the client area of the window. That is, for the device context gotten by the former function, its origin is located in upper-left corner of the window, and its output is clipped within the window area. For the device context gotten by the latter function, its origin is located in upper-left corner of the window client area, and its output is limited within the range of the window client area. GetSubDC function can get the son DC of pointed DC, and the son DC includes only a limited area of the pointed DC. Following are the prototypes of these four functions (minigui/gdi.h): ```cplusplus HDC GUIAPI GetDC (HWND hwnd); HDC GUIAPI GetSubDC (HDC hdc, int off_x, int off_y, int width, int height); HDC GUIAPI GetClientDC (HWND hwnd); void GUIAPI ReleaseDC (HDC hdc); ``` GetDC, GetSubDC and GetClientDC get a currently unused device context form some DCs reserved by the system. Therefore, the following two points should be noted: - After finishing using a device context gotten by GetDC, GetSubDC or GetClientDC, you should release it as soon as possible by calling ReleaseDC. - Avoid using multiple device contexts at the same time, and avoid calling GetDC, GetSubDC and GetClientDC in a recursive function. For programming convenience and improving the painting efficiency, MiniGUI also provides functions to set up private device context. The private device context is valid in the whole life cycle of the window, thereby avoiding the getting and releasing process. The prototypes of these functions are as follow: ```cplusplus HDC GUIAPI CreatePrivateDC (HWND hwnd); HDC GUIAPI CreatePrivateSubDC(HDC hdc, int off_x, int off_y, int width, int height); HDC GUIAPI CreatePrivateClientDC (HWND hwnd); HDC GUIAPI GetPrivateClientDC (HWND hwnd); void GUIAPI DeletePrivateDC (HDC hdc); ``` When creating a main window, if WS_EX_USEPRIVATEDC style is specified in the extended style of the main window, CreateMainWindow function will automatically set up a private device context for the window client area. You can get a device context through GetPrivateClientDC function. For a control, if the control class has CS_OWNDC property, all the controls belonging to this control class will automatically set up a private device context. DeletePrivateDC function is used to delete the private device context. For the two cases above, the system will automatically call DeletePrivateDC function when destroy the window. ### 13.3.3 Saving and Restoring of Device Context The device context can be saved and restored through SaveDC and RestoreDC function. The prototypes of these two functions are as follow (minigui/gdi.h): ```cplusplus int GUIAPI SaveDC (HDC hdc); BOOL GUIAPI RestoreDC (HDC hdc, int saved_dc); ``` ### 13.3.4 Device Context in Memory MiniGUI also provides the creating and destroying function of the device context in memory. Using the memory device context, you can set up a region similar to the video memory in the system memory, perform painting operations in this region, and copy to the video memory when finishing painting. There are many advantages using this painting method, e.g. fast speed, reducing the blinking phenomenon caused by direct operation on the video memory, etc. The prototypes of the function used to create and destroy the memory device context are as follow (minigui/gdi.h): ```cplusplus HDC GUIAPI CreateCompatibleDCEx (HDC hdc, int width, int height); #define CreateCompatibleDC(hdc) CreateCompatibleDCEx(hdc, 0, 0) void GUIAPI DeleteMemDC (HDC mem_dc); #define DeleteCompatibleDC(hdc) DeleteMemDC(hdc) ``` In order to realize the special effects like Apple, MiniGUI add a dual buffer function of main window. When creating Main window, if the extending style of main window is pointing WS_EX_AUTOSECONDARYDC style, MiniGUI will call CreateSecondaryDC function to create memory DC Compatible to DC of main window, then set memory DC into main window by SetSecondaryDC function to realize types of UI special effects. When the main window with WS_EX_AUTOSECONDARYDC style is being distroyed, DeleteSecondaryDC function will be called to release memory DC. The prototypes of the functions are as following: ```cplusplus HDC GUIAPI CreateSecondaryDC (HWND hwnd); HDC GUIAPI SetSecondaryDC (HWND hwnd, HDC secondary_dc, ON_UPDATE_SECONDARYDC on_update_secondarydc); HDC GUIAPI GetSecondaryDC (HWND hwnd); HDC GUIAPI GetSecondaryClientDC (HWND hwnd); void GUIAPI ReleaseSecondaryDC (HWND hwnd, HDC hdc); void GUIAPI DeleteSecondaryDC (HWND hwnd); ``` Above functions will be descripted in chapter 11, and not descripted here. ### 13.3.5 Screen Device Context MiniGUI sets up a global screen device context after started up. This DC is for the whole screen, and has no predefined clipping region. In some applications, you can use directly this device context to paint, which may increase the paint efficiency remarkably. In MiniGUI, the screen device context is identified by HDC_SCREEN, and need no getting and releasing operations for this DC. ## 13.4 Mapping Mode and Coordinate Space ### 13.4.1 Mapping Mode Once the Device Context (DC) has been initialized, the origin of the coordinates is usually the upper-left corner of the output rectangle, while the x coordinate axis is horizontal right and the y coordinate axis is vertical downward, with both using pixel as unit. Usually, in MiniGUI, the default unit used to draw graphics is pixel, however, we can choose other ways by changing GDI mapping mode. Mapping mode offers the measurement unit that can be used to convert page space (logical coordinate) into device space (device coordinate). The mapping mode of GDI is a device context property that almost influences the graphics result in any client area. There are four other device context properties that are closely related to the mapping mode: window origin, window scope, viewport origin, and viewport scope. Most GDI functions use coordinate value as arguments, which are called “logical coordinates”. Before drawing something, MiniGUI firstly converts “logical coordinates” into “device coordinates”, that is, pixel. The mapping mode, window and viewport origin, and window and viewport scope control such conversion. In addition, mapping mode also provides the direction of both x and y coordinate axis; in other words, it helps to confirm whether the x value is increasing or decreasing while you move to the left or right of the screen, so is the y value while the screen is moved up and down. At present MiniGUI only supports two types of mapping modes: - MM_TEXT - Each logical unit is mapped as a device pixel. X coordinate increases progressively from left to right, while y coordinates increases progressively from top to bottom. - MM_ANISOTROPIC - Logical unit is mapped as arbitrary device space unit; the proportion of the coordinate scale is also arbitrary. Using SetWindowExt and SetViewPortExt to define unit, direction and scale. The default mapping mode is MM_TEXT. Under this mapping mode, the logical coordinate is equal to the device coordinate. That is, the default unit of drawing graphics is pixel. Changing mapping mode helps us to avoid scaling by ourselves; it is very convenient in some conditions. You can use SetMapMode function to set your mapping mode: ```cplusplus Uint32 GUIAPI SetDCAttr (HDC hdc, int attr, Uint32 value); #define SetMapMode(hdc, mapmode) \ SetDCAttr (hdc, DC_ATTR_MAP_MODE, (DWORD)mapmode) ``` The argument mapmode is one of the two mapping modes above. You can also use GetMapMode function to get current mapping mode: ```cplusplus mapmode = GetMapMode (hdc); ``` ### 13.4.2 Viewport and Window Mapping modes are used to define the mapping from “window” (logical coordinates) to “viewport” (device coordinates). “Window” is a rectangular area in the page coordinate space, while viewport is a rectangular area of the device coordinate space. It is “window” that determines which part of the geometric model of the page space should be displayed, while “viewport” determines where to draw. The scale between them determines the zoom of the coordinates. Viewport is pixel-based (device coordinates), while window is logical-based. The following formulas can be used to convert between page space (window) coordinates and device space (viewport) coordinates: ```cplusplus xViewport = ((xWindow - xWinOrg) * xViewExt / xWinExt) + xViewOrg yViewport = ((yWindow - yWinOrg) * yViewExt / yWinExt) + yViewOrg ``` - xViewport, yViewPort the x value, y value in device unit - xWindow, yWindow the x value, y value in logical unit (page space unit) - xWinOrg, yWinOrg window x origin, window y origin - xViewOrg, yViewOrg viewport x origin, viewport y origin - xWinExt, yWinExt window x extent, window y extent - xViewExt, yViewExt viewport x extent, viewport y extent The transfer principal of above formulas is: the scale of certain distance value in device space and extent value of coordinates should be equal to the scale of page space, in other words, the logical origin (xWinOrg, yWinOrg) is always mapped as device origin (xViewOrg, yViewOrg). These two formulas use the origin and extent of both window and viewport. We can see from this that the scale between the extent of viewport and the extent of window is the conversion factor. MiniGUI provides two functions to realize the conversion between device coordinates and logical coordinates. LPtoDP is used to convert from logical coordinates to device coordinates, while DPtoLP is used to convert from device coordinates to logical coordinates: ```cplusplus void GUIAPI DPtoLP (HDC hdc, POINT* pPt); void GUIAPI LPtoDP (HDC hdc, POINT* pPt); ``` This conversion relies on the mapping mode of device context hdc as well as the origin and extent of the window and the viewport. Those x and y coordinates included in the structure POINT pPt will be converted into other coordinates in another coordinate system. In the MiniGUI’s source codes (src/newgdi/coor.c), the conversion between LPtoDP and DPtoLP are implemented as follow. It can be seen that the coordinate conversion between them is based on the formulas mentioned above. ```cplusplus void GUIAPI LPtoDP(HDC hdc, POINT* pPt) { PDC pdc; pdc = dc_HDC2PDC(hdc); if (pdc->mapmode = MM_TEXT) { pPt->x = (pPt->x - pdc->WindowOrig.x) * pdc->ViewExtent.x / pdc->WindowExtent.x + pdc->ViewOrig.x; pPt->y = (pPt->y - pdc->WindowOrig.y) * pdc->ViewExtent.y / pdc->WindowExtent.y + pdc->ViewOrig.y; } } void GUIAPI DPtoLP (HDC hdc, POINT* pPt) { PDC pdc; pdc = dc_HDC2PDC (hdc); if (pdc->mapmode = MM_TEXT) { pPt->x = (pPt->x - pdc->ViewOrig.x) * pdc->WindowExtent.x / pdc->ViewExtent.x + pdc->WindowOrig.x; pPt->y = (pPt->y - pdc->ViewOrig.y) * pdc->WindowExtent.y / pdc->ViewExtent.y + pdc->WindowOrig.y; } } ``` In addition, the function LPtoSP and function SPtoLP can be used to convert between logical coordinates and screen coordinates: ```cplusplus void GUIAPI SPtoLP(HDC hdc, POINT* pPt); void GUIAPI LPtoSP(HDC hdc, POINT* pPt); ``` ### 13.4.3 Conversion of Device Coordinates The mapping mode determines how MiniGUI maps logical coordinates into device coordinates. Device coordinates use pixel as unit, the value of x coordinate progressively increase from left to right, while the value of coordinate progressively increase from top to bottom.. There are three types of device coordinates in MiniGUI: screen coordinates, window coordinates, and client area coordinates. Usually device coordinates rely on the type of chosen device context to choose. The (0, 0) point in screen coordinates is on the upper-left corner of the whole screen. When we need to use the entire screen, we can do it according to the screen coordinates. Screen coordinates are usually used in the functions that are irrelevant to window or functions that are tightly related to the screen, such as GetCursorPos and SetCursorPos. If the device context used by GDI functions is HDC_SCREEN, the logical coordinates will be mapped as screen coordinates. The coordinates in the window coordinates are based on entire window, including window border, caption bar, menu bar and scroll bar, in which the origin of window coordinates is the upper-left corner of the window. While using the device context handle returned by GetDC, the logical coordinates passed to GDI functions will be converted into window coordinates. The point (0, 0) of the client area coordinates is the upper-left corner of this area. When we use the device context handle returned GetClientDC or BeginPaint, the logical coordinates passed to GDI functions will be converted to the client area coordinates. When programming we need to know on which coordinate system the coordinates or position is based, as the meaning of position may be different under different situation. Some time we need get the coordinates in another coordinate system. MiniGUI provides functions that realize the conversion among those three device coordinate systems: ```cplusplus void GUIAPI WindowToScreen (HWND hWnd, int* x, int* y); void GUIAPI ScreenToWindow (HWND hWnd, int* x, int* y); void GUIAPI ClientToScreen (HWND hWnd, int* x, int* y); void GUIAPI ScreenToClient (HWND hWnd, int* x, int* y); ``` WindowToScreen converts window coordinates into screen coordinates, while ScreenToWindow converts screen coordinates to window coordinates. The converted value is stored in the original place. ClientToScreen converts client coordinates into screen coordinates, while ScreenToClient converts screen coordinates to client coordinates. ### 13.4.4 The Deviation and Zoom of Coordinate System MiniGUI provides a set of functions that can be used to realize the deviation, zoom of the coordinate system. The prototypes of these functions are as follow: ```cplusplus void GUIAPI GetViewportExt(HDC hdc, POINT* pPt); void GUIAPI GetViewportOrg(HDC hdc, POINT* pPt); void GUIAPI GetWindowExt(HDC hdc, POINT* pPt); void GUIAPI GetWindowOrg(HDC hdc, POINT* pPt); void GUIAPI SetViewportExt(HDC hdc, POINT* pPt); void GUIAPI SetViewportOrg(HDC hdc, POINT* pPt); void GUIAPI SetWindowExt(HDC hdc, POINT* pPt); void GUIAPI SetWindowOrg(HDC hdc, POINT* pPt); ``` Get-functions are used to get the origin and extent of the window and/or the viewport, the value is stored in POINT structure pPt; Set-functions use the value of pPt to set the origin and the extent of the window and/or the viewport. ## 13.5 Rectangle and Region Operations ### 13.5.1 Rectangle Operations Rectangle usually refers to a rectangular region on the screen. It is defined in MiniGUI as follows: ```cplusplus typedef struct _RECT { int left; int top; int right; int bottom; } RECT; ``` In short, rectangle is a data structure used to represent a rectangular region on the screen. It defines the x coordinate and y coordinate of the upper-left corner of the rectangle (left and top), as well as the x coordinate and y coordinate of the lower-bottom corner of the rectangle. It is necessary to notice that the right and bottom borders are not included by MiniGUI’s rectangle. For example, if we want to figure a scan line on the screen, we should use ```cplusplus RECT rc = {x, y, x + w , y + 1}; ``` to represent it. In that x is the jumping-off point while y is the vertical place of that scan line, and w is the width of that scan line. MiniGUI provides a group of functions, which can operate on RECT objects: - SetRect assigns each parameter of a RETC object. - SetRectEmpty sets a RECT object to be empty. In MiniGUI, the empty rectangle is defined as a rectangle with its width or height as zero. - IsRectEmpty determines if the given RECT object is empty. - NormalizeRect normalizes a given rectangle. The rectangle should meet the requirement of right > left and bottom > top. Those rectangles that meet the above requirements are called normalized rectangles. This function can normalize any rectangle. - CopyRect copies between two rectangles. - EqualRect determines if two RECT objects are equal, that is, if the all parameters are equal. - IntersectRect gets the intersection of two RECT objects. If there is no intersection between those two rectangles, the function will return to FALSE. - DoesIntersect determines if the two rectangles are intersected. - IsCovered determines if RECT A completely overlay RECT B, that is, if RECT B is the true subset of RECT A. - UnionRect gets the union of two rectangles. If there is no union, the function will return FALSE; any point included in the union should also belong to either of the rectangles. - GetBoundRect gets the union of two rectangles. If there is no union, the function will return FALSE; any point included in the union should also belong to either of the rectangles. - SubstractRect subtracts one rectangle from another one. Such subtraction may result in four non-intersected rectangles. This function will return the number of the result rectangles. - OffsetRect offsets the given RECT object. - InflateRect inflates the given RECT object. The width and height of the inflated rectangle will be twice of the given inflation value. - InflateRectToPt inflates the given RECT object to a given point. - PtInRect determines if the given point lies in the given RECT object. MiniGUI also provides two groups of macro to get the width and height of RECT object. one macro is for inputting pointers of RECT, and the other is for inputing variables of RECT. - #define RECTWP(prc) ((prc)->right - (prc)->left) - #define RECTHP(prc) ((prc)->bottom - (prc)->top) - #define RECTW(rc) ((rc).right - (rc).left) - #define RECTH(rc) ((rc).bottom - (rc).top) ### 13.5.2 Region Operations Region is a scope on the screen, which is defined as a collection of non-intersected rectangles and represented as a linked list. Region can be used to represent the clipped region, invalid region, and visible region. In MiniGUI, the definition of region equals to the definition of clipped region, which is defined as follows (minigui/gdi.h): ```cplusplus typedef struct _CLIPRECT { RECT rc; struct _CLIPRECT* next; #ifdef _USE_NEWGAL struct _CLIPRECT* prev; #endif } CLIPRECT; typedef CLIPRECT* PCLIPRECT; typedef struct _CLIPRGN { #ifdef _USE_NEWGAL BYTE type; /* type of region */ BYTE reserved[3]; #endif RECT rcBound; PCLIPRECT head; PCLIPRECT tail; PBLOCKHEAP heap; } CLIPRGN; ``` Each clipped region has one BLOCKHEAP member, which is the private heap of RECT objects used by the clipped region. Before using a region object, we should firstly build up a BLOCKHEAP object, and then initialize the region object. Showed as follows: ```cplusplus static BLOCKHEAP sg_MyFreeClipRectList; ... CLIPRGN my_region InitFreeClipRectList (&sg_MyFreeClipRectList, 20); InitClipRgn (&my_regioni, &sg_MyFreeClipRectList); ``` When being actually used, multiple regions can share one BLOCKHEAP object. Following operations can be done after initializing the region object: - SetClipRgn sets only one rectangle in the region; - ClipRgnCopy copies one region to another; - ClipRgnIntersect gets the intersection of two regions; - GetClipRgnBoundRect gets the bounding box of the region; - IsEmptyClipRgn determines if the region is empty, that is, if the region includes any rectangle; - EmptyClipRgn releases the rectangles in the region and empty the region; - AddClipRect adds a rectangle to the region, but it does not determine if the region intersects with the rectangle; - IntersectClipRect gets the intersection of region and given rectangle; - SubtractClipRect subtracts the given rectangle from the region. - CreateClipRgn creates an empty region. - DestroyClipRgn clears and destroys a region. The operations of rectangles and regions form the main algorithms of window management. It is very important in GUI programming, as it is also one of the basic algorithms of advanced GDI function. ## 13.6 Basic Graphics Drawing ### 13.6.1 Basic Drawing Attributes Before understanding basic drawing functions, we need to know basic drawing attributes. In the current MiniGUI version, the drawing attributes include pen color, brush color, text background mode, text color, TAB width, and so on. The operation functions for these attributes are listed in Table 13.1.