設備描述表還真是十分重要,以至於我們在這裏還要再學習它。這節內容比較少,相信你很快就應該能看完。Let’go!
獲取設備描述表句柄
複習:當你想在一個圖形輸出設備(諸如屏幕或者打印機)上繪圖時,你首先必須獲得一個設備描述表(或者DC)的句柄。將句柄傳回給程序 時,Windows就給了你使用設備的權限。然後你在GDI函數中將這個句柄作爲一個參數,向Windows標識你想在其上進行繪圖的設備。如果在處理一 個消息時取得了設備描述表句柄,應該在退出窗口函數之前釋放它(或者刪除它)。一旦釋放了句柄,它就不再有效了。
Windows提供了幾種取得設備描述表句柄的方法,如下。
方法一:
這是獲取並釋放設備描述表句柄最常用的方法,即在處理WM_PAINT消息時使用BeginPaint和EndPaint函數。這兩個函數需要程序的窗口 句柄和PAINTSTRUCT結構的變量的地址爲參數。Windows程序員通常把這一結構變量命名爲ps並且在窗口過程中定義它:
PAINTSTRUCT ps ;
在處理WM_PAINT消息時,窗口過程首先調用BeginPaint。一般在BeginPaint函數的調用中,如果 客戶區的無效區域的背景還未被擦除,則由Windows來擦除。(它使用已註冊的窗口類WNDCLASS結構的hbrBackground成員變量中指定 的畫刷來擦除背景,上回的註釋1我們提過)
BeginPaint調用使整個客戶區有效,並且返回一個設備描述表句柄,這一返回值通常被保存在叫做hdc的變量中。該函數也填入ps結構的字段(可以回憶一下上一回的註釋1)。
設備描述表句柄在窗口過程中的定義如下:
HDC hdc ;//定義設備描述表句柄變量
然後,程序就可以使用需要設備描述表句柄的TextOut等GDI函數。在窗口的客戶區顯示文字和圖形需要設備描述表句柄,但是從BeginPaint返回的設備描述表句柄不能在客戶區域之外繪圖。調用EndPaint即可釋放設備描述表句柄。
一般地,處理WM_PAINT消息的形式如下:
caseWM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
[other program lines]
EndPaint (hwnd, &ps) ;
return 0 ;
我們再來強調一下吧(別怪我囉嗦):BeginPaint調用使整個客戶區有效(阻止WM_PAINT消息一直髮送,見 附錄),填充ps結構的字段,返回的設備描述表句柄。使用這個句柄只能在ps結構中的rcPaint字段規定的區域內繪圖。EndPaint調用可釋放設 備描述表句柄。兩個函數必須成對使用,一般只用在處理WM_PAINT消息中。
Windows程序在處理非WM_PAINT消息時還可以用以下方法獲得設備描述表句柄,當然以下函數調用不會使客戶區中的無效區域變成有效。
方法二:
hdc = GetDC (hwnd) ;
[other program lines]
ReleaseDC (hwnd, hdc) ;
GetDC函數調用後會返回hwnd參數所指定的窗口的客戶區所對應的設備描述表句柄。可見GetDC調用與BeginPaint的基本區別是,利用從GetDC返回的句柄可以在整個客戶區上繪圖。如果hwnd參數設置爲NULL,那麼函數會返回整個桌面的設備描述表句柄。當不再需要該設備環境時,需要調用ReleaseDC函數釋放設備描述表。
方法三:
hdc = GetWindowDC (hwnd) ;
[other program lines]
ReleaseDC (hwnd, hdc) ;
GetWindowDC返回可以在整個窗口(包括客戶區部分和標題欄、菜單、滾動條、框架等非客戶區部分)繪圖的設備描述表句柄,不過此函數很少使用。當不再需要該設備描述表時,需要調用ReleaseDC函數釋放設備描述表。
方法四:
hdc =CreateDC (lpszDriver, lpszDevice, lpszOutput, lpData) ;
[other program lines]
DeleteDC (hdc) ;
BeginPaint、GetDC和GetWindowDC獲得的設備內容都與顯示器上的某個特定窗口(即hwnd)相關。CreateDC是取得設備描述表句柄一個更通用的函數,它甚至可以獲取非顯示器輸出設備描述表句柄。當不再需要該設備描述表時只可調用DeleteDC函數刪除它。
當然,獲得設備描述表的函數還有很多,我們就先簡單介紹到這兒,其他類似的函數我們以後遇到再說。
設備描述表的屬性——我們上回提到過哦
設備描述表中包含許多確定GDI函數如何在設備上工作的當前“屬性”,這些屬性允許傳遞給GDI函數的參數只包含起始座標或者尺寸信息,而不必包含 Windows在設備上顯示對象時需要的所有其它信息。例如,上回我們調用TextOut時,我們只需要在函數中給出設備描述表句柄、起始座標、文字和文 字的長度。而不必指定字體、文字顏色、文字後面的背景色彩以及字符間距,因爲這些屬性都是設備描述表的一部分。當你想改變這些屬性之一時,您調用一個可以 改變設備描述表中屬性的函數(例如把文字顏色設置成紅色),以後針對該設備描述表的TextOut調用
就可以使用改變後的屬性了(例如可以輸出紅色字體)。
我在此列出如下表,方便大家查閱。
屬性 |
默認值 |
相關函數 |
背景色 |
WHITE |
GetBkColor |
SetBkColor |
||
背景模式 |
OPAQUE |
GetBkMode |
SetBkMode |
||
位圖 |
NONE |
CreateBitMap |
CreateBitMapIndirect |
||
CreateCompatibleBitmap |
||
SelectObject |
||
畫刷 |
WHITE_BRUSH |
CreateBrushIndirect |
CreateDIBPatternBrush |
||
CreateHatchBrush |
||
CreatePatternBrush |
||
CreateSolidBrush |
||
SelectObject |
||
畫刷起始位置 |
(0,0) |
GetBrushOrg |
SetBrushOrg |
||
UnrealizeObject |
||
剪裁域 |
DISPLAY SURFACE |
ExcludeClipRect |
IntersetClipRect |
||
OffsetClipRgn |
||
SelectClipPath |
||
SelectObject |
||
SelectClipRgn |
||
顏色調色板 |
DEFAULT_PALETTE |
CreatePalette |
RealizePatte |
||
SelectPalette |
||
繪圖方式 |
R2_COPYPEN |
GetROP2 |
SetROP2 |
||
字體 |
SYSTEM_FONT |
CreateFont |
CreateFontIndirect |
||
SelectObject |
||
字符間距 |
0 |
GetTextCharacterExtra |
SetTextCharacterExtra |
||
映射方式 |
MM_TEXT |
GetMapMode |
SetMapMode |
||
畫筆 |
BLACK_PEN |
CreatePen |
CreatePenIndirect |
||
SelectObject |
||
多邊形填充方式 |
ALTERNATE |
GetPolyFillMode |
SetPolyFileMode |
||
縮放模式 |
BLACKONWHITE |
SetStretchBltMode |
GetStretchBltMode |
||
文本顏色 |
BLACK |
GetTextColor |
SetTextColor |
||
視圖範圍 |
(1,1) |
GetViewportExtEx |
SetViewportExtEx |
||
ScaleViewportExtEx |
||
視圖原點 |
(0,0) |
GetViewportOrgEx |
SetViewportOrgEx |
||
窗口範圍 |
(1,1) |
GetWindowExtEx |
SetWindowExtEx |
||
ScaleWindowExtEx |
||
窗口原點 |
(0,0) |
GetWindowOrgEx |
OffsetWindowOrgEx |
||
SetWindowOrgEx |
一般以Get開頭的函數作用是獲取該屬性值,一般以Set開頭的函數作用是設置該屬性值,若有不太清楚的函數,你就自己動手查吧。
保存設備描述表
通常,在你調用GetDC或BeginPaint時,Windows用默認值建立一個新的設備描述表,你對屬性所做的一切改變在設備描述表調用 ReleaseDC或EndPaint釋放時,都會丟失。如果你的程序需要使用非默認的設備描述表屬性,則你必須在每次取得設備描述表句柄時初始化設備內 容:
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
[initialize device context attributes]
[paint client area of windows]
EndPaint (hwnd, &ps) ;
return 0 ;
雖然在通常情況下這種方法已經很令人滿意了,但是你還可能想要在釋放設備描述表之後,仍然保存程序中對設備描述表屬性所做的改變,以便在下一次調用 GetDC和BeginPaint時它們仍然能夠起作用。爲此,可在設計窗口類時,將CS_OWNDC標誌包含爲窗口類的一部分:
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC ;
現在,基於這個窗口類所創建的每個窗口都將擁有自己的設備描述表,它一直存在,直到窗口被刪除。如果使用了CS_OWNDC風格,就只需初始化設備描述表一次,可以在處理WM_CREATE消息處理期間完成這一操作:
case WM_CREATE:
hdc = GetDC (hwnd) ;
[initialize device context attributes]
ReleaseDC (hwnd, hdc) ;
這些屬性在改變之前一直有效。
CS_OWNDC風格只影響GetDC和BeginPaint獲得的設備內容,不影響其它函數獲得的設備描述表。使用了CS_OWNDC,你仍然應該在退出窗口過程之前釋放設備描述表。
某些情況下,你可能想改變某些設備描述表屬性,用改變後的屬性進行繪圖,然後恢復原來的設備描述表屬性。要簡化這一過程,可以通過如下調用來保存設備描述表的狀態:
int idSaved = SaveDC (hdc) ;
現在,可以改變一些屬性,在想要回到調用SaveDC前存在的設備內容時,調用:
RestoreDC (hdc, idSaved) ;
你可以在調用RestoreDC之前調用SaveDC數次。
大多數程序寫作者以不同的方式使用SaveDC和RestoreDC。然而,更像彙編語言中的PUSH和POP指令,當你調用SaveDC時,可以不需要保存返回值:
SaveDC (hdc) ;
然後,你可以更改某些屬性並再次調用SaveDC。要將設備內容恢復到一個已經保存的狀態,調用:
RestoreDC (hdc, -1) ;
這就將設備描述表恢復到最近由SaveDC函數保存的狀態中。
附錄
小技巧:
在處理WM_PAINT消息時,爲了在更新的矩形外繪圖,可以在調用BeginPaint之前使用如下函數:
InvalidateRect (hwnd, NULL, TRUE) ;
它使整個顯示區域變爲無效,並擦除背景。但是,如果最後一個參數等於FALSE,則不擦除背景,原有的東西將保留在原處。
通常這是Windows程序在無論何時收到WM_PAINT消息而不考慮rcPaint結構的情況下簡單地重畫整個客戶區最方便的方法。例如,如果 在客戶區的顯示輸出中包括了一個圓,但是隻有圓的一部分落到了無效矩形中,它就僅繪製圓在無效矩形中的部分。這對於畫整個圓來說是無意義的。注意:使用從 BeginPaint返回的設備描述表句柄時,Windows不會繪製rcPaint矩形外的任何部分。在BeginPaint調用之前使用上面那個函 數,可使rcPaint結構中的無效矩形爲整個客戶區,就不愁繪製圓了。
在處理WM_PAINT消息時,必須成對地調用BeginPaint和EndPaint。如果窗口過程不處理 WM_PAINT消息,則它必須將WM_PAINT消息傳遞給Windows中DefWindowProc(默認窗口過程)。DefWindowProc 以下列代碼處理WM_PAINT消息:
case WM_PAINT:
BeginPaint (hwnd, &ps) ;
EndPaint (hwnd, &ps) ;
return 0 ;
這兩個BeginPaint和EndPaint調用之間中沒有任何語句,僅僅使先前無效區域變爲有效。但以下方法是錯誤的:
case WM_PAINT:
return 0 ; // WRONG !!!
Windows將一個WM_PAINT消息放到消息隊列中,是因爲客戶區的一部分無效。如果不調用BeginPaint和EndPaint(或者 ValidateRect),則Windows不會使該區域變爲有效。若一直保持無效,Windows將會再發送另一個WM_PAINT消息,且一直髮送 下去。