c++winapipngbmpimagelist

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


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?


Solution

  • 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:


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

    (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.