如何添加透明的 PNG 作为工具栏图标?

How can I add a transparent PNG as a toolbar icon?

我的目的是在 Win32 中创建一个包含透明图标的工具栏。我已尝试使用以下代码创建一个简单的工具栏,其中包含一个带有自定义图像的按钮:

// Create the toolbar
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME,
                             NULL,
                             WS_CHILD | TBSTYLE_FLAT | TBSTYLE_AUTOSIZE | TBSTYLE_LIST | CCS_BOTTOM,
                             0, 0, 0, 0,
                             hwnd,
                             NULL,
                             ghInstance, // <-this is the HINSTANCE of the application
                             NULL);

// Set the font (this cannot be the problem)
SendMessage(hToolbar,
            WM_SETFONT,
            (WPARAM)hFontBold,
            static_cast<LPARAM>(MAKELONG(TRUE, 0)));

auto hImagelist = ImageList_Create(32, 32,ILC_COLOR24 | ILC_MASK, 1, 0);

HBITMAP bitmap = static_cast<HBITMAP>(LoadImage(ghInstance,
    /* ID_IMG_SPAWN is my custom resource -> */ MAKEINTRESOURCE(ID_IMG_SPAWN), 
                                                IMAGE_BITMAP,
                                                32, 32,
                                                NULL));
ImageList_AddMasked(hImagelist,
                    bitmap,
                    RGB(255,255,255) /* white is the transparent color */);
SendMessage(hToolbar,
            TB_SETIMAGELIST,
            static_cast<WPARAM>(0),
            (LPARAM)hImagelist);

ImageList_Create 仅支持 24 位位图,这意味着没有用于透明度的 alpha 通道。但是,我可以通过 ImageList_AddMasked 使用遮罩颜色来模拟透明效果。 (在这里,我将白色(RGB(255, 255, 255))设置为蒙版颜色。)

这很好用,但是以这种方式显示的图像非常sharp/jagged,因为 alpha 通道缺乏粒度(每个像素要么是透明的,要么是完全透明的)不透明)。

我知道 PNG 格式可以解决这个问题,因为它提供了真正的 alpha 通道。我知道 Win32 ImageLists 支持 PNG 格式,但我不知道如何正确使用它。 (PNG资源可以添加到Visual Studio资源中,但我不知道如何从代码中使用它们。)

我找不到任何方法让 LoadImage 加载 PNG。唯一支持的类型是 IMAGE_BITMAP IMAGE_CURSORIMAGE_ICON。我将资源 (ID_IMG_SPAWN) 更改为 PNG 文件,并逐一尝试了这三种类型,但所有结果都只是像这样的空白显示:

谁能帮帮我?如何使用 LoadImage 加载透明 PNG 并将其用作工具栏图像?

LoadImage 将在尝试加载 PNG 资源时 return NULL

您可以将 PNG 资源添加为 ICON。否则使用 Windows Imaging Component 或 Gdiplus+ 加载 png 资源。

读取PNG资源如下:

HBITMAP loadimage(HINSTANCE hinst, const wchar_t* name)
{
    HBITMAP hbitmap = NULL;
    ULONG_PTR token;
    Gdiplus::GdiplusStartupInput tmp;
    Gdiplus::GdiplusStartup(&token, &tmp, NULL);
    if(auto hres = FindResource(hinst, name, RT_RCDATA))
        if(auto size = SizeofResource(hinst, hres))
            if(auto data = LockResource(LoadResource(hinst, hres)))
                if(auto stream = SHCreateMemStream((BYTE*)data, size))
                {
                    Gdiplus::Bitmap bmp(stream);
                    stream->Release();
                    bmp.GetHBITMAP(Gdiplus::Color::Transparent, &hbitmap);
                }
    Gdiplus::GdiplusShutdown(token);
    return hbitmap;
}

...
auto hbitmap = loadimage(ghinst, MAKEINTRESOURCE(ID_PNG1 ));
if(hbitmap)
{
    ImageList_AddMasked(himage, hbitmap, 0);
    DeleteObject(hbitmap);
}

资源定义应如下所示:

ID_PNG1 RCDATA "file.png"

ImageList_Create only supports 24-bit bitmaps, which means there is no alpha channel for transparency.

不,那是错误的。 ImageList_Create 也支持 32 位位图

由于您打算在 Win32 中创建一个包含透明图标的工具栏,因此根本不需要加载 PNG。如果你想要 PNG,你可能需要像@barmak 所说的那样使用 GdiPlus。

32 位位图有 8 位用于 ALPHA。使用 32 位位图可以产生与 PNG 相同的效果。


您说您执行这些操作时按钮图像显示为空白:

  • 已将 ILC_COLOR24 更改为 ILC_COLOR32

  • ID_IMG_SPAWN 的资源更改为 32 位位图


事实上要正确显示 32 位位图,您必须:

  • ILC_COLOR24更改为ILC_COLOR32

  • ID_IMG_SPAWN 的资源更改为具有预乘 alpha 的 32 位位图

  • 加载时为位图创建一个 DIB 部分

(Win32的格式要求很严格)


问:如何为位图创建 DIB 部分?

A:在LoadImage的最后一个参数中指定LR_CREATEDIBSECTION

解释:

LoadImage((HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,32, 32,NULL)

这是您的 LoadImage 函数的代码。参见MSDN document of LoadImage,要创建DIB部分,你只需要在LoadImage的最后一个参数中指定LR_CREATEDIBSECTION


问:如何得到预乘alpha的BMP?

答:Pixelformer 可以帮助您将 alpha 通道文件转换为预乘 alpha BMP。

步骤是

  1. 在 Pixelformer 中打开您的图像(任何格式)并从菜单中选择“导出”

  1. Select A8:R8:G8:B8(32bpp)和预乘Alpha,然后点击确定。

然后你就可以保存你的BMP文件了!将此 BMP 文件导入 Visual Studio 资源,替换您之前的 24 位 BMP。


那么,您就不需要再使用ImageList_AddMasked(它使图像更清晰),因为您的32 位BMP 中已经有了可识别的ALPHA。所以,直接使用 ImageList_Add.

好的,在上面解释的操作之后你的代码应该是这样的:

// Create the toolbar
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME,NULL,
     WS_CHILD | TBSTYLE_FLAT | TBSTYLE_AUTOSIZE | TBSTYLE_LIST | CCS_BOTTOM,
     0, 0, 0, 0, hwnd, NULL, ghInstance, NULL);

// Set the font (this cannot be the problem)
SendMessage(hToolbar, WM_SETFONT, (WPARAM)hFontBold,
     static_cast<LPARAM>(MAKELONG(TRUE, 0)));

auto hImagelist =
ImageList_Create(32, 32,ILC_COLOR32 /*DON'T NEED THE MASK. CHANGED TO ILC_COLOR32.*/, 1, 0);

HBITMAP bitmap = static_cast<HBITMAP>(LoadImage((HINSTANCE)GetWindowLong(hwnd,
      GWL_HINSTANCE), MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,
      32, 32, LR_CREATEDIBSECTION  /*THIS IS IMPORTANT*/   ));

ImageList_Add(hImagelist, bitmap, NULL);
SendMessage(hToolbar, TB_SETIMAGELIST, static_cast<WPARAM>(0), (LPARAM)hImagelist);

效果很好,如下所示。


我上面回答的这些已经可以解决这个问题了

有关 DIB bitmaps and Premultiplied Alpha 的详细信息,请参阅链接。