Search code examples
mfccmenu

How to show menu bitmaps with transparent background


I am using this code:

m_bmpSwap.LoadBitmap(IDB_BITMAP2);
pMnuPopup->SetMenuItemBitmaps(0, MF_BYPOSITION, &m_bmpSwap, &m_bmpSwap);

It looks like:

Menu

It was only a test image:

Bitmap

How exactly do I get my image to look as if it has a transparent background?

It is 24 bit image.

I have seen this but I can't work it out.

I adjusted to a 8 bit image with 192/192/192 as the background and loaded like this:

HBITMAP hBmp;

hBmp = (HBITMAP)::LoadImage(AfxGetResourceHandle(),
    MAKEINTRESOURCE(IDB_BITMAP2),
    IMAGE_BITMAP,
    0, 0, // cx,cy
    LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS);
m_bmpSwap.Attach(hBmp);

pMnuPopup->SetMenuItemBitmaps(0, MF_BYPOSITION, &m_bmpSwap, &m_bmpSwap);

That seems better if I am not running WindowsBlinds:

Menu

But when I put WindowsBlinds back on and show it again:

Menu

I am colourblind myself, but I can tell that the background actually matches the dialog background and not the menu colour background.

Is this the best I can do?

Just how can I have a 24 bit or 32 bit image as a menu bitmap?


Solution

  • Add LR_LOADTRANSPARENT flag as well as LR_LOADMAP3DCOLORS

    This will work with 8-bit or 4-bit images (not tested with Windows blind)


    Or you can manually change the background color

    void swap_color(HBITMAP hbmp)
    {
        if(!hbmp)
            return;
        HDC hdc = ::GetDC(HWND_DESKTOP);
        BITMAP bm;
        GetObject(hbmp, sizeof(bm), &bm);
        BITMAPINFO bi = { 0 };
        bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bi.bmiHeader.biWidth = bm.bmWidth;
        bi.bmiHeader.biHeight = bm.bmHeight;
        bi.bmiHeader.biPlanes = 1;
        bi.bmiHeader.biBitCount = 32;
    
        std::vector<uint32_t> pixels(bm.bmWidth * bm.bmHeight);
        GetDIBits(hdc, hbmp, 0, bm.bmHeight, &pixels[0], &bi, DIB_RGB_COLORS);
    
        //assume that the color at (0,0) is the background color
        uint32_t color_old = pixels[0];
    
        //this is the new background color
        uint32_t bk = GetSysColor(COLOR_MENU);
    
        //swap RGB with BGR
        uint32_t color_new = RGB(GetBValue(bk), GetGValue(bk), GetRValue(bk));
    
        for (auto &pixel : pixels)
            if(pixel == color_old)
                pixel = color_new;
    
        SetDIBits(hdc, hbmp, 0, bm.bmHeight, &pixels[0], &bi, DIB_RGB_COLORS);
        ::ReleaseDC(HWND_DESKTOP, hdc);
    }
    

    Usage:

    CBitmap bmp;
    bmp.LoadBitmap(IDB_BITMAP1);
    swap_color(bmp);
    menu.SetMenuItemBitmaps(0, MF_BYPOSITION, &bmp, &bmp);