使用 Windows API 将图标转换成位图
在不依赖任何第三方库、仅使用 Windows API 的前提下,需要将窗口图标(HICON)转换成作为菜单图标的位图(HBITMAP)。
起初使用 GetIconInfo()
函数:
c++HBITMAP IconToBitmap(HICON icon)
{
ICONINFO iconInfo;
GetIconInfo(icon, &iconInfo);
DeleteObject(iconInfo.hbmMask);
return iconInfo.hbmColor;
}
然而绘制的菜单图标会有黑色背景,并且无法调整位图大小。
再尝试用 DrawIconEx()
函数将图标绘制成位图:
c++HBITMAP IconToBitmap(HICON icon, int width = 0, int height = 0)
{
// 如果未提供位图尺寸,则获取系统默认小图标尺寸
if (!width) width = GetSystemMetrics(SM_CXSMICON);
if (!height) height = GetSystemMetrics(SM_CYSMICON);
HDC hdc = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdc);
// 创建兼容位图(DDB)
HBITMAP hbmpMem = CreateCompatibleBitmap(hdcMem, width, height);
if (hbmpMem) {
auto oldObj = SelectObject(hdcMem, hbmpMem);
DrawIconEx(hdcMem, 0, 0, icon, width, height, 0, NULL, DI_NORMAL);
SelectObject(hdcMem, oldObj);
}
DeleteDC(hdcMem);
ReleaseDC(NULL, hdc);
return hbmpMem;
}
可以定义位图大小了,但是菜单绘制的图标仍有黑色背景。
最后尝试用 CreateDIBSection()
函数替代 CreateCompatibleBitmap()
函数:
c++HBITMAP IconToBitmap(HICON icon, int width = 0, int height = 0)
{
if (!width) width = GetSystemMetrics(SM_CXSMICON);
if (!height) height = GetSystemMetrics(SM_CYSMICON);
HDC hdc = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdc);
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
// 创建设备无关位图(DIB)
HBITMAP hbmpMem = CreateDIBSection(hdcMem, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
if (hbmpMem) {
auto oldObj = SelectObject(hdcMem, hbmpMem);
DrawIconEx(hdcMem, 0, 0, icon, width, height, 0, NULL, DI_NORMAL);
SelectObject(hdcMem, oldObj);
}
DeleteDC(hdcMem);
ReleaseDC(NULL, hdc);
return hbmpMem;
}
成功绘制了透明背景的菜单图标。
另外,微软的开源项目 PowerTroys 源代码中也实现了一个 CreateBitmapFromIcon()
函数:
c++HBITMAP CreateBitmapFromIcon(_In_ HICON hIcon, _In_opt_ UINT width, _In_opt_ UINT height)
{
HBITMAP hBitmapResult = NULL;
// Create compatible DC
HDC hDC = CreateCompatibleDC(NULL);
if (hDC != NULL)
{
// Get bitmap rectangle size
RECT rc = { 0 };
rc.left = 0;
rc.right = (width != 0) ? width : GetSystemMetrics(SM_CXSMICON);
rc.top = 0;
rc.bottom = (height != 0) ? height : GetSystemMetrics(SM_CYSMICON);
// Create bitmap compatible with DC
BITMAPINFO BitmapInfo;
ZeroMemory(&BitmapInfo, sizeof(BITMAPINFO));
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = rc.right;
BitmapInfo.bmiHeader.biHeight = rc.bottom;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
HDC hDCBitmap = GetDC(NULL);
HBITMAP hBitmap = CreateDIBSection(hDCBitmap, &BitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0);
ReleaseDC(NULL, hDCBitmap);
if (hBitmap != NULL)
{
// Select bitmap into DC
HBITMAP hBitmapOld = static_cast<HBITMAP>(SelectObject(hDC, hBitmap));
if (hBitmapOld != NULL)
{
// Draw icon into DC
if (DrawIconEx(hDC, 0, 0, hIcon, rc.right, rc.bottom, 0, NULL, DI_NORMAL))
{
// Restore original bitmap in DC
hBitmapResult = static_cast<HBITMAP>(SelectObject(hDC, hBitmapOld));
hBitmapOld = NULL;
hBitmap = NULL;
}
if (hBitmapOld != NULL)
{
SelectObject(hDC, hBitmapOld);
}
}
if (hBitmap != NULL)
{
DeleteObject(hBitmap);
}
}
DeleteDC(hDC);
}
return hBitmapResult;
}