我们经常遇到这种情况,就是需要把当前桌面的指定区域大小保存为图片以供以后使用,类似于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; }