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