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