標籤:windows setdibitstodevice stretchdibits 位元影像
DIB(裝置無關位元影像)是儲存在磁碟上的位元影像檔案,可以從磁碟讀到記憶體中或從記憶體儲存到磁碟上,它的磁碟檔案結構是標準化的,在Linux、Unix及Windows上都可以以同樣效果顯示。位元影像是最接近硬體的映像格式,Windows顯示的核心是位元影像,它的SDK API專門提供了一組用於操作DIB檔案的函數。但是由於這樣或那樣的原因,高效合理的使用這些DIB API是需要瞭解不少曆史和使用背景的,在這裡我抽繭剝絲介紹和示範DIB的使用,相信對你更好的使用DIB檔案有協助,由於DIB函數比較多,這裡分為三部分介紹,首先是DIB的讀入、儲存和顯示。
1.DIB檔案的組成
關於DIB檔案的組成很多地方有詳細描述,這裡不再詳細贅述。主要分為如下幾個部分:
- 檔案表頭
- 資訊表頭
- RGB色彩對照表(不一定有)
- 位元影像像素位
其中
- 檔案表頭包含了檔案類型(BMP)、檔案大小及檔案中實際像素資料儲存的位移值
- 資訊頭包含了位元影像的大小、位面數、位元深度、壓縮及掩碼等資訊
- RGB色彩對照表也稱調色盤,8位以下位元影像可能包含有調色盤資訊,16位及以上一般沒有調色盤
- 位元影像像素位為實際位元影像資料儲存區
其中,資訊表頭和RGB調色盤合稱為位元影像資訊。
2.Windows DIB記憶體資料結構
我們要把DIB資料讀入到記憶體中,那麼就要分配相應的記憶體,把讀入的資料寫到對應的記憶體區中,這裡SDK 提供的資料結構是各種結構體,結構體的各個欄位對應磁碟檔案中各個資訊值。我們這裡為了邏輯清楚,使用最常使用的DIB結構體。
,檔案中的資料讀到對應的記憶體結構中完成檔案的讀入,記憶體結構中的資料寫到對應的檔案中完成檔案的儲存。
其中
- 檔案資訊頭資料讀到BITMAPFILEHEADER結構體中
- 位元影像資訊頭讀到BITMAPINFOHEADER結構體中
- 位元影像調色盤讀到RGBQUAD結構體數組中
- 位元影像資料讀到根據位元影像資訊頭提供的資訊而分配的相應大小的資料區中
把BITMAPINFOHEADER和RGBQUAD[0]作為BITMAPINFO結構體成員,這樣一方面是為了和磁碟檔案對應,另一方面也是為了訪問調色盤資料方便。
這裡的磁碟檔案中各個段在磁碟上儲存位置必須是連續的,但是對應的記憶體中檔案資訊頭、位元影像資訊和位元影像資料三大塊不一定要是連續的。因此,在讀入檔案中你既可以一次性讀入磁碟檔案到連續的記憶體中,也可以分開讀入到三個分別連續的記憶體中,後面會做相關示範。
3.DIB顯示前面講了讀入DIB到對應的記憶體結構中,現在我們要怎麼把對應的記憶體結構中的像素資料顯示出來呢?如,為記憶體中指定點的像素值是怎麼樣一步步顯示到顯示器上指定位置的,這裡做了一些簡化,沒有考慮壓縮位元影像和顯示器的顯示精度
這個圖看起來很複雜,實際上在這裡我也沒想把它完全講出來,如果你之前有一定的DIB使用經驗,那麼這幅圖可以幫你更好的理解整個顯示過程,如果沒有DIB使用經驗,那麼不用仔細看這幅位元影像。從這幅圖,我們知道顯示的時候需要的資料是:記憶體中位元影像資料、記憶體中位元影像資訊、記憶體中位元影像指定要顯示地區、顯示器上用於顯示的地區。
提供了需要的資訊後,Windows提供了SetDIBitsToDevice和StretchDIBits函數幫我們完成這一顯示過程。兩個函數的原型分別如下
int SetDIBitsToDevice( HDC hdc, // handle to DC int XDest, // x-coord of destination upper-left corner int YDest, // y-coord of destination upper-left corner DWORD dwWidth, // source rectangle width DWORD dwHeight, // source rectangle height int XSrc, // x-coord of source lower-left corner int YSrc, // y-coord of source lower-left corner UINT uStartScan, // first scan line in array UINT cScanLines, // number of scan lines CONST VOID *lpvBits, // array of DIB bits CONST BITMAPINFO *lpbmi, // bitmap information UINT fuColorUse // RGB or palette indexes );
int StretchDIBits( HDC hdc, // handle to DC int XDest, // x-coord of destination upper-left corner int YDest, // y-coord of destination upper-left corner int nDestWidth, // width of destination rectangle int nDestHeight, // height of destination rectangle int XSrc, // x-coord of source upper-left corner int YSrc, // y-coord of source upper-left corner int nSrcWidth, // width of source rectangle int nSrcHeight, // height of source rectangle CONST VOID *lpBits, // bitmap bits CONST BITMAPINFO *lpBitsInfo, // bitmap data UINT iUsage, // usage options DWORD dwRop // raster operation code );
可以看到兩個函數的從上到下依次需要提供的資訊為:待顯示視窗的DC、待顯示視窗的用於顯示的地區、位元影像中要顯示部分、位元影像資料、位元影像資訊、其它選項,具體各個參數含義參見MSDN。
4.代碼示範這裡的示範程式介面如下
在讀入中可以選擇一次性讀入還是分區段讀入,兩種方式的區別在於讀入以後在記憶體中位元影像各個段是否連續。儲存也對應讀入的兩種方式。顯示可以選擇正常置中顯示還是展開到整個視窗螢幕顯示,前者對應SetDIBitsToDevice函數,後者對應StretchDIBits函數。這裡只提供一次性讀入、儲存和顯示代碼,完整代碼參見附件。
BOOL DibTotalLoad(PTSTR szBmpFile, PBITMAPFILEHEADER *ppbmfh, PBITMAPINFO *ppbmi, PBYTE *ppBits, PLONG pBmpWidth, PLONG pBmpHeight){HANDLEhFile;DWORDdwFileSize, dwBytesRead;BOOLbSuccess;//開啟檔案hFile = CreateFile( szBmpFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);if (INVALID_HANDLE_VALUE == hFile){return FALSE;}//判斷BMP檔案大小(所有部分)dwFileSize = GetFileSize(hFile, NULL);//分配對應大小記憶體用於儲存磁碟BMP檔案內容*ppbmfh = malloc(dwFileSize);if (!(*ppbmfh)){CloseHandle(hFile);return FALSE;}//讀入檔案內容bSuccess = ReadFile(hFile, *ppbmfh, dwFileSize, &dwBytesRead, NULL);CloseHandle(hFile);//校正讀入是否正確和檔案是否為BMP檔案if (!bSuccess ||(dwBytesRead != dwFileSize) ||(*ppbmfh)->bfType != *(WORD *)"BM"){free(*ppbmfh);return FALSE;}//計算剩餘的返回參數*ppbmi = (PBITMAPINFO)(*ppbmfh+1);*ppBits = (PBYTE)(*ppbmfh) + (*ppbmfh)->bfOffBits;*pBmpWidth = (*ppbmi)->bmiHeader.biWidth;*pBmpHeight = (*ppbmi)->bmiHeader.biHeight;return TRUE;}BOOL DibTotalSave(PTSTR szBmpFile, PBITMAPFILEHEADER pbmfh){BOOLbSuccess;DWORDdwBytesWrite;HANDLEhFile;//開啟要寫入的檔案hFile = CreateFile(szBmpFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (INVALID_HANDLE_VALUE == hFile){return FALSE;}//寫入檔案bSuccess = WriteFile(hFile, pbmfh, pbmfh->bfSize, &dwBytesWrite, NULL);CloseHandle(hFile);if (!bSuccess || dwBytesWrite != pbmfh->bfSize){DeleteFile(hFile);return FALSE;}return TRUE;}void ShowDib(HDC hdc, PBITMAPINFO pbmi, PBYTE pBits, long nBmpWidth, long nBmpHeight, long cxClient, long cyClient, BOOL bFull){if (FALSE == bFull)//置中顯示{SetDIBitsToDevice(hdc,(cxClient-nBmpWidth)/2, (cyClient-nBmpHeight)/2,nBmpWidth, nBmpHeight,0, 0,0, nBmpHeight, pBits, pbmi,DIB_RGB_COLORS);}else//展開顯示{SetStretchBltMode(hdc, COLORONCOLOR);StretchDIBits(hdc, 0, 0, cxClient, cyClient, 0, 0, nBmpWidth, nBmpHeight, pBits, pbmi, DIB_RGB_COLORS, SRCCOPY);}}
這裡需要補充說明的是,為了保證邏輯清晰,本文沒有考慮位元影像的OS/2相容格式和自頂向下位元影像等特殊情況,錯誤處理可能也不是特別完善(沒有考慮超大位元影像的情況),示範的代碼在大部分情況下是適用的,只是為了起拋磚引玉的作用。關於DIB的詳細描述當推Petzold的《Windows 程式設計》的“與裝置無關的位元影像”一章,這一章描述非常詳細,但是個別地方有些晦澀,結合本文來看可以加深理解。
完整原始碼下載連結原創,轉載請註明來自http://blog.csdn.net/wenzhou1219