Win32 Unable to add custom toolbar icon having transparency

允我心安 提交于 2019-12-11 11:29:11

问题


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:


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.


回答1:


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




回答2:


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

No, that's wrong. Visual Studio supports alpha-premultiplied 32-bit bitmaps.

32-bit bitmap has 8 bits for ALPHA, it's capable to make a smooth effect.


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

  • specify 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. You may wonder "What means DIB bitmaps?" "What is Premultiplied Alpha?". Those are the deep topics.

To learn DIB bitmaps and Premultiplied Alpha, see the links.




回答3:


If you are using Visual Studio 2010 or higher (i guess), you can use PNG files. As stated here.



来源:https://stackoverflow.com/questions/58749796/win32-unable-to-add-custom-toolbar-icon-having-transparency

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