【问题标题】:Create 32 bit color Icon programmatically以编程方式创建 32 位颜色图标
【发布时间】:2017-01-08 13:22:14
【问题描述】:

我想使用 C++ 和 Win API 以编程方式创建 32 位颜色图标。为此,我使用了我找到的以下代码here

HICON CreateSolidColorIcon(COLORREF iconColor, int width, int height)
{
    // Obtain a handle to the screen device context.
    HDC hdcScreen = GetDC(NULL);

    // Create a memory device context, which we will draw into.
    HDC hdcMem = CreateCompatibleDC(hdcScreen);

    // Create the bitmap, and select it into the device context for drawing.
    HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, width, height);
    HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmp);

    // Draw your icon.
    // 
    // For this simple example, we're just drawing a solid color rectangle
    // in the specified color with the specified dimensions.
    HPEN hpen = CreatePen(PS_SOLID, 1, iconColor);
    HPEN hpenOld = (HPEN)SelectObject(hdcMem, hpen);
    HBRUSH hbrush = CreateSolidBrush(iconColor);
    HBRUSH hbrushOld = (HBRUSH)SelectObject(hdcMem, hbrush);
    Rectangle(hdcMem, 0, 0, width, height);
    SelectObject(hdcMem, hbrushOld);
    SelectObject(hdcMem, hpenOld);
    DeleteObject(hbrush);
    DeleteObject(hpen);

    // Create an icon from the bitmap.
    // 
    // Icons require masks to indicate transparent and opaque areas. Since this
    // simple example has no transparent areas, we use a fully opaque mask.
    HBITMAP hbmpMask = CreateCompatibleBitmap(hdcScreen, width, height);
    ICONINFO ii;
    ii.fIcon = TRUE;
    ii.hbmMask = hbmpMask;
    ii.hbmColor = hbmp;
    HICON hIcon = CreateIconIndirect(&ii);
    DeleteObject(hbmpMask);

    // Clean-up.
    SelectObject(hdcMem, hbmpOld);
    DeleteObject(hbmp);
    DeleteDC(hdcMem);
    ReleaseDC(NULL, hdcScreen);

    // Return the icon.
    return hIcon;
}

原则上代码可以工作,我可以使用它在运行时使用 Win API 创建彩色图标。但是,我有一些关于该代码(以及一般创建图标)的问题和疑问,我想讨论一下。

  • 使用此功能创建的图标似乎不是 32 位色深。如果我使用像 RGB(218, 112, 214) 这样的颜色,我希望它是浅紫色。但是,实际显示的颜色是灰色的。如何更改代码以使颜色真正为 32 位 RGB?
  • 创建的图标完全填充了颜色,我想在它周围有一个黑色的薄边框...如何实现?
  • 在 MSDN documentation 中(有点往下)提到“在关闭之前,您的应用程序必须使用 DestroyIcon 销毁它使用 CreateIconIndirect 创建的任何图标。没有必要销毁其他创建的图标" 但是,在文档中,例如CreateIcon 在 MSDN 中据说 “当你使用完图标后,使用 DestroyIcon 函数将其销毁。” 这几乎是一个矛盾。我什么时候必须真正销毁图标?
  • 当我将图标添加到图像列表并将此列表添加到组合框时,这些规则是否也适用? IE。我是否必须清理图像列表和每个关联的图标?

非常感谢任何帮助。

【问题讨论】:

  • 嗯,那个面具看起来不像一个合适的面具。它用于 AND 操作,因此您可以简单地使用 hbmp 代替。
  • 是的,对于蒙版需要使用CreateBitmapcBitsPerPel==1 而不是CreateCompatibleBitmap,对于颜色也需要CreateBitmapcBitsPerPel==32RGB 你不使用alpha
  • 嗯,但是在 CreateBitmap 的 MSDN 文档中,它说对于彩色位图,出于性能原因应该使用 CreateCompatibleBitmap 而不是 CreateBitmap,所以两者都不应该工作吗?我为位图尝试了 CreateBitmap(width, height, 1, 32, 0),为掩码尝试了 hbmpMask = CreateBitmap(width, height, 1, 1, 0) 但没有任何改变(但我不确定其余参数如以 lpvBits 为例)

标签: c++ winapi


【解决方案1】:

我实际上什么时候必须销毁图标?

阅读DestroyIcon

只需要为图标和光标调用DestroyIcon 使用以下函数创建:CreateIconFromResourceEx(如果 在没有LR_SHARED 标志的情况下调用)、CreateIconIndirectCopyIcon。请勿使用此功能破坏共享图标。 一个 共享图标只要加载它的模块就有效 留在记忆中。以下函数获取共享图标。

  • LoadIcon
  • LoadImage(如果您使用 LR_SHARED 标志)
  • CopyImage(如果您使用LR_COPYRETURNORG 标志并且hImage 参数是共享图标)
  • CreateIconFromResource
  • CreateIconFromResourceEx(如果您使用 LR_SHARED 标志)

所以你需要在使用完 not shared 图标时调用DestroyIcon

ComboBoxEx 不会破坏您使用 CBEM_SETIMAGELIST 分配给它的图像列表 - 因此此图像列表必须在 ComboBoxEx 有效之前有效,您必须稍后自行销毁它。

ImageList_AddIcon

由于系统不保存hicon,可以在保存后销毁 宏返回

换句话说ImageList_AddIcon复制你的图标,你可以在宏返回后破坏你原来的图标

要创建 32 位颜色图标,请尝试如下代码:

HICON CreateGradientColorIcon(COLORREF iconColor, int width, int height)
{
    HICON hIcon = 0;

    ICONINFO ii = { TRUE };

    ULONG n = width * height;

    if (PULONG lpBits = new ULONG[n])
    {
        PULONG p = lpBits;

        ULONG x, y = height, t;
        do 
        {
            x = width, t = --y << 8;
            do 
            {
                *p++ = iconColor | ((t * --x) / n << 24);
            } while (x);

        } while (y);

        if (ii.hbmColor = CreateBitmap(width, height, 1, 32, lpBits))
        {
            if (ii.hbmMask = CreateBitmap(width, height, 1, 1, 0))
            {
                hIcon = CreateIconIndirect(&ii);

                DeleteObject(ii.hbmMask);
            }

            DeleteObject(ii.hbmColor);
        }

        delete [] lpBits;
    }

    return hIcon;
}

当我在绿色网格上绘制 (DrawIconEx(, DI_IMAGE|DI_MASK)) 这个图标时,我接下来会查看:

【讨论】:

  • 谢谢,只是一个评论:该函数需要 BGR 格式的颜色,而不是 RGB(我花了很多时间才弄清楚:D)
  • @SampleTime - 是的,我混淆了颜色顺序。真的在COLORREF iconColor 中,最低字节必须是蓝色。当RGB 使用红色作为最低字节。因为在RGB(218, 112, 214) 红色和蓝色几乎相等 - 我没有注意到这一点
【解决方案2】:

对于偶然发现此解决方案的每个人,我只是发布了 RbMm 答案的更多记录解决方案。这与他的解决方案基本相同(可能不如性能,我不确定):

static HICON CreateIconFromBytes(HDC DC, int width, int height, uint32* bytes) {
        HICON hIcon = NULL;

        ICONINFO iconInfo = {
            TRUE, // fIcon, set to true if this is an icon, set to false if this is a cursor
            NULL, // xHotspot, set to null for icons
            NULL, // yHotspot, set to null for icons
            NULL, // Monochrome bitmap mask, set to null initially
            NULL  // Color bitmap mask, set to null initially
        };

        uint32* rawBitmap = new uint32[width * height];

        ULONG uWidth = (ULONG)width;
        ULONG uHeight = (ULONG)height;
        uint32* bitmapPtr = rawBitmap;
        for (ULONG y = 0; y < uHeight; y++) {
            for (ULONG x = 0; x < uWidth; x++) {
                // Bytes are expected to be in RGB order (8 bits each)
                // Swap G and B bytes, so that it is in BGR order for windows
                uint32 byte = bytes[x + y * width];
                uint8 A = (byte & 0xff000000) >> 24;
                uint8 R = (byte & 0xff0000) >> 16;
                uint8 G = (byte & 0xff00) >> 8;
                uint8 B = (byte & 0xff);
                *bitmapPtr = (A << 24) | (R << 16) | (G << 8) | B;
                bitmapPtr++;
            }
        }

        iconInfo.hbmColor = CreateBitmap(width, height, 1, 32, rawBitmap);
        if (iconInfo.hbmColor) {
            iconInfo.hbmMask = CreateCompatibleBitmap(DC, width, height);
            if (iconInfo.hbmMask) {
                hIcon = CreateIconIndirect(&iconInfo);
                if (hIcon == NULL) {
                    Log::Warning("Failed to create icon.");
                }
                DeleteObject(iconInfo.hbmMask);
            } else {
                Log::Warning("Failed to create color mask.");
            }
            DeleteObject(iconInfo.hbmColor);
        } else {
            Log::Warning("Failed to create bitmap mask.");
        }

        delete[] rawBitmap;

        return hIcon;
}

此解决方案将与 STB 图像库一起用于加载图像。所以你可以直接用 stb 加载图像,然后将字节数据传递给这个函数,结果你会得到一个图标。我在设置图标时也遇到了一些麻烦,最终这样做是为了让它发挥作用:

HICON icon = CreateIconFromBytes(DC, image.m_Width, image.m_Height, image.m_Pixels);
SendMessage(WND, WM_SETICON, ICON_SMALL, (LPARAM)icon);
SendMessage(WND, WM_SETICON, ICON_BIG, (LPARAM)icon);
SendMessage(WND, WM_SETICON, ICON_SMALL2, (LPARAM)icon);

您应该注意的唯一一点是,您可能应该为 SendMessage() 函数使用 3 个不同大小的图标,但除此之外,这对我很有用 :)

编辑: 这里也是官方 MSDN 文档的链接。
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createiconindirect
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbitmap
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createcompatiblebitmap
https://docs.microsoft.com/en-us/windows/win32/menurc/using-icons

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-29
    • 1970-01-01
    相关资源
    最近更新 更多