基于 Linux 和 MiniGUI 的嵌入式系統軟件開發指南(四)
2024-07-21 02:38:19
供稿:網友
作者:魏永明
使用 GDI 函數
本文講述 MiniGUI 中 GDI 函數及其使用。主要包括:設備上下文的概念、獲取和釋放;矩形操作和區域操作;基本繪圖函數;位圖操作函數;邏輯字體操作函數等。
1 引言
GUI 系統的一個重要組成部分就是 GDI,即圖形設備接口(Graphics Device Interface)。通過 GDI,GUI 程序就可以在計算機屏幕上,或者其他的顯示設備上進行圖形輸出,包括基本繪圖和文本輸出。本文將具體描述 MiniGUI 中的 GDI 函數,并舉例說明重要函數的用法。其中包括:DC 的概念、獲取和釋放;矩形操作和剪切域操作;基本繪圖函數;位圖操作函數;邏輯字體操作函數等。
2 圖形設備上下文
在 MiniGUI 中,采用了在 Windows 和 X Window 中普遍采用的圖形設備概念。每個圖形設備定義了計算機顯示屏幕上的一個矩形輸出區域。在調用圖形輸出函數時,均要求指定經初始化的圖形設備上下文(Device Context,DC),也稱作"設備環境"。從程序員的角度看,一個經過初始化的圖形設備上下文確定了其后進行圖形輸出的一些基本屬性,并一直保持這些屬性,直到被改變為止。這些屬性包括:輸出的線條顏色、填充顏色、字體顏色、字體外形等等。而從 GUI 系統角度來講,一個圖形設備上下文所代表的含義就要復雜得多,它起碼應該包含如下內容:
該設備上下文本所在設備信息(顯示模式、色彩深度、顯存布局等等);
該設備上下文所代表的窗口以及該窗口被其他窗口剪切的信息(在 MiniGUI 中,稱作"全局剪切域");
該設備上下文的基本操作函數(點、直線、多邊形、填充、塊操作等),及其上下文信息;
由程序設定的局部信息(繪圖屬性、映射關系和局部剪切域等)。
所以,從程序員的角度看來,他所關心的僅僅是設備上下文本身的一小部分東西。
2.1 設備上下文的獲取和釋放
在 MiniGUI 中,所有繪圖相關的函數均需要有一個設備上下文。設備上下文可通過 GetClientDC 和 ReleaseDC 獲取和釋放。由 GetDC 所獲取的設備上下文是針對整個窗口的,而 GetClientDC 所獲取的設備上下文是針對窗口客戶區,也就是說,前一個函數獲得的設備上下文,其坐標原點位于窗口左上角,輸出被限定在窗口范圍之內;后一個函數獲得的設備上下文,其坐標原點位于窗口客戶區左上角,輸出被限定在窗口客戶區范圍之內。下面是這三個函數的原型說明(include/gdi.h):
398 HDC GUIAPI GetDC (HWND hwnd);
399 HDC GUIAPI GetClientDC (HWND hwnd);
400 void GUIAPI ReleaseDC (HDC hdc);
GetDC 和 GetClientDC 是從系統預留的若干個 DC 當中獲得一個目前尚未使用的設備上下文。所以,應該注重如下兩點:
在使用完成一個由 GetDC 返回的設備上下文之后,應該盡快調用 ReleaseDC 釋放。
避免同時使用多個設備上下文,并避免在遞歸函數中調用 GetDC 和 GetClientDC。
為了方便程序編寫,提高繪圖效率,MiniGUI 還提供了建立私有設備上下文的函數,所建立的設備上下文在整個窗口生存期內有效,從而免除了獲取和釋放的過程。這些函數的原型如下:
403 HDC GUIAPI CreatePRivateDC (HWND hwnd);
404 HDC GUIAPI CreatePrivateClientDC (HWND hwnd);
405 HDC GUIAPI GetPrivateClientDC (HWND hwnd);
406 void GUIAPI DeletePrivateDC (HDC hdc);
在建立主窗口時,假如主窗口的擴展風格中指定了 WS_EX_USEPRIVATEDC 風格,則 CreateMainWindow 函數會自動為該窗口的客戶區建立私有設備上下文。通過 GetPrivateClientDC 函數,可以獲得該設備上下文。對控件而言,假如控件類具有 CS_OWNDC 屬性,則所有屬于該控件類的控件將自動建立私有設備上下文。DeletePrivateDC 函數用來刪除私有設備上下文。對上述兩種情況,系統將在銷毀窗口時自動調用 DeletePrivateDC 函數。
另外一個獲取和釋放設備上下文的方法是通過 BeginPaint 和 EndPaint 函數。這兩個函數只能在處理 MSG_PAINT 的消息中調用。MiniGUI 在 BeginPaint 函數中通過 GetClientDC 獲取客戶區設備上下文,然后將窗口當前的無效區域選擇到窗口的剪切區域中;而 EndPaint 函數則清空窗口的無效區域,并釋放設備上下文。這兩個函數的原型如下(include/window.h):
623 HDC GUIAPI BeginPaint(HWND hWnd);
624 void GUIAPI EndPaint(HWND hWnd, HDC hdc);
因為 BeginPaint 函數將窗口的無效區域選擇到了設備上下文中,所以,可以通過一些必要的優化來提高 MSG_PAINT 消息的處理效率。比如,某個程序要在窗口客戶區中填充若干矩形,就可以在 MSG_PAINT 函數中如下處理:
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;
}
這樣可以避免不必要的重繪操作,從而提高繪圖效率。
2.2 系統內存中的設備上下文
MiniGUI 也提供了內存設備上下文的創建和銷毀函數。利用內存設備上下文,可以在系統內存中建立一個類似顯示內存的區域,然后在該區域中進行繪圖操作,結束后再復制到顯示內存中。這種繪圖方法有許多好處,比如速度很快,減少直接操作顯存造成的閃爍現象等等。不過,目前 MiniGUI 中只能建立和顯示內存,也就是物理設備上下文一樣的內存設備上下文。用來建立和銷毀內存設備上下文的函數原型如下(include/gdi.h):
401 HDC GUIAPI CreateCompatibleDC (HDC hdc);
402 void GUIAPI DeleteCompatibleDC (HDC hdc);
2.3 屏幕設備上下文
MiniGUI 在啟動之后,就建立了一個全局的屏幕設備上下文。該 DC 是針對整個屏幕的,并且沒有任何預先定義的剪切域。在某些應用程序中,可以直接使用該設備上下文進行繪圖,將大大提高繪圖效率。在 MiniGUI 中,屏幕設備上下文用 HDC_SCREEN 標識,不需要進行任何獲取和釋放操作。
2.4 映射模式
一個設備上下文被初始化之后,其坐標系原點通常是輸出矩形的左上角,而 x 軸水平向左,y 軸垂直向下,并以象素為單位。這種坐標的映射模式標識為 MM_TEXT。MiniGUI 提供了一套函數,可以改變這種映射方式,包括對默認坐標系進行偏移、縮放等操作。這些函數的原型如下(include/gdi.h):
453 int GUIAPI GetMapMode (HDC hdc);
454 void GUIAPI GetViewportExt (HDC hdc, POINT* PPT);
455 void GUIAPI GetViewportOrg (HDC hdc, POINT* pPt);
456 void GUIAPI GetWindowExt (HDC hdc, POINT* pPt);
457 void GUIAPI GetWindowOrg (HDC hdc, POINT* pPt);
458 void GUIAPI SetMapMode (HDC hdc, int mapmode);
459 void GUIAPI SetViewportExt (HDC hdc, POINT* pPt);
460 void GUIAPI SetViewportOrg (HDC hdc, POINT* pPt);
461 void GUIAPI SetWindowExt (HDC hdc, POINT* pPt);
462 void GUIAPI SetWindowOrg (HDC hdc, POINT* pPt);
GetMapMode 函數返回當前的映射模式,若不是 MM_TEXT 模式,則返回MM_ANISOTROPIC。SetMapMode 函數設置映射模式,MiniGUI 目前只支持兩種映射模式,即MM_ANISOTROPIC 和 MM_TEXT。Get 函數組用來返回映射模式信息,包括偏移量、縮放比例等等,而 Set 函數組用來設置相應的映射信息。
通常情況下,MiniGUI 的 GDI 函數所指定的坐標參數稱為"邏輯坐標",在繪制之前,首先要轉化成"設備坐標"。當使用 MM_TEXT 映射模式時,邏輯坐標和設備坐標是等價的。LPtoDP 函數用來完成邏輯坐標到設備坐標的轉換,DPtoLP 函數用來完成從設備坐標到邏輯坐標的轉換。邏輯坐標和設備坐標的關系可從 LPtoDP 函數中看到(src/gdi/coor.c):
61 void GUIAPI LPtoDP(HDC hdc, POINT* pPt)
62 {
63 PDC pdc;
64
65 pdc = dc_HDC2PDC(hdc);
66
67 if (pdc->mapmode != MM_TEXT) {
68 pPt->x = (pPt->x - pdc->WindowOrig.x)
69 * pdc->ViewExtent.x / pdc->WindowExtent.x
70 + pdc->ViewOrig.x;
71
72 pPt->y = (pPt->y - pdc->WindowOrig.y)
73 * pdc->ViewExtent.y / pdc->WindowExtent.y
74 + pdc->ViewOrig.y;
75 }
76 }
77
另外,LPtoSP 函數和 SPtoLP 函數完成邏輯坐標和屏幕坐標之間的轉換。
3 矩形操作和區域操作
3.1 矩形操作
在 MiniGUI 中,矩形是如下定義的(include/common.h):
120 typedef strUCt tagRECT
121 {
122 int left;
123 int top;
124 int right;
125 int bottom;
126 } RECT;
127 typedef RECT* PRECT;
128 typedef RECT* LPRECT;
簡而言之,矩形就是用來表示屏幕上一個矩形區域的數據結構,定義了矩形左上角的 x, y 坐標(left 和 top)以及右下角的 x, y 坐標(right 和 bottom)。
需要注重的是,MiniGUI 中的矩形,其右側的邊和下面的邊是不屬