具有透明背景的win32菜单项位图
Posted
技术标签:
【中文标题】具有透明背景的win32菜单项位图【英文标题】:win32 menu item bitmap with transparent background 【发布时间】:2020-02-11 20:24:01 【问题描述】:我尝试完成的工作非常简单:将图像添加到菜单项。我正在使用 win32 API 在 C 中编程。图像/位图显示,但背景是白色的。我想要的是把白色背景变成透明的。
我一直在阅读我能找到的所有信息,包括 ***,但信息似乎不一致。有人说位图不能具有任何形式的透明度,而另一些人则说它可以。例如看这个问题:
Simple way to show a bitmap next to a menu item in a CMenu with correct transparency
SetMenuItemBitmaps()
和 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
menubitmap.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;
在这里你可以找到bitmap
这是 bas64 格式的位图:
Qk12BgAAAAAAADYAAAAoAAAAFAAAABQAAAABACAAAAAAAEAGAADEDgAAxA4AAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAplgAAKZZABOp
XgBFqmAATapgAEyqYABMqmAATKpgAEyqYABMqmAATalfAEinWgAaploAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAKhcAACLKwADsm0Ck8SKBPnHjwT6x48E+sePBPrHjwT6x48E+sePBPrHjwT6xYwE
+7VxAqyeTAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqFwAAJhBAAu6egXE1KUM/9GgC//Omwr/
zpsK/86bCv/Omwr/zpsK/9CfCv/Wpwz/v4EG2qFQAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACp
XAAAmEEADLt7CMTVphT/voEK6K9nAoaxagN6sWoDe7FqA3uvaAJ/unoI3NWmFP+/ggraoVAAHwAA
AAAAAAAAAAAAAC4nwQAvJ8ETKybAZSklxSKqRQAKvHwMxNaoHP++gA7SnkoAFahbAAAAAAAAp1sA
AI4yAAe5eQu61qcc/8CDDtqgTwAfAAAAAAAAAAAtJ8EAMSbAEyYnwZ4gJsD7KSbCW9pTAAe6eg3E
06Ig/71+D9OfTQAVqVwAAAAAAACoXAAAjjIAB7p5DrrXqST/wYQT2qBPAB8AAAAALSbBADEkwBYm
K8OjGTDF/RkuxP4rJ8Jgq0UACKpjC4OzcBCtq2QLjIxFLRIAAP8BQhG2AKhcAACNMQAHu3oRutmr
LP/ChRfaoE8AHy0mwQAvJL8ZJi7Fqhk4yv4TO8z/GTfK/yUvxc0kMMWuJTDEsSUvw7ElL8SxJS/F
sSgrw38wJMAIYz5sAIwwAAe7exS62qw0/8KGG9qgTwAfMiG9DScwxqYZQM//FEXS/xRE0f8URNH/
FUPQ/xVD0P8VQ9D/FUPQ/xVC0P8WQtD/IjTI2DIivxhJM5YAiy8AB7x8F7rcrjz/w4cf2qBOAB8y
IL0NJzDGqRpE0v8VTNb/FUzW/xVL1f8WSdX/FknU/xZJ1P8WSdT/FknU/xZJ1P8iN8rYMSG+GUkz
lgCLLgAHvHwaut2wRP/EiCPan04AHy0nwQAvJL8cJjLHrhpI1P4UUtr/GknV/yU0yNAlNMm0JjTH
tiUzxbcmNMe2JTTItykuxYUwIr8JYT1vAIotAAe9fR2637JL/8WJKNqfTgAfAAAAAC0nwQAwIr4Y
JzLIqBpL1v4bStX+KyrDYZc4EwmqYxOEtHIfrqtlFI2EQDQUBgD/AkAKsQCoXAAAiSwAB759ILrg
tFP/xYos2p9NAB8AAAAAAAAAAC0mwQAwIL0VJzPIoiE/z/sqLMVb1UkAB75/I8TdrlT/woQo051K
ABWpXAAAAAAAAKhcAACIKwAHvn4juuK1W//GizDan00AHwAAAAAAAAAAAAAAAC4mwAAuJL8UKyrD
ayooxiSmPgAKwYMqxOO4ZP/FiDDSm0cAFahbAAAAAAAAp1sAAIcpAAe+fya647dj/8eMNNqeTQAf
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqVwAAJI6AAzCgy3E5bps/8eLNuixahGGtG8WerRvFnu0
bxZ7smsSf8GCLNzlumz/yI042p5NAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACoXAAAkTgAC8KE
MMTnvXb/47dv/9+wZ//fsGf/37Bn/9+wZ//fsGb/4rVt/+nAev/IjTzankwAHgAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAKhcAAB8GQADt3Mdk9GbUPnWo1n61qNZ+tajWfrWo1n61qNZ+tajWfrWoln6
055U+7t5JayaRgANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKVXAQClWAATql8FRathB02r
YQdMq2EHTKthB0yrYQdMq2EHTKthB02qYAZIplkBGqZaAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AA==
【问题讨论】:
如果您使用带有预乘 alpha 的真实 32bpp - 显示正确的图像 位图 version 5 支持每像素 Alpha 透明度。在此过程中,您可能正在丢失您的 Alpha 通道。你是如何加载位图的? 我正在使用一个资源:ID_BITMAP_EXIT BITMAP DISCARDABLE "Exit-icn.bmp"
并使用btmp=LoadBitmap((HINSTANCE) GetModuleHandle (NULL), MAKEINTRESOURCE(ID_BITMAP_EXIT));
加载它所以资源中的透明度丢失了?
LoadBitmap
可用于“要求”部分中指定的操作系统。在后续版本中可能会更改或不可用。相反,请使用LoadImage
和DrawFrameContro
l。] 您可以上传您的 32bpp 位图以供我们测试。如果您能提供a Minimal, Reproducible Example,我将不胜感激。
切换到LoadImage()
并没有什么不同。我尝试了不同的程序来制作 bmp。结果是白色背景或黑色背景。使用资源黑客检查可执行文件显示位图透明。根据前 2 个 cmets,它应该正确显示。如何向您发送 MRE?
【参考方案1】:
Windows 的 GDI 中的 Alpha 透明度是一个雷区。它添加得很晚,只有少数 API 调用真正能够处理专用的 Alpha 通道。 LoadBitmap 不是支持(或至少不破坏)alpha 透明度的那些之一。当您从应用程序的资源加载图像时,alpha 通道会丢失。
要解决这个问题,您必须使用LoadImage,并传入正确的标志。 LR_CREATEDIBSECTION
是重要的,因为它保留了源位图中的 Alpha 通道。
修复就像替换一样简单
LoadBitmap((HINSTANCE) GetModuleHandle (NULL), MAKEINTRESOURCE(ID_BITMAP_EXIT))
与
(HBITMAP)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_BITMAP_EXIT),
IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)
设置好之后,您将看到以每像素 Alpha 透明度显示的菜单图标:
【讨论】:
【参考方案2】:您可以手动更改位图的背景颜色以实现透明度。
代码如下:
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;
调试:
您提到的link 中的img,我将其转换为32-bit BMP picture。
参考:How to show menu bitmaps with transparent background
【讨论】:
你重新发明了LR_LOADTRANSPARENT
。这是一种伪造透明度的方法,它会给您留下真正的 Alpha 透明度不存在的视觉伪影。
@IInspectable 是的,这是一种变通方法。似乎在菜单项中加载 32 位 BMP 透明位图并不容易。如果您有任何想法,请随时告诉我。以上是关于具有透明背景的win32菜单项位图的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SwiftUI 中以降低透明度为菜单项获取 AccentColor 背景?
将文本创建为位图的工具(消除锯齿文本、自定义间距、透明背景)