游戲開發新手入門之調色板和像素1
簡介 今天我們將分別使用調色板和RGB模式來熟悉DirectDraw的基本圖形。它們有什么不同呢?如果你曾經在DOS下編程,你可能使用過調色板映射模式。調色板是個顏色查詢表,為了繪制象素,你將一個單獨的字節寫入視頻內存,通過這個字節你可以索引到一個擁有各種
簡介
今天我們將分別使用調色板和RGB模式來熟悉DirectDraw的基本圖形。它們有什么不同呢?如果你曾經在DOS下編程,你可能使用過調色板映射模式。調色板是個顏色查詢表,為了繪制象素,你將一個單獨的字節寫入視頻內存,通過這個字節你可以索引到一個擁有各種顏色的鏈表,這個顏色的鏈表,或查詢表就叫作調色板。而RGB模式是不同的,因為它不需要顏色查詢表。在RGB模式下繪制一個象素,你可以直接把紅色、綠色和藍色的值寫入視頻內存。任何色彩深度高于8位的調色板都可以用RGB模式取代。
編寫本文時,我假設你已經讀過了前面幾章,知道了怎樣設置DirectDraw和創建表面。我們將使用DirectX7,它包含了最新的DirectDraw接口。實際上,DirectX 7 中的DirectDraw接口可能是最后的升級版本了!不用擔心,未來的版本一定會兼容它的,但是未來可能是一個DirectDraw和Direct3D綜合的產品,管它那,我們學的不會沒有用的。
在開始前我還有最后一件事要提醒你:在我的后續文章中關于調色板的部分可能再也用不到了,所以,如果你對于調色板模式不是很感興趣,你可以跳過文章的前一部分,從象素格式開始看起。調色板的開發和使用是PC中使用的原始視頻系統的內存限制帶來的直接后果?,F在由于充足的顯存使調色板模式幾乎廢棄不用了。值得保留調色板模式的一個原因是,執行調色板操作可以實現一些有趣的動畫效果。不羅嗦了,讓我們開始吧!
創建DirectDraw的調色板
當你在色彩深度為8位或低于8位的模式下顯示圖形時,你必須創建調色板,也就是顏色查詢表。更明確的講,對于DirectX,調色板就是PALETTEENTRY結構。要建立一個調色板,我們要做如下三步:
1、 創建顏色查詢鏈表。
2、 得到指向IDirectDrawPalette接口的指針。
3、 把調色板鏈接到DirectDraw表面。
我假設我們使用的是8位色彩深度。如果你要用16位或更高位的色彩深度編寫游戲,你就不用繼續看以下這段瘋狂的Windows素材了??傊?,8位色彩深度,我們可以有一個256個條目的調色板。所以,創建顏色查詢鏈表,有256個條目在其中:
clearcase/" target="_blank" >cc66 width="90%" align=center bgColor=#dadacf border=1>
typedef struct tagPALETTEENTRY { // pe BYTE peRed; BYTE peGreen; BYTE peBlue; BYTE peFlags; } PALETTEENTRY; |
頭三個參數很明顯,分別是紅色、綠色和藍色的強度。每一個取值范圍0-255,BYTE是無符號數據類型。最后一個參數是控制標志,應該設置為PC_NOCOLLAPSE。原因我就不說了。
現在,我們需要把256個條目有秩序的排列好,也就是為了一下能找到,我們為鏈表設置一個數組,象這樣:
PALETTEENTRY palette[256]; |
Ok,我們有了數組了,你可以裝載顏色了。當我工作在調色板模式下時,通常把顏色存儲在一個外部文件里,然后用一些如下的東東裝載顏色:
FILE* file_ptr; int x;
if ((file_ptr = fopen("palette.dat", "rb")) != NULL) { fread(palette, sizeof(PALETTEENTRY), 256, file_ptr); fclose(file_ptr); } |
All right,第一步完成了?,F在我們需要得到調色板的接口。交給IDirectDraw7::CreatePalette()函數就好了:
HRESULT CreatePalette( DWORD dwFlags, LPPALETTEENTRY lpColorTable, LPDIRECTDRAWPALETTE FAR *lplpDDPalette, IUnknown FAR *pUnkOuter ); |
返回類型是HRESULT,你知道它的,所以可以用FAILED()和SU
CCEEDED()這兩個宏檢測函數是否調用成功。參數的說明如下:
※ DWORD dwFlags:描述調色板對象的標志常量。當然,你可以用“|”組合它們:
· DDPCAPS_1BIT:1位色彩,對應2色調色板。
· DDPCAPS_2BIT:2位色彩,對應4色調色板。
· DDPCAPS_4BIT:4位色彩,對應16色調色板。
· DDPCAPS_8BIT:8為色彩,對應256色調色板。
· DDPCAPS_8BITENTRIES:指出引用一個8位色彩索引。就是說,每個顏色條目是它本身的到目的表面8位調色板的索引。這叫作被變址的調色板。它必須同DDPCAPS_1BIT、DDPCAPS_2BIT,或者DDPCAPS_4BIT合用。亂套吧!^_^
· DDPCAPS_ALPHA:每一個PALETTEENTRY的peFlags成員都應該被認為是阿爾發值。用這些標志創建的調色板可以被粘貼在Dierct3D紋理表面,因為DirectDraw本身并不支持阿爾發混合。
· DDPCAPS_ALLOW256:允許8位調色板的全部256個條目被使用。通常,0指向黑色,255指向白色。
· DDPCAPS_INITIALIZE:指出應該用PALETTEENTRY的數組初始化調色板。
· DDPCAPS_PRIMARYSURFACE:調色板將鏈接到主表面,好快速更改顯示顏色。
· DDPCAPS_VSYNC:一般畫圓時用到它。
大多數情況,你將使用DDPCAPS_8BIT | DDPCAPS_INITIALIZE,如果你剛好想建立一個空的調色板,稍后再設置它,你可以去掉后者,就是DDPCAPS_INITIALIZE。當然,你還可以使用DDPCAPS_ALLOW256,如果你真的想改變這兩個常用的條目。
※ LPPALETTEENTRY lpColorTable:這個指針指向我們創建的查詢表,把數組的名稱傳遞給它就好了。
※ LPDIRECTDRAWPALETTE FAR *lplpDDPalette:這是指向IDirectDrawPalette接口指針的地址。如果函數調用成功,它將被初始化。
※ IUnkown FAR *pUnkOuter:同以前一樣,這總是為COM高級應用準備的。設置為NULL好了。
不是太糟糕吧!現在我們可以建立我們的調色板對象了。最后一步是把調色板鏈接到一個表面,這只需要一個函數就好了——IDirectDrawSurface7::Setpalette()。它的原形如下:
HRESULT SetPalette(LPDIRECTDRAWPALETTE lpDDPalette); |
很簡單,是不是?你只要把上一步得到的接口指針傳遞給它就可以了。那好,讓我們把學到的綜合到一起,下面我給你一個程序框架,我假設我們已經利用調色板的數組建立了一個索引鏈表,就像我們上一步做的。該框架是建立DirectDraw調色板來控制顏色,并且把它鏈接到主表面(當然,主表面是我們事先做好的):
LPDIRECTDRAWPALETTE lpddpal; // create the palette object if (FAILED(lpdd7->CreatePalette(DDPCAPS_8BIT | DDPCAPS_INITIALIZE, palette, &lpddpal, NULL))) { // error-handling code here }
// attach to primary surface if (FAILED(lpddsPrimary->SetPalette(lpddpal))) { // error-handling code here } |
就是這么簡單。一旦你的調色板建立完成,繪制象素部分同RGB模式就沒有什么不同了。從此時開始,我將同時介紹RGB模式和調色板模式,在我們真正的顯示圖象前,我需要告訴你什么是RGB象素格式。
象素格式 象我前面說過的,當你把一個調色板模式的象素寫入內存時,你同時分配了一個字節,每個字節表示一個到色彩查詢表的索引。在RGB模式下,你只需要把顏色描述值寫入內存,但每個顏色需要的字節數都要多于一個字節。字節的多少同色彩的深度相關。對于16-bit色彩,你要為每個象素準備兩個字節(16位),以此類推,你可以猜到32-bit色彩是怎么回事了,這些都是很容易理解的。32-bit色彩對于一個象素來說,每一位的字符如下:
AAAA AAAA RRRR RRRR GGGG GGGG BBBB BBBB |
“A”是代表“alpha”(阿爾發),表示一個透明的值,這是為Direct3D準備的。我以前說過,DirectDraw不支持α混合,所以當你為DirectDraw創建32-bit色彩時,把高位都設置為0好了。下一個8位表示紅色強度的值,再下一個8位表示綠色,最后8位表示藍色。
一個32-bit色彩的象素需要32位,所以我們一般用UINT類型來定義相對應的變量類型,這是一個無符號實數類型。通常我用一個宏來把RGB數據轉換成正確的象素格式。讓我給你看看它的樣子,希望這能更好的幫助你理解象素格式:
#define RGB_32BIT(r, g, b) ((r << 16) | (g << 8) | (b)) |
就象你看到的,這個宏通過位移在相應的位置寫入了相應的紅、綠、藍的強度值,并且完全符合正確的象素格式。是不是開始感覺有點兒入門了?要建立一個32-bit的象素,你就可以調用這個宏。紅、綠、藍每一個顏色的強度值都是8位,它們的取值范圍都是從0——255。例如建立一個白色的象素,你可以這樣:
UINT white_pixel = RGB_32BIT(255, 255, 255); |
24-bit色彩基本相同,道理實際上是一樣的,只是24-bit沒有關于α的描述,也就是少了α那8位。象素格式如下:
RRRR RRRR GGGG GGGG BBBB BBBB |
所以紅色、綠色、藍色仍然都分別是8位,這就意味著24-bit色彩和32-bit色彩實際上是有相同顏色深度的,只是32-bit多了個α混合?,F在,你一定會想,24-bit比32-bit要好,真的是這樣嗎?否,因為使用24-bit有一些麻煩,事實上沒有24-bit的數據類型,在你建立象素時,你不得不分三步寫入紅、綠、藍的強度值,而不是象32-bit一次就完成。盡管32-bit色彩需要更多的內存,但在大多數的機器上,它要更快一些。實際上,很多顯示卡不支持24-bit色彩模式,因為每一個象素占用3個字節是很不方便的。
現在,輪到16-bit色彩了,它有一點兒小麻煩,因為對于16-bit色彩,不是每一種顯示卡都使用相同的象素格式!有兩種格式。其中一種,也是比較流行的,紅色占據5位,綠色占據6位,藍色占據剩下的5位。另一種格式是分別都占據5位,剩下的一位,也就是高位不使用,一些老的顯示卡都使用這種格式。所以這兩種格式看起來是這樣的:
565 format: RRRR RGGG GGGB BBBB 555 format: 0RRR RRGG GGGB BBBB |
當你工作在16-bit色彩深度下,你首先需要檢測顯示卡是支持565格式還是555格式,然后使用適當的方式。這是很討厭的,但你堅持用16-bit色彩,這是沒有辦法避免的。由于存在兩種格式,你就需要兩種宏:
#define RGB_16BIT565(r, g, b) ((r << 11) | (g << 5) | (b)) #define RGB_16BIT555(r, g, b) ((r << 10) | (g << 5) | (b)) |
對于565格式,紅色和藍色的取值范圍是0——31,綠色是0——63;對于555格式,取值范圍都是0——31,所以當要創建一個白色象素時,就會有所不同:
USHORT white_pixel_565 = RGB_16BIT565(31, 63, 31); USHORT white_pixel_555 = RGB_15BIT555(31, 31, 31); |
這個USHORT是無符號短實數類型,對應的變量只有16位。存在兩種格式把事情搞得有些復雜,但在實際的游戲編程過程中,你將會感覺到這并沒有你想象的那么討厭。順便說一下,有些時候555格式被稱為15-bit色彩深度,所以在以后如果我這樣談到了它,你一定要心領神會哦!
現在或許是告訴你在16-bit色彩深度模式下,怎樣檢測顯示卡到底支持哪種格式的時機了,是555還是565呢?最簡單的辦法就是調用IDirectDrawSurface7接口下的GetPixelFormat()函數,它的原形如下:
HRESULT GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat); |
參數是指向DDPIXELFORMAT結構的指針。你只要聲明它,初始化它,然后傳遞它的地址就一切OK了。這個結構的本身是巨大的,所以我就不列舉它了,但我得告訴你它的三個成員,都是DWORD類型的,它們是dwRBitMask、dwGBitMask、和dwBBitMask。你可以從dwRBitMask、dwGBitMask和dwBBitMask中獲得掩碼值(新東東,先不用太明白)。你也可以用它們檢測顯示卡支持的格式。如果顯示卡支持565,dwGBitMask將為0x07E0。如果是555格式,dwGbitMask為0x03E0。
現在,我們已經學習了所有我們可能用到的象素格式,可以進入在DirectX下顯示圖象的實際階段了。你已經等待了很久了,不是嗎?在把象素放到表面上前,我們需要鎖定表面,至少是鎖定表面的一部分。鎖定表面返回一個指向表面在內存里位置的指針,然后,我們就可以為所欲為了。
原文轉自:http://www.kjueaiud.com