Windows DIB文件操作详解-4.使用DIB Section
前面講了為了提高DIB的顯示性能和效率,我們將DIB轉換成DDB,但是這又遇到一個問題,如果我想操作DIB的數據的話,顯然是不能使用DDB:一是因為DIB轉DDB時發生了顏色轉換,再就是DDB無法直接提取指定像素點的數據。那么我們怎么辦呢,Windows使用一種折中的方式來達到這一目標(既提高了顯示效率和性能,又可以直接操作像素點)。
1.DIB Section存儲和顯示
Windows使用DIB塊(DIB Section)來存儲DIB數據,其內存結構示意圖如下
其實,和我們自己讀入DIB數據到自己分配的各個數據區感覺是一樣的,只是現在這些DIB的各個數據區是由Windows自己分配維護的,值得注意的是這些Windows自己維護的DIB數據區是通過HBITMAP句柄來組織的,這個HBITMAP句柄和BITMAP的HBITMAP句柄是不一樣的,至于為什么同一種句柄可表示不同的對象可以查看筆者的博文“深入了解Windows句柄到底是什么”。
繼續說現在的問題,這里存儲的是DIB的數據,只有在使用BitBlt和StretchBlt顯示的時候,才會發生DIB->DDB的轉換,顯然這里BitBlt和StretchBlt會對句柄屬性做一個判斷來確認指向BITMAP的HBITMAP及指向DIB Section的HBITMAP的不同操作。當然,這里的DIB Section各個區不一定是連續的,這是一定要注意的。
這里Windows自己維護DIB各個數據區,做了一定優化,所以比我們自己分配和存儲DIB各個數據區的顯示效率和性能要高。
2.DIB Section的相關函數使用
明白了Windows對于DIB Section的存儲原理和轉換規則以后,我們說一下DIB Section的相關函數使用。
1.CreateDIBSection
HBITMAP CreateDIBSection(HDC hdc, // 設備描述表句柄CONST BITMAPINFO *pbmi, // 包含Info Header、Mask、Color Table數據的BITMAPINFO指針UINT iUsage, // DIB_PAL_COLORS或DIB_RGB_COLORSVOID *ppvBits, // 指向存儲位圖數據的地址的指針HANDLE hSection, DWORD dwOffset );
使用如下:
1.一般不考慮后兩個參數
2.傳入包含DIB位圖信息頭(Info Header)、壓縮掩碼(Mask)及調色板信息(Color Table,主要針對位圖深度<=8)的BITMAPINFO* pbmi指針,這三者必須是連續存儲的,一般指明DIB_RGB_COLORS參數,這樣Windows會自動按照pbmi提供的信息分配對應的DIB Section,包括Info Header、Mask、Color Table及Bits四個區,其中分配的Bits區的地址被寫到ppvBits指向的指針中。
即常用調用步驟如下:
a.讀入DIB的位圖信息頭(Info Header)、壓縮掩碼(Mask)及調色板信息(Color Table)到pbmi指向內存中
b.hBitmap = CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, &pBits, NULL, 0);
c.讀入DIB的Bits數據到pBits指向的內存中
d.BitBlt或StretchBlt顯示hBitmap
3.只有使用DIB_PAL_COLORS參數時才需要hdc參數。只有DIB的調色板使用索引存儲方式才需要使用這兩個參數。實際上,這里的hdc和DIB_PAL_COLORS實際上最終被SetDIBitsToDevice和StretchDIBits函數調用,可以查看他們兩個的參數。
4.pBits指向的內存由Windows操作系統托管,但是用戶可以操作pBits數據,刪除hBitmap時pBits內存區同時也會釋放掉。
2.GetDIBColorTable和SetDIBColorTable
兩個函數定義如下: UINT GetDIBColorTable(HDC hdc, // 設備描述表句柄UINT uStartIndex, // 調色板起始索引UINT cEntries, // 要獲取的調色板項個數RGBQUAD *pColors // 存儲調色板項的地址 );UINT SetDIBColorTable(HDC hdc, // 設備描述表句柄UINT uStartIndex, // 調色板起始索引UINT cEntries, // 要設置的調色板項個數RGBQUAD *pColors // 存儲調色板項的地址 );分別用來獲取和設置指定的調色板項,一般如下使用。hdcMem = CreateCompatibleDC(NULL); SelectObject(hdcMem, hBitmap); GetDIBColorTable(hdcMem, uFirstIndex, uNumEntries, &prgb); DeleteDC(hdcMem);
hdcMem = CreateCompatibleDC(NULL); SelectObject(hdcMem, hBitmap); SetDIBColorTable(hdcMem, uFirstIndex, uNumEntries, &prgb); DeleteDC(hdcMem);
通過DIB Section來獲取和設置調色板,可以屏蔽OS/2兼容位圖帶來的差異(BITMAPCOREINFO調色板項采用RGBTRIPLE結構體而不是BITMAPINFOHEADER采用的RGBQUAD)。
3.獲取DIBSECTION
DIBSECTION定義如下: typedef struct tagDIBSECTION { BITMAP dsBm; BITMAPINFOHEADER dsBmih; DWORD dsBitfields[3]; HANDLE dshSection; DWORD dsOffset; } DIBSECTION;使用 GetObject(hBitmap, sizeof(DIBSECTION), &dibsection);不同于BITMAP,DIB Section使用GetObject獲取的是DIB Section,可以看到DIBSECTION將BITMAP設為第一個屬性,這是為了保證和BITMAP的兼容,萬一你不知道hBitmap的屬性是指向DIB Section的,那么GetObject(hBitmap, sizeof(BITMAP), &bitmap)也不至于發生錯誤。
通過DIB Section來獲取位圖信息,可以不考慮不同DIB位圖格式帶來的差異,位圖信息頭均使用BITMAPINFOHEADER,壓縮掩碼使用DWORD來單獨指定,不用考慮BITMAPCOREHEADER、BITMAPV4HEADER、BITMAPV5HWEADER帶來的差異。
3.代碼演示
在演示程序中,我們讀入一幅圖片(8bit、16bit、24bit)創建成DIB Section形式顯示、查看調色板及壓縮掩碼 //讀入DIB文件并轉換成DIB Section HBITMAP CreateDibSectionFromDibFile(PTSTR szFileName) {BITMAPFILEHEADER bmfh;BITMAPINFO *pbmi;BYTE *pBits;BOOL bSuccess;DWORD dwInfoSize, dwBytesRead;HANDLE hFile;HBITMAP hBitmap;//打開文件hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);if (INVALID_HANDLE_VALUE == hFile){return NULL;}//讀入DIB文件頭bSuccess = ReadFile(hFile, &bmfh, sizeof(BITMAPFILEHEADER), &dwBytesRead, NULL);if (!bSuccess || (dwBytesRead != sizeof(BITMAPFILEHEADER)) || (bmfh.bfType != *(WORD *)"BM")){CloseHandle(hFile);return NULL;}//為DIB BITMAPINFO分配內存,并讀入DIB數據dwInfoSize = bmfh.bfOffBits - sizeof(BITMAPFILEHEADER);pbmi = malloc(dwInfoSize);if (NULL == pbmi){CloseHandle(hFile);return NULL;}bSuccess = ReadFile(hFile, pbmi, dwInfoSize, &dwBytesRead, NULL);if (!bSuccess || (dwBytesRead != dwInfoSize)){free(pbmi);CloseHandle(hFile);return NULL;}//創建DIB SectionhBitmap = CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, &pBits, NULL, 0);free(pbmi);if (NULL == hBitmap){CloseHandle(hFile);return NULL;}//讀入位圖數據到分配的DIB Section的Bits區(pBits指向)bSuccess = ReadFile(hFile, pBits, bmfh.bfSize-bmfh.bfOffBits, &dwBytesRead, NULL);CloseHandle(hFile);if (!bSuccess || (dwBytesRead != (bmfh.bfSize-bmfh.bfOffBits))){return NULL;}return hBitmap; }//如果有調色板則獲得第一個調色板項 BOOL GetFirstColorTableItem(HBITMAP hBitmap, RGBQUAD *prgb) {HDC hdcMem;int iNum;hdcMem = CreateCompatibleDC(NULL);SelectObject(hdcMem, hBitmap);iNum = GetDIBColorTable(hdcMem, 0, 1, prgb);DeleteDC(hdcMem);return 0==iNum ? FALSE : TRUE; }//獲得第一個調色板項 DWORD GetFirstMaskItem(HBITMAP hBitmap) {DIBSECTION ds;GetObject(hBitmap, sizeof(DIBSECTION), &ds);return ds.dsBitfields[0]; }
在后續我會講解使用DIB Section來完成圖像指定像素點的讀寫,實際上,一旦能夠操作pBits,那么完成指定像素點的讀寫也不是什么難事。
完整演示代碼下載鏈接 原創,轉載請注明來自http://blog.csdn.net/wenzhou1219
總結
以上是生活随笔為你收集整理的Windows DIB文件操作详解-4.使用DIB Section的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SourceTree使用教程(Mac版)
- 下一篇: 微信小程序制作日常生活首页