在開始學習DirectDraw編程之前,有一些題外話要說明,以下內容均是個人的心得和體會,如果其中有什麼謬誤之處,敬請諒解,同時個人不對可能造成的後果負責。。
以下幾點是在編制DirectX應用程序時應該注意的:
- 盡管使用VB或DELPHI都可以制作DirectX應用程序,但考慮到代碼的效率,還是應使用C或C++。其中,C++是面向對象的編程語言,可以使你的程序更易于維護,如果再考慮到代碼的兼容性,我推薦使用Microsoft Visual C++ 5.0集成開發環境。當然,使用Borland C++ 5.0也是一個不錯的選擇。
- 你應該已經有編制Windows應用程序的經驗,以及C++的基礎。
- 熟悉你將要使用的編程工具的運用。例如,如何為集成開發環境設置各種參數,以及編譯程序和連接程序的命令行參數。
- 確認你已下載了微軟的DirectX 5.0 SDK中的相關文件,特別是頭文件和庫文件,這些都是編程所必需的。
- 確認你已經安裝了DirectX 5.0或更高版本的運行時刻庫,因為本教程的所有程序均使用了DirectX的新接口(Interface),必須要5.0或更高版本支持。
DirectDraw是什麼?
DirectDraw是DirectX中的關于視頻輸入輸出的基本部分,使用DirectDraw可以方便地編制出高效的視頻處理程序,只要用戶的硬件支持DirectDraw,就能保證你的代碼可以處理它們。 簡單地說,一個DOS程序員可以方便地直接訪問視頻顯存,從而高效地處理視頻動畫,而在Windows的32位環境中,使用DirectDraw可以做類似的工作(而且做得更好)。例如,在DOS環境中的320x200x256色圖形模式中,你可以通過在地址A000:0000開始處的一片內存區進行直接讀寫的方式來處理視頻操作,而在Windows環境中,DirectDraw也提供一片內存區供你直接讀寫,使你能更好地完成相似的操作。 當然,DirectDraw提供的遠不止這些,但一個遊戲程序員可能更關心如何直接訪問視頻顯存,以及如何高效地完成位塊拷貝操作等等。
讓我們從消息循環開始
DirectX最初是為遊戲開發而推出的,編制遊戲的程序員都很貪婪,他們會盡量榨取系統資源,並試圖讓自己的程序永遠具有最高的效率。但Windows是一個多任務的操作系統,當它發現所有的程序都處于空閒時,便會減少給這些程序的資源,其中之一就是開始清理交換文件,為了讓自己的程序給Windows以始終繁忙的假象,不妨用一些新的代碼來代替常規的方法。
這是常規的消息循環處理
while(GetMessage(&msg,NULL,NULL,NULL)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
//---------------
這是改進的消息循環處理
for(;;){
if(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE)){
if(msg.message==WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg)
}else{
if(AppPaused) WaitMessage();
else{
// 這裡進行任何不基于消息循環的處理
// 例如動畫制作
}
}
}
return msg.wParam; |
上例中,新的消息循環處理方式使得Windows認為程序始終繁忙,從而提高了程序的性能。注意,其中的AppPaused變量為真時表示程序未在前台運行,該變量的取值為WM_ACTIVATEAPP消息的wParam參數。
第一個DirectDraw程序
現在咱們開始編制第一個DirectDraw的應用程序,來示例如何建立一個全屏幕獨佔方式的應用程序,並利用HDC來實現簡單的文本輸出。 這個示例與微軟的示例程序不同,它使用了DirectX 5.0中的新接口,在制作最終可執行程序時應確保為LINK程序指定ddraw.lib和dxguid.lib這兩個文件。 示例程序的可執行程序由VC 5.0生成,只在基于Intel奔騰芯片、Windows95環境的機器上才能運行。
下面重點介紹一下建立和銷毀DirectDraw基本對象的方法,以及如何請求新接口。 首先來說明一下以後要用到的術語:
- DirectDraw基本對象。
這個對象的實體由DirectX建立,程序只能通過指向該對象實體的指針來訪問它,這一點與Delphi十分相似,在Delphi中,一個TLabel類型的變量實際只是一個指針,它指向一個TLabel類的對象。建立DirectDraw基本對象的函數是DirectDrawCreate(),該函數可在內部建立一個DirectDraw對象實體,並將指向該實體的指針返回給應用程序,以後的所有操作都基于這個指針。
- 主平面。
可以將主平面簡單地理解為“視頻顯存區”,任何直接對主平面的訪問都立刻反映到屏幕上。主平面也是一個對象(程序員只能使用指向該對象的指針),該對象封裝了幾乎所有的輸入輸出操作,包括直接訪問、利用設備相關(DC)訪問以及位塊拷貝(Blt)操作等等。主平面可以附帶一個或多個“後台緩衝區”。
- 後台緩衝區。
後台緩衝區同樣也是一個對象,也同樣只提供一個指向該對象的指針供程序員使用。後台緩衝區與主平面的操作方式幾乎一樣,不同的是所有對後台緩衝區的操作不直接反映在屏幕上,例如,你可以在後台緩衝區中放置好圖片,然後通過位塊拷貝操作(或“彈出”操作)將後台緩衝區中的內容快速映射到主平面(即屏幕),這種操作方式可以避免出現不愉快的閃爍現象。
- 位塊拷貝。
位塊拷貝是將一塊矩形區域從一個平面拷貝到另一個平面,可以進行縮放、旋轉、鏡像、透明等附加動作。DirectDraw提供的位塊拷貝方法效率十分高,程序員勿需自行編制代碼來完成相似工作。
- “彈出”操作。
Flip這個單詞有反轉、彈出等含義,在DirectDraw中,可以將其理解為迅速互換兩個平面,這種互換操作並不是內容的互換,而只是指針的互換,因此其速度比位塊拷貝快得多。例如,在全屏幕獨佔方式下,可以利用“彈出”操作快速地將後台緩衝區與主平面互換(指針的互換),從而獲得高速的視頻動畫效果。只有具有可“彈出”屬性的平面才能使用“彈出”操作。
- 釋放。
在DirectX中,各個對象均有Release方法,這個方法(成員函數)並不見得就是銷毀對象,它只是對內部計數器進行遞減操作。例如,用DirectDrawCreate()函數建立了DirectDraw基本對象後,該對象的內部計數器就置為值1,如果此時再調用該對象的Release方法,就會使內部計數器減去1,若結果為零則該對象被銷毀,但若在調用Release方法之前又為該對象請求新的接口(如IDirectDraw2),則內部計數器又會加一,需要兩次Release方法才能完全銷毀該對象。
在示例程序一中,從WinMain函數開始,在登記了窗口類並建立了程序主窗口後,就開始建立DirectDraw的基本對象了:
ddrval = DirectDrawCreate(NULL, &_lpDD, NULL);
if (ddrval!=DD_OK){
// 失敗
} |
函數DirectDrawCreate在成功時返回DD_OK(零),若是失敗則返回一個32位(HRESULT)的錯誤代碼。不止是這個函數,所有DirectX對象的成員函數(方法)均採用這種方式來返回表示成功或失敗的值。 不必去管該函數的第一個和第三個參數,現在只需要把它們設為空值即可,而第二個參數是一個指針,這個指針指向了一個LPDIRECTDRAW類型的變量,而該變量實際上也是一個指針,如果函數成功則指向IDirectDraw對象實體--------是不是有一點繞來繞去的? 當基本對象成功建立後,就需要將它“變”成較新的對象以增進性能,這也正是本示例程序為什麼需要5.0版本的DirectX來支持的緣故。
ddrval=_lpDD->QueryInterface(IID_IDirectDraw2, (void **)&lpDD);
_lpDD->Release();
if (ddrval!=DD_OK){
// 失敗
} |
下一步是利用新建立的基本對象來建立與該基本對象相關聯的主平面,這個主平面實際上就是可視的屏幕,所有對主平面的訪問都直接反映在屏幕。在建立主平面之前我們首先將基本對象的屬性設為全屏幕獨佔方式,這樣才能改變顯示模式,以及建立可“彈出”的平面集。具體的設置方法參見源程序。 在設置了全屏幕獨佔方式並設置了適當的顯示模式之後,程序建立了一個主平面,這個主平面附帶一個後台緩衝區,程序員可以使用“設備相關把柄(HDC)”或直接訪問的方式(以後再講)來對後台緩衝區進行圖形操作,所有對後台緩衝區的操作並不能反映在屏幕上,在圖形操作完成後,可以利用“彈出”操作來將後台緩衝區“彈”至主平面,從而在屏幕上顯示出來。 本示例程序在進行圖形操作示例時,首先獲取後台緩衝區的HDC,然後用標準的GDI函數來繪制文本,一旦繪制完畢,就立刻釋放剛獲取的HDC,因為任何一個平面的HDC被獲取後,該平面就被隱式地鎖定,無法進行位塊拷貝或彈出操作,只有當該HDC被釋放後,一個隱式的Unlock才被調用,程序才能對該平面進行前述操作。 完成後台緩衝區的圖形繪制後,程序調用主平面的成員函數(方法)Flip來將後台緩衝區彈至主平面,你將看到屏幕內容已被改變。
這是我個人搞的一個小封裝,可用于非C++的編程語言(如Delphi)。使用此封裝庫可比較容易地編制DirectDraw應用程序。此封裝為靜態鏈接庫,提供Visual C++和BorlandC++兩種版本,其中包括: Visual C++版庫文件 Borland C++版庫文件 相關的頭文件 兩個示例源程序 說明文檔(HLP文檔格式) |