Win32 无法添加具有透明度的自定义工具栏图标

Posted

技术标签:

【中文标题】Win32 无法添加具有透明度的自定义工具栏图标【英文标题】:Win32 Unable to add custom toolbar icon having transparency 【发布时间】:2019-11-07 13:28:33 【问题描述】:

我正在使用此代码向窗口添加一个工具栏,其中一个按钮具有自定义图像:

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

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

//↓↓↓↓↓**ILC_COLOR32 is specificied here but 32bit bmp is still not showing**//
auto hImagelist = ImageList_Create(32, 32,ILC_COLOR32|ILC_MASK,1, 0);
//↑↑↑↑↑**ILC_COLOR32 is specificied here but 32bit bmp is still not showing**//

HBITMAP bitmap = static_cast<HBITMAP>(ghInstance,
          MAKEINTRESOURCE(ID_IMG_SPAWN),// <- ID_IMG_SPAWN is my custom resource
          IMAGE_BITMAP, 32, 32, NULL));

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

在上面的代码中,ID_IMG_SPAWN 是我导入到 Visual Studio 的资源 bmp 图像。但是,Visual Studio 抛出一个错误,说它无法识别我的 bmp,并且在运行应用程序时 bmp 显示为空白。


Visual Studio 告诉我我的 bmp 无法识别,请参阅以下错误:


应用程序运行时如下所示:


我了解到 Visual Studio 在此仅识别 24 位 bmp。

所以,我将 bmp 转换为 24 位并再次导入,将 ILC_COLOR32 更改为 ILC_COLOR24,确实有效。没有错误,按钮上显示了我的图像。

但是,我丢失了 Alpha 通道。因为 24 位位图不支持 Alpha 通道,所以我的图像最终具有难看的方形背景。

【问题讨论】:

你能上传32位的图片吗?我从来没有遇到过使用 32 位位图作为 WinAPI 控制背景的问题。你试过 GDIPlus 来加载它吗? @Brandon drive.google.com/open?id=1w2BUSUA9h31FbpvZFWmBbVaIfViefsoN 这是图片。我也没有将它用作背景:我需要保留 alpha 通道 请使用粗体格式。这为每个字母增加了很多像素,使文本更难阅读。在您的帖子中,我发现没有理由使用它,因此我将其删除。请仅将其用于非常长的帖子中的段落标题,或者0.001%的单词通过重组周围的句子无法正确强调。通常它只会让人们望而却步,导致投反对票和/或答案减少。 问题在这里已经有了答案***.com/questions/58751417/… 【参考方案1】:

尝试这样的事情(注意我使用了"BINARY" 资源..也许它也适用于其他人,但不确定..此代码适用于 32 位位图、alpha 透明 PNG 和 JPEG):

#include <windows.h>
#include <gdiplus.h>

HBITMAP LoadBitmapFromResource(DWORD ResourceID, bool transparent = true)

    HANDLE hGlobal = NULL;
    ULONG_PTR GDIToken = 0;
    Gdiplus::Image* Img = NULL;
    Gdiplus::GdiplusStartupInput GDIStartInput = NULL;


    Gdiplus::GdiplusStartup(&GDIToken, &GDIStartInput, NULL);

    HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(ResourceID), "BINARY");
    if (!hResource) return NULL;

    HGLOBAL hFileResource = LoadResource(NULL, hResource);
    if (!hFileResource) return NULL;

    LPVOID lpFile = LockResource(hFileResource);
    if (!lpFile) return NULL;

    DWORD dwSize = SizeofResource(NULL, hResource);
    if (!dwSize) return NULL;

    void* data = LockResource(hFileResource);
    if (!data) return NULL;

    IStream* pStream = NULL;
    hGlobal = GlobalAlloc(GMEM_FIXED, dwSize);

    memcpy(hGlobal, data, dwSize);
    UnlockResource(hFileResource);
    FreeResource(hFileResource);

    if (CreateStreamOnHGlobal(hGlobal, true, &pStream) == S_OK)
    
        Img = new Gdiplus::Image(pStream, false);
        pStream->Release();
        GlobalFree(hGlobal);
        hGlobal = NULL;

        HBITMAP hBitmap = NULL;
        static_cast<Gdiplus::Bitmap*>(Img)->GetHBITMAP(transparent ? Gdiplus::Color::Transparent : Gdiplus::Color(0, 0, 0), &hBitmap);

        delete Img;
        Gdiplus::GdiplusShutdown(GdiImage::GDIToken);
        GDIStartInput = NULL;
        GDIToken = 0;

        return hBitmap;
    

    GlobalFree(hGlobal);
    hGlobal = NULL;
    Gdiplus::GdiplusShutdown(GdiImage::GDIToken);
    GDIStartInput = NULL;
    GDIToken = 0;

    return NULL;

您必须链接到GDIPlus。为您加载的每个图像不断启动和关闭 GDIPlus 并不是很好。所以最好将它移动到某个地方。但除此之外,一切都应该正常工作。

没有 GDIPlus 也有其他方法,但它会使帖子很长..

【讨论】:

对不起,win7系统一般支持gdi+吗?如果是,一切都很好。我希望该应用程序即使在以后没有安装任何框架的新 win7 系统上也能运行。 @AkutaHinako 是的,它在 windows7 中。试试看吧。【参考方案2】:

我了解到 Visual Studio 在此仅识别 24 位 bmp。

不,那是错误的。 Visual Studio 支持 alpha 预乘 32 位位图

32位位图有8位ALPHA,可以做出平滑的效果。


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

指定ILC_COLOR32

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

在加载时为您的位图创建一个 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。

步骤是

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

    选择 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 位图?” “什么是预乘 Alpha?”。这些都是深层次的话题。

要了解DIB bitmaps 和Premultiplied Alpha,请查看链接。

【讨论】:

【参考方案3】:

如果您使用的是 Visual Studio 2010 或更高版本(我猜),您可以使用 PNG 文件。如here所述。

【讨论】:

您似乎不理解这个问题,并在 Google 上搜索了“visual studio png”并粘贴了第一个链接。请注意,此链接与问题无关,不鼓励仅提供链接的答案。

以上是关于Win32 无法添加具有透明度的自定义工具栏图标的主要内容,如果未能解决你的问题,请参考以下文章

创建透明的自定义位图画笔

无法从具有相同身份验证器的不同应用添加新的自定义帐户

如何使用 Python 和 GTK 创建具有自定义文本和透明背景的状态图标/系统托盘图标?

向 Bootstrap 3 添加额外的自定义字形图标

添加具有订单详细信息功能的自定义按钮 - Magento 2

在 Ionic 2 中添加自定义图标