GDI 匯編:
Windows是一個基于圖形的操作系統,它的圖形功能是通過一個稱為GDI圖形子系統來實現的。通過使用GDI,你可以向系統中的任何設備上畫圖比如:顯示器、系統內存、打印機等等,因為GDI為我們作了很多幕后工作。
不幸的是,GDI在有些時候不能滿足你性能上的要求,特別是游戲,對于圖形功能要求是很高的,同時對于速度的要求也是很苛刻的,這就是DrectX出現的原因。我們首先來學習一下GDI,因為即使你要用DirectX,一些GDI知識也是必備的。
矩形和點:
在我們介紹GDI對象之前,我們先來看看兩個非常有用的結構---點和矩形---以及作用于他們上的功能函數。矩形在DirectX中也是經常用的,在以后的學習過程中你將會發現這一點。
我們首先來看這兩種結構的定義:
typedef struct tagPOINT {
LONG x;
LONG y;
} POINT, *PPOINT;
typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT;
矩形是由四個點來描述的:(左,上),(右,上),(左,下),(右,下)。在這里需要注意的是矩形的右和下兩條邊時不屬于矩形的。
用于操作矩形和點的函數:
我把這些函數分為三類:第一類是用于創建或者賦值的,第二類是用于操作的,第三類是用于測試的。
函數名稱 類型 作用
SetRect 創建賦值 用于設置RECT的成員變量
SetRectEmpty 同上 用于將RECT的所有成員置0的
CopyRect 同上 用于將一個矩形的成員值賦給另外一個矩形的
IntersectRect 操作 用于找到兩個矩形的交集
UnionRect 同上 用于創建同時包含兩個矩形的最小矩形
OffsetRect 同上 用于移動一個矩形
EqualRect 測試 測試兩個矩形的成員是否相等
IsRectEmpty 同上 測試一個矩形的成員是否都為0
PtInRect 同上 測試一個點是否位于一個矩形之內
關于這些函數的具體定義請大家參閱MSDN.
窗口點滴:
我們都很清楚,一個窗口所包含的不只是我們能夠繪畫的區域.根據用處的不同,一個窗口包含最大化,最小化按鈕,標題欄,一個關閉按鈕,一個系統菜單.滾動條等等.所包含的元素取決于你所創建的窗口樣式.不過這些都不用我們擔心,系統會為我們很好的繪制他們,我們所要關心的就是在客戶區如何把自己的東西繪制好.
正如上面所提到的,我們可以自由繪制的區域是"客戶區".除客戶區以外的任何區域都屬于非客戶區.任何時候我們想重新繪制客戶區的話都可以通過發送WM_PAINT消息來實現.
GetClientRect()
這個函數用于得到客戶區的大小,并把它保存到一個矩形里面.
GetWindowRect()
這個函數用于得到整個窗口的大小,并把結果村到一個矩形里面.
以上兩個函數的具體定義請參閱MSDN.
AdjustWindowRect() & AdjustWindowRectEx()
這兩個函數用于讓客戶區適應窗口的大小,使用哪個取決于你創建窗口時使用的是:CreateWindow()還是CreateWindowEx()
設備環境:
Windows可以向多種設備上繪制圖形,比如顯示器,打印機等.這是通過使用設備環境來實現的.設備環境是對具體設備的抽象.設備環境為我們屏蔽了諸多硬件的不同,讓我們以同一種方式向所有的設備上繪制圖形.項具體設備上轉化的工作就交給了設備環境. 這就是所謂的設備無關性.
當然,設備無關性是有代價的.使用設備環境要比直接跟硬件交互慢得多,因為繪制命令要經過好幾層才能到達所要求的硬件.這個問題解決的方案就是DirectX,它提供了一種更為底層的操作硬件的方式(DirectX直接跟設備驅動程序交互).
下面是有關設備環境的函數:
GetDC() 這個函數為特定的窗口向系統"借"設備環境,大家記清楚了,是借,要還的!
ReleaseDC() 這個函數是將設備環境還給系統.
以上函數的具體定義請參閱MSDN.
難道所有的設備環境都必須跟窗口相關嗎?答案是否定的,內存設備環境將是這個問題的最好的例子:
內存設備環境:本質上就是一塊內存,但你可以象是對待設備一樣對待這塊內存.大家可能會問這有什么用處呢?這當然有用處,大多數游戲都是使用雙緩沖以消除閃爍,為顯示器創建一塊內存設備環境就能消除閃爍.在以后的學習中我們會逐步介紹.
相關函數:
CreateCompatibleDC() 創建內存環境,實質上就是申請一塊內存
DeleteDC() 釋放內存
對于這塊剛剛申請的內存環境里面有什么呢?如果你非要問個究竟的話那我只能告訴你:里面只有一個1*1的單色位圖.1*1單色位圖有什么用處呢?答案是為了內存設備環境的存在,因為空的設備環境是沒有辦法存活的.
GDI對象
一提到對象大家馬上就想到了OO,但是這些GDI對象跟現在的面向對象里面的對象是有區別的,當然也有相同的地方。我
們是通過句柄來操作對象的。
有五種GDI對象是需要我們學習的,他們分別是:HBITMAP,HBRUSH,HPEN,HFONT,HRGN,以下就是對他們的詳細介紹:
.HBITMAP 位圖對象 它是由一個二維的圖像組成的,在Windows中,它通常是利用一個位圖創建的。
.HBURSH 畫刷對象 可以把它認為是一種沾滿油漆的刷子,那么它的作用也就很顯而易見了,就是把設備環境的某個特定區域刷成特定的顏色。
.HPEN 畫筆對象 是具有一定顏色跟風格的畫筆,它主要是用來繪制圖元(線,矩形,橢圓等)
.HFONT 字體對象 可以認為它是具有各種風格的字符集,用來向設備環境上繪制文本。
.HRGN 區域對象 是可以被修剪,填充的一個具有某種形狀的區域,可以使矩形,橢圓,多邊形,或者你能想到的任何形狀。
在一個特定時刻里一個設備環境里各種GDI對象只能存在一個實例,也就是說,在一個時刻里,一個設備環境里面只能有一個位圖,一枝畫筆,一個畫刷,一種字體,一種特定區域形狀??梢园阉墓ぷ饔靡韵聢鼍皝砻枋觯阂粋€用于繪制圖形的設備(設備環境),它首先需要一張紙(位圖對象)來繪制圖形,一枝筆(畫筆對象)來畫,一個刷子(畫刷對象)來填充背景,一種字體(字體對象)來寫字,以及一種風格(區域對象)來畫圖.
CreatePen() 用于創建畫筆對象
CreateSolidBrush() 用于創建畫刷對象
CreateBitmap() 用于創建位圖對象
CreateFont 用于創建字體對象
DeleteObject() 銷毀所創建的對象
SelectObject() 用于將指定的對象選入設備環境
切記:
從技術角度上來看,Windows為每個應用程序維護一個特定的內存區域,當這個應用程序結束時Windows會自動釋放該應用程序所占有的內存區域,我們似乎不用把創建的對象刪除掉,但是當這個程序要運行很長時間,并且不斷向系統申請內存的時候我們就該傻眼了,因為我們的程序就會狂吃內存,這是我們所不愿見看到的,所以,作為一個良好的編程習慣,一定把自己所申請的內存給釋放掉.
關于像素:
我們的顯示器是由一個個點組成的,其中的一個點就是一個像素.這是我們所能操作的最小單元,當然點越多越小時我們就感覺越清晰.在我工作的大部分時間里我的顯示器的分辨率都是1028*768,這意味著我在我的顯示器上有超過750,000個點。除了顯示器的寬和高你有一個顏色深度是你必須關心的,即一個像素的顏色是用幾個比特來表示的,如果是一個比特,很顯然只能夠表示兩種顏色,也就是單色顯示器。最常用的是8,16,24和32比特顏色深度。8位色又稱為時索引色,它涉及到調色板的相關知識,這里我就不詳細介紹了,相關的介紹已經很多了。
對于16,24,32位色,它們都是通過RGB三原色來表示一個像素的顏色的----紅,綠,藍。
每個像素的表示方式在DirectX里特別關心的,在GDI里,我們可以把一切都看成是24比特的,系統會為我們作相應的轉換工作的。
在GDI里,每種顏色都是由COLORREF來表示的。COLORREF在本質上就是一個整數。共24比特,每種顏色用8比特來表示。
下面是RGB宏定義:
#define RGB(r,g,b)
((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
有關像素的操作:
SetPixel() 將特定像素變成特定顏色
SetPixelV() 功能同上,但返回值不同
GetPixel() 返回指定像素的顏色值
以上函數詳見MSDN
將這段代碼防盜消息處理函數里面:
case WM_MOUSEMOVE
{
if(wParam & MK_LBUTTON)
{
int x=LOWORD(lParam);
int y=HIWORD(lParam);
HDC hdc=GetDC(hWindow);
SetPixelV(hdc,x,y,RGB(255,255,255));
ReleaseDC(hWindow,hdc);
}
return 0;
}
break;
你將看到什么呢?哈哈,這就是我們畫圖軟件的雛形,鼠標所到之處都會留下痕跡,這也是讓我們感到興奮的地方.當然這里面有太多的不足,可別忘記了,這才剛剛開始啊!!
使用畫筆:
在前面我們完成了一個畫圖軟件的雛形,我們看到了能夠操作像素給我們帶來的快樂,但是如果我們只能夠操作像素的話那就很不爽了,試想在一個場景多變的游戲里面,如果我們來一個像素一個像素的來處理的話那么游戲編程絕對是件痛苦的事情。好在我們有畫筆,畫刷,以及后面將要講到的高級工具。
前面我們已經提到過關于畫筆的創建工作,在這里我們再稍微提及一下:
CreatePen() 用于畫筆的創建,它的參數決定了它的風格
詳見MSDN
圖形繪制函數:
在介紹圖形繪制函數之前,我們先來討論一下設備環境中的當前位置。設備環境本身維護著一個當前位置,在繪制的過程中它記錄著你所走過的軌跡(點)。你可以通過以下函數來修改這個當前位置(CP):
MoveToEx() 用于設定當前位置CP
GetCurrentPosition() 用于獲得當前位置CP
詳見MSDN
現在我們已經可以改變和得到當前位置CP,但僅僅能做這些事是遠遠不夠的。
LineTo() 用于畫一條從CP開始到指定位置的直線
我們來看下面的代碼片斷:
case WM_LBUTTONDOWN
{
int x=LOWORD(lParam);
int y=HIWORD(lParam);
HDC hdc=GetDC(hWindow);
MoveToEx(hdc,x,y,NULL); //修改當前位置CP
ReleaseDC(hWindow,hdc);
return 0;
}
break;
在上面這段代碼里面我們是根據鼠標左擊的位置來設定設備的當前位置CP,接著看這段代碼:
case WM_MOUSEMOVE
{
if(wParam & MK_LBUTTON)
{
int x=LOWORD(lParam);
int y=HIWORD(lParam);
HDC hdc=GetDC(hWindow);
LineTo(hdc,x,y)
ReleaseDC(hWindow,hdc);
}
return 0;
}
break;
在我們按下鼠標左鍵的時候,我們用鼠標的當前位置設置了CP,當鼠標移動的時候我們時刻記錄著光標的當前位置并CP向這個位置畫一條直線。
使用畫刷:
在GDI里,我們用畫筆對象來繪制我們想要得的圖形,如果我們要想填充的話就要依靠畫刷了,有很多創建畫刷的函數,在這里我給大家介紹兩個最常用的,其它的大家可以參閱MSDN作更為詳細的了解:
CreateSolidBrush() 用于創建純色的畫刷
CreateHatchBrush() 用于創建具有特定風格的畫刷
填充矩形區域:
FillRect() 用于填充特定的矩形區域
一起使用畫筆跟畫刷,圖元的繪制:
現在我們可以使用畫筆來畫線,這對于我們來說是個好消息,能夠用畫刷來填充一個圖形看起來也挺酷的。我們也需要功能更為強大的API以便來實現復雜圖形的繪制,比如說圓,矩形,多邊形等,幸運的是GDI為我們提供了相關的API。這里需要提出的是,GDI是使用默認的畫筆來繪制這些圖元的,也使用默認的畫刷來填充這些圖形的。
下面是這些圖元的繪制函數:
Ellipse() 繪制橢圓
Rectangle() 繪制矩形
RoundRect() 繪制圓角矩形
Polygon() 繪制多邊形
在這里我想重點說一下多邊形的繪制,它是通過將多邊形的各個點存儲在一個點數組里來實現,然后依次連接這些點就實現了多邊形的繪制。
我們還可以改變多邊形的填風格:
SetPolyFillMode() 設置多邊形的填充風格
GetPolyFillMode() 返回多邊形的填充風格
以上API的詳細含義及定義請參閱MSDN
總結:在這一章里面我們粗略的學習了GDI的幾個方面,坦率地說,這些東西我們都不太經常用,但最好是了解這些,就像本章的主題“千里之行,始于足下”,在學會飛行之前我們要先會走路,如果我們連走路也不會,那我們要先學會爬行。
原文轉自:http://www.anti-gravitydesign.com