c++ - 如何从设备相关的 HBITMAP 构造 GDI+ 位图对象

标签 c++ winapi gdi+ gdi color-palette

我想使用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 位的,我不知道你如何获得它的调色板。

要严格遵循文档,您可以使用 CreateDIBSectionGetDIBits 将 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/

相关文章:

c# - 什么是适合 .NET 的基于 f/oss GDI+(System.Drawing)的图形和图表组件?

c# - DrawImage 调整后的图像太小

c++ - IImageList->Draw - 透明度损坏

c++ - select() 没有获取标准输入

c++ - std::async 与 std::launch::async 策略的行为

c# - 异步拖放到 Windows 资源管理器

C++:删除打印机队列

c++ - 表示日期的字符串并对其进行转换

c++ - 在大型文本文件中搜索数千个字符串

c# - 从 UWP 应用程序异步拖放