?? 007.txt
字號:
預 定 義 值
說 明
BLACK_PEN
WHITE_PEN
NULL_PEN
BLACK_BRUSH
DKGRAY_BRUSH
GRAY_BRUSH
LTGRAY_BRUSH
WHITE_BRUSH
HOLLOW_BRUSH或NULL_BRUSH
ANSI_FIXED_FONT
ANSI_VAR_FONT
DEFAULT_GUI_FONT(Win95)
OEM_FIXED_FONT
SYSTEM_FONT
DEFAULT_PALETTE
黑色畫筆
白色畫筆
空畫筆
黑色畫刷
深灰色畫刷
灰色畫刷
淺灰色畫刷
白色畫刷
空畫刷
等寬系統字體
不等寬系統字體
默認系統字體(用于菜單、對話框等)
OEM等寬字體
默認系統字體(用于菜單、對話框等)
默認圖案
NULL_PEN和NULL_BRUSH是空畫筆和空畫刷,之所以有空的對象,是因為繪制填充區域的函數同時用到了畫筆和畫刷——繪制的外框使用當前畫筆,中間用當前畫刷填充。使用空對象可以有機會畫出沒有邊框線只有填充圖案,或者只有邊框線而不填充的區域來。
用GetStockObject函數得到對象句柄以后,就可以用SelectObject函數將對象句柄設置到DC中了。例子文件Clock.asm中的_ShowTime函數中用GetStockObject函數獲取了一個BLACK_BRUSH畫刷,用來繪畫時鐘的刻度。
2. 使用自定義的畫筆和畫刷
使用GetStockObject函數得到的對象是最“簡陋”的,如畫筆只能是白色或黑色的寬度為1像素的實線,畫刷只能是白色、黑色和有限的幾種灰色色塊。要想使用彩色的、多種多樣風格的畫筆和畫刷,就必須用自定義的方法。
創建自定義的畫筆可以使用CreatePen,ExtCreatePen或CreatePenIndirect函數,CreatePen函數的使用方法是:
invoke CreatePen,fnPenStyle,dwWidth,dwColor
mov hPen,eax
fnPenStyle參數是畫筆風格,它可以是兩種實線風格PS_SOLID,PS_INSIDEFRAME或空畫筆PS_NULL,以及幾種虛線風格PS_DASH,PS_DOT,PS_DASHDOT或PS_DASHDOTDOT。它們對應的線條如圖7.6所示,圖中從上到下分別是PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_DASHDOTDOT和PS_INSIDEFRAME風格的線條,幾種虛線的風格很好記,只要記得“點”就是DOT,“劃”就是DASH就可以了,如PS_DASHDOTDOT風格就是由“劃、點、點”重復組成的虛線。
PS_SOLID和PS_INSIDEFRAME風格的畫筆使用的都是實線線條,它們之間的區別在于當畫筆的寬度大于1像素。在使用區域繪畫函數的時候,PS_SOLID線條會居中畫于邊線上,而PS_INSIDEFRAME線條會全部畫在邊線里面,它的寬度會向區域的內部擴展,所以它的名稱是InsideFrame。
圖7.6 幾種自定義畫筆風格
CreatePen 函數的dwWidth參數定義了畫筆的寬度,單位是DC坐標映射方法中定義的邏輯單位,如果這個參數使用NULL,那么函數會使用1像素的寬度。寬度參數會影響到風格參數:當寬度大于1的時候,畫筆風格不能使用虛線,這時候即使指定了虛線風格,函數也會自動使用PS_SOLID風格。dwColor參數指定了畫筆的顏色。
例子源代碼的_ShowTime子程序中用不同寬度的線條來繪畫時、分、秒指針,繪畫前就使用CreatePen函數創建了不同寬度的畫筆。
如果需要創建更復雜的畫筆,可以使用ExtCreatePen函數。這個函數除了有CreatePen的全部功能外,還可以讓用戶自己定義線條的樣子,這樣可以不必限制于上面的點點劃劃了。函數的用法讀者可以參考函數手冊。
創建自定義畫刷可以使用的函數有:CreateSolidBrush,CreateHatchBrush,CreatePatternBrush和CreateBrushIndirect。
CreateSolidBrush創建單色的畫刷:
invoke CreateSolidBrush,dwColor
mov hBrush,eax
要輸入的惟一參數是畫刷的顏色。而CreateHatchBrush可以創建幾種預定義圖案的畫刷:
invoke CreateHatchBrush,iHatchStyle,dwColor
mov hBrush,eax
dwColor指定了圖案線條的顏色,iHatchStyle定義了不同的圖案線條,這些圖案線條實際上是以8×8的位圖重復鋪開組成的,iHatchStyle的定義值可以是HS_BDIAGONAL,HS_CROSS,HS_DIAGCROSS,HS_FDIAGONAL,HS_HORIZONTAL和HS_VERTICAL,這6種圖案的花樣在圖7.7中從左到右排列顯示。
圖7.7 CreateSolidBrush中的畫刷圖案
如果這些簡單的圖案不能滿足使用要求,CreatePatternBrush是個很好的選擇:
invoke CreatePatternBrush,hBitmap
mov hBrush,eax
這個函數用一個位圖當做畫刷的圖案,當要繪畫的區域大于位圖尺寸的時候,位圖被重復鋪開,就像HTML文件中的背景圖案一樣。讀者可以嘗試一下用一幅做網頁文件背景的位圖創建一個位圖畫刷,并且在RegisterClassEx時在WNDCLASSEX結構中的hbrBackground字段中使用這個畫刷,這樣創建出來的窗口背景會和網頁背景一樣華麗!讀者可以參考所附光盤的Chapter07\TestObject目錄中的源代碼。
對于自定義的畫筆和畫刷,還有其他自定義的對象,在不再需要的時候必須使用DeleteObject函數刪除,但是要注意:當對象還是一個DC的當前對象的時候不要將它刪除,在刪除前應該確定DC中已經選入了其他的對象。與之相反,用GetStockObject獲取的預定義對象使用后不需要刪除,但是對它們調用DeleteObject也沒有關系,因為它們不會被真正刪除。由于SelectObject返回值就是DC原來使用的對象句柄,所以刪除對象的一個好時機就是當SelectObject返回的時候,如例子程序的_ShowTime子程序中用的:
invoke CreatePen,PS_SOLID,2,0
invoke SelectObject,_hDC,eax
invoke DeleteObject,eax
SelectObject將CreatePen創建的畫筆句柄選入DC,返回值eax就是以前使用的畫筆句柄,這個句柄不再使用了,所以可以在下面用DeleteObject直接刪除,而這次建立的畫筆可以在下次執行SelectObject后用同樣的方法刪除。
7.2.2 繪制像素點
在DC上繪制像素點是繪圖最基本的操作,使用的方法是:
invoke SetPixel,hDC,dwX,dwY,dwColor
SetPixel函數在hDC的dwX、dwY位置以dwColor為顏色畫上一個像素點,如果需要獲取hDC中某個像素點當前的顏色值,那么可以使用GetPixel函數:
invoke GetPixel,hDC,dwX,dwY
mov dwColor,eax
雖然繪畫像素是最基本的繪圖操作方法,但是在程序中一般很少使用SetPixel函數,因為它的開銷太大了,只適合用在需要少量繪畫像素的地方,如果要繪畫一個線條或者整個區域,那么最好使用畫線函數或者填充函數,因為這些函數是在驅動程序級別上完成的,所有的硬件加速功能都可以用上。
圖形處理前最基本的步驟是獲取像素,但也不應該用GetPixel函數來獲取一大塊的像素數據,理由是同樣的。如果要分析整個區域的像素數據,最好的辦法就是用GetDIBits函數將全部數據拷貝到內存中再進行處理。
7.2.3 繪制圖形
GDI的圖形繪制函數主要有繪制線條和填充區域兩大類。繪制線條的函數以當前畫筆繪制線條;繪制填充區域的函數以當前畫筆繪制邊線,并以當前畫刷填充中間的區域。
1. 繪制線條
繪制線條的函數有畫直線的LineTo,畫多條直線的Polyline和PolylineTo,畫貝塞兒曲線的PolyBezier和PolyBezierTo,畫弧線的Arc和ArcTo。
DC的數據結構中有一個“當前點”,LineTo函數就是從當前點畫一條直線到參數中指定的點,并把參數中指定的點設置為新的當前點。畫線函數中所有以To結尾的函數都是從當前點開始繪畫的,如LineTo,PolylineTo,PolyBezierTo和ArcTo,這些函數在繪畫結束后會把繪制的最后一點設置為新的當前點,所以在使用這些函數的時候要考慮到當前點也是參與繪制的坐標一部分。而其余的Polyline,PolyBezier和Arc函數則和當前點沒有關系,也不會影響當前點的位置。
如果要設置當前點的位置,可以使用MoveToEx函數:
invoke MoveToEx,hDC,dwX,dwY,lpPoint
dwX和dwY指出了新的當前點的坐標,lpPoint指向一個空的POINT結構,用來返回原來的當前點位置,如果不需要的話,這個參數可以使用NULL。
另一個函數也可以得到當前點的坐標:
invoke GetCurrentPositionEx,hDC,lpPoint
同樣,lpPoint指向一個用來返回當前點坐標的POINT結構地址。
如果要繪制一條直線,必須配合使用MoveToEx和LineTo函數,首先由MoveToEx函數設置一個當前點當做起始坐標,然后用LineTo繪畫到結束坐標,如Clock.asm中的_DrawLine子程序中就是這樣繪制時鐘指針的:
invoke MoveToEx,_hDC,@dwX1,@dwY1,NULL
invoke LineTo,_hDC,@dwX2,@dwY2
這兩句代碼繪畫一條從@dwX1,@dwY1到@dwX2,@dwY2的直線。
如果要繪制是相連的多條直線,可以使用Polyline或PolylineTo函數:
invoke PolylineTo,hDC,lpPoint,cPoints
invoke Polyline,hDC,lpPoint,cPoints
lpPoint指向一個包含一系列POINT結構的緩沖區,由于POINT結構只有X和Y兩個字段,所以緩沖區中的數據實際上是x1,y1,x2,y2,x3,y3,…,cPoints參數指出了點的數目,注意:PolylineTo畫出的直線是從當前點坐標(x,y)開始,然后到(x1,y1),再到(x2,y2),…,而Polyline函數畫出的直線是從(x1,y1)開始的,對于這個函數,如果cPoints參數指定了n個點,那么直線的數量實際上是n?1。當繪制的相連直線很多的時候,用Polyline或PolylineTo比多次使用LineTo的速度要快很多,就像用填充函數比多次使用SetPixel要快一樣。
表7.3舉例說明了這些畫線函數的功能,表中的(x1,y1)或(x2,y2)等表示點1或點2的坐標,(xc,yc)表示當前點的坐標,當前點在圖中用c表示。
表7.3 畫線函數的功能
函 數
說 明
圖 例
LineTo(hDC,x,y)
從當前點到(x,y)點
PolylineTo(hDC,lpPoint,5)
lpPoint指向存放(x1,y1)到(x5,y5)的緩存區,函數畫的線條從(xc,yc)到(x1,y1)到(x2,y2)…到(x5,y5),共5條直線
Polyline(hDC,lpPoint,5)
lpPoint指向存放(x1,y1)到x5、y5的緩存區,函數畫的線條從(x1,y1)到(x2,y2)…到(x5,y5),共4條直線
PolyBezierTo(hDC,lpPoint,3)
繪畫的Bezier曲線的控制點為(xc,yc)和(x1,y1)和(x2,y2)和(x3,y3)
PolyBezier(hDC,lpPoint,4)
繪畫的Bezier曲線的控制點為(x1,y1)和(x2,y2)和(x3,y3)和(x4,y4)
ArcTo(hDC,x1,y1,x2,y2,
x3,y3,x4,y4)
首先畫(xc,yc)到起始點的直線,再畫起始點到結束點的弧線
Arc(hDC,x1,y1,x2,y2,
x3,y3,x4,y4)
畫起始點到結束點的弧線
對于Arc和ArcTo函數,參數(x1,y1)和(x2,y2)定義了一個矩形的對角點,然后在和這個矩形相切的橢圓上面,以橢圓的中心(也就是矩形的中心)畫兩條假想的直線到(x3,y3)和(x4,y4),這兩條直線和橢圓相交的點就是圓弧的起始點和結束點。在默認情況下,圓弧由起始點沿著橢圓從逆時針方向畫到結束點。不過繪畫方向可以由SetArcDirection函數重新規定:
invoke SetArcDirection,hDC,AD_COUNTERCLOCKWISE ;逆時針方向
invoke SetArcDirection,hDC,AD_CLOCKWISE ;順時針方向
讀者一定注意到了一個問題:在畫線的時候,如果當前的畫筆是虛線的話,虛線的不連續部分實際上是由白色組成的,當虛線畫在非白色的背景上的時候這一點顯得特別明顯。實際上,可以選擇這些不連續部分的顏色,用以下的語句就可以做到這一點:
invoke SetBkColor,hDC,dwColor
調用后不連續的部分就將用dwColor指定的顏色繪畫。
但是改變顏色也并不是惟一的選擇,GDI允許這部分并不繪畫任何顏色,也就是可以是“透明”的,用下面的調用可以將模式在透明和非透明之間切換:
invoke SetBkMode,hDC,OPAQUE ;非透明模式
invoke SetBkMode,hDC,TRANSPARENT ;透明模式
兩種模式以及繪畫顏色不單影響虛線的空隙部分,同樣也影響CreateHatchBrush函數創建的畫刷,因為這種畫刷使用幾種由線條構成的圖案,當用這種畫刷填充一個區域的時候,線條圖案的空隙部分同樣受SetBkColor函數和SetBkMode函數的影響。
2. 繪制邊界框和填充區域
繪制邊界框和填充區域其實是同一件事情。如果當前畫筆是NULL_PEN的話,畫出來的是沒有邊線的填充區域;如果當前畫刷是NULL_BRUSH的話,那么只有邊線而不會填充;如果當前畫刷既不是NULL_PEN也不是NULL_BRUSH,那么畫出來的圖形既有邊線也是填充的。
繪制區域的函數有畫矩形的Rectangle,畫圓角矩形的RoundRect,畫多邊形的Polygon,畫弦的Chord,畫圓餅的Pie和畫橢圓的Ellipse。這些函數的使用效果見如7.4所示。
表7.4 填充函數的功能
函 數
說 明
圖 例
Rectangle(hDC,x1,y1,x2,y2)
畫以(x1,y1)和(x2,y2)為對角坐標的填充矩形
RoundRect(hDC,x1,y1,x2,y2,w,h)
畫以(x1,y1)和(x2,y2)為對角坐標的填充矩形,四個角以一個小橢圓來畫圓角,小橢圓的寬和高為w和h
函 數
說 明
圖 例
Polygon(hDC,lpPoint,5)
lpPoint指向存放(x1,y1)到(x5,y5)的緩存區,函數從(x1,y1)到(x2,y2)…到(x5,y5),再回到(x1,y1),一共畫5條直線并填充
Chord(hDC,x1,y1,x2,y2,
x3,y3,x4,y4)
以和Arc函數同樣的方法畫弧,然后連接弧的兩個端點并填充
Pie(hDC,x1,y1,x2,y2,
x3,y3,x4,y4)
以和Arc函數同樣的方法畫弧,然后將弧的兩個端點分別和橢圓中心連接并填充
Ellipse(hDC,x1,y1,x2,y2)
以(x1,y1)和(x2,y2)為對角定義一個矩形,然后畫矩形相切的橢圓并填充
在這些函數中,Polygon的調用方式和Polyline很相似,只不過如果最后一點和第一點不同的話,函數自動再畫一條和起始點相連的直線將整個區域閉合起來。用Polygon繪畫的多邊形中各條直線可能相交,Windows允許程序自行選擇填充的模式,可以是表7.4中Polygon一欄中的上面那個圖例(填充全部區域),也可以是下面那個圖例(間隔填充區域)。可以用下面的函數切換填充的模式:
7.2 繪 制 圖 形(6)
invoke SetPolyFillMode,_hDC,ALTERNATE ;間隔填充
invoke SetPolyFillMode,_hDC,WINDING ;填充全部區域
Chord函數和Pie函數的參數使用和畫弧線的Arc函數相似,只不過Chord函數將弧線的兩端直接相連,形成一個“弦”,而Pie函數將兩端和圓心相連,形成一個“圓餅”,這兩個函數繪畫的方向同樣受SetArcDirection函數設置的影響。
在例子Clock.asm中,程序在_DrawDot子程序中用Ellipse函數繪畫時鐘的刻度,讀者也可以將程序改動一下,嘗試著用Polygon畫五角星來當做時鐘的刻度。
除了這些函數,還有3個和矩形有關的填充函數:FillRect,FrameRect和InvertRect,這些函數不使用當前畫筆畫邊線,也不用當前畫刷填充,其中FillRect函數用指定的畫刷hBrush填充一個lpRect指定的矩形區域,lpRect指向一個RECT結構;FrameRect函數用指定畫刷hBrush繪畫邊線;InvertRect函數將lpRect指定的矩形區域中的顏色值取反。用法如下。
invoke FillRect,hDC,lpRect,hBrush
invoke FrameRect,hDC,lpRect,hBrush
invoke InvertRect,hDC,lpRect
假設背景為白色,而參數中hBrush指定的畫刷為灰色畫刷,那么上述3個函數的運行結果如圖7.8所示。
圖7.8 FillRect,FrameRect和InvertRect函數的運行結果
圖中左邊是FillRect的運行結果,可以看到圖案沒有邊線;中間是FrameRect的運行結果,它用灰色畫刷繪畫邊線,得到了一個灰色的矩形邊框;右邊是InvertRect的運行結果,由于底色是白色的,白色取反得到的是黑色,所以整個矩形都變成了黑色。
7.2.4 繪圖模式
在前面的內容中我們都是嘗試在DC上用繪圖函數畫出需要的圖形,對于DC上被繪畫上去的像素來說,相當于用畫筆(或畫刷)的像素點代替了原來的像素點,但Windows也可以用
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -