4

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
    Use ILC_COLOR32 if you want an alpha channel – David Heffernan Nov 07 '19 at 20:58
  • 1
    Is this similar to your [another question](https://stackoverflow.com/questions/58749796/win32-unable-to-add-custom-toolbar-icon-having-transparency)? – Strive Sun Nov 08 '19 at 07:10
  • [See this answer on using `LoadIcon`](https://stackoverflow.com/a/26897254/) to load a PNG as an icon – bobobobo Nov 08 '19 at 07:28
  • @bobobobo That answer is wrong, or at least very misleading. `LoadIcon` is a wrapper around `LoadImage`, and neither one can load PNGs. If you follow the instructions in the answer, you'll see what they're *actually* doing is copying the PNG image into an icon using the Visual Studio icon editor. So, yeah, `LoadImage` with `IMAGE_ICON` will definitely load an icon. But there's no PNGs involved. – Cody Gray - on strike Nov 08 '19 at 19:33

2 Answers2

3

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.

1

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"
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • 1
    [ ** You can add your PNG resource as ICON ** ] Is that to use `LoadIcon` function to load the PNG file? Or convert the PNG file into ICO file? –  Nov 08 '19 at 01:04
  • Above `loadimage` function is for loading PNG resource. See also "Resource definition" for the PNG resource. – Barmak Shemirani Nov 08 '19 at 01:48
  • Sorry but, is gdi+ supported ordinarily by win7 system? I want the application to run on even a new win7 system without any framework installed later. –  Nov 08 '19 at 03:02
  • 1
    Don't look now, but this an XY problem, and you've given a solution to the "Y" part. Loading a PNG in Win32 is difficult and completely unnecessary. All he really needs is a 32-bpp bitmap, which doesn't require GDI+ or any manual resource traversal. – Cody Gray - on strike Nov 08 '19 at 04:55
  • @CodyGray 32-bit bitmap is not the solution here. It provides only one transparency color, and it won't give smooth edge against any background. I believe the question specifically mentioned this issue. You have to use either icon or png with multiple transparency color. Optionally, use Gdi+ or Direct2D to draw smooth edges of that button. – Barmak Shemirani Nov 08 '19 at 10:34
  • 1
    @BarmakShemirani my understanding of a 32-bit bitmap is that it's 8 bits of red, 8 bits of green, 8 bits of blue, and 8 bits of transparency. That transparency is applied to all 24 bits of the RGB. – Mark Ransom Nov 08 '19 at 18:52
  • I mean a 32-bpp bitmap with pre-multiplied alpha. It's definitely the solution. You get a full 8-bit alpha channel. Supported in ICO resources, too, since XP, if I remember correctly. There are only two challenges: (1) finding an image editor that can export a bitmap in such a format (they exist, but some of the lower-quality editors won't get it right), and (2) ensuring that you load it as a DIB, not allow it to get converted to a DDB. – Cody Gray - on strike Nov 08 '19 at 19:31
  • @Cody and Mark, you are correct on 32-bpp with pre-multiplied alpha, but PNG is still better because the images are smaller. There are other issues as you mentioned, such as support in image editors ... Another option is ICON format which can use PNG to store the image. Anyway, this could still be XY problem, for example maybe the goal was to draw the button's round borders ... – Barmak Shemirani Nov 08 '19 at 20:26
  • @barmak well, to load PNG, workarounds of GDI+ is required. It seems the windows system itself only recognizes BMP bitmaps, In your code, you also translated the PNG into BMP. Is that right? –  Nov 09 '19 at 13:57
  • 1
    GDI+ can load bmp, png, jpg, gif, and ico files. Windows GDI functions are limited to bmp and ico. – Barmak Shemirani Nov 09 '19 at 14:52