?? directdraw
字號:
DirectDraw 游戲編程基礎(3)
游戲使計算機的發展超越了晶體管時代
微軟公司供稿
例一的擴展(DDEX2和DDEX3)
DDEX1包含了一個最基本的DirectDraw的實現方法。它生成了DirectDraw和DirectDrawSurface對象,同時也生成了一個主表面(Surface)和與之相關的后臺緩沖區,并在后臺緩沖區打印文本,并可以在表面(Surface)之間進行切換。
在DirectX 3 SDK(DDEX2)中的第二個DirectDraw 例程擴展了關于DDEX1應用程序。DDEX2包括將一個位圖文件載入到后臺緩沖區的函數。
第三個DirectDraw 例程將這一函數進一步地擴展了。除了主表面(Surface)和后臺緩沖區之外,DDEX3還生成了兩個隱屏表面(Surface),并且在每一個隱屏表面(Surface)之中都載入了一個位圖文件。然后,DDEX3使用IDirectDrawSurface::BltFast方法,將一個隱屏的內容復制到后臺緩沖區中。之后,彈出這些緩沖區,并且將下一個隱屏表面(Surface)的內容復制到后臺緩沖區。
以下的部分將更詳細地檢查這一新的函數。
在一個表面(Surface)上載入一個位圖(Bitmap)
就與DDEX1一樣,dolnit是DDEX2應用程序的初始化函數。雖然,在DDEX2中,DirectDraw的初始化方式表面上與在 DDEX1中的DirectDraw的初始化方式不太一樣,但它們的實質是一樣的。這一過程如下列的程序代碼所示:
LPddPal = DDLoadPalette(LpDD, szBackground);
if (LpddPal == NULL)
goto error;
ddrval = LpDDSprimary->SetPalette(LpDDPal);
if( ddral != DD_OK )
goto error;
// Load a bitmap into the back buffer。
Ddrval = DDReLoadBitmap(LpDDSBack, szBackground);
if( ddrval != DD_OK )
goto error;
生成調色板
這個程序代碼的第一行是:從DDLoadPalette函數返回一個值。如果你想知道在哪能找到DDLoadPalette,你可以在\DXSDK|SAMPLES|MISC目錄中的Ddutil.cpp文件中找到它。你會發現,在DirectX 3 SDk的大部分DirectDraw例程中都使用了Ddutil.cpp文件。最為關鍵的是:該文件上包括了能從文件中或是從資源中載入位圖和調色板的函數。這些函數的代碼并非一遍遍地重復出現在例程文件中,而且被放置在能被重復使用的同一文件之中。
注意:如果你正在使用MS Developer Studio(微軟開發工作室)編輯DDEX2和用DirectX 3 SDK提供的其它工具,你必須把Ddutil.cpp文件插入到DDEXx文件工作區的文件表中。重申一遍:在工作區中必須包括Ddutil.cpp:
1. 在插入(insert)菜單上,單擊Files進入Projeects。
2. 單擊Browse.
3. 單擊DXSDK\SDK\SAMPLES\MISC\目錄。
4. 單擊Ddutil.cpp
5. 單擊ADD
對于DDEX2來說,從Back.bmp文件中,DDLoadPalette創建了一個DirectDraew對象。DDLoadPalette函數實際上是來檢查用以產生調色板的一個文件或資源是否存在。如果不存在的話,該函數就創建一個缺省的調色板,對于DDEX2 來說,DDLoadPalette函數從文件中提取調色板信息,并通過ape指針將其存儲在一個指定的結構中,然后它生成DirectDrawPalette 對象。如下面的代碼所示:
pdd->CreatePalette(DDPCAPS_8BIT, ape, &ddpal, NULL);
return ddpal;
當IDirectDraw::Createpalette方法返回后,ddpal參數將指向DirectDrawPalette對象,其中,對象DirectDrawPalette是從DDLoadPalettede的調用返回的。
Ape參數是一個指針,它可以包括2,4,16或256個入口,呈直線分布。這些入口的數目由IDirctDraw::CreatePalette參數決定。在這種情況下,dwFLags參數被設置為DDPCAPS_8BIt,它表示:在這個結構中有256個入口。每個入口包括4位(一位紅通道,一位綠通道,一位蘭通道和一個標志位)。
設置調色板
在生成調色板之后,你要通過調用IDirectDrawSurface::SetPalette方法,將DirectDrawPalette對象的指針轉到主表面(Surface)上,如下列代碼所示:
ddrval = LpDDSPrimary->SetPalette(LpDDPal);
if( ddrval != DD_Ok )
// SetPalette failed
一旦你已經調用了IdirectDrawSurface::SetPalette,DirectDrawPalette對象就被嵌入到DirectDrawSurface對象中了。不論何時你需要改變這一調色板,你要作的就是生成一個新的調色板并重新設置該調色板。(這就如例程中所做的一樣。然而,也有其它的改變調色板的方式。我們可以在其它例程中看到)。
在后臺緩沖區中載入一個位圖文件
一旦DirectDrawPalette對象被嵌入到DirectDrawSurface對象之中,DDEX2就將Back.bmp bitmap載入到后臺緩沖區中。使用下例的程序代碼可實現該過程:
// Load a bitmap into the back buffer.
Ddrval = DDReLoadBitmap(LpDDSBack, szBackground);
if( ddrval != DD_Ok )
// Load failed
DDReLoadBitmap是出現在Ddutil.cpp中的另一個函數。它從一個文件或資源中將一個位圖文件載入到一個已經存在的DirectDraw表面(Surface)之中。(就象在DDEX5中那樣,你可以使用DDLoadBitmap創造一個表面(Surface)并且將位圖載入那個表面(Surface))。對于DDEX2來說,DDReLoadBitmap把szBackground指向的Back.bmp載入到ipDDSBack指向的后臺緩沖區,DDReLoadBitmap調用DDCopyBitmap函數,將文件復制到后臺緩沖區中,并且將緩沖區擴展到適當的。
DDCopyBitmap函數將位圖復制到內存之中,然而利用GetObject函數得到位圖的大小。DDCopyBitmap然后使用下列的代碼得到后臺緩沖區的大小(它可以放置位圖):
//
// get size of surface
//
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_HEIGHT DDSD_WIDTH;
pdds->GetSurfaceDesc(&ddsd);
ddsd是指向DDSRFACEDESC結構的一個指針。該結構存儲了DirectDraw表面(Surface)的當前描述。在這種情況下,我們需要注意的是:DDSURFACEDESC的成員描述這個表面(Surface)的高度和寬度,分別表示為:DDSD_HEIGHT和 DDSD_WIDTH。調用IDirectDrawSurface::GetSurfaceDesc方法,把適當的值來載入到這個結構。對于DDEX2來說,這些值將是:高480和寬640。
DDCopyBitmap函數鎖定表面(Surface)并將位圖文件復制到后臺緩沖區,使用StretchBit函數延伸或壓縮后臺緩沖區到可適用的大小。表示如下:
if ((hr = pdds->GetDC(&hdc)) == DD_OK)
{
StretchBlt(hdc, 0,0,ddsd.dwWidth, ddsd.dwHeight, hdcImage,x, y, dx, dy, SRCCOPY);
pdds->ReleaseDC(hdc);
}
彈出表面(Surface)
在DDEX2例程中的彈出表面(Surface)操作本質上與在DDEX1例程中的彈出操作是同樣的過程。但是在表面(Surface)丟失的情況下,你必須通過調用DDReLoadBitmap函數,在表面(Surface)恢復之后,,將bitmap再次載入到后臺緩沖區中。
從一個隱屏表面(Surface)按位隔行拷貝
DDEX2是在后臺緩沖區中取出和放入位圖的,然后在后臺緩沖區和主緩沖區之間切換。這并不是一個展示位圖的很實際的方法。DDEX3擴展了DDEX2的功能,它包括了兩個隱屏緩沖區,且在其內部存放有兩個位圖(一個對應于偶行屏幕,另一個對應于奇行屏幕)。DDEX3把一個屏幕按位隔行拷貝到后臺緩沖區中,再把另外一個屏幕按位隔行拷貝到另一個后臺緩沖區中,然后彈出表面(Surface)。
生成隱屏表面(Surface)
下列的代碼在DDEX3 中加到dolnit 函數可生成兩個隱屏 緩沖區:
// Create an offscreen bitmap.
Ddsd.dwFlags = DDSD_CAPS DDSD_HEIGHT DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwHeight = 480;
ddsd.dwWidth = 640;
ddrval = lpDD->CreateSurface( &ddsd,&lpm DDSOne,NULL);
if (ddrval != DD_OK)
{
return initFail(hwnd);
}
//Create another offscreen bitmap.
Ddrval = lpDD->CreateSurface( &ddsd,&lpm DDSTwo,NULL);
if (ddrval != DD_OK)
{
return initFail(hwnd);
}
如代碼中所示,dwFlags成員設定了應用程序將使用DDSAPS結構,并且設置緩沖區的高度和寬度。表面(Surface)是一個平面式隱屏緩沖區,就如同通過設置在DDSCAPS結構中的DDSCAPS_OFFSCREEN標志所表示的一樣。在DDSURFACEDESc結構中,高度和寬度被分別設置為480和640。通過使用IDirectDraw::CreateSurface方法,表面(Surface)就這樣被生成了。
因為兩個隱屏緩沖區有著同樣的大小,故生成第二個緩沖區的唯一要求就是再運行IDirectDraw::CreateSurface(當然,要用不同的指針名字)。
通過在DDSCAPS結構中,或是設置DDSCAPS_SYSTEMMEMORY,或是設置DDSCAP_VIDEOMEMORY的容量,你可以將該:隱屏緩沖區或是放置在系統內存中或是顯存中。通過將位圖存盤在顯存中,你可以增加隱屏表面(Surface)和緩沖區之間切換的速度。當我們開始討論位圖動畫時,速度將變得更加重要。但是,此時你應當注意:如果你僅為隱屏緩沖區設置DDScAPS_VIDEOMEMORY,而沒有足夠的顯存來保存整個位圖文件,那么,當你試圖創建表面(Surface)時,就會返回一個DDERR_OUTOFVIDEOMEMORY的錯誤值。
將位圖文件載入后臺緩沖區
在兩個隱屏表面(Surface)生成后,DDEX#使用INITSURFACES函數,從Frnt.bmp文件中將位圖文件載入到表面(Surface)中。InitSurfaces函數使用Ddutil.cpp中的DDCopyBitmap載入這兩個位圖文件,如下列代碼所示:
// Load our bitmap resource.
Hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL),szBitmap,
IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
if (hbm == NULL)
return FALSE;
DDCopyBitmap(lpDDSone,hbm,0,0,640,480);
DDCopyBitmap(lpDDSTwo,hbm,480,640,480);
DeleteObject(hbm);
return TRUE;
如果你在MS Paint(微軟畫筆)或是另一個繪畫程序中看到Frnt.bmp文件,你可以看到位圖文件是由兩個屏幕組成的(其中一個在另一個的上部)。DDCopyBitmap函數在屏幕相匯點上將位圖文件一分為二,并將第一份位圖文件載入第一個隱屏表面(Surface)(IPDDSOne)中,同時將第二份位圖載入第二個隱屏表面(Surface)(IPDDSTwo)中。
將隱屏表面(Surface)按位隔行拷貝到后臺緩沖區
WM TIMER包含了寫表面(Surface)和彈出表面(Surface)的代碼。在DDEX3的情況下,它包含下列的代碼,用來選擇適當的隱屏表面(Surface),并將它按位隔行拷貝到后臺緩沖區中。
rcRECT.LEFT =0;
RCRECT.TOP =0;
RCRECT.right =640;
rcRect.bottom =480;
if(phase)
{
pdds = lpDDSTwo;
phase = 0;
}
else
{
pdds = lpDDSOne;
phase = 1;
}
while(1)
{
ddrval =lpDDSBack->BltFast(0,0,pdds,&rcRect,FALSE);
if(ddrval == DD_OK)
{
break;
}
"phase"決定了將哪一個隱屏表面(Surface)按位隔行拷貝到后臺緩沖區中。然后,IDirectDrawSurface::BltFAst方法被調用,并將已經被選擇好的隱屏表面(Surface)按位隔行拷貝在后臺緩沖區中,開始位置為(0,0),它位于屏幕的左上角。參數rcRect指向結構Rect,它定義了隱屏表面(Surface)的左上角和右下角。最后的參數被設置為FALSE(或0),這就表明了沒有專門的轉移標志以備使用。
在這里,我很想補充說明的是:在何種情況下應該選擇IDirectDrawSurface::Blt方法,在何種情況下應該選擇IDirectDrawSurface::BltFast方法。如果你正在從一個隱屏緩沖區中進行一次按位隔行拷貝,你應當使用IDirectDrawSurface::BltFast。如果你的系統顯存中是使用硬件進行按位隔行拷貝,你雖然不會真正提高拷貝的速度,但是,它會節省系統模擬硬件時間,從而使整個按位隔行拷貝時間縮短約10%。因此,我推薦讀者使用IDirectDrawSurface::BltFast進行所有的顯示操作(從顯存按位隔行拷貝到顯存中)。如果你正在從系統內存中按位隔行拷貝,或者要求專門的硬件標志位,這樣的話,你就必須使用IDirectDrawSurface::Blt。
一旦隱屏表面(Surface)被載入后臺緩沖區中,后臺緩沖區和主表面(Surface)就如同前邊的例程中所顯示的一樣被彈出。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -