#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)//计算图像每行象素所占的字节数目; HANDLE m_hDIB;//存放位图数据的句柄; CPalette* m_palDIB;//指向调色板Cpalette类的指针; CSize m_sizeDoc;//初始化视图的尺寸,该尺寸为位图的尺寸; |
BOOL CDibDoc::OnOpenDocument(LPCTSTR lpszPathName) { LOGPALETTE *pPal;//定义逻辑调色板指针; pPal=new LOGPALETTE;//初始化该指针; CFile file; CFileException fe; if (!file.Open(lpszPathName, CFile::modeRead | CFile::shareDenyWrite, &fe)) {//以“读”的方式打开文件; AfxMessageBox("图像文件打不开!"); return FALSE; } DeleteContents();//删除文挡; BeginWaitCursor(); BITMAPFILEHEADER bmfHeader;//定义位图文件头结构; LPBITMAPINFO lpbmi; DWORD dwBitsSize; HANDLE hDIB; LPSTR pDIB;//指向位图数据的指针; BITMAPINFOHEADER *bmhdr;//指向位图信息头结构的指针 dwBitsSize = file.GetLength();//得到文件长度 if (file.Read((LPSTR)&bmfHeader, sizeof(bmfHeader)) !=sizeof(bmfHeader)) return FALSE;//读取位图文件的文件头结构信息; if (bmfHeader.bfType != 0x4d42) //检查该文件是否为BMP格式的文件; return FALSE; hDIB=(HANDLE) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwBitsSize); //为读取图像文件数据申请缓冲区 if (hDIB == 0) { return FALSE; } pDIB = (LPSTR) ::GlobalLock((HGLOBAL)hDIB); //得到申请的缓冲区的指针; if (file.ReadHuge(pDIB, dwBitsSize - sizeof(BITMAPFILEHEADER)) != dwBitsSize - sizeof(BITMAPFILEHEADER) ) { ::GlobalUnlock((HGLOBAL)hDIB); hDIB=NULL; return FALSE; }//此时pDIB数据块中读取的数据包括位图头信息、位图颜色表、图像像素的灰度值; bmhdr=(BITMAPINFOHEADER*)pDIB;//为指向位图信息头结构的指针赋值; ::GlobalUnlock((HGLOBAL)hDIB); if ((*bmhdr).biBitCount!=8)//验证是否为8bit位图 { AfxMessageBox("该文件不是灰度位图格式!"); return FALSE; } m_hDIB=hDIB;//将内部变量数据赋于全局变量; //下面是记录位图的尺寸; m_sizeDoc.x=bmhdr->biWidth; m_sizeDoc.y=bmhdr->biHeight; //下面是根据颜色表生成调色板; m_palDIB=new Cpalette; pPal->palVersion=0x300;//填充逻辑颜色表 pPal->palNumEntries=256; lpbmi=(LPBITMAPINFO)bmhdr; for(int i=0;i<256;i++) {//每个颜色表项的R、G、B值相等,并且各个值从“0”到“255”序列展开; 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); //根据读入的数据得到位图的宽、高、颜色表; if(pPal) delete pPal; EndWaitCursor(); SetPathName(lpszPathName);//设置存储路径 SetModifiedFlag(FALSE); // 设置文件修改标志为FALSE return TRUE; } |
///////////////////////////////////////////////////////////////// HANDLE LoadDIB(UINT uIDS, LPCSTR lpszDibType) { LPCSTR lpszDibRes =MAKEINTRESOURCE(uIDS);//根据资源标志符确定资源的名字; HINSTANCE hInst=AfxGetInstanceHandle();//得到应用程序的句柄; HRSRC hRes=::FindResource(hInst,lpszDibRes, lpszDibType);//获取资源的句柄,这里lpszDibType为资源的名字“DIB”; If(hRes==NULL) return NULL HGLOBAL hData=::LoadResource(hInst, hRes);//转载资源数据并返回该句柄; return hData; } |
/////////////////////////////////////////////////////////////////// BOOL CDibDoc::OnSaveDocument(LPCTSTR lpszPathName) { CFile file; CFileException fe; BITMAPFILEHEADER bmfHdr; // 位图文件头结构; LPBITMAPINFOHEADER lpBI;//指向位图头信息结构的指针; DWORD dwDIBSize;; if (!file.Open(lpszPathName, CFile::modeCreate |CFile::modeReadWrite | CFile::shareExclusive, &fe)) { AfxMessageBox("文件打不开"); return FALSE; }//以读写的方式打开文件; BOOL bSuccess = FALSE; BeginWaitCursor(); lpBI = (LPBITMAPINFOHEADER) ::GlobalLock((HGLOBAL) m_hDIB); if (lpBI == NULL) return FALSE; dwDIBSize = *(LPDWORD)lpBI + 256*sizeof(RGBQUAD); //图像的文件信息所占用的字节数; DWORD dwBmBitsSize;//BMP文件中位图的像素所占的字节数 dwBmBitsSize=WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) *lpBI->biHeight;// 存储时位图所有像素所占的总字节数 dwDIBSize += dwBmBitsSize; //BMP文件除文件信息结构外的所有数据占用的总字节数; lpBI->biSizeImage = dwBmBitsSize; // 位图所有像素所占的总字节数 //以下五句为文件头结构填充值 bmfHdr.bfType =0x4d42; // 文件为"BMP"类型 bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER);//文件总长度 bmfHdr.bfReserved1 = 0; bmfHdr.bfReserved2 = 0; bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + 256*sizeof(RGBQUAD); //位图数据距离文件头的偏移量; file.Write((LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));//向文件中写文件头信息; file.WriteHuge(lpBI, dwDIBSize); //将位图信息(信息头结构、颜色表、像素数据)写入文件; ::GlobalUnlock((HGLOBAL) m_hDIB); EndWaitCursor(); SetModifiedFlag(FALSE); // 将文档设为“干净”标志,表示此后文档不需要存盘提示; return TRUE; } |
typedef struct tagLOGPALETTE { WORD palVersion;//调色板的板本号,应该指定该值为0x300; WORD palNumEntries;//调色板中的表项数,对于灰度图像该值为256; PALETEENTRY palPalEntry[1];//调色板中的颜色表项,由于该表项的数目不一定,所以这里数组长度定义为1,灰度图像对应的该数组的长度为256; }LOGPALETTE; 颜色表项结构PALETTEENTRY定义了调色板中的每一个颜色表项的颜色和使用方式,定义如下: typedef struct tagPALETTEENTRY { BYTE peRed; //R分量值; BYTE peGreen; //G分量值; BYTE peBlue; //B分量值; BYTE peFlags; // 该颜色被使用的方式,一般情况下设为“0”; }PALETTEENTRY; |
////////////////////////////////////////////////////////// void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd) {//总实现活动视的调色板 CMDIFrameWnd::OnPaletteChanged(pFocusWnd); CMDIChildWnd* pMDIChildWnd = MDIGetActive();//得到活动的子窗口指针; if (pMDIChildWnd == NULL) return CView* pView = pMDIChildWnd->GetActiveView();//得到视图的指针; ASSERT(pView != NULL); SendMessageToDescendants(WM_DOREALIZE, (WPARAM)pView->m_hWnd); //通知所有子窗口系统调色板已改变 } //////////////////////////////////////////////// BOOL CMainFrame::OnQueryNewPalette()//提供实现系统调色板的机会 { // 实现活动视的调色板 CMDIChildWnd* pMDIChildWnd = MDIGetActive();//得到活动的子窗口指针; if (pMDIChildWnd == NULL) return FALSE;//no active MDI child frame (no new palette) CView* pView = pMDIChildWnd->GetActiveView();//得到活动子窗口的视图指针; ASSERT(pView != NULL); //通知活动视图实现系统调色板 pView->SendMessage(WM_DOREALIZE, (WPARAM)pView->m_hWnd); return TRUE; } ///////////////////////////////////////////////// BOOL CDibView::OnDoRealize(WPARAM wParam, LPARAM)//实现系统调色板 { ASSERT(wParam != NULL); CDibDoc* pDoc = GetDocument(); if (pDoc->m_hDIB == NULL) return FALSE; // must be a new document CPalette* pPal = pDoc->m_palDIB; //调色板的颜色表数据在InitDIBData()函数中实现 if (pPal != NULL) { CMainFrame* pAppFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd;//得到程序的主框架指针; ASSERT_KINDOF(CMainFrame, pAppFrame); CClientDC appDC(pAppFrame);//获取主框架的设备上下文; CPalette* oldPalette = appDC.SelectPalette(pPal, ((HWND)wParam) != m_hWnd); //只有活动视才可以设为"FALSE",即根据活动视的调色板设为"前景"调色板; if (oldPalette != NULL) { UINT nColorsChanged = appDC.RealizePalette();//实现系统调色板 if (nColorsChanged > 0) pDoc->UpdateAllViews(NULL);//更新视图 appDC.SelectPalette(oldPalette, TRUE); //将原系统调色板置为背景调色板 } else { TRACE0(“\tSelectPalette failed in”); } return TRUE; } |
///////////////////////////////////////////////////////// CDibDoc::CDibDoc() { ………………………. LOGPALETTE *Pal; Pal=new LOGPALETTE; m_palDIB=new Cpalette; pPal->palVersion=0x300; pPal->palNumEntries=256; for(int i=0;i<256;i++) {//每个颜色表项的R、G、B值相等,并且各个值从“0”到“255”序列展开; Pal->palPalentry[i].peRed=i; pPal->palPalentry[i].peGreen=i; pPal->palPalentry[i].peBlue=i; pPal->palPalentry[i].peFlags=0; } m_palDIB->CreatePalette(pPal); ………………….. } 三、 图像的显示 显示DIB位图数据可以通过设备上下文CDC对象的成员函数 CDC::Bitblt()或CDC::StretchBlt()来实现,也可以通过API函数SetDIBBitsToDevice()或 StretchDIBBits()来实现,函数中具体所用到的各个参数的意义可以参考MSDN。其中StretchDIBBits()和 CDC::StretchBlt()可以将图像进行放大和缩小显示。当从文档中装入位图文件时,CDIBView类的OnInitialUpdate函数将被调用,因此可以在该函数中实现对视图尺寸的设置,用于正确的显示位图,然后就可以在视图类的OnDraw()函数中正确的显示位图了。这两个函数的具体实现代码分别如下所示:
四、 小结 在本期讲座里我们主要介绍了如何操作灰度位图,它具有较强的代表性,同时为后续的图像处理编程的学习作了必要的准备工作,经过学习,对于如何操作其它类型的BMP格式的图像文件,可以达到举一反三的作用。 |