Search code examples
c++winapibmphwndimagelist

Win32 Unable to add custom toolbar icon having transparency


I am using this code to add a toolbar to the window with one button having a custom image:

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

In the code above, ID_IMG_SPAWN is the resource bmp image I imported to Visual Studio. However, Visual Studio threw an error saying it didn't recognize my bmp, and the bmp showed up blank when running the application.


Visual Studio told me my bmp wasn't recognized, see the below error:

VS error message


When the app runs it looks as below:


I learned that Visual Studio hereby recognizes ONLY 24bit bmp.

So, I converted the bmp to 24bit and imported it again, changed ILC_COLOR32 to ILC_COLOR24, that really worked. No error and my image were shown on the button.

However, I lost my alpha channel. Because 24bit bitmap does not support an alpha channel, my image ended up having an ugly square background.


Solution

  • Try something like this (Note I used a "BINARY" resource.. maybe it will work with others too but not sure.. This code works with 32-bit bitmaps, alpha-transparent PNGs, and 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;
    }
    

    You will have to link against GDIPlus. It's not very nice to constantly startup and shutdown GDIPlus for every image you load.. so it's best to move that somewhere.. but other than that, everything should work just fine.

    There are other ways without GDIPlus but it makes for a long post..