将HDC保存为BMP文件
//========================================================================
//TITLE:
// 将HDC保存为BMP文件
//AUTHOR:
// norains
//DATE:
// Friday 25-September-2009
//Environment:
// WINDOWS CE 5.0
//========================================================================
HDC在MSDN中的全称为:The handle of device context。通常,我们都是用来做相应的显示操作。
熟悉WIN32的朋友对于其应该不会陌生,经常采用GetDC,GetWindowDC等等来获取其句柄。而用得最多的,可能就是BeginPaint,如:
case WM_PAINT: HDC hdc = BeginPaint(hWnd,&ps); ... EndPaint(hdc,&ps); break;
使用起来非常简单,但如果想将其内容保存为普通的图像文档,可就没那么容易。确切地说,在只知道HDC句柄的情况下,我们是无法保存其内容的;但我们可以剑走偏锋,将HDC的内容写到一个缓存中,然后我们再保存该缓存的内容即可。
听起来很简单,却又像很复杂,不是么?没关系,我们现在一步一步来。
首先,我们需要一个HDC的句柄。如同前面所说,你可以有多种方法,比如GetDC,GetWindowDC,甚至是CreateDC。反正呢,你用什么方法我不管,我只要有一个HDC的句柄就好了。
有了HDC的句柄,接下来我们所需要做的是,知道这HDC的大小,也就是宽度和长度。这个不难,我们只要简单地调用GetDeviceCaps,然后参数给予HORZRES或VERTRES即可:
int iWidth = GetDeviceCaps(hdc,HORZRES); int iHeight = GetDeviceCaps(hdc,VERTRES);
为什么要知道大小呢?因为我们要用它来创建缓存。而这缓存,说白了,其实就是一个BMP格式的数据结构而已。
为了创建这个关键的缓存,我们必须调用CreateDIBSection函数,而该函数形参又用到BITMAPINFOHEADER,所以我们的一切,就先从填充该结构体开始。
该结构体定义如下:
typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } BITMAPINFO;
结构体里面还有一个BITMAPINFOHEADER,其定义如下:
typedef struct tagBITMAPINFOHEADER { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER;
这么多变量,是不是有点头晕?大可不必紧张,其实我们只需要填充其中几个,其它统统置为0即可:
BITMAPINFO bmpInfo = {0}; bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = iWidth; bmpInfo.bmiHeader.biHeight = iHeight; bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 24;
一切从最简单做起,对于BMP而言,最简单的自然是24位位图,这就是为什么biPlanes和biBitCount分别设置为1和24的原因。
填充完BITMAPINFO结构,我们还是不能马上调用CreateDIBSection,因为形参中还有一个HDC。虽然我们可以直接采用已知的HDC句柄,但接下来还要将创建的HBITMAP和HDC相连接,所以我们还是先创建一个缓存DC:
HDC hdcMem = CreateCompatibleDC(hdc);
一切准备就绪之后,就调用CreateDIBSection吧:
BYTE *pData = NULL; hBmp = CreateDIBSection(hdcMem,&bmpInfo,DIB_RGB_COLORS,reinterpret_cast<VOID **>(&pData),NULL,0);
pData是分配的一个内存空间,将来用来存储HDC的内容,只不过现在一切都是空的。如果你将这数据保存出来,你会发现一团漆黑。
将HBITMAP和HDC结合:
hOldObj = SelectObject(hdcMem, hBmp);
至此为止,我们前期工作已经准备就绪,我们只需要将HDC的内容用BitBlt绘制到缓存中即可:
BitBlt(hdcMem, 0, 0, iWidth, iHeight, hdc, 0, 0, SRCCOPY);
这里其实还有一个小技巧,如果你是想绘制HDC的某个区域,你只需要用StretchBlt替代即可:
StretchBlt(hdcMem, 0, 0, iWidth, iHeight, hdc, rcDC.left, rcDC.top, rcDC.right - rcDC.left + 1, rcDC.bottom - rcDC.top + 1, SRCCOPY);
喜欢追究问题的你,也许会发现,在调用该函数之后,pData所指向的内存缓冲区已经改变。是的,没错,这些改变的数据就是我们所需要的。接下来我们所需要做的仅仅是,将这数据按BMP文件的格式,保存下来即可。
BMP文件格式其实很简单,最开始是文件头信息,然后是图片信息,接下来是数据。我们只需要按照这格式,顺序将数据写入即可。
文件头信息和图片信息,微软已经为我们定义好了相应的结构体:
BMP信息:
typedef struct tagBITMAPINFOHEADER { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER;
文件头信息:
typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER;
我们首先填充这两个结构体数值:
BITMAPINFOHEADER bmInfoHeader = {0}; bmInfoHeader.biSize = sizeof(BITMAPINFOHEADER); bmInfoHeader.biWidth = iWidth; bmInfoHeader.biHeight = iHeight; bmInfoHeader.biPlanes = 1; bmInfoHeader.biBitCount = 24; //Bimap file header in order to write bmp file BITMAPFILEHEADER bmFileHeader = {0}; bmFileHeader.bfType = 0x4d42; //bmp bmFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bmFileHeader.bfSize = bmFileHeader.bfOffBits + ((bmInfoHeader.biWidth * bmInfoHeader.biHeight) * 3); ///3=(24 / 8)
接下来的事情,估计大家都轻车熟路了。创建文件,然后写入数据,保存,完毕。
HANDLE hFile = CreateFile(strFile.c_str(),GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); DWORD dwWrite = 0; WriteFile(hFile,&bmFileHeader,sizeof(BITMAPFILEHEADER),&dwWrite,NULL); WriteFile(hFile,&bmInfoHeader, sizeof(BITMAPINFOHEADER),&dwWrite,NULL); WriteFile(hFile,&vtData[0], vtData.size(),&dwWrite,NULL); CloseHandle(hFile);
文章的最后,是参考源代码:
#ifdef UNICODE #ifndef TSTRING #define TSTRING std::wstring #endif#else #ifndef TSTRING #define TSTRING std::string #endif#endifBOOL WriteBmp(const TSTRING &strFile,const std::vector<BYTE> &vtData,const SIZE &sizeImg);BOOL WriteBmp(const TSTRING &strFile,HDC hdc);BOOL WriteBmp(const TSTRING &strFile,HDC hdc,const RECT &rcDC);BOOL WriteBmp(const TSTRING &strFile,const std::vector<BYTE> &vtData,const SIZE &sizeImg) { BITMAPINFOHEADER bmInfoHeader = {0}; bmInfoHeader.biSize = sizeof(BITMAPINFOHEADER); bmInfoHeader.biWidth = sizeImg.cx; bmInfoHeader.biHeight = sizeImg.cy; bmInfoHeader.biPlanes = 1; bmInfoHeader.biBitCount = 24; //Bimap file header in order to write bmp file BITMAPFILEHEADER bmFileHeader = {0}; bmFileHeader.bfType = 0x4d42; //bmp bmFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bmFileHeader.bfSize = bmFileHeader.bfOffBits + ((bmInfoHeader.biWidth * bmInfoHeader.biHeight) * 3); ///3=(24 / 8) HANDLE hFile = CreateFile(strFile.c_str(),GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if(hFile == INVALID_HANDLE_VALUE) { return FALSE; } DWORD dwWrite = 0; WriteFile(hFile,&bmFileHeader,sizeof(BITMAPFILEHEADER),&dwWrite,NULL); WriteFile(hFile,&bmInfoHeader, sizeof(BITMAPINFOHEADER),&dwWrite,NULL); WriteFile(hFile,&vtData[0], vtData.size(),&dwWrite,NULL); CloseHandle(hFile); return TRUE;} BOOL WriteBmp(const TSTRING &strFile,HDC hdc){ int iWidth = GetDeviceCaps(hdc,HORZRES); int iHeight = GetDeviceCaps(hdc,VERTRES); RECT rcDC = {0,0,iWidth,iHeight}; return WriteBmp(strFile,hdc,rcDC); }BOOL WriteBmp(const TSTRING &strFile,HDC hdc,const RECT &rcDC){ BOOL bRes = FALSE; BITMAPINFO bmpInfo = {0}; BYTE *pData = NULL; SIZE sizeImg = {0}; HBITMAP hBmp = NULL; std::vector<BYTE> vtData; HGDIOBJ hOldObj = NULL; HDC hdcMem = NULL; //Initilaize the bitmap information bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = rcDC.right - rcDC.left; bmpInfo.bmiHeader.biHeight = rcDC.bottom - rcDC.top; bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 24; //Create the compatible DC to get the data hdcMem = CreateCompatibleDC(hdc); if(hdcMem == NULL) { goto EXIT; } //Get the data from the memory DC hBmp = CreateDIBSection(hdcMem,&bmpInfo,DIB_RGB_COLORS,reinterpret_cast<VOID **>(&pData),NULL,0); if(hBmp == NULL) { goto EXIT; } hOldObj = SelectObject(hdcMem, hBmp); //Draw to the memory DC sizeImg.cx = bmpInfo.bmiHeader.biWidth; sizeImg.cy = bmpInfo.bmiHeader.biHeight; StretchBlt(hdcMem, 0, 0, sizeImg.cx, sizeImg.cy, hdc, rcDC.left, rcDC.top, rcDC.right - rcDC.left + 1, rcDC.bottom - rcDC.top + 1, SRCCOPY); vtData.resize(sizeImg.cx * sizeImg.cy * 3); memcpy(&vtData[0],pData,vtData.size()); bRes = WriteBmp(strFile,vtData,sizeImg); SelectObject(hdcMem, hOldObj); EXIT: if(hBmp != NULL) { DeleteObject(hBmp); } if(hdcMem != NULL) { DeleteDC(hdcMem); } return bRes;}
一共有三个WriteBmp函数,使用上非常简单。
比如,我想保存一个HDC,只需要简单地调用:
HDC hdc = GetDC(NULL); WriteBmp(TEXT("//NAND//DCSave.bmp")); ReleaseDC(NULL,hdc);
如果想保存HDC的某一个部分,同样也很简单:
HDC hdc = GetDC(NULL); RECT rcDC = {0,0,100,100}; WriteBmp(TEXT("//NAND//DCSavePart.bmp"),rcDC); ReleaseDC(NULL,hdc);
这个函数还能做到一个很有意思的功能,就是截取屏幕。对于屏幕来说,也是一个HDC,我们只要获取屏幕的HDC句柄,剩下的就没有什么难度了:
HDC hdc = CreateDC(_T("DISPLAY"), NULL, NULL, NULL); WriteBmp(TEXT("//NAND//ScreenCapture.BMP"),hdc); DeleteDC(hdc);
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow
将HDC保存为BMP文件相关推荐
- [Win] 利用Memory DC抽取EXE的图标并保存为BMP文件
预告篇- 由于时间比较紧.下次等整理好再放上来吧. 大致过程就是 创建窗口句柄->得到HDC->创建MEMDC->再copy数据->最后显示或者保存为BMP文件, 转载于:ht ...
- 【转】DCMTK开源库的学习笔记1:将DCM文件保存成BMP文件或数据流(即数组)
转自:https://blog.csdn.net/zssureqh/article/details/8784980 DCMTK开源库介绍: DCMTK是目前最全面实现DICOM3.0标准的开源库,通过 ...
- 截取屏幕并保存为BMP文件
CDC *pDC;//屏幕DCpDC = CDC::FromHandle( ::GetDC(NULL) );//获取当前整个屏幕DCint BitPerPixel = pDC->GetDevic ...
- android 二进制流保存为bmp文件
不同的图片读取像素可能不同,要根据自己需要的像素进行提取,因此函数 private byte[] addBMP_RGB_888(int[] b,int w, int h)需要更改.注意,调色表可有可无 ...
- VC屏幕截图并保存为bmp文件
保存抓图的简单实现 实现类似的抓屏功能(简单的截图功能 ) 其实只要两个函数就行了,以下从网上摘抄的一段代码的改写, 算是原创吧 (http://www.wenyiwen.cn/a24/how1614 ...
- 截取屏幕指定区域保存为BMP文件
HBITMAP CopyScreenToBitmap(LPRECT lpRect) { //屏幕和内存设备描述表 HDC hSrcDC, hMemDC; //位图句柄 HBITMAP hBitmap, ...
- VC屏幕截图,保存为Bmp文件
新建一个MFC基于对话框的应用程序,在界面上放一个Button,为其实现点击事件,代码如下: void CScreenShotDlg::OnBtnScreenshot() {RECT rect = { ...
- 使用ffmpeg将BMP图片编码为x264视频文件,将H264视频保存为BMP图片,yuv视频文件保存为图片的代码
ffmpeg开源库,实现将bmp格式的图片编码成x264文件,并将编码好的H264文件解码保存为BMP文件. 实现将视频文件yuv格式保存的图片格式的测试,图像格式png,jpg, gif等等测试均O ...
- C语言生成BMP文件
C 语言生成BMP 文件 针对这个话题其实可以分解为两个议题,一个是 BMP 文件的格式,一个是 C语言如何操作文件. BMP 文件格式 BMP 是微软在 windows 系统中使用的一种位图图像格式 ...
- BMP文件解析及显示
我们在LCD上显示内容的时候,经常会有显示bmp文件的需求,例如显示一个logo.菜单图标等.我们可以在应用程序中打开bmp文件,然后将它解析成rgb格式的数据写入frame buffer中就可以完成 ...
最新文章
- Jsoncpp 在C++开发中的一些使用记录
- mongorepository查询条件_MongoDB动态条件之分页查询
- poj 3006 java
- 倒计时3天:3D音带你起飞
- CentOS安装KVM步骤虚拟机,绝对实用!
- 人才空缺4600万!大厂优先录用,这个职业今年火遍全网
- GLUEscript(wxJavascript)
- Qt基于TCP网络程序发包封包抽象
- nio的优势_BIO、NIO、AIO 介绍和适用场景分析
- Web开发敏捷之道-应用Rails进行敏捷Web开发(第三版)pdf
- Java 11 中 11 个不为人知的瑰宝
- 联想服务器RD450 配置RAID5阵列图文方法
- mongodb入门基本语法
- Ubuntu下安装MeshLab教程
- Android MVVM开发框架
- 批量修改字幕文件中的时间,c语言实现
- 赫兹的单位换算_单位换算大全!
- 小程序在wxml里转数字_微信小程序 之wxml保留小数点后两位数的方法及转化为字符串的方法...
- 属性动画实现卫星菜单效果
- 应付职称评定的论文-《七星彩神经网络预测系统》原型开发构想
热门文章
- Revit安装错误1308?Revit软件安装时出现安装错误1308怎么办?
- netty Force-closing a channel whose registration task was not accepted by an event loop问题
- 怎么做移动APP测试,移动应用测试有哪些?
- Servlet小服务程序(Service + Applet)
- 临别给《生活大爆炸》做个台词数据分析,你猜谢耳朵最爱说什么?
- 7-33 电话聊天狂人 (25 分)(map水题)
- CQOI2016滚粗记
- 《正见——佛陀的证悟》读后感
- 512-rear chassis fan not detected的解决方法
- 个人wiki搭建资料整理