關於GetDIBits的使用 抓狂搞了1整天 備註待查

 

HBITMAP是常用的GDI對象,而GetDIBits可以從一個HBITMAP對象中獲得其對應的位數據。
其原型如下:
int GetDIBits(    HDC hdc, // handle to DC
hdc, // handle to DC
                            HBITMAP hbmp, // handle to bitmap
                            UINT hbmp, // handle to bitmap
                            UINT uStartScan, // first scan line to set
                            UINT cScanLines, // number of scan lines to copy
                            LPVOID cScanLines, // number of scan lines to copy
                            LPVOID lpvBits, // array for bitmap bits
                            LPBITMAPINFO lpbi, // bitmap data buffer
                            UINT lpbi, // bitmap data buffer
                            UINT uUsage // RGB or palette index );

標準的GetDIBits調用方式是兩次調用:
第一次傳入空的 lpvBits,此時的lpbi作爲傳出參數,從中可以獲得lpvBits所需的內存區域大小。
    BYTE *lpvBits = NULL;
    BITMAPINFO bmpInfo = {0};
    bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);

    /*    第一次調用GetDIBits獲得bmpInfo    */
    nRet = ::GetDIBits(hDC, hBitmap, 0, pBmpData->bmHeight, NULL, &bmpInfo, DIB_RGB_COLORS);
    if (nRet == 0) {
        nRet = GetLastError();
        TRACE( _T("GetDIBits for bmpInfo failed %d/n"), nRet);
        goto err;
    }

以上調用如果成功,就會在bmpInfo.bmiHeader.biSizeImage記錄位數據所佔的內存區大小,
此時就可以動態分配內存,然後再次調用GetDIBits獲得實際的位數據:

    lpvBits= new BYTE[bmpInfo.bmiHeader.biSizeImage];
    if (NULL == pBitsBuffer) {
        nRet = -1;
        TRACE( _T("Allocate memory for lpvBits failed/n"));
        goto err;
    }

    /*    第二次調用GetDIBits獲得位圖數據    */
    nRet = ::GetDIBits(hDC, hBitmap, 0, pBmpData->bmHeight, lpvBits, &bmpInfo, DIB_RGB_COLORS);
    if (nRet == 0) {
        nRet = GetLastError();
        TRACE( _T("GetDIBits for lpvBits failed %d/n"), nRet);
        goto err;
    }

很多時候,因爲位圖的長寬和顏色深度都是已知的,因此位數據所佔的內存區大小可以由公式
 width * heigth * pixelBits / 8 計算獲得

那是否可以省略第一步調用呢?
答案是:最好不要

原因是 bmpInfo.bmiHeader.biSizeImage 並不一定等於 width * heigth * pixelBits / 8

經過多次測試發現,對於16位顏色深度的位圖,如果其寬度爲奇數,則第一次GetDIBits獲得
的位數據大小爲 (width+1) * heigth * pixelBits / 8

也就是說對於16位顏色深度的位圖,HBITMAP對象對其進行存儲時,自動將寬度調整爲了偶
數,也即將每行數據對齊到4字節(一個DWORD的長度)。

暫時沒有查到相關的解釋,我的猜想是這是爲了在進行像素操作時能直接以DWORD爲單位,
這樣比以WORD爲單位能減少一半的操作次數。

 

 

////

16位操作系統下如果使用這種方法獲得的將是16位圖

biBitCount=16 

表示位圖最多有2^16種顏色。每個像素用16位(2個字節)表示。這種格式叫作高彩色,或叫增強型16位色,或64K色。它的情況比較複雜,當biCompression成員的值是BI_RGB時,它沒有調色板。16位中,最低的5位表示藍色分量,中間的5位表示綠色分量,高的5位表示紅色分量,一共佔用了15位,最高的一位保留,設爲0。//->備註1

這種格式也被稱作555 16位位圖。

內存分佈如下


如果biCompression成員的值是BI_BITFIELDS(const DWORD BI_BITFIELDS = 3;)那麼情況就複雜了,首先是原來調色板的位置被三個DWORD變量佔據,稱爲紅、綠、藍掩碼。分別用於描述紅、綠、藍分量在16位中所佔的位置。在Windows 95(或98)中,系統可接受兩種格式的位域:555565,在555格式下,紅、綠、藍的掩碼分別是:0x7C000x03E00x001F,而在565格式下,它們則分別爲:0xF8000x07E00x001F。你在讀取一個像素之後,可以分別用掩碼上像素值,

 

從而提取出想要的顏色分量(當然還要再經過適當的左右移操作)。在NT系統中,則沒有格式限制,只不過要求掩碼之間不能有重疊。(注:這種格式的圖像使用起來是比較麻煩的,不過因爲它的顯示效果接近於真彩,而圖像數據又比真彩圖像小的多,所以,它更多的被用於遊戲軟件)。我們只需要讀取其中的R或者G的掩碼,來判斷是那種格式。以紅色掩碼爲例 0111110000000000的時候就是555格式 1111100000000000就是565格式。

555 格式 xrrrrrgggggbbbbb

565 格式 rrrrrggggggbbbbb

解析555格式的代碼:

BYTE b= bmpData[i*storeWidth+j];

BYTE g=((bmpData[i*storeWidth+j +1]<<6)>>3)+(buffer[i*storeWidth+j]>>5);

BYTE r=(bmpData[i*storeWidth+j +1]<<1)>>3;

有一點值得提醒的是由於有較多的位操作,所以在處理的時候在前一次操作的上面加上一對括號。

現在我們得到了55RGB各自的分量,但是還有一個新的問題,那就是由於兩字節表示了3個顏色  555下每個顏色最多到0x1F。所以我們需要一個轉換,這就是掩碼的用武之地,將得到的各顏色分量和相應的掩碼做與運算,或者乘8就可以了,推薦用與運算。

 

以下是565格式時的數據分離:

BYTE b= bmpData[i*storeWidth+j];

BYTE g=((bmpData[i*storeWidth+j+1]<<5)>>2)+(buffer[i*storeWidth+j]>>5);

BYTE r=bmpData[i*storeWidth+j +1]>>3;

 

現在我們得到了565RGB各自的分量,但是仍然還有一個新的問題,565格式下最大的綠色分量也就0x3F。所以我們需要一個轉換,這就是掩碼的用武之地,將各顏色分量和相應的掩碼做與運算,

 

 

 備註一:

           所以這種方法需要通過位移來操作 在寫抓屏程序的時候顯示不如後邊的32位圖來的方便, 但是在顏色質量爲16位的操作系統下如何抓屏到32位的圖像呢

通過上邊的兩個GetDIBits的方法是不可行的  第一次LPVOID lpvBits, 爲空 獲得了BITMAPINFO bi信息bi.bmiHeader.biBitCount = 16;

如果想通過修改bi.bmiHeader.biBitCount = 32; 後來第二次調用GetDIBits就會失敗 ;

 

那如何在顏色質量爲16位的操作系統下如何獲得32位的圖像呢?

方法如下:

首先使用 GetObject(HBITMAP bmpScreen) 獲得信息

然後通過修改BITMAPINFO   中的biBitCount 爲32就可以獲得32位結構的LPVOID 了 再進行操作就方便的多了

biBitCount=32 

 

表示位圖最多有2^32種顏色。這種位圖的結構與16位位圖結構非常類似,當biCompression成員的值是BI_RGB時,它也沒有調色板,32位中有24位用於存放RGB值,順序是:最前一字節保留,紅8位、綠8位、藍8位。這種格式也被成爲888 32位圖。如果 biCompression成員的值是BI_BITFIELDS時,原來調色板的位置將被三個DWORD變量佔據,成爲紅、綠、藍掩碼,分別用於描述紅、綠、藍分量在32位中所佔的位置。在Windows 95(or98)中,系統只接受888格式,也就是說三個掩碼的值將只能是:0xFF00000xFF000xFF。而在NT系統中,你只要注意使掩碼之間不產生重疊就行。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章