位元影像
位元影像是一種圖形化對象,用於在裝置環境裡建立、繪製、操縱和接收圖片。從[開始按鈕]上的小Winodws標誌到標題列上的[關閉]按鈕,位元影像在Windows裡無處不在。位元影像可以看作是一種由像素數組構成的圖片,這些像素可以在螢幕上進行繪製。和所有圖片一樣,位元影像有自己的高度和寬度。也提供方法來判斷位元影像使用什麼顏色。最後,位元影像也是一個描述位元影像中每個像素的位(bits)數組。
習慣上,Windows下的位元影像被劃分成兩種類型:裝置相關位元影像(DDBs)和裝置無關位元影像(DIBs)。DDBs是一種和具體DC的特性有緊密關係的位元影像,不容易在有不同特性的DC上繪製。DIBs則相反,它與具體裝置無關,因此需要攜帶足夠的資訊以便於在任何裝置上準確的繪製。
Windwos CE包含了許多在其它Windows版本裡可以使用的位元影像函數。不同之處包括只有Windows CE才支援的一種新的四色格式和對DIBs不同的操縱方式。
裝置相關位元影像
可以使用CreateBitmap函數來建立裝置相關位元影像,函數原型如下:
HBITMAP CreateBitmap (int nWidth, int nHeight, UINT cPlanes, UINT cBitsPerPel, CONST VOID *lpvBits);
nWidth和nHeight表示位元影像的尺寸。cPlanes是一個曆史產物,當時顯示器採用不同的硬體平面來實現像素裡的每個顏色。對Windows CE來說,該參數必須是1。cBitspPerPel表示每個像素使用的位元。顏色數是cBitspPerPel的2次冪。在Windows CE下,允許使用的是1、2、4、8、16和24。正如我說過的,4色位元影像是Windows CE特有的,其它Windows 平台不則不支援。
最後一個參數是指向位元影像位元的指標。在Windows CE下,位元總是按壓縮像素格式排列的。也就是,每個像素按位元組儲存成一系列的位元位,下一個像素緊接上一個。位元組的第一個像素是位元影像左上方的像素。像素沿位元影像頂行排列,隨後是第二行,依次類推。位元影像每行必須是雙字(4位元組)對齊排列。為了對齊下一行,可在行尾使用0來填充。圖2-5示範了這種相片順序,圖中展示了一個126*64像素的位元影像,每個像素使用8位。
圖2-5(略) 位元影像裡的位元組布局
函數CreateCompatibleBitmap,其原型如下:
HBITMAP CreateCompatibleBitmap (HDC hdc, int nWidth, int nHeight);
用該函數可以建立一個格式與傳入的裝置環境相容的位元影像。所以如果裝置環境是四色DC,建立的位元影像也是一個四色位元影像。當您要在螢幕上操縱圖片時,使用該函數很就很方便,因為該函數可以很容易建立一個與螢幕直接顏色相容的空位元影像。
裝置無關位元影像
裝置無關位元影像和裝置相關位元影像之間的基本差異是儲存成DIBs的圖象有自己的顏色資訊。自從使用BMP作為副檔名的Windows 3.0起,幾乎每個位元影像檔案都含有在Windows裡建立DIB時所需要的資訊。
在Windows早期,寫程式手工讀DIB檔案並把資料轉換為位元影像是程式員必備的技能。現在,這個煩冗的任務可以通過Windows CE特有的函數SHLoadDIBitmap來完成,函數原型如下:
HBITMAP SHLoadDIBitmap (LPCTSTR szFileName);
該函數直接從位元影像檔案裡裝載位元影像並提供位元影像控制代碼。在Windows XP裡可以使用帶LR_LOADFROMFILE參數標誌的LoadImage函數來完成同樣的處理,但Windows CE下的LoadImage不支援這個標誌。
DIB片段
雖然Windows CE裡很容易裝載位元影像檔案,但有時您必須讀螢幕象、操縱圖象以及將圖象重畫到螢幕上。這是DIBs比DDBs更好一些的地方之一。雖然裝置相關位元影像的位元據可以擷取得到,但緩衝區的格式直接依賴於螢幕格式。而通過使用DIB,或者更準確地說,通過使用DIB片段,您的程式可以把位元影像讀到預定義格式的緩衝區裡,而不用擔心顯示裝置的格式。
雖然從Windows 3.0起就不斷加入許多DIB建立函數,但Windows CE只支援XP中的一部分DIB函數。CreateDIBSection是這些函數中的第一個:
HBITMAP CreateDIBSection (HDC hdc, const BITMAPINFO *pbmi, UINT iUsage, void *ppvBits,
HANDLE hSection, DWORD dwOffset);
因為他們是相當晚才加到Win32 API裡的,所以DIB片段對程式員來說可能是比較新鮮的。使用DIB片段是為了改進Winodows NT上直接操縱位元影像的應用程式效能。簡而言之,DIB片段允許程式員在直接存取位元影像位元據時,選擇一個裝置環境裡的DIB。為達到這個目的,DIB片段將一個緩衝區與記憶體DC結合到一起,該緩衝區同時包含了該DC的位元據。因為圖象是映射到一個DC的,所以可以使用其它圖形函數調用來修改圖片。同時,DC裡的DIB格式的原始位元據可以被直接操縱。能改進在NT上的效能固然是很好,但對Window CE程式員來說,能夠簡化位元影像的使用和操作位元影像的內容才是最有價值的。
該函數中最重要的參數是指向BITMAPINFO結構的指標。該結構描述了裝置無關位元影像的布局和顏色構成,該結構包含一個BITMAPINFOHEADER結構和一個代表位元影像使用的調色盤的RGBQUAD數組。
BITMAPINFOHEADER定義如下
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
如你所見,該結構包含的資訊遠多於傳給CreateBitmap的參數。第一個域是該結構的尺寸,必須由調用者填充,用於區別由OS/2管理器沿襲來的BITMAPCOREINFOHEADER結構。biWidth, biHeight, biPlanes,和biBitCount都和CreateBitmap裡的同名參數類似,但有一個例外,biHeight的加號或減號指定了位元組的組織相片順序。如果biHeight是正數,位元組按由上到下的格式排列,這一點和CreateBitmap相同。如果biHeight是負數,位元組則按由下到上的格式排列,也就是位元影像的底部行定義在該位元組的首位。和CreateBitmap一樣,biPlanes必須設定為1。
biCompression指出位元組使用的壓縮方式。Windows CE裡,允許使用的標誌有,BI_RGB,指出緩衝區沒有壓縮;BI_BITFIELDS,指出像素格式被定義在顏色表的頭三個入口裡。biSizeImage用於指出位元組的大小。但是,當使用BI_RGB標誌時,biSizeImage可以設定為0,表示用BITMAPINFOHEADER結構裡提供的尺寸(dimensions )和像素的位元來計算數組的大小。
biXPelsPerMeter和biYPelsPerMeter提供圖片的準確尺寸資訊。但是對於CreateBIBSection來說,這些參數可以設定為0。biClrUsed指出實際使用的調色盤裡的顏色數。在256色圖片裡,調色盤有256個入口,但畏途自身可能只需要大約100個不同的顏色。這個域協助調色盤管理器--Windows管理顏色匹配的組件--將系統調色盤裡的顏色同位元影像要求的顏色進行匹配。biClrImportant進一步指出真正需要的顏色。對更多顏色的位元影像,這兩個網域設定為0,表示使用所有顏色並且所有顏色都重要。
前面提到過,BITMAPINFOHEADER結構之後是RGBQUAD結構數組。該結構定義如下:
typedef struct tagRGBQUAD { /* rgbq */
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD
該結構允許有紅藍綠各256級色度(shade )。雖然用該結構可以建立幾乎任何色度,但裝置上實際渲染的顏色是受裝置能顯示的顏色的限制的。
總體來看,RGBQUAD結構數組描述了DIB的調色盤。調色盤是位元影像裡的顏色列表。如果位元影像有調色盤,位元影像數組的每個入口包含的就不再是顏色,而是包含每個像素顏色的調色盤索引。雖然對單色位元影像來說是多餘的,但當在彩色裝置上繪製彩色位元影像時,調色盤就相當重要了。例如,雖然256色位元影像中每個像素一個位元組,但該位元組指向一個代表紅綠藍色的24位值。所以儘管256色位元影像只能包含256個不同的顏色,但由於是使用24位調色盤入口進行顏色繪製的,因而這些顏色中的每個都可使用出的1千6百萬種顏色中的一個。為了方便在32位中使用,每個只包含24位顏色資訊的調色盤入口都被擴充到32位寬度了,這也是RGBQUAD名字的來源。(譯者註:QUAD有四個一套的意思)
CreateDIBSection剩餘的四個參數中只有兩個用於Windows CE。IUsage指出調色盤裡的顏色是如何被繪製的。如果該參數是DIB_RGB_COLORS,表示位元影像裡的位元據包含了每個像素的全部RGB顏色資訊;DIB_PAL_COLORS,表示位元影像像素包含DC裡當前選擇的調色盤的索引。PpvBits 是指向構成位元影像圖象的位元據的指標。最後兩個參數,hSection和dwOffset,Windows CE不支援它們,必須設定為0。在Windows的其它版本裡,它們允許使用記憶體對應檔來給出位元據。因為Windows CE不支援記憶體對應檔,所以它們不能被CreateDIBSection支援。
GetDIBColorTable和SetDIBColorTable是管理DIB調色盤的兩個函數,它們的原型如下:
UINT GetDIBColorTable (HDC hdc, UINT uStartIndex, UINT cEntries, RGBQUAD *pColors);
和
UINT SetDIBColorTable (HDC hdc, UINT uStartIndex, UINT cEntries, RGBQUAD *pColors);
對這兩個函數來說,uStartIndex指出將被設定或者查詢的調色盤的第一個入口。CEntries指出有多少調色盤入口將改變。指向RGBQUAD數組的指標是用於設定(對SetDIBColorTable)或者查詢(對GetDIBColorTable)的顏色數組。
繪製位元影像
能夠建立和裝載位元影像固然很好,但如果您建立的位元影像不能繪製在螢幕上,那就沒什麼大用處。繪製位元影像可能並不是您想象的那麼簡單。位元影像被繪製到螢幕DC之前,必須先將位元影像選進一個DC,再將其複製到螢幕裝置環境裡。雖然這個過程聽起來可能有點費解,但這是有合理的原因的。
把位元影像選擇到一個裝置環境的過程與把邏輯字型選擇到裝置環境的過程類似。下面讓我們把理想變成現實吧。正如Windows要為請求的字型找到最可能匹配的字型一樣,位元影像選擇過程也必須為位元影像要求的顏色找到匹配的裝置上可用的顏色。只有在這個過程完成後,位元影像才能繪製到螢幕上。為了協助完成這一中間步驟,Windows提供了一個替身DC—記憶體裝置環境。
要建立記憶體裝置環境,可以使用函數CreateCompatibleDC:
HDC CreateCompatibleDC (HDC hdc);
該函數建立一個與當前螢幕DC相容的記憶體DC。一旦建立成功,可以使用您以前用來選擇邏輯字型的SelectObject函數將源位元影像選進這個記憶體DC。最後,用BitBlt 或StretchBlt將位元影像從記憶體DC複製到螢幕DC。
位元影像函數的主力是
BOOL BitBlt (HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop);
BitBlt函數發音為“bit blit”,它是一個有意思的函數,它在裝置環境上操作,而不是記憶體裡,它有時是一個很特別的函數。第一個參數是位元影像即將被複製到其上的目的裝置環境的控制代碼。接下來的4個參數規定了位元影像最終位於的目的矩形的位置和大小。接下來的3個參數規定了源裝置環境的控制代碼以及源圖象左上方在該DC裡的位置。
最後一個參數dwRop規定圖象如何從源裝置環境複製到目的裝置環境。ROP代碼規定了源位元影像和當前目的裝置如何組合來產生最終圖片。ROP代碼為SRCOPY,表示簡單複製源圖象。ROP代碼為SRCPAINT,表示源圖象和目的之間進行或操作。ROP代碼為SRCINVERT,表示複製一個邏輯反轉圖象,本質上是一個負的源圖象。一些ROP代碼還將當前選擇的畫刷(brush)一起作為計算結果圖象的因素。ROP代碼很多,所以這裡不可能覆蓋全,要獲得全部列表,請參考Windows CE編程文檔。
下面的程式碼片段總結了如何繪製位元影像:
// Create a DC that matches the device.
hdcMem = CreateCompatibleDC (hdc);
// Select the bitmap into the compatible device context.
hOldSel = SelectObject (hdcMem, hBitmap);
// Get the bitmap dimensions from the bitmap.
GetObject (hBitmap, sizeof (BITMAP), &bmp);
// Copy the bitmap image from the memory DC to the screen DC.
BitBlt (hdc, rect.left, rect.top, bmp.bmWidth, bmp.bmHeight,
hdcMem, 0, 0, SRCCOPY);
// Restore original bitmap selection and destroy the memory DC.
SelectObject (hdcMem, hOldSel);
DeleteDC (hdcMem);
建立記憶體裝置環境,即將被繪製的位元影像被選進該DC。因為您可能沒有儲存即將被繪製的位元影像尺寸,通常可以調用GetObject來獲得。GetObject返回關於繪圖物件的資訊,在本例中是一個位元影像。可以用這個很有用的函數來查詢字型和其它圖象對象的資訊。接下來,使用BitBlit將位元影像複製進螢幕DC。為了清理,需要將位元影像從記憶體裝置環境中取消選擇,並使用DeleteDC將該記憶體DC刪除。不要將DeleteDC同ReleaseDC混淆,ReleaseDC是釋放一個顯示DC。DeleteDC只應該和CreateCompatibleDC一起使用,ReleaseDC只應該和GetDC或GetWindowsDC一起使用。
用StretchBlt除了複製位元影像,還可以展開或者壓縮位元影像。該函數原型如下:
BOOL StretchBlt (HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, DWORD dwRop);
StretchBlt裡的參數和BitBlt裡的基本相同,但是有一點例外的是現在可以指定源圖象的寬度和高度。同樣的,這裡的ROP代碼規定了源和目的之間如何組合來產生最終圖象。
Windows CE還有另外一個位元影像函數TransparentImage,原型如下:
BOOL TransparentImage (HDC hdcDest, LONG DstX, LONG DstY, LONG DstCx,
LONG DstCy, HANDLE hSrc, LONG SrcX, LONG SrcY,
LONG SrcCx, LONG SrcCy, COLORREF TransparentColor);
該函數同StretchBlt類似,但有兩個很重要的例外。首先,您可以指定位元影像裡作為透明色的顏色。當把位元影像複製到目標中時,位元影像裡透明色的像素不被複製。第二個不同是hSrc參數要麼是裝置環境要麼是位元影像控制代碼,該參數允許您不必理會在螢幕上繪製圖象前必須將源圖象選進一個裝置環境的要求。TransparentImage與Windows 2000中的TransparentBlt函數基本相同,除了TransparetBlt不能直接使用位元影像作為源圖象以外。
和其它版本的Windows一樣,Windows CE還支援2個其它blit函數:PatBlt和MaskBlt。PatBlt函數將當前選擇的畫刷同目的DC裡當前圖象進行組合,產生結果圖象。我在本章後面會談到畫刷。MaskBlt函數與BitBlt類似,但包含一個掩碼圖象,用來提供只繪製源圖象的一部分到目的DC裡的功能。