一,、 BMP位圖操作
首先我們回顧一下上講中的重要信息:BMP位圖包括位圖文件頭結(jié)構(gòu)BITMAPFILEHEADER、位圖信息頭結(jié)構(gòu)BITMAPINFOHEADER,、位圖顏色表RGBQUAD和位圖像素?cái)?shù)據(jù)四部分,。處理位圖時(shí)要根據(jù)文件的這些結(jié)構(gòu)得到位圖文件大小、位圖的寬,、高,、實(shí)現(xiàn)調(diào)色板、得到位圖像素值等等,。這里要注意的一點(diǎn)是在BMP位圖中,,位圖的每行像素值要填充到一個(gè)四字節(jié)邊界,即位圖每行所占的存儲(chǔ)長(zhǎng)度為四字節(jié)的倍數(shù),,不足時(shí)將多余位用0填充,。 有了上述知識(shí),可以開(kāi)始編寫(xiě)圖像處理的程序了,關(guān)于在VC的開(kāi)發(fā)平臺(tái)上如何開(kāi)發(fā)程序的問(wèn)題這里不再贅述,,筆者假定讀者都具有一定的VC開(kāi)發(fā)經(jīng)驗(yàn),。在開(kāi)發(fā)該圖像處理程序的過(guò)程中,筆者沒(méi)有采用面向?qū)ο蟮姆椒?,雖然面向?qū)ο蟮姆椒梢詫?shù)據(jù)封裝起來(lái),,保護(hù)類(lèi)中的數(shù)據(jù)不受外界的干擾,提高數(shù)據(jù)的安全性,,但是這種安全性是以降低程序的執(zhí)行效率為代價(jià)的,,為此,我們充分利用了程序的文檔視圖結(jié)構(gòu),,在程序中直接使用了一些API函數(shù)來(lái)操作圖像,。在微軟的MSDN中有一個(gè)名為Diblook的例子,該例子演示了如何操作Dib位圖,,有興趣的讀者可以參考一下,,相信一定會(huì)有所收獲。 啟動(dòng)Visual C++,,生成一個(gè)名為Dib的多文檔程序,,將CDibView類(lèi)的基類(lèi)設(shè)為CscrollView類(lèi),這樣作的目的是為了在顯示位圖時(shí)支持滾動(dòng)條,,另外在處理圖像應(yīng)用程序的文檔類(lèi)(CDibDoc.h)中聲明如下宏及公有變量:
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)//計(jì)算圖像每行象素所占的字節(jié)數(shù)目,; HANDLE m_hDIB;//存放位圖數(shù)據(jù)的句柄; CPalette* m_palDIB;//指向調(diào)色板Cpalette類(lèi)的指針,; CSize m_sizeDoc;//初始化視圖的尺寸,,該尺寸為位圖的尺寸; |
最后將程序的字符串表中的字符串資源IDR_DibTYPE修改為:“\nDib\nDib\nDib Files(*.bmp;*.dib)\n.bmp\nDib.Document\nDib Document”,。這樣作的目的是為了在程序文件對(duì)話框中可以選擇BMP或DIB格式的位圖文件,。 1、 讀取灰度BMP位圖 可以根據(jù)BMP位圖文件的結(jié)構(gòu),,操作BMP位圖文件并讀入圖像數(shù)據(jù),,為此我們充分利用了VC的文檔視圖結(jié)構(gòu),重載了文擋類(lèi)的OnOpenDocument()函數(shù),,這樣用戶就可以在自動(dòng)生成程序的打開(kāi)文件對(duì)話框中選擇所要打開(kāi)的位圖文件,,然后程序?qū)⒆詣?dòng)調(diào)用該函數(shù)執(zhí)行讀取數(shù)據(jù)的操作。該函數(shù)的實(shí)現(xiàn)代碼如下所示:
BOOL CDibDoc::OnOpenDocument(LPCTSTR lpszPathName) { LOGPALETTE *pPal;//定義邏輯調(diào)色板指針,; pPal=new LOGPALETTE;//初始化該指針,; CFile file; CFileException fe; if (!file.Open(lpszPathName, CFile::modeRead | CFile::shareDenyWrite, &fe)) {//以“讀”的方式打開(kāi)文件; AfxMessageBox("圖像文件打不開(kāi),!"); return FALSE; } DeleteContents();//刪除文擋,; BeginWaitCursor(); BITMAPFILEHEADER bmfHeader;//定義位圖文件頭結(jié)構(gòu),; LPBITMAPINFO lpbmi; DWORD dwBitsSize; HANDLE hDIB; LPSTR pDIB;//指向位圖數(shù)據(jù)的指針; BITMAPINFOHEADER *bmhdr;//指向位圖信息頭結(jié)構(gòu)的指針 dwBitsSize = file.GetLength();//得到文件長(zhǎng)度 if (file.Read((LPSTR)&bmfHeader, sizeof(bmfHeader)) !=sizeof(bmfHeader)) return FALSE;//讀取位圖文件的文件頭結(jié)構(gòu)信息,; if (bmfHeader.bfType != 0x4d42) //檢查該文件是否為BMP格式的文件,; return FALSE; hDIB=(HANDLE) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwBitsSize); //為讀取圖像文件數(shù)據(jù)申請(qǐng)緩沖區(qū) if (hDIB == 0) { return FALSE; } pDIB = (LPSTR) ::GlobalLock((HGLOBAL)hDIB); //得到申請(qǐng)的緩沖區(qū)的指針; if (file.ReadHuge(pDIB, dwBitsSize - sizeof(BITMAPFILEHEADER)) != dwBitsSize - sizeof(BITMAPFILEHEADER) ) { ::GlobalUnlock((HGLOBAL)hDIB); hDIB=NULL; return FALSE; }//此時(shí)pDIB數(shù)據(jù)塊中讀取的數(shù)據(jù)包括位圖頭信息,、位圖顏色表,、圖像像素的灰度值; bmhdr=(BITMAPINFOHEADER*)pDIB;//為指向位圖信息頭結(jié)構(gòu)的指針賦值,; ::GlobalUnlock((HGLOBAL)hDIB); if ((*bmhdr).biBitCount!=8)//驗(yàn)證是否為8bit位圖 { AfxMessageBox("該文件不是灰度位圖格式,!"); return FALSE; } m_hDIB=hDIB;//將內(nèi)部變量數(shù)據(jù)賦于全局變量; //下面是記錄位圖的尺寸,; m_sizeDoc.x=bmhdr->biWidth; m_sizeDoc.y=bmhdr->biHeight; //下面是根據(jù)顏色表生成調(diào)色板,; m_palDIB=new Cpalette; pPal->palVersion=0x300;//填充邏輯顏色表 pPal->palNumEntries=256; lpbmi=(LPBITMAPINFO)bmhdr; for(int i=0;i<256;i++) {//每個(gè)顏色表項(xiàng)的R,、G,、B值相等,并且各個(gè)值從“0”到“255”序列展開(kāi),; Pal->palPalentry[i].peRed=lpbmi->bmiColors[i].rgbRed; pPal->palPalentry[i].peGreen=lpbmi->bmiColors[i].rgbGreen; pPal->palPalentry[i].peBlue= lpbmi->bmiColors[i].rgbBlue;; pPal->palPalentry[i].peFlags=0; } m_palDIB->CreatePalette(pPal); //根據(jù)讀入的數(shù)據(jù)得到位圖的寬,、高、顏色表; if(pPal) delete pPal; EndWaitCursor(); SetPathName(lpszPathName);//設(shè)置存儲(chǔ)路徑 SetModifiedFlag(FALSE); // 設(shè)置文件修改標(biāo)志為FALSE return TRUE; } |
上面的方法是通過(guò)CFile類(lèi)對(duì)象的操作來(lái)讀取位圖文件的,,它需要分析位圖中的文件頭信息,,從而確定需要讀取的圖像長(zhǎng)度。這種方法相對(duì)來(lái)說(shuō)有些繁瑣,,其實(shí)還可以以一種相對(duì)簡(jiǎn)單的方法讀取位圖數(shù)據(jù),,首先在程序的資源中定義DIB類(lèi)型資源,然后添加位圖到該類(lèi)型中,,將圖像數(shù)據(jù)以資源的形式讀取出來(lái),,這時(shí)候就可以根據(jù)所獲取的數(shù)據(jù)中的位圖信息結(jié)構(gòu)來(lái)獲取、顯示圖像數(shù)據(jù)了,。下面的函數(shù)實(shí)現(xiàn)了以資源形式裝載圖像文件數(shù)據(jù),,該函數(shù)的實(shí)現(xiàn)代碼如下所示:
///////////////////////////////////////////////////////////////// HANDLE LoadDIB(UINT uIDS, LPCSTR lpszDibType) { LPCSTR lpszDibRes =MAKEINTRESOURCE(uIDS);//根據(jù)資源標(biāo)志符確定資源的名字; HINSTANCE hInst=AfxGetInstanceHandle();//得到應(yīng)用程序的句柄,; HRSRC hRes=::FindResource(hInst,lpszDibRes, lpszDibType);//獲取資源的句柄,,這里lpszDibType為資源的名字“DIB”; If(hRes==NULL) return NULL HGLOBAL hData=::LoadResource(hInst, hRes);//轉(zhuǎn)載資源數(shù)據(jù)并返回該句柄; return hData; } |
2,、 灰度位圖數(shù)據(jù)的存儲(chǔ) 為了將圖像處理后所得到的像素值保存起來(lái),,我們重載了文檔類(lèi)的OnSaveDocument()函數(shù),這樣用戶在點(diǎn)擊Save或SaveAs子菜單后程序自動(dòng)調(diào)用該函數(shù),,實(shí)現(xiàn)圖像數(shù)據(jù)的存儲(chǔ),。該函數(shù)的具體實(shí)現(xiàn)如下:
/////////////////////////////////////////////////////////////////// BOOL CDibDoc::OnSaveDocument(LPCTSTR lpszPathName) { CFile file; CFileException fe; BITMAPFILEHEADER bmfHdr; // 位圖文件頭結(jié)構(gòu); LPBITMAPINFOHEADER lpBI;//指向位圖頭信息結(jié)構(gòu)的指針; DWORD dwDIBSize;,; if (!file.Open(lpszPathName, CFile::modeCreate |CFile::modeReadWrite | CFile::shareExclusive, &fe)) { AfxMessageBox("文件打不開(kāi)"),; return FALSE; }//以讀寫(xiě)的方式打開(kāi)文件; BOOL bSuccess = FALSE; BeginWaitCursor(); lpBI = (LPBITMAPINFOHEADER) ::GlobalLock((HGLOBAL) m_hDIB); if (lpBI == NULL) return FALSE; dwDIBSize = *(LPDWORD)lpBI + 256*sizeof(RGBQUAD); //圖像的文件信息所占用的字節(jié)數(shù),; DWORD dwBmBitsSize;//BMP文件中位圖的像素所占的字節(jié)數(shù) dwBmBitsSize=WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) *lpBI->biHeight;// 存儲(chǔ)時(shí)位圖所有像素所占的總字節(jié)數(shù) dwDIBSize += dwBmBitsSize; //BMP文件除文件信息結(jié)構(gòu)外的所有數(shù)據(jù)占用的總字節(jié)數(shù),; lpBI->biSizeImage = dwBmBitsSize; // 位圖所有像素所占的總字節(jié)數(shù) //以下五句為文件頭結(jié)構(gòu)填充值 bmfHdr.bfType =0x4d42; // 文件為"BMP"類(lèi)型 bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER);//文件總長(zhǎng)度 bmfHdr.bfReserved1 = 0; bmfHdr.bfReserved2 = 0; bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + 256*sizeof(RGBQUAD); //位圖數(shù)據(jù)距離文件頭的偏移量; file.Write((LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));//向文件中寫(xiě)文件頭信息,; file.WriteHuge(lpBI, dwDIBSize); //將位圖信息(信息頭結(jié)構(gòu),、顏色表、像素?cái)?shù)據(jù))寫(xiě)入文件,; ::GlobalUnlock((HGLOBAL) m_hDIB); EndWaitCursor(),; SetModifiedFlag(FALSE); // 將文檔設(shè)為“干凈”標(biāo)志,,表示此后文檔不需要存盤(pán)提示,; return TRUE; } |
|