我想使用GDI+方法Image::Save()
保存 DDB到以下场景中的文件:
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, 200, 200) ;
...
//hBitmap is a DDB so I need to pass an HPALETTE
Gdiplus::Bitmap(hBitmap, ???HPALETTE??? ).Save(L"file.png", ...) ;
问题是 Bitmap
当位图不是与设备无关的位图时,构造函数会请求 HPALETTE
。
我从哪里可以获得必要的 HPALETTE?
后续:
答案之一建议将 NULL 作为 HPALETTE
参数传递。
这是一个这样做的工作示例。结果是纯黑白图像,所有颜色都丢失了。
#include <windows.h>
#include <gdiplus.h>
int main(){
using namespace Gdiplus ;
GdiplusStartupInput gdiplusStartupInput ;
ULONG_PTR gdiplusToken ;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) ;
CLSID pngEncoder = {0x557cf406, 0x1a04, 0x11d3, {0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} } ;
HDC dcHndl = CreateCompatibleDC(NULL) ;
HBITMAP hBitmap = CreateCompatibleBitmap(dcHndl, 200, 200) ;
SelectObject(dcHndl, hBitmap) ;
BitBlt(dcHndl, 0,0, 200,200, GetDC(NULL), 0,0, SRCCOPY|CAPTUREBLT) ;
Bitmap(hBitmap, NULL).Save(L"file.png", &pngEncoder) ;
}
最佳答案
首先(这与您的主要问题无关):
创建屏幕截图位图时,不要使用内存 dc,因为这会创建单色位图。这是您获得黑白图像的主要原因(在我的计算机上,我只获得黑色图像)。
不要在另一个函数中使用GetDC(0)
。每次调用 GetDC
匹配都会有一个匹配的 ReleaseDC
以避免资源泄漏。
调用 BitBlt
后,最好从 dc
中选择 hbitmap
,因为您基本上已完成在 dc 上的绘制。
以下代码适用于 Windows 10
int w = 800;
int h = 600;
HDC hdc = GetDC(HWND_DESKTOP);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, w, h);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
BitBlt(memdc, 0, 0, w, h, hdc, 0, 0, SRCCOPY | CAPTUREBLT);
SelectObject(memdc, oldbmp);
Bitmap(hbitmap, NULL).Save(filename, &pngEncoder);
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(HWND_DESKTOP, hdc);
回到您有关文档的问题:
Type: HPALETTE
Handle to a GDI palette used to define the bitmap colors if hbm is not a device-independent bitmap (DIB).
此外,
Do not pass to the Bitmap::FromHBITMAP method a GDI bitmap or a GDI palette that is currently (or was previously) selected into a device context.
我发布的代码仅遵循一条规则,即当前未将 GDI 位图选择到设备上下文中(但之前已选择过)。
该文档可能适用于旧版本的 Windows。据我所知,MFC 的 CImage
类并不遵循所有这些规则。新的计算机显示器都是 24 或 32 位的,我不知道你如何获得它的调色板。
要严格遵循文档,您可以使用 CreateDIBSection
和 GetDIBits
将 DDB 转换为 DIB 部分。使用 Bitmap::FromHBITMAP
中的新 DIB 部分 hbitmap_dib
。这将满足所有条件:hbitmap
是 dib,它没有(也没有)被选择到设备上下文中。
或者,Gdiplus::Bitmap
有另一个方法 Bitmap::FromBITMAPINFO
。如果没有调色板,您可以使用以下代码代替:
HDC hdc = GetDC(HWND_DESKTOP);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, w, h);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
BitBlt(memdc, 0, 0, 800, 600, hdc, 0, 0, SRCCOPY | CAPTUREBLT);
SelectObject(memdc, oldbmp);
BITMAP bm;
GetObject(hbitmap, sizeof(bm), &bm);
int size = ((bm.bmWidth * bm.bmBitsPixel + 31) / 32) * 4 * bm.bmHeight;
BITMAPINFO info{ sizeof(info), bm.bmWidth, bm.bmHeight, 1, bm.bmBitsPixel, BI_RGB, size };
std::vector<char> bits(size);
GetDIBits(memdc, hbitmap, 0, bm.bmHeight, &bits[0], &info, DIB_RGB_COLORS);
Bitmap *bitmap = Bitmap::FromBITMAPINFO(&info, &bits[0]);
bitmap->Save(filename, &pngEncoder);
delete bitmap;
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(HWND_DESKTOP, hdc);
关于c++ - 如何从设备相关的 HBITMAP 构造 GDI+ 位图对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45319996/