我们经常遇到这种情况,就是需要把当前桌面的指定区域大小保存为图片以供以后使用,类似于QQ的截图工具。
其实方法很简单,就是创建与指定设备兼容的内存设备上下文环境(DC),然后创建与指定的设备环境相关的设备兼容的位图,接着把这个设备兼容的位图选入到设备兼容的内存中。最后就是将这个位图导出即可。
不过在此之前需要了解一下两个知识点:一个是如何创建和使用与指定设备兼容的内存设备上下文环境,二是如何将位图导出为本地图片
1.与指定设备兼容的内存设备上下文环境(双缓冲)
HDC CreateCompatibleDC(HDC hdc);
平常我们在使用GetDC获取HDC直接与相关设备沟通,比如在一个对话框对应的类中直接使用GetDC获取的HDC就是直接与这个对话框相关联,而我们在这获取的设备兼容得到的HDC是和内存中的一个表面想关联。
内存设备上下文环境是仅在内存中存在的设备上下文环境,当内存中设备上下文被创建时,它的显示标准是一个单色像素宽和一个单色像素高,当一个应用程序使用设备上下文环境进行绘图之前,它必须选定一个高和宽都正确设定的位图到设备上下文环境中,这里可以使用CreateCompatibleBitmap函数指定高、宽和色彩组合以指定函数调用的需要来创建位图。实例:
CBitmap bitmap;
CDC dcMemSave;
dcMemSave.CreateCompatibleDC(NULL);
CDC *pDC = GetDC();
bitmap.CreateCompatibleBitmap(pDC,m_PaintRect.right-m_PaintRect.left,m_PaintRect.bottom-m_PaintRect.top);
dcMemSave.SelectObject(&bitmap);
dcMemSave.BitBlt(0,0,m_PaintRect.right-m_PaintRect.left,m_PaintRect.bottom-m_PaintRect.top,pDC,m_PaintRect.left,m_PaintRect.top,SRCCOPY);
HBITMAP hBmp = (HBITMAP)bitmap;
SaveBMP(hBmp); //保存位图到本地
DeleteObject(bitmap);
CreateCompatibleDc函数只适用于支持光栅操作的设备,应用程序可以通过调用GetDeviceCaps函数来确定一个设备是否支持这些操作。当不再需要内存设备上下文环境时,可调用DeleteDc函数删除它。
2.将bmp保存到本地bmp图片
BMP是一种与硬件设备无关的图像文件格式,使用非常广。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。典型的BMP图像文件由三部分组成:位图文件头数据结构,它包含BMP图像文件的类型、显示内容等信息;
位图文件头(bitmap-file header) | 所用结构: BITMAPFILEHEADER | 包含了图像类型、图像大小、图像数据存放地址和两个保留未使用的字段 |
位图信息头(bitmap-information header) | 所用结构:BITMAPINFOHEADER | 包含了位图信息头的大小、图像的宽高、图像的色深、压缩说明图像数据的大小和其他一些参数 |
彩色表/调色板(color table) | RGBQUAD | 颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,24色位图没有颜色表项,位图信息头BITMAPINFOHEADER和颜色表RGBQUAD组成位图信息BITMAPINFO |
位图数据(bitmap-data) | char* | 位图数据信息HBITMAP格式 |
导出图片源码如下:
bool CDotChart::SaveBMP(HBITMAP &map)
{
OpenFileDialog(m_hWnd);
if(GetSaveFileName(&ofn))
{
wsprintf(filepath,"%s",ofn.lpstrFile);
}
else
{
return false;
}
//把位图的信息保存到bmpinfo
BITMAP bmpinfo;
GetObject(map,sizeof(BITMAP),&bmpinfo);
DWORD dwBmBitsSize = ((bmpinfo.bmWidth * 32+31)/32) * 4 * bmpinfo.bmHeight;
//位图文件头 14字节
BITMAPFILEHEADER bf;
bf.bfType = 0x4D42; //BM
bf.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwBmBitsSize;
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//位图信息头
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpinfo.bmWidth;
bi.biHeight = bmpinfo.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 8;
bi.biClrImportant = 0;
//颜色表由红、绿、蓝(RGB)三个直接值构成
//调用GetDIBits直接绘制设备无关图,并复制到缓冲区中
char* context = new char[dwBmBitsSize];
HDC dc = ::GetDC(NULL);
GetDIBits(dc, map, 0, bi.biHeight, context, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
//写位图到本地图片
HANDLE file = ::CreateFile(filepath,GENERIC_WRITE|GENERIC_READ,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS ,FILE_ATTRIBUTE_NORMAL,NULL);
DWORD Num;
if(INVALID_HANDLE_VALUE != file )
{
WriteFile(file,&bf,sizeof(BITMAPFILEHEADER),&Num,NULL);
WriteFile(file,&bi,sizeof(BITMAPINFOHEADER),&Num,NULL);
WriteFile(file,context,dwBmBitsSize,&Num,NULL);
::CloseHandle(file);
}
delete context;
return 0;
}
bool CDotChart::OpenFileDialog(HWND hWnd)
{
char szFile[260]; // 用于文件名的缓冲区
// 初始化OPENFILENAME
ZeroMemory(&ofn, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.lpstrTitle = "另存为";
ofn.lpstrFile = szFile;
ofn.hwndOwner = hWnd;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = TEXT("图片文件(*.bmp)\0*.bmp\0所有文件(*.*)\0*.*\0");
ofn.lpstrDefExt = "*.bmp";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = "NULL";
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
return 0;
}