Java中使用DirectDraw
注釋:DirectDraw是微軟DirectX SDK的一個組成部分。Java版的DirectX包含在Java 2.0 SDK中。Java中通過同DirectX一起安裝的com.ms.directX包中的一套類訪問DirectDraw。
介紹
本文將探討用於Java的DirectDraw SDK的一些優點、結構和使用。過去,使用動畫的程式需要用C++編寫(或者組合語言),原因是動畫需要很快的處理速度。動畫是通過快速連續地顯示一系列畫面(或者叫做幀)實現的。為了使使用者觀看動畫時沒有閃爍感,至少需要達到每秒12幀的速率。更高的畫面播放速率會產生更平滑的動畫:例如,動畫片的畫面播放速率通常為每秒24幀。要達到最低的每秒12幀要求,在640´480,256色模式下每秒需要處理3.6M位元組的圖象資料。處理如此數量的視頻資料是非常困難的。DirectDraw現在為Java開發人員提供了這種視頻資料處理能力。
開始使用DirectDraw API需要一些基本步驟。現將其列舉如下,並且將會在後面的部分中解釋:
- 建立DirectDraw對象。
- 設定協作層級。
- 建立需要的表面。
- 裝入需要的位元影像。
- 顯示表面。
DirectDraw對象
要在Java中使用DirectDraw,必須先建立DirectDraw對象:
dd = new DirectDraw();
一旦建立了DirectDraw對象,就需要設定協作層級。
協作層級
必須要設定DirectDraw在最高層視窗上擁有的控制層級。在最簡單的層級上,DirectDraw的功能同其他程式相同,限制在Windows 95或者Windows NT視窗內。這是DirectDraw的正常層級操作。正常協作層級可以通過下面的語句設定:
dd.setCooperativeLevel(hwnd, DDSCL_NORMAL);
注意 為了使程式能夠利用更進階的DirectDraw特性,如改變顯示模式或者修改DirectDraw的表面的行為,需要使用獨佔模式。然而,使用進階特性超出了本文的範圍。要得到關於使用進階特性的更多資訊,請參考DirectX文檔。
建立表面
DirectDraw使用術語表面(surface)代表顯示內容。表面既可以放在顯示記憶體中,也可以放在系統記憶體中。然而,如果顯示硬體沒有足夠的記憶體存放表面,DirectDraw會在系統記憶體中類比表面。DirectDraw表面的最大優點是它總是以線性記憶體地區的形式提供給開發人員。即使程式設定的顯示模式不是線性,表面為開發人員提供的仍然是線性記憶體地區。考慮下面的例子,ModeX中顯示記憶體是以一系列位面(plane)配置的(圖1)。為了畫一個點,必須防衛適當的記憶體位面以及正確的地址。DirectDrawSurface對象處理了所有這些細節。
圖 1. Mode X中的顯示記憶體組織。
一個DirectDraw表面可以包含多個記憶體緩衝區,允許構建可以切換的表面。切換表面使程式可以利用雙緩衝技術的優點。在雙緩衝方法中,程式在背景緩衝區中繪製一些內容,畫完後,快速地將背景緩衝區切換或者複製到前端緩衝區,使該畫面顯示出來。雙緩衝方法速度非常快,尤其是當前台和背景緩衝區都在顯示記憶體中時。此時,切換操作甚至不消耗任何CPU時間。
基於DirectDraw程式的使用者看到的總是原始表面(Primary surface)。為了改變使用者看到的內容,可以簡單地改變原始表面的內容。通常,需要建立一個或多個後台表面,這些表面中包含不同時間顯示的畫面。圖2說明了一個複雜的雙緩衝原始表面。該原始表面由一個背景緩衝區、一個前端緩衝區和四個後台表面組成。每個後台表麵包含一個不同旋轉階段的位元影像。要使圓柱體動起來,程式將每個位元影像從後台表面中複製到原始表面的背景緩衝區中的一個位置。每當每個圓柱體的複製完成時,程式將背景緩衝區切換到前端緩衝區。
圖2. 雙緩衝表面之間的關係
DirectDraw在Java中的實現包括兩個為表面工作而特別設計的類。DDSurfaceDesc類用來描述所建立表面的屬性。DirectDrawSurface類包含為DirectDraw表面工作所做的定義、方法和變數。
要建立DirectDraw表面,不管是原始表面還是後台表面,必須先建立DDSurfaceDesc對象,設定表面屬性,並且把表面描述結構傳遞給DirectDraw對象的CreateSurface()方法。下面的語句建立了一個由一個緩衝區組成的原始表面:
// Create a primary surface containing a single buffer.
ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS ;
ddsd.ddsCaps = DDSCAPS_PRIMARYSURFACE;
pdds = dd.createSurface( ddsd );
下面的語句建立了一個雙緩衝原始表面:
//Create a primary surface containing a back //buffer for double buffering.
ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps = DDSCAPS_PRIMARYSURFACE | DDSCPAS_FLIP | DDSCAPS_COMPLEX;
ddsd. BackBufferCount = 1;
pdds = dd.createSurface( ddsd );
下面的語句說明了如何建立一個320象素寬200行高的後台表面:
Ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.width = 320;
ddsd.height = 200;
pdds = dd.createSurface( ddsd );
將位元影像裝入到DirectDraw表面中
位元影像是程式可以用來顯示圖形內容的圖片。圖2中的四個圓柱體每一個都是一張位元影像。Java中的DirectDraw為操作位元影像檔案提供了DirectDrawBitmap類。DirectDrawBitmap對象建立後,可以複製到DirectDraw表面。下面的語句將一個叫作Frntback.bmp的位元影像檔案讀入到DirectDraw表面:
//Read the bitmap file.
Bm = new DirectDrawBitmap();
bm.filename(“Frntback.bmp”);
bm.initWidth(dx);
bm.initHeight(dy);
if( bm.loaded() != 0 )
{
// Create a DirectDrawSurface for
// this bitmap.
ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS |
DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.width = bm.width();
ddsd.height = bm.height();
pdds = dd.createSurface( ddsd );
pdds.copyBitmap(bm, 0, 0, 0, 0);
}
//Off-screen surface pdds now contains the
//bitmap data.
顯示表面
當所有表面都建立並初始化(例如將位元影像複製到表面中)以後,程式需要顯示它們時就可以將它們複製到原始表面。這種表面複製通過DirectDrawSurface對象的blt方法完成。在預設的操作模式下,如果切換程式忙(進行中切換),DDS.blt()方法立即返回一個錯誤碼。因此,應該以某種迴圈的形式使用DDS.blt()方法,或者指定DDS.blt()方法的DDBLT_WAIT標誌。第二個選項改變了DDS.blt()的行為,使得此方法等待,直到切換可以進行或者出現另一個錯誤。
下面的代碼說明了如何使用DirectDrawSurface的blt方法。首先,有兩點需要闡明。如果顯示卡的模式改變或者程式使用對顯示卡的獨佔訪問而釋放了當前在顯卡上分配的所有表面記憶體,DirectDrawSurface就會丟失。當表面丟失時,需要恢複。恢複可以通過兩個步驟完成。首先調用DirectDrawSurface對象的Restore方法重新分配表面記憶體以及重新添加DirectDrawSurface對象。然後,重建相關表面的內容。
rc.Left = 0;
rc.Top = 0;
rc.Right = bm.width();
rc.Bottom = bm.height();
// Show the off-screen surface on the
//primary surface.
done = 0;
do
{
int retval;
retval = ddsPrimary.blt( rc, ddsOne, rc, 0);
if( retval == DD_OK )
{
//If the bitmap has been successfully
//copied, exit loop.
done = 1;
}
else if ( retval == DDERR_SURFACELOST )
{
while( ddsPrimary.Restore() != DD_OK &&
ddsOne.Restore() != DD_OK )
{
ReloadBitmap(ddsOne, szBitmap);
}
}
else if( retval != DDERR_WASSTILLDRAWING)
{
// Undetermined error; quit the loop.
done = 1;
}
} while( done == 0);
其他資訊
本文提供了初始化和使用Java中DirectDraw SDK的背景和基本步驟。要獲得Java的DirectX SDK以及相關文檔,可以從http://www.microsoft.com/java/下載Microsoft SDK for Java 2.0(其中包含DirectX SDK)。其中還包括幾個樣本程式,對於使用DirectX for Java建立動畫非常有用。這些例子列舉如下:
樣本 |
目錄 |
樣本內容 |
Ddex3 |
DDraw/ddex3/ddraw.html |
DirectDraw的基本使用 |
Flipcube |
d3d/flipcube/D3D.html |
3D立即模式的基本使用 |
Viewer |
d3drm/Viewer/direct3dRM.html |
Direct3D保持模式的完整使用 |
Castle |
d3drm/Castle/Castle.html |
Direct3D保持模式的高效能 |
DirectInput |
dInput/ddex3/dinput.html |
操縱杆、滑鼠、鍵盤和遊戲鍵盤的使用 |