在 C++ 中使用 Gdiplus 创建透明位图
Posted
技术标签:
【中文标题】在 C++ 中使用 Gdiplus 创建透明位图【英文标题】:Create transparent bitmap with Gdiplus in C++ 【发布时间】:2019-01-04 10:23:12 【问题描述】:在 C++ 中,我想用 Gdiplus 创建一个简单的透明图像并将其保存为 png。我有以下代码:
// These variables are class members and got initialized and are used elsewhere
BITMAPINFO bmi;
HDC hdc;
void* pvBits;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = some_width;
bmi.bmiHeader.biHeight = some_height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = ((((bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount) + 31) & ~31) >> 3) * bmi.bmiHeader.biHeight;
HBITMAP hBM = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0);
FillMemory(pvBits, bmi.bmiHeader.biSizeImage, 255);
HGDIOBJ oldObj = SelectObject(hDC, hBM);
ReleaseDC(NULL, hDC);
GdiFlush();
GdiPlusBitmap* bitmap = new Gdiplus::Bitmap(&bmi, pvBits);
当我将图像保存为 png 时,我可以看到有一个 alpha 通道,但它设置为 0,所以没有任何透明(RGB 全部设置)。我也尝试将 255 更改为 0,但这只会给我一个黑色的图像,没有蒸腾作用。为什么Fillmemory
调用没有填充 alpha 通道,还是我错过了其他东西?
【问题讨论】:
您在哪里/如何编写 PNG?你能分享它创建的PNG吗?您是否尝试过查看您尝试使用调试器填充的内存? 我们看不到您是如何保存位图的。一个非常常见的错误是使用错误的编码但正确的文件扩展名保存文件。这不会混淆其他程序,它们会查看文件头而不是扩展名。 为什么首先创建一个 DIB 部分,而不是从头开始创建 constructing 和Gdiplus::Bitmap
,指定所需的 PixelFormat?
@zett42 是旧代码,hdc、bmi等都是代码中用到的成员变量,我试着缩短了。
保存 png 工作正常,当我从头开始创建位图并指定正确的像素格式时,我可以在保存的 png 中看到透明度。
【参考方案1】:
FillMemory(pvBits, bmi.bmiHeader.biSizeImage, 255)
将用纯白色填充内存。如果您使用 GDI 函数覆盖图像,则 alpha 值保持不变。即使文件支持,您也不会看到任何透明度。
要创建 32 位图像,您只需要 Gdiplus::Bitmap(w, h, PixelFormat32bppARGB)
。不需要BITMAPINFO
和CreateDIBSection
如果您将 GDI+ 与 GDI 函数混合使用,您可能希望在使用 GDI 函数编写后重置 alpha。例如:
void test()
int w = 100;
int h = 100;
int bitcount = 32;
int size = ((((w * bitcount) + 31) & ~31) >> 3) * h;
BITMAPINFO bmi = 0 ;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = bitcount;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = size;
HDC hdc = GetDC(0);
BYTE* pvBits = NULL;
HBITMAP hbitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS,
(void**)&pvBits, NULL, 0x0);
FillMemory(pvBits, size, 0);
auto memdc = CreateCompatibleDC(hdc);
auto oldbmp = SelectObject(memdc, hbitmap);
SetBkColor(memdc, RGB(255, 0, 0));
TextOut(memdc, 0, 0, L"123", 3);
//GDI cleanup, don't delete hbitmap yet
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
ReleaseDC(0, hdc);
//make the non-zero colors transparent:
for(int i = 0; i < size; i += 4)
int n = *(int*)(pvBits + i);
if (n != 0)
pvBits[i + 3] = 255;
CLSID clsid_png;
CLSIDFromString(L"557cf406-1a04-11d3-9a73-0000f81ef32e", &clsid_png);
Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(w, h, PixelFormat32bppARGB);
Gdiplus::BitmapData data;
bitmap->LockBits(&Gdiplus::Rect(0, 0, w, h),
Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &data);
memcpy(data.Scan0, pvBits, size);
bitmap->UnlockBits(&data);
//safe to delete hbitmap
DeleteObject(hbitmap);
bitmap->Save(L"test.png", &clsid_png);
delete bitmap;
【讨论】:
【参考方案2】:您的代码遇到与previous question 相同的hdc 问题。填充为 0 的图像应该会产生完全透明的黑色图像。
但我认为直接创建位图指定尺寸和像素格式并稍后设置内容会更简单:
Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(width, height, PixelFormat32bppARGB);
【讨论】:
这是旧代码,我不想深入其中... GetPixelFormat 返回 PixelFormat32bppRGB,但是,当我保存图像时,它具有 alpha 通道... HDC 是一个成员变量在其他地方使用并初始化,我在缩短示例时错过了这一点 @Kackao 还有另一个接受像素数组的构造函数:GdiPlusBitmap* bitmap = new Gdiplus::Bitmap(width, height, stride, Gdiplus::PixelFormat32bppARGB, pvBits);
。将width * 4
传递给stride
。【参考方案3】:
在透明图像中,值 0 表示“完全透明”,值 255 表示 “完全不透明”。
【讨论】:
是的,但他明确地用 255 填充内存。 也尝试使用 0 而不是 255 -> 我得到一个黑色图像 (R=G=B=0) 但又没有透光。 我从 128 开始,当我不确定直到我得到一些东西时——也许我是一种 "glass half full" 和 "glass half empty" 同时人?以上是关于在 C++ 中使用 Gdiplus 创建透明位图的主要内容,如果未能解决你的问题,请参考以下文章
使用 GDIPlus (WIN32 C++) 显示存储为 alpha 资源的图标