Winapi 具有透明背景的win32菜单项位图

Winapi 具有透明背景的win32菜单项位图,winapi,bitmap,transparency,menuitem,Winapi,Bitmap,Transparency,Menuitem,我尝试完成的事情非常简单:向菜单项添加图像。我正在使用win32 API用C编程。显示图像/位图,但背景为白色。我想要的是把白色背景变成透明的 我一直在阅读所有我能找到的信息,包括stackoverflow,但这些信息似乎不一致。有人说位图不能有任何形式的透明性,而其他人说它可以。例如,见本问题: setmenuitembitmap()和SetMenuItemInfo()都提供白色背景。上面的链接说,如果位图是32bpp的预乘alpha,那么应该正确显示。因此,要么根本不可能这样做,要么我使用

我尝试完成的事情非常简单:向菜单项添加图像。我正在使用win32 API用C编程。显示图像/位图,但背景为白色。我想要的是把白色背景变成透明的

我一直在阅读所有我能找到的信息,包括stackoverflow,但这些信息似乎不一致。有人说位图不能有任何形式的透明性,而其他人说它可以。例如,见本问题:

setmenuitembitmap()
SetMenuItemInfo()
都提供白色背景。上面的链接说,如果位图是32bpp的预乘alpha,那么应该正确显示。因此,要么根本不可能这样做,要么我使用的bmp格式错误。谁能对这个问题给出明确的答案。如果使用
SetMenuItemInfo()
无法解决此问题,那么解决此问题的最简单方法是什么?我尽量避免使用“所有者绘制”解决方案,因为我觉得这有点过头了。此外,据我所知,业主绘制的解决方案很难尊重Windows主题

menubitmap.rc:

#include "menubitmap.h"

ID_ICON             ICON    DISCARDABLE "menu1.ico"
ID_BITMAP_EXIT      BITMAP  DISCARDABLE "Exit-icn.bmp"

ID_MENU MENU DISCARDABLE 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "E&xit",                       ID_FILE_EXIT
    END
END
菜单映射c:

#include <windows.h>
#include "menubitmap.h"

const char g_szClassName[] = "myWindowClass";

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch(Message)
    {
        case WM_CREATE:
        {
            HBITMAP btmp;
            MENUITEMINFOA miinfo;
            HMENU menu;

            btmp=LoadBitmap((HINSTANCE) GetModuleHandle (NULL), MAKEINTRESOURCE(ID_BITMAP_EXIT));
            menu=GetMenu(hwnd);
            miinfo.cbSize=sizeof(MENUITEMINFO);
            if(!GetMenuItemInfo(menu,ID_FILE_EXIT,FALSE,&miinfo)){
                    printf("getmenuiteminfo failed\r\n");
            }else{
                    miinfo.fMask |= MIIM_BITMAP;
                    miinfo.hbmpItem=btmp;
                    if(SetMenuItemInfo(menu,ID_FILE_EXIT,FALSE,&miinfo)){
                            printf("setmenuiteminfo");
                    }
            }

        }
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case ID_FILE_EXIT:
                    PostMessage(hwnd, WM_CLOSE, 0, 0);
                break;
            }
        break;
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON));
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = MAKEINTRESOURCE(ID_MENU);
    wc.lpszClassName = g_szClassName;
    wc.hIconSm       = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON), IMAGE_ICON, 16, 16, 0);

    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "A Menu",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
        NULL, NULL, hInstance, NULL);

    if(hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

可以手动更改位图的背景色以实现透明度

代码如下:

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);
}
调试:

从你提到的,我把它转换成一个


请参阅:

Windows的GDI中的Alpha透明是一个雷区。它添加得很晚,只有少数API调用真正能够处理专用的alpha通道。不是那些支持(或至少不破坏)alpha透明度的公司之一。从应用程序资源加载图像时,alpha通道丢失

要解决这个问题,您必须使用,传入正确的标志
LR_CREATEDIBSECTION
是重要的一个,因为它保留了源位图中的alpha通道

修复方法很简单,只需更换

LoadBitmap((HINSTANCE)GetModuleHandle(NULL),MAKEINTRESOURCE(ID\u BITMAP\u EXIT))

(HBITMAP)加载映像(GetModuleHandle(NULL)、MAKEINTRESOURCE(ID\u位图\u退出),
图像(位图,0,0,LR_部分)
安装后,您将看到菜单图标以每像素alpha透明度显示:


如果使用带预乘alpha的real 32bpp,则图像正确显示位图支持每像素alpha透明度。您可能正在丢失您的alpha通道。如何加载位图?我正在使用一个资源:
ID\u bitmap\u EXIT bitmap可丢弃的“EXIT icn.bmp”
并使用
btmp=LoadBitmap((HINSTANCE)GetModuleHandle(NULL),MAKEINTRESOURCE(ID\u bitmap\u EXIT))加载它
因此资源中的透明度会丢失?
LoadBitmap
可在“需求”部分中指定的操作系统中使用。它可能会在后续版本中被更改或不可用。相反,请使用
LoadImage
drawframecontrol
l.]您可以上传32bpp位图以进行测试。如果您能提供,我将不胜感激。切换到
LoadImage()
没有什么区别。我尝试了不同的程序来制作bmp。结果是白色背景或黑色背景。使用resource hacker检查可执行文件显示位图透明。根据前两条注释,它应该正确显示。我如何向您发送MRE?您已经发明了
LR\u LOADTRANSPARENT
。这是一种伪造透明度的方法,它会给您留下真实alpha透明度所不存在的视觉瑕疵。@IInspectable是的,这是一种解决方法。在菜单项中加载32位BMP透明位图似乎并不容易。如果你有任何想法,请随时告诉我。
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);
}
HMENU m_hMenu;
HBITMAP g_BitMap;
...
case WM_CONTEXTMENU:
        {           
            m_hMenu = CreatePopupMenu();
            g_BitMap = (HBITMAP)LoadImage(NULL, L"UNTITLED.bmp", IMAGE_BITMAP, 16, 16, LR_LOADFROMFILE);
            swap_color(g_BitMap);
            InsertMenu(m_hMenu, 1, MF_BYPOSITION | MF_POPUP, NULL, L"Windows");
            MENUITEMINFO mii = { sizeof(MENUITEMINFO) };
            mii.fMask = MIIM_BITMAP;
            mii.hbmpItem = g_BitMap;
            SetMenuItemInfo(m_hMenu, 0, true, &mii);            
            TrackPopupMenu(m_hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_HORPOSANIMATION, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hWnd, NULL);   
        }
        break;