Windows DIB文件操作详解-1.DIB的读入、保存和显示
DIB(設備無關位圖)是存儲在磁盤上的位圖文件,可以從磁盤讀到內存中或從內存保存到磁盤上,它的磁盤文件結構是標準化的,在Linux、Unix及Windows上都可以以同樣效果顯示。位圖是最接近硬件的圖像格式,Windows顯示的核心是位圖,它的SDK API專門提供了一組用于操作DIB文件的函數。但是由于這樣或那樣的原因,高效合理的使用這些DIB API是需要了解不少歷史和使用背景的,在這里我抽繭剝絲介紹和演示DIB的使用,相信對你更好的使用DIB文件有幫助,由于DIB函數比較多,這里分為三部分介紹,首先是DIB的讀入、保存和顯示。
1.DIB文件的組成
關于DIB文件的組成很多地方有詳細描述,這里不再詳細贅述。主要分為如下幾個部分:
- 文件表頭
- 信息表頭
- RGB色彩對照表(不一定有)
- 位圖像素位
其中
其中,信息表頭和RGB調色板合稱為位圖信息。
2.Windows DIB內存數據結構
我們要把DIB數據讀入到內存中,那么就要分配相應的內存,把讀入的數據寫到對應的內存區中,這里SDK 提供的數據結構是各種結構體,結構體的各個字段對應磁盤文件中各個信息值。我們這里為了邏輯清楚,使用最常使用的DIB結構體。
如圖,文件中的數據讀到對應的內存結構中完成文件的讀入,內存結構中的數據寫到對應的文件中完成文件的保存。
其中
把BITMAPINFOHEADER和RGBQUAD[0]作為BITMAPINFO結構體成員,這樣一方面是為了和磁盤文件對應,另一方面也是為了訪問調色板數據方便。
這里的磁盤文件中各個段在磁盤上保存位置必須是連續的,但是對應的內存中文件信息頭、位圖信息和位圖數據三大塊不一定要是連續的。因此,在讀入文件中你既可以一次性讀入磁盤文件到連續的內存中,也可以分開讀入到三個分別連續的內存中,后面會做相關演示。
3.DIB顯示
前面講了讀入DIB到對應的內存結構中,現在我們要怎么把對應的內存結構中的像素數據顯示出來呢? 如下圖,為內存中指定點的像素值是怎么樣一步步顯示到顯示器上指定位置的,這里做了一些簡化,沒有考慮壓縮位圖和顯示器的顯示精度這個圖看起來很復雜,實際上在這里我也沒想把它完全講出來,如果你之前有一定的DIB使用經驗,那么這幅圖可以幫你更好的理解整個顯示過程,如果沒有DIB使用經驗,那么不用仔細看這幅位圖。從這幅圖,我們知道顯示的時候需要的數據是:內存中位圖數據、內存中位圖信息、內存中位圖指定要顯示區域、顯示器上用于顯示的區域。
提供了需要的信息后,Windows提供了SetDIBitsToDevice和StretchDIBits函數幫我們完成這一顯示過程。 兩個函數的原型分別如下 int SetDIBitsToDevice( HDC hdc, // handle to DCint XDest, // x-coord of destination upper-left cornerint YDest, // y-coord of destination upper-left corner DWORD dwWidth, // source rectangle widthDWORD dwHeight, // source rectangle heightint XSrc, // x-coord of source lower-left cornerint YSrc, // y-coord of source lower-left cornerUINT uStartScan, // first scan line in arrayUINT cScanLines, // number of scan linesCONST VOID *lpvBits, // array of DIB bitsCONST BITMAPINFO *lpbmi, // bitmap informationUINT fuColorUse // RGB or palette indexes);
int StretchDIBits(HDC hdc, // handle to DCint XDest, // x-coord of destination upper-left cornerint YDest, // y-coord of destination upper-left cornerint nDestWidth, // width of destination rectangleint nDestHeight, // height of destination rectangleint XSrc, // x-coord of source upper-left cornerint YSrc, // y-coord of source upper-left cornerint nSrcWidth, // width of source rectangleint nSrcHeight, // height of source rectangleCONST VOID *lpBits, // bitmap bitsCONST BITMAPINFO *lpBitsInfo, // bitmap dataUINT iUsage, // usage optionsDWORD dwRop // raster operation code);
可以看到兩個函數的從上到下依次需要提供的信息為:待顯示窗口的DC、待顯示窗口的用于顯示的區域、位圖中要顯示部分、位圖數據、位圖信息、其它選項,具體各個參數含義參見MSDN。
4.代碼演示
這里的演示程序界面如下在讀入中可以選擇一次性讀入還是分片段讀入,兩種方式的區別在于讀入以后在內存中位圖各個段是否連續。保存也對應讀入的兩種方式。顯示可以選擇正常居中顯示還是拉伸到整個窗口屏幕顯示,前者對應SetDIBitsToDevice函數,后者對應StretchDIBits函數。這里只提供一次性讀入、保存和顯示代碼,完整代碼參見附件。
BOOL DibTotalLoad(PTSTR szBmpFile, PBITMAPFILEHEADER *ppbmfh,PBITMAPINFO *ppbmi,PBYTE *ppBits,PLONG pBmpWidth,PLONG pBmpHeight) {HANDLE hFile;DWORD dwFileSize, dwBytesRead;BOOL bSuccess;//打開文件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) {BOOL bSuccess;DWORD dwBytesWrite;HANDLE hFile;//打開要寫入的文件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
總結
以上是生活随笔為你收集整理的Windows DIB文件操作详解-1.DIB的读入、保存和显示的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Rk3566 Rk3326s Andro
- 下一篇: I2C驱动_linux