位图读、写、显示的C++实现实例

2024-09-01 10:32
文章标签 c++ 实现 显示 实例 位图

本文主要是介绍位图读、写、显示的C++实现实例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对图像进行处理的前提是,要能实现对象的打开保存和显示,这是处理的前提。以下在VS2010中基于MFC的框架实现对位图文件的打开、保存和显示功能。

第一步:打开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;}//从文件读入位图文件头BITMAPFILEHEADERif(!ReadFile(hFile,(LPBYTE)&bmfHeader,sizeof(BITMAPFILEHEADER),&dwRead,NULL)){goto ErrorExit;}if(sizeof(BITMAPFILEHEADER) !=dwRead){goto ErrorExit;}//读入位图信息头BITMAPINFOHEADERif(!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;elsereturn 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);//若分配失败,则返回NULLif(!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) //如果创建失败,则返回NULLreturn 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则不能显示,返回FALSEif(!hBMP)return FALSE;//使lpBMPHdr指向位图信息头lpBMPHdr=(LPBYTE)GlobalLock(hBMP);//使lpBMPBits指向位图像素阵列lpBMPBits=lpBMPHdr+sizeof(BITMAPINFOHEADER)+BMPColorNum(lpBMPHdr)*sizeof(RGBQUAD);//若没有指定调色板,则调用函数CreateBMPalette()根据要显示位图的颜色表创建调色板if(!hPalette){hPalette=CreateBMPPalette(lpBMPHdr);}//选择并实现指定的或新建的调色板,同时记录原调色板hOldPalif(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 classm_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 herePaintBMP(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++实现实例》.科学出版社

这篇关于位图读、写、显示的C++实现实例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1126680

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名