位图读、写、显示的C++实现实例
2015-08-22 21:56
519 查看
对图像进行处理的前提是,要能实现对象的打开保存和显示,这是处理的前提。以下在VS2010中基于MFC的框架实现对位图文件的打开、保存和显示功能。
第一步:打开MFC应用程序向导,创建一个单文档的MFC应用程序,向导中的其它参数均可保持默认。
第二部:实现读写和显示功能:
1.打开类视图,为CBMPTestView类添加以下5个函数,方法是右击CBMPTestView,在弹出的菜单中选择添加->添加函数,将会弹出添加成员函数向导,如下图:
如果自动向导添加的函数与下面不符,应该手动添加或者手动修改正确后,再继续。
五个函数原型声明为:
2.定义上面5个函数:
在函数SaveBMP()中为使像素阵列每行的字节数为4的整数倍,使用宏WIDTHBYTES.在BMPTestView.cpp文件开始处添加如下宏定义:
3.添加成员变量:
在类资源视图中右击CBMPTestView类,在弹出的菜单中选择添加->添加变量选择,将会弹出添加成员变量向导对话框,然后为CBMPTestView添加两个公共public的成员变量:
4.重写OnInitialUpdate()函数载入位图
应用程序调用OnDraw()函数之前会先调用OnInitialUpdate()函数。因此可重写OnInitialUpdate()函数,在其中添加代码来载入位图并获取相关位图信息。
在类视图中右击CBMPTestView类,在弹出的对话框中选择属性,然后选择重写(overrides)栏,
然后单击OnInitialUpdate()函数项的下拉按钮,选择“<添加>OnInitialUpdate()"选项,就可以重写OnInitialUpdate()函数,在其中添加如下代码,调用函数LoadBMP()载入位图
5.函数OnDraw()中添加代码实现位图显示
OnDraw()函数是CView类的成员函数,当视图变得无效(大小改变、移动或遮盖等)时,才被调用,以重绘视图。因此一般将绘制代码放在OnDraw()中。此处将调用PaintBMP()函数显示位图。
6.任找一张BMP图像,将其命名为test.bmp,然后将其放在工程目录下,运行程序,结果如下所示:
7.存储位图:
载入位图后,就可以获得位图数据缓冲区的句柄,之后就可以对位图进行相关处理,以及将处理后的位图放到指定文件。示例中,在函数OnInitialUpdate()函数末尾添加如下语句,调用SaveBMP()函数来存储位图到项目目录下的test1.bmp;
同路径下的文件名不能相同,否则会覆盖原文件。
再次运行程序后,会在项目目录下生成一个test1.bmp的位图文件。
参考文献:张俊华.《医学图像三维重建和可视化——VC++实现实例》.科学出版社
第一步:打开MFC应用程序向导,创建一个单文档的MFC应用程序,向导中的其它参数均可保持默认。
第二部:实现读写和显示功能:
1.打开类视图,为CBMPTestView类添加以下5个函数,方法是右击CBMPTestView,在弹出的菜单中选择添加->添加函数,将会弹出添加成员函数向导,如下图:
如果自动向导添加的函数与下面不符,应该手动添加或者手动修改正确后,再继续。
五个函数原型声明为:
HANDLE LoadBMP(LPCTSTR lpFileName); BOOL SaveBMP(HANDLE hBMP,LPCTSTR lpFileName); WORD BMPColorNum(LPBYTE lpbmInfo); HPALETTE CreateBMPPalette(LPBYTE lpBMP); BOOL PaintBMP(HDC hDC,LPRECT lpDCRect,HANDLE hBMP,LPRECT lpBMPRect,HPALETTE hPalette);
2.定义上面5个函数:
/******************************************************************************************** *LoadBMP(LPCTSTR lpFileName) *功能:从文件装载位图数据到内存 *参数:LPCTSTR lpFileName,指定位图文件的文件名(路径名) *返回值:HANDLE,位图数据缓冲区句柄,包括位图信息块和像素阵列 *********************************************************************************************/ HANDLE CBMPTestView::LoadBMP(LPCTSTR lpFileName) { HANDLE hBMP; //载入数据所在缓冲区的句柄 HANDLE hFile; BITMAPFILEHEADER bmfHeader; //文件头 UINT nNumColors; //位图颜色表的颜色数 HANDLE hBMPtmp; LPBITMAPINFOHEADER lpbmInfo; //指向信息头的指针 DWORD dwOffBits; //像素阵列偏移量 DWORD dwRead; if((hFile=CreateFile((LPCWSTR)lpFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL))==INVALID_HANDLE_VALUE) { return NULL; //若打开文件失败,则返回NULL } //为位图信息头和颜色表分配初始内存,后续可根据实际需要扩大内存 hBMP=GlobalAlloc(GMEM_MOVEABLE,(DWORD)(sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD))); if(!hBMP) { CloseHandle(hFile); return NULL; } lpbmInfo=(LPBITMAPINFOHEADER)GlobalLock(hBMP); //指向信息头的指针 if(!lpbmInfo) { goto ErrorExit_NoUnlock; } //从文件读入位图文件头BITMAPFILEHEADER if(!ReadFile(hFile,(LPBYTE)&bmfHeader,sizeof(BITMAPFILEHEADER),&dwRead,NULL)) { goto ErrorExit; } if(sizeof(BITMAPFILEHEADER) !=dwRead) { goto ErrorExit; } //读入位图信息头BITMAPINFOHEADER if(!ReadFile(hFile,(LPBYTE)lpbmInfo,sizeof(BITMAPINFOHEADER),&dwRead,NULL)) { goto ErrorExit; } if(sizeof(BITMAPINFOHEADER)!=dwRead) { goto ErrorExit; } //确定颜色大小 if(!(nNumColors=(UINT)lpbmInfo->biClrUsed)) { if(lpbmInfo->biBitCount !=24) //如果非真彩色,则根据biBitCount计算颜色项数 { nNumColors=1<<lpbmInfo->biBitCount; } } if(lpbmInfo->biClrUsed == 0) { lpbmInfo->biClrUsed=nNumColors; } //计算像素阵列占用空间,字节对齐 if(lpbmInfo->biSizeImage == 0) { lpbmInfo->biSizeImage=((((lpbmInfo->biWidth*(DWORD)lpbmInfo->biBitCount)+31)&~31)>>3)*lpbmInfo->biHeight; } //重新根据图像实际大小分配内存,用于存放信息头、颜色表和像素阵列 GlobalUnlock(hBMP); hBMPtmp=GlobalReAlloc(hBMP,lpbmInfo->biSize+nNumColors*sizeof(RGBQUAD)+lpbmInfo->biSizeImage,0); if(!hBMPtmp) { goto ErrorExit_NoUnlock; } else { hBMP=hBMPtmp; } lpbmInfo=(LPBITMAPINFOHEADER)GlobalLock(hBMP); //读入颜色表 ReadFile(hFile,(LPBYTE)(lpbmInfo)+lpbmInfo->biSize,nNumColors*sizeof(RGBQUAD),&dwRead,NULL); //计算像素阵列偏移量 dwOffBits=lpbmInfo->biSize+nNumColors*sizeof(RGBQUAD); if(bmfHeader.bfOffBits!=0L) { SetFilePointer(hFile,bmfHeader.bfOffBits,NULL,FILE_BEGIN); } //读入图像像素阵列数据 if(ReadFile(hFile,(LPBYTE)lpbmInfo+dwOffBits,lpbmInfo->biSizeImage,&dwRead,NULL)) { goto SuccessExit; } ErrorExit: GlobalUnlock(hBMP); ErrorExit_NoUnlock: GlobalFree(hBMP); CloseHandle(hFile); return NULL; SuccessExit: CloseHandle(hFile); GlobalUnlock(hBMP); return hBMP; }
/******************************************************************************************** *SaveBMP(HANDLE hBMP,LPCTSTR lpFileName) *功能:存储位图数据到指定文件 *参数:HANDLE hBMP,要存储的位图数据缓存区的句柄,包括位图信息块和像素阵列 LPCTSTR lpFileName,指定位图文件的文件名(路径名) *返回值:TRUE,存储成功 FALSE,存储失败 *********************************************************************************************/ BOOL CBMPTestView::SaveBMP(HANDLE hBMP,LPCTSTR lpFileName) { BITMAPFILEHEADER bmfHeader; //位图文件头 LPBITMAPINFOHEADER lpbmInfo; //指向文件头的指针 HANDLE hFile; //文件句柄 DWORD dwBMPSize,dwBitsSize; //位图的信息头、颜色表、像素阵列大小,像素阵列大小 DWORD dwWritten; if(!hBMP) { return FALSE; } //根据指定的文件(路径)名创建文件 hFile=CreateFile(lpFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL); if(hFile==INVALID_HANDLE_VALUE) { return FALSE; } //获得指向位图数据缓冲区(包括位图信息块和像素阵列)的指针,即该指针指向信息头 lpbmInfo=(LPBITMAPINFOHEADER)GlobalLock(hBMP); if(!lpbmInfo) { CloseHandle(hFile); return FALSE; } if(lpbmInfo->biSize != sizeof(BITMAPINFOHEADER)) { GlobalUnlock(hBMP); CloseHandle(hFile); return FALSE; } //计算位图信息块大小(包括信息头和颜色表),调用BMPColorNum()计算颜色表项数 dwBMPSize=*(LPDWORD)lpbmInfo+BMPColorNum((LPBYTE)lpbmInfo)*sizeof(RGBQUAD); //计算像素阵列大小 dwBitsSize=WIDTHBYTES(lpbmInfo->biWidth*(DWORD)lpbmInfo->biBitCount)*lpbmInfo->biHeight; lpbmInfo->biSizeImage=dwBitsSize; //填写信息头的biSizeImage,即像素阵列占用字节数 //位图大小(信息块和像素阵列) dwBMPSize+=dwBitsSize; //填写文件头结构 bmfHeader.bfType=0x4d42; bmfHeader.bfSize=dwBMPSize+sizeof(BITMAPFILEHEADER); //位图文件大小 bmfHeader.bfReserved1=0; bmfHeader.bfReserved2=0; //像素阵列相对文件头的偏移量 bmfHeader.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+lpbmInfo->biSize+BMPColorNum((LPBYTE)lpbmInfo)*sizeof(RGBQUAD); //写文件头 WriteFile(hFile,(LPBYTE)&bmfHeader,sizeof(BITMAPFILEHEADER),&dwWritten,NULL); //写信息头、颜色表和像素阵列数据 WriteFile(hFile,(LPBYTE)lpbmInfo,dwBMPSize,&dwWritten,NULL); GlobalUnlock(hBMP); CloseHandle(hFile); if(dwWritten==0) return FALSE; else return TRUE; return 0; }
/******************************************************************************************** *BMPColorNum(LPBYTE lpbmInfo) *功能:计算颜色表的颜色项数 *参数:LPBYTE lpbmInfo,位图缓冲区地址,即指向位图信息头的指针 *返回值:DOWRD,颜色表中的颜色项数 *********************************************************************************************/ WORD CBMPTestView::BMPColorNum(LPBYTE lpbmInfo) { WORD wBitCount; //根据信息头的biBitCount计算颜色表项数 wBitCount=((LPBITMAPINFOHEADER)lpbmInfo)->biBitCount; switch(wBitCount) { case 1: return 2; case 4: return 16; case 8: return 256; default: return 0; } }
/******************************************************************************************** *CreateBMPPalette(LPBYTE lpBMP) *功能:根据位图颜色表创建调色板 *参数:LPBYTE lpBMP,位图缓冲区地址,即指向位图信息头的指针 *返回值:HPALETTE,创建的调色板句柄,若创建失败,则返回NULL *********************************************************************************************/ HPALETTE CBMPTestView::CreateBMPPalette(LPBYTE lpBMP) { LPLOGPALETTE lpPalette; //指向逻辑调色板的指针 HANDLE hLogPal; //逻辑调色板句柄 HPALETTE hPalette=NULL; //初始化调色板 LPBITMAPINFO lpbmInfo; //指向LPBITMAPINFO结构的指针 int wNumColors; //颜色表的颜色数 int i; if(!lpBMP) //若不是有效指针,则返回NULL,表示创建失败 return NULL; lpbmInfo=(LPBITMAPINFO)lpBMP; //指向位图信息块的指针 wNumColors=BMPColorNum((LPBYTE)lpBMP); //获取位图颜色表中的颜色数 if(wNumColors) { //为逻辑调色板分配内存空间 hLogPal=GlobalAlloc(GHND,sizeof(LOGPALETTE)+sizeof(PALETTEENTRY)*wNumColors); //若分配失败,则返回NULL if(!hLogPal) return NULL; lpPalette=(LPLOGPALETTE)GlobalLock(hLogPal); //使lpPalette指向逻辑调色板 //设置逻辑调色板 lpPalette->palVersion=0x300; //逻辑调色板版本号 lpPalette->palNumEntries=wNumColors; //设置调色板颜色数 //根据位图颜色表设置调色板颜色项 for(i=0;i<wNumColors;i++) { lpPalette->palPalEntry[i].peRed=lpbmInfo->bmiColors[i].rgbRed; lpPalette->palPalEntry[i].peGreen=lpbmInfo->bmiColors[i].rgbGreen; lpPalette->palPalEntry[i].peBlue=lpbmInfo->bmiColors[i].rgbBlue; lpPalette->palPalEntry[i].peFlags=0; } hPalette=CreatePalette(lpPalette); //创建调色板 GlobalUnlock(hLogPal); GlobalFree(hLogPal); if(!hPalette) //如果创建失败,则返回NULL return NULL; } return hPalette; //若创建成功则返回调色板句柄 }
/******************************************************************************************** *PaintBMP(HDC hDC,LPRECT lpDCRect,HANDLE hBMP,LPRECT lpBMPRect,HPALETTE hPalette) *功能:显示位图 *参数:HDC hDC,显示位图的设备描述句柄 LPRECT lpDCRect,设备中显示位图的矩形区域 HANDLE hBMP,要显示的位图数据缓冲区句柄 LPRECT lpBMPRect,要显示的位图矩形区域 HPALETTE hPalette,用来显示位图的调色板句柄,若为NULL则根据位图的颜色表来显示 *返回值:TRUE,显示成功 FALSE,显示失败 *********************************************************************************************/ bool CBMPTestView::PaintBMP(HDC hDC,LPRECT lpDCRect,HANDLE hBMP,LPRECT lpBMPRect,HPALETTE hPalette) { LPBYTE lpBMPHdr; //指向位图数据的指针,即指向位图信息头 LPBYTE lpBMPBits; //指向位图要素阵列的指针 BOOL bSuccessful=FALSE; //函数StretchDIBits()是否成功执行 HPALETTE hOldPal=NULL; //原调色板句柄 //若位图句柄hBMP=0则不能显示,返回FALSE if(!hBMP) return FALSE; //使lpBMPHdr指向位图信息头 lpBMPHdr=(LPBYTE)GlobalLock(hBMP); //使lpBMPBits指向位图像素阵列 lpBMPBits=lpBMPHdr+sizeof(BITMAPINFOHEADER)+BMPColorNum(lpBMPHdr)*sizeof(RGBQUAD); //若没有指定调色板,则调用函数CreateBMPalette()根据要显示位图的颜色表创建调色板 if(!hPalette) { hPalette=CreateBMPPalette(lpBMPHdr); } //选择并实现指定的或新建的调色板,同时记录原调色板hOldPal if(hPalette) { hOldPal=SelectPalette(hDC,hPalette,TRUE); RealizePalette(hDC); } //设置位图拉伸模式 SetStretchBltMode(hDC,COLORONCOLOR); //使用函数StretchDIBits()将位图图像矩形区域中像素使用的颜色数据复制到指定目标矩形中 bSuccessful=StretchDIBits(hDC,lpDCRect->left,lpDCRect->top,lpDCRect->right-lpDCRect->left,lpDCRect->bottom-lpDCRect->top, lpBMPRect->left,((LPBITMAPINFOHEADER)lpBMPHdr)->biHeight-lpBMPRect->top-(lpBMPRect->bottom-lpBMPRect->top), lpBMPRect->right-lpBMPRect->left,lpBMPRect->bottom-lpBMPRect->top, lpBMPBits,(LPBITMAPINFO)lpBMPHdr,DIB_RGB_COLORS,SRCCOPY); //重新选择原调色板 if(hOldPal) { SelectPalette(hDC,hOldPal,FALSE); } GlobalUnlock(hBMP); return bSuccessful; }
在函数SaveBMP()中为使像素阵列每行的字节数为4的整数倍,使用宏WIDTHBYTES.在BMPTestView.cpp文件开始处添加如下宏定义:
#define WIDTHBYTES(bits) (((bits)+31)/32*4) //使像素阵列每一行字节数是4的倍数
3.添加成员变量:
在类资源视图中右击CBMPTestView类,在弹出的菜单中选择添加->添加变量选择,将会弹出添加成员变量向导对话框,然后为CBMPTestView添加两个公共public的成员变量:
<span style="white-space:pre"></span><pre name="code" class="cpp">HANDLE m_hBMP;<span style="font-family: Arial, Helvetica, sans-serif;"> </span><span style="font-family: Arial, Helvetica, sans-serif;">// 图像数据内存句柄</span>
CRect m_rcBMP;<span style="font-family: Arial, Helvetica, sans-serif;"> </span><span style="font-family: Arial, Helvetica, sans-serif;">// 图像矩形区域</span>
4.重写OnInitialUpdate()函数载入位图
应用程序调用OnDraw()函数之前会先调用OnInitialUpdate()函数。因此可重写OnInitialUpdate()函数,在其中添加代码来载入位图并获取相关位图信息。
在类视图中右击CBMPTestView类,在弹出的对话框中选择属性,然后选择重写(overrides)栏,
然后单击OnInitialUpdate()函数项的下拉按钮,选择“<添加>OnInitialUpdate()"选项,就可以重写OnInitialUpdate()函数,在其中添加如下代码,调用函数LoadBMP()载入位图
/*********************************************************************************************** *OnInitialUpdate() *功能:初始化应用程序,载入位图并获取位图信息 *参数:无 *返回值:void ***********************************************************************************************/ void CBMPTestView::OnInitialUpdate() { CView::OnInitialUpdate(); // TODO: Add your specialized code here and/or call the base class m_hBMP=LoadBMP((CString)"test.bmp"); //载入位图 //获取位图信息,以初始化显示区域 LPBITMAPINFOHEADER lpBMP=(LPBITMAPINFOHEADER)GlobalLock(m_hBMP); m_rcBMP.left=0; m_rcBMP.top=0; m_rcBMP.right=lpBMP->biWidth; m_rcBMP.bottom=lpBMP->biHeight; GlobalUnlock(m_hBMP); }
5.函数OnDraw()中添加代码实现位图显示
OnDraw()函数是CView类的成员函数,当视图变得无效(大小改变、移动或遮盖等)时,才被调用,以重绘视图。因此一般将绘制代码放在OnDraw()中。此处将调用PaintBMP()函数显示位图。
void CBMPTestView::OnDraw(CDC* pDC) { CBMPTestDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here PaintBMP(pDC->GetSafeHdc(),m_rcBMP,m_hBMP,m_rcBMP,NULL); }
6.任找一张BMP图像,将其命名为test.bmp,然后将其放在工程目录下,运行程序,结果如下所示:
7.存储位图:
载入位图后,就可以获得位图数据缓冲区的句柄,之后就可以对位图进行相关处理,以及将处理后的位图放到指定文件。示例中,在函数OnInitialUpdate()函数末尾添加如下语句,调用SaveBMP()函数来存储位图到项目目录下的test1.bmp;
SaveBMP(m_hBMP,(CString)"test1.bmp"); //保存位图
同路径下的文件名不能相同,否则会覆盖原文件。
再次运行程序后,会在项目目录下生成一个test1.bmp的位图文件。
参考文献:张俊华.《医学图像三维重建和可视化——VC++实现实例》.科学出版社
相关文章推荐
- Effective C++ 条款14 在资源管理类中小心copying行为
- 零基础学C语言 笔记五 算术表达式
- c++ stl 2
- hdu3790 dijkstra算法
- HDU5417水题
- c++运算符重载总结
- visual c++ 2010安装失败导致CRM2015安装失败
- C++类的使用案例
- 关于C++内存生长
- C++ 中BSS、数据段、代码段、堆、栈的区别
- C语言-10
- C++ Vector 最大 最小值 索引 位置
- c++_学习笔记0822
- Effective C++ 条款13 以对象管理资源
- c++操作符重载
- LeetCode[85]::Maximal Rectangle C++
- c++与Tcl通过管道通信,并传递参数,获取测试仪实时测试进度
- c++/tcl编程总结
- C++设计模式——组合模式
- C++ 常量折叠和C语言中const常量对比