1 概 述數據結構
DirectX是Microsoft爲軟件開發人員提供的一套精心設計的接口,用於開發高性能、實時的應用程序。它以COM(component object modal)爲基本結構[1],位於硬件和軟件之間,像gdi(graphics device interface)同樣提供了硬件無關的API(application programming interface)接口;它和GDI有一重要不一樣點:DirectX是一套底層的API接口,它提供了直接訪問硬件的能力,使得DirectX應用程序能充分發揮硬件的威力。
DirectDraw是DirectX中提供直接操縱顯存、執行硬件映射、硬件覆蓋及切換顯示頁等功能的組件。它不但兼容已有的Windows應用程序和驅動程序,並且還兼允許多顯示卡:從簡單的SVGA到支持圖像剪裁、拉伸和非RGB格式圖像的高檔顯示卡。DirectDrawHAL(hardware-abstraction layer)抽象了顯示卡的硬件功能,以設備無關的方式提供了一些之前是設備相關的功能,如多顯示頁技術、訪問並控制顯示卡映射寄存器、支持3DZ-Buffer、支持Z-order硬件覆蓋、訪問圖像拉伸硬件,以及同時訪問標準顯存和控制顯存。正由於DirectDraw有這些優勢,如今許多基於Windows95/98/NT的遊戲程序都使用了DirectDraw;而它的設備無關性使開發者擺脫了繁重的顯示卡接口工做,集中精力實現程序的主要功能。DirectX中的DDraw.dll實現了DirectDraw所須要的函數、對象及其接口。下面先介紹DirectDraw中主要的函數、對象和接口,而後再說明DirectDraw的使用方法。app
2 DirectDraw中的函數、對象和接口簡介異步
DirectDraw包括一個表明顯示卡的主對象DirectDraw,由函數DirectDrawCreate建立,實現接口IDirectDraw和IDirectDraw2。若是機器上裝有多個顯示卡,能夠爲每一個顯示卡建立一個DirectDraw對象。也能夠建立多個DirectDraw對象,它們各自獨立並表明同一物理對象。IDirectDraw是爲了和之前版本DirectX兼容而保留的接口,基於DirectX3以上版本的程序應該使用IDirectDraw2。IDirectDraw2和IDirectDraw接口所包含的方法定義基本相同,但具體實現不一樣。DirectDraw對象主要用於建立其它3個對象:DirectDrawSurface、DirectDrawPalette、DirectDrawClipper。
DirectDrawSurface對象由函數IDirectDraw2::CreateSurface建立,表明顯存中一塊線性區域,實現接口IDirectDrawSurface和IDirectDrawSurface2。IDirectDrawSurface是爲了和之前版本DirectX兼容而保留的接口,基於DirectX3以上版本的程序應該使用IDirectDrawSurface2。兩者的接口定義也基本相同。能夠爲一個DirectDraw對象建立多個IDirectDrawSurface對象,表明物理屏幕或邏輯屏幕,經過IDirectDrawSurface2::Flip、IDirectDrawSurface2::BltFast等方法切換顯示頁或映射部分屏幕內容。
DirectDrawPalette對象由函數IDirectDraw2::CreatePalette建立,實現接口IDirectDrawPalette。它表明顯示卡的物理調色板,能夠是16色或256色。每一個DirectDrawPalette必須附着(attach)在一個DirectDrawSurface上,不一樣的DirectDrawSurface對象能夠有不一樣的DirectDrawPalette。
DirectDrawClipper對象由函數IDirectDraw2::CreateClipper或DirectDrawCreateClipp
er建立,實現接口IDirectDrawClipper。DirectDraw用它來處理屏幕的剪貼。它經常使用於在Window模式(與全屏模式相對應)下運行的DirectDraw程序,在使用前也必須被附着在一個DirectDrawSurface上。ide
3 DirectDraw程序通常工做過程函數
和通常的Windows程序[2]同樣,DirectDraw程序要先建立一個主窗口,而後進行DirectDraw的初始化:建立所須要的對象,設置程序的工做模式,創建必要的數據結構。在初始化工做完成後,就能夠在主窗口的消息循環中根據用戶的輸入調用相應對象的方法。oop
3.1 初始化DirectDraw
3.1.1 建立DirectDraw對象
首先調用DirectDrawCreate建立表明某個顯示卡的DirectDraw對象:
ddres=DirectDrawCreate(NULL,&lpDD,NULL);
DirectDrawCreate的第一項參數是表明顯示卡驅動程序的GUID(globally unique identifier),若爲NULL則表示採用系統默認的驅動程序。第二項參數是IDirectDraw接口類型指針的地址,用於接收指向由DirectDrawCreate所建立對象的指針。這個指針不需應用程序預先分配內存。DirectDrawCreate成功時會調用AddRef將對象的引用計數加1。若是要知道系統中全部驅動程序的GUID,能夠調用DirectDrawEnumerate,它接收兩個參數:回調函數的地址和傳給回調函數的自定義數據的地址,其工做方式和Win32 API Enum Windows相似。
3.1.2 設置DirectDraw的工做模式
建立DirectDraw對象後應該立刻設置DirectDraw對象的工做模式:
ddres=lpDD→SetCooperativeLevel(hWnd,DDSCL_EXCLUSIVE|DDSCLFUL_LSCREEN);
SetCooperativeLevel的第一個參數是和DirectDraw對象關聯的窗口句柄,通常是程序主窗口的句柄;第二個參數指明瞭DirectDraw對象的工做模式。DirectDraw對象有兩種工做模式:普通模式(Windowedmode,參數DDSCL_NORMAL)和獨佔模式(Full_Screen mode,參數DDSCL_EXCLUSIVE)。普通模式下DirectDraw和普通Windows程序的區別不大,主要是DirectDraw程序能夠爲所欲爲地讀取整個屏幕的內容或在屏幕的任意位置輸出,而其它的Windows程序毫無察覺。獨佔模式就是遊戲「紅色警報」和「赤壁」所採用的方式,並必須和全屏模式(參數DDSCL_FULLSCREEN)聯用,此時程序的主窗口被擴展爲整個屏幕。其它應用程序都成爲後臺程序,使用Alt+Tab鍵能夠在程序間切換。
3.1.3 獲得IDirectDraw2類型的接口
接下來利用COM的重要方法QueryInterface,經過IDirectDraw接口獲得一個IDirectDraw2類型的接口。QueryInterface成功時會將對象的引用計數加1,而咱們也再也不須要IDirectDraw接口,所以這裏調用IDirectDraw::Release將對象的引用計數減1,也即釋放先前獲得的IDirectDraw接口。
3.1.4 根據須要切換屏幕顯示模式
若是DirectDraw的工做模式設定爲全屏獨佔模式,則能夠根據須要切換屏幕顯示模式:
ddres=lpDD2→SetDisplayMode(800,600,16);
SetDisplayMode的前兩個參數是屏幕的橫、縱分辨率,最後一個是每一個像素點的顏色位數。上例將屏幕設爲800×600,16位色。
3.1.5 建立DirectDrawSurface對象並獲得IDirectDrawSurface2接口
建立DirectDraw對象後,下一步調用IDirectDraw2::CreateSurface建立表明物理屏幕或邏輯屏幕的DirectDrawSurface對象。IDirectDraw2::CreateSurface的第一個參數是DDSURFACEDESC結構的地址,第二個參數是一個IDirectDrawSurface接口類型的指針地址,第三個參數必須是NULL。數據結構DDSURFACEDESC包含了建立DirectDrawSurface所需信息。在獲得一個IDirectDrawSurface類型接口後,仍使用QueryInterface獲得一個IDirectDrawSurface2類型的接口,並釋放先前獲得的IDirectDrawSurface接口:
//Get the IDirectDrawSurface2interface.
LPDIRECTDRAWSURFACE2lpDDSPrimary=NULL;
ddres=lpDDSPrimaryTemp>QueryInterface(IID_IDirectDrawSurface2,(LPVOID)&lpDDSPrimary;
lpDDSPrimaryTemp→Release();
上面獲得的lpDDSPrimary指向表明物理屏幕的對象。還需調用IDirectDrawSurface2::GetAttachedSurface獲得指向建立時附帶的表明相關邏輯屏幕的對象指針。至此,DirectDraw初始化工做完成。性能
3.2 使用DirectDraw
3.2.1 使用GDI函數向物理屏幕或邏輯屏幕輸出
調用IDirectDrawSurface2::GetDC可得表明某個屏幕的設備描述表的句柄,使用GDI函數輸出,最後調用IDirectDrawSurface2::ReleaseDC釋放句柄。爲防止在對屏幕做圖期間其它應用程序爭奪顯存,IDirectDrawSurface2::GetDC調用IDirectDrawSurface2::Lock獲得Winl6 Lock。這意味着其它程序在該程序釋放Win16Lock前都不能訪問GDI和USER資源。IDirectDrawSurface2::ReleaseDC調用IDirectDrawSurface2::Unlock釋放Win16 Lock。因而,在調用IDirectDrawSurface2::GetDC和IDirectDrawSurface2::ReleaseDC期間,Windows將被掛起。所以,應用程序應儘可能縮短這一對函數調用之間的間隔時間,並且調試程序也沒法跟蹤這段時間內執行的操做。
3.2.2 交替切換物理屏幕和邏輯屏幕或執行屏幕內容的映射
準備好內存中的邏輯屏幕後,能夠調用IDirectDrawSurface2::Flip方法切換物理屏幕和邏輯屏幕,也可調用IDirectDrawSurface2::BltFast、IDirectDrawSurface2::Blt等方法執行部分屏幕內容的映射。通常狀況下程序採用異步方式,在顯示卡硬件執行切換動做的同時準備下一頁屏幕,使CPU和顯示卡硬件並行,提升總體執行速度。
使用Flip切換物理屏幕和邏輯屏幕後,原指向物理屏幕的指針仍然指向物理屏幕,原指向邏輯屏幕的指針仍然指向邏輯屏幕,即指針所指內容也被交換了,便於程序操縱各個屏幕而不至於混淆。
3.2.3 釋放DirectDraw對象
程序結束以前要釋放所建立的DirectDraw對象。這隻要在相應接口上調用Release方法便可。設計
4 使用DirectDraw的技巧和注意事項指針
4.1 檢查方法的返回值
正確執行DirectDraw方法時都返回DDOK。且其值是零。返回其它值代表發生了某種錯誤。通常地,程序應檢查這些返回值以決定是否出錯。調試
4.2 檢查Flip和Blt的狀態
若是在Flip或Blt操做的返回值是DDERR_WASSTILLDRAWING狀況,爲提升效率,DirectDraw提供了IDirectDrawSurface2::GetFlipStatus和IDirectDrawSurface2::GetBltStatus方法。它們能當即返回當前的Flip和Blt狀態,因而應用程序能夠在上一操做完成以前執行某些其餘的任務。
4.3 在位圖映射中使用ColorKey
ColorKey是一種或幾種顏色的集合,用於在位圖映射操做中區分前景色和背景色。ColorKey包括兩種:Source color key和Destination color key。前者是指源位圖中表明透明色的顏色,在執行映射操做時將不被映射到目標位圖上;後者是指目標位圖中將被源位圖中相應位置顏色取代的顏色,若是目標位圖指定了Destination color key,則只有這些指定的顏色被替換。能夠在建立DirectDrawSurface對象時指定Color Key;也可以使用方法IDirectDrawSurface2::GetColorKey和IDirectDrawSurface2::SetColorKey以獲取和設置已有的DirectDrawSurface對象的ColorKey。
4.4 GDI重定向
因爲GDI在Windows系統啓動時先於DirectDraw被裝入,而DirectDraw工做時又繞過了GDI,所以GDI不知道DirectDraw對物理屏幕所進行的操做。即便DirectDraw調用了Flip方法將先前的物理屏幕切換爲邏輯屏幕,如不採起措施,GDI將仍然向切換後的邏輯屏幕上輸出。若是DirectDraw程序擁有菜單、滾動條等由GDI負責繪製的元素,那麼在Flip完成後這些元素就會成爲不可見,而在對應的邏輯屏幕被切換成物理屏幕時又會顯示出來。爲避免屏幕閃爍,DirectDraw提供了IDirectDraw2::GetGDISurface,用於肯定當前被GDI認爲是物理屏幕的DirectDrawSurface對象;以及IDirectDraw2::FlipToGDISurface,用於將GDI的輸出從新定向到當前的物理屏幕上。若是須要,能夠在每次Flip操做後調用它,以保證屏幕正常。
4.5 在顯存中存放位圖
因爲從顯存到顯存的映射比從系統內存到顯存的映射快,因此常常將須要映射的位圖存放在顯存中以提升速度。大多數顯示卡在存放了物理屏幕和相關邏輯屏幕以外還有足夠的內存能夠用來存放位圖。能夠調用IDirectDraw2::GetCaps檢查顯存;在建立DirectDrawSurface對象時能夠經過結構DDSURFACEDESC中的DDSCAPS域指定該對象存在於顯存或系統內存中。若是指定在顯存中建立對象,而顯存又沒有地方容納該對象,IDirectDraw2::CreateSurface會返回錯誤信息DDERR_OUTOFVIDEOMEMORY。若是沒有指定建立的位置,DirectDraw老是儘可能利用空閒的顯存。
4.6 檢查硬件的性能
雖然DirectDraw經過HAL和HEL屏蔽了硬件的具體特性,但應用程序也需根據硬件的不一樣性能來改變自身的執行方式。利用IDirectDraw2::GetCaps方法能夠獲得有關硬件性能的詳細信息。
4.7 保持主窗口的消息循環暢通 在調用SetCooperativeLevel設置DirectDraw的工做模式時,應用程序爲DirectDraw指定了主窗口。因爲DirectDraw直接操縱硬件可能致使死機,所以DirectDraw在後臺監視主窗口的消息循環,當消息循環長時間沒有反應時,DirectDraw就釋放全部的資源,結束應用程序的執行。因此DirectDraw程s序應該注意避免長時間封鎖消息循環