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

送分小仙女□ 提交于 2019-12-23 13:06:30

问题


My intention is to create a toolbar in Win32 containing a transparent icon. I have tried the following code to create a simple toolbar with one button having a custom image:

// 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 only supports 24-bit bitmaps, which means there is no alpha channel for transparency. However, I can simulate a transparency effect by using a mask color via ImageList_AddMasked. (Here, I am setting white (RGB(255, 255, 255)) to the mask color.)

This worked fine, but the image displayed this way is extremely sharp/jagged because of the lack of granularity in the alpha channel (each pixel is either transparent or fully opaque).

I understand that the PNG format can solve this, since it provides a true alpha channel. I know that the PNG format is supported by Win32 ImageLists, but I don't know how to use it properly. (PNG resources can be added to Visual Studio resources, but I don't know how to use them from code.)

I couldn't find any way to make LoadImage load a PNG. The only supported types are IMAGE_BITMAP IMAGE_CURSOR and IMAGE_ICON. I changed the resource (ID_IMG_SPAWN) to a PNG file and tried each of those three types one by one, but all resulted in merely a blank display like this:

Can anyone help me out? How can I use LoadImage to load a transparent PNG and use it as a toolbar image?


回答1:


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

No, that's wrong. ImageList_Create supports 32-bit bitmaps as well.

Since you intend to create a toolbar in Win32 containing a transparent icon, you do NOT need to load a PNG at all. If you desire PNG you may have to work around with GdiPlus as @barmak says.

32-bit bitmap has 8 bits for ALPHA. Using 32-bit bitmaps can make the same effect as PNG does.


You say your button image was showing blank when you did these:

  • changed ILC_COLOR24 to ILC_COLOR32

  • changed the resource of ID_IMG_SPAWN to a 32-bit bitmap


IN FACT To show a 32-bit bitmap properly, you have to:

  • change ILC_COLOR24 to ILC_COLOR32

  • change the resource of ID_IMG_SPAWN to a 32-bit bitmap with premultiplied alpha.

  • create a DIB section to your bitmap when loading

(Win32's format requirement is very strict)


Q: How to create a DIB section to the bitmap?

A: Specify LR_CREATEDIBSECTION in the last parameter of LoadImage.

Explanation:

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

This is your code of the LoadImage function. See the MSDN document of LoadImage, to create the DIB section, all you need is to specify LR_CREATEDIBSECTION in the last parameter of LoadImage.


Q: How to get a BMP with premultiplied alpha?

A: Pixelformer can help you convert your alpha-channeled file to a premultiplied-alpha BMP.

The steps are

  1. Open your image (any format) in Pixelformer and choose Export from the menu

  1. Select A8:R8:G8: B8 (32bpp) and Premultiplied Alpha, then click Ok.

Then you can save your BMP file! Import this BMP file to Visual Studio resources, replacing your previous 24-bit BMP.


Then, you don't need to use the ImageList_AddMasked (which makes the image sharp) anymore, because you already have a recognizable ALPHA in your 32-bit BMP. So, straight use ImageList_Add.

Okay, after the manipulations explained above your code should be this:

// 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);

This worked fine as below.


These I answered above is well enough to solve this problem.

For more information about DIB bitmaps and Premultiplied Alpha, see the links.




回答2:


LoadImage will return NULL when trying to load PNG resource.

You can add your PNG resource as ICON. Otherwise use Windows Imaging Component, or Gdiplus+ to load the png resource.

Read PNG resource as follows:

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);
}

Resource definition should look like this:

ID_PNG1 RCDATA "file.png"


来源:https://stackoverflow.com/questions/58751417/how-can-i-add-a-transparent-png-as-a-toolbar-icon

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!