2

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?

sergiol
  • 4,122
  • 4
  • 47
  • 81
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164

2 Answers2

3

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);
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • Thanks but it doesn't seem to work. For example, if I have a 24 bit image with a white background and a small red rectangle, in my menu I end up with a white square. – Andrew Truckle Apr 12 '18 at 15:24
  • According to https://msdn.microsoft.com/en-us/library/windows/desktop/ms648045(v=vs.85).aspx for both of those flags it states: Do not use this option if you are loading a bitmap with a color depth greater than 8bpp. – Andrew Truckle Apr 12 '18 at 15:26
  • If I reduce teh same image down to 8 bits (with white background) it will render better, but, with the dialog color as the background and not the menu color. – Andrew Truckle Apr 12 '18 at 15:28
  • Really, the background should end up as `GetSysColor(COLOR_MENU)`. – Andrew Truckle Apr 12 '18 at 15:30
  • I added code to manually change the color to `GetSysColor(COLOR_MENU)`, it should work with any bitmap – Barmak Shemirani Apr 12 '18 at 17:10
  • I have it working. I found out that my PC has some corruption so when I am not using WindowsBlinds the `COLOR_MENU` value is wrong. On another PC it works well. And I just had to use `::GetDC` and `::ReleaseDC`. – Andrew Truckle Apr 12 '18 at 20:29
  • Question: How come we did not have to pass the handle by reference to your function? – Andrew Truckle Apr 12 '18 at 20:30
  • I also fixed my colour corruption in the registry. All god. Thanks. – Andrew Truckle Apr 12 '18 at 20:54
  • 1
    @AndrewTruckle `CBitmap` has `HBITMAP()` operator, it returns bitmap handle when requested. Using the debugger, you can step in to `swap_color(bmp)` it leads to `swap_color(bmp.m_hObject)` – Barmak Shemirani Apr 12 '18 at 22:03
1

I found this article. I replicate the made code here:

#define COLORREF2RGB(Color) (Color & 0xff00) | ((Color >> 16) & 0xff) \
                                 | ((Color << 16) & 0xff0000)

//-------------------------------------------------------------------------------
// ReplaceColor
//
// Author    : Dimitri Rochette drochette@coldcat.fr
// Specials Thanks to Joe Woodbury for his comments and code corrections
//
// Includes  : Only <windows.h>

//
// hBmp         : Source Bitmap
// cOldColor : Color to replace in hBmp
// cNewColor : Color used for replacement
// hBmpDC    : DC of hBmp ( default NULL ) could be NULL if hBmp is not selected
//
// Retcode   : HBITMAP of the modified bitmap or NULL for errors
//
//-------------------------------------------------------------------------------
HBITMAP ReplaceColor(HBITMAP hBmp,COLORREF cOldColor,COLORREF cNewColor,HDC hBmpDC)
{
    HBITMAP RetBmp=NULL;
    if (hBmp)
    {
        HDC BufferDC=CreateCompatibleDC(NULL);    // DC for Source Bitmap
        if (BufferDC)
        {
            HBITMAP hTmpBitmap = (HBITMAP) NULL;
            if (hBmpDC)
                if (hBmp == (HBITMAP)GetCurrentObject(hBmpDC, OBJ_BITMAP))
            {
                hTmpBitmap = CreateBitmap(1, 1, 1, 1, NULL);
                SelectObject(hBmpDC, hTmpBitmap);
            }

            HGDIOBJ PreviousBufferObject=SelectObject(BufferDC,hBmp);
            // here BufferDC contains the bitmap

            HDC DirectDC=CreateCompatibleDC(NULL); // DC for working
            if (DirectDC)
            {
                // Get bitmap size
                BITMAP bm;
                GetObject(hBmp, sizeof(bm), &bm);

                // create a BITMAPINFO with minimal initilisation 
                // for the CreateDIBSection
                BITMAPINFO RGB32BitsBITMAPINFO; 
                ZeroMemory(&RGB32BitsBITMAPINFO,sizeof(BITMAPINFO));
                RGB32BitsBITMAPINFO.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
                RGB32BitsBITMAPINFO.bmiHeader.biWidth=bm.bmWidth;
                RGB32BitsBITMAPINFO.bmiHeader.biHeight=bm.bmHeight;
                RGB32BitsBITMAPINFO.bmiHeader.biPlanes=1;
                RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32;

                // pointer used for direct Bitmap pixels access
                UINT * ptPixels;    

                HBITMAP DirectBitmap = CreateDIBSection(DirectDC, 
                                       (BITMAPINFO *)&RGB32BitsBITMAPINFO, 
                                       DIB_RGB_COLORS,
                                       (void **)&ptPixels, 
                                       NULL, 0);
                if (DirectBitmap)
                {
                    // here DirectBitmap!=NULL so ptPixels!=NULL no need to test
                    HGDIOBJ PreviousObject=SelectObject(DirectDC, DirectBitmap);
                    BitBlt(DirectDC,0,0,
                                   bm.bmWidth,bm.bmHeight,
                                   BufferDC,0,0,SRCCOPY);

                       // here the DirectDC contains the bitmap

                    // Convert COLORREF to RGB (Invert RED and BLUE)
                    cOldColor=COLORREF2RGB(cOldColor);
                    cNewColor=COLORREF2RGB(cNewColor);

                    // After all the inits we can do the job : Replace Color
                    for (int i=((bm.bmWidth*bm.bmHeight)-1);i>=0;i--)
                    {
                        if (ptPixels[i]==cOldColor) ptPixels[i]=cNewColor;
                    }
                    // little clean up
                    // Don't delete the result of SelectObject because it's 
                    // our modified bitmap (DirectBitmap)
                       SelectObject(DirectDC,PreviousObject);

                    // finish
                    RetBmp=DirectBitmap;
                }
                // clean up
                DeleteDC(DirectDC);
            }            
            if (hTmpBitmap)
            {
                SelectObject(hBmpDC, hBmp);
                DeleteObject(hTmpBitmap);
            }
            SelectObject(BufferDC,PreviousBufferObject);
            // BufferDC is now useless
            DeleteDC(BufferDC);
        }
    }
    return RetBmp;
}

Now, if I add a 24 bit bitmap to my project, and set the background as 71/71/71 and load it like this:

HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetResourceHandle(),
    MAKEINTRESOURCE(IDB_BITMAP1),
    IMAGE_BITMAP,
    0, 0, // cx,cy
    LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS);   
HBITMAP hBmp2 = ReplaceColor(hBmp, RGB(71, 71, 71), GetSysColor(COLOR_MENU), NULL);
DeleteObject(hBmp);

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

The result:

Menu

Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164