65.9K
CodeProject 正在变化。 阅读更多。
Home

在ReactOS上无资源嵌入图标介绍

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2019年10月21日

CPOL

3分钟阅读

viewsIcon

6151

downloadIcon

82

如何在Win32程序中嵌入图标,而无需使用资源——对于没有资源编辑器/资源编译器的平台(例如ReactOS)很有用。 了解有关 .ico 格式的缺失内容。

引言

本文超越了关于 .ico 格式的优秀 Microsoft 在线文章,并介绍了处理二进制 .ico 数据的缺失内容。

我写这篇文章是为了让其他开发人员免于收集关于 .ico 数据格式的所有必要信息的麻烦。

“嵌入图标而不使用资源”这个问题的动机源于以下事实

  • 我没有可用的资源编译器,
  • 但我想嵌入图标

用于我使用 ReactOS 进行的应用程序编程测试。 如果您对 ReactOS 编程感兴趣,也可以看看我的技巧 "ReactOS上的OpenGL简介(使用C/C++)" 和 "ReactOS上的C#简介"。 此技巧是 "ReactOS上的OpenGL简介(使用C/C++)" 技巧的直接延续。

背景

在 ReactOS 上,最稳定的编程接口是纯粹的 Win32 C API——它提供了许多图标函数(参见 Microsoft 文章“图标”),但它们都不适用于流或 BYTE[]。 最接近的一个是 LoadImage,它支持位图、图标和光标。 但是 LoadImage 从文件系统加载,我不认为用程序附带很多 .ico 文件是一个好风格——更喜欢嵌入 .ico 文件。 不幸的是,纯粹的 Win32 C API 中没有合适的函数来执行此操作——所以我被迫编写我自己的函数。

关于图标的一个小提示

在深入研究图标之前,我不太喜欢 Windows 的“图标”功能。 然而,今天我却有不同的看法,原因如下

  • 在 ReactOS 下,STATIC 窗口类目前不支持位图,但支持图标。
  • 引人入胜的图标总是需要对透明像素进行遮罩。 要用位图实现这一点,至少需要两个文件。
  • 16 或 256 色的 16x16 像素或 32x32 像素的旧限制已被移除。 如今,图标也可以更大,并且是真彩色。

Using the Code

注意:我将在此处提供的代码是为以下图标开发的

  • 每个文件一张图片
  • 16x16 像素和
  • 16 色调色板

其他格式可能有效,但我怀疑它们会。 尽管如此,代码仍然包含您根据需要扩展功能所需的所有信息。

一个图标可以很容易地嵌入到一个程序中。 .ico 文件只需从任何 HEX 编辑器复制到 C 文件并格式化为 BYTE[],例如

WORD ICO_CLOCKWISE_16_ByteCount = 318;
BYTE ICO_CLOCKWISE_16_Bytes[318] = {
    0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
    0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00,
    0x28, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
// 00 + 22 = 22
    0x28, 0x00, 0x00, 0x00,
    0x10, 0x00, 0x00, 0x00,
    0x20, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x04, 0x00,
    0x00, 0x00,
    0x00, 0x00,
    0x80, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x10, 0x00, 0x00, 0x00,
    0x10, 0x00, 0x00, 0x00,
// 22 + 40 = 62 - START COLORTABLE
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x80, 0x00,
    0x00, 0x80, 0x00, 0x00,
    0x00, 0x80, 0x80, 0x00,
    0x80, 0x00, 0x00, 0x00,
    0x80, 0x00, 0x80, 0x00,
    0x80, 0x80, 0x00, 0x00,
    0x40, 0x40, 0x40, 0x00,
    0x60, 0x60, 0x60, 0x00,
    0x00, 0x00, 0xff, 0x00,
    0x00, 0xff, 0x00, 0x00,
    0x00, 0xff, 0xff, 0x00,
    0xff, 0x00, 0x00, 0x00,
    0xff, 0x00, 0xff, 0x00,
    0xff, 0xff, 0x00, 0x00,
    0xff, 0xff, 0xff, 0x00,
// IMAGE
    0x00, 0x00, 0x87, 0x00, 0x07, 0x80, 0x00, 0x00,
    0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x07, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x70,
    0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00,
    0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00,
    0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00,
    0x00, 0x00, 0x08, 0x70, 0x00, 0x78, 0x00, 0x00,
// MASK
    0x70, 0x1f, 0x00, 0x00, 0x20, 0x0f, 0x00, 0x00,
    0x01, 0xc7, 0x00, 0x00, 0x07, 0xe3, 0x00, 0x00,
    0x07, 0xf3, 0x00, 0x00, 0x03, 0xf1, 0x00, 0x00,
    0xff, 0xf1, 0x00, 0x00, 0xff, 0xf1, 0x00, 0x00,
    0x8f, 0xff, 0x00, 0x00, 0x8f, 0xff, 0x00, 0x00,
    0x8f, 0xe0, 0x00, 0x00, 0xcf, 0xf0, 0x00, 0x00,
    0xc7, 0xf0, 0x00, 0x00, 0xe1, 0xc0, 0x00, 0x00,
    0xf0, 0x04, 0x00, 0x00, 0xf8, 0x0e, 0x00, 0x00
};

这个示例 .ico 定义只包含一个 16x16 像素的图像。 我的应用程序调用

HICON hIcon = Utils_CreateIconFromBytes(ICO_CLOCKWISE_16_Bytes(),
                                        ICO_CLOCKWISE_16_ByteCount(), 16, 16);

在缓冲数据时,我非常贴近优秀的 Microsoft 关于 .ico 格式的在线文章

typedef struct tagICONIMAGE
{
    BITMAPINFOHEADER   iiHeader;       // DIB header
    RGBQUAD            iiColors[1];    // Color table
    BYTE*              iiXOR;          // DIB bits for XOR mask
    BYTE*              iiAND;          // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;

typedef struct tagICONDIRENTRY
{
    BYTE              deWidth;         // Width, in pixels, of the image
    BYTE              deHeight;        // Height, in pixels, of the image
    BYTE              deColorCount;    // Number of colors in image (0 if >=8bpp)
    BYTE              deReserved;      // Reserved ( must be 0)
    WORD              dePlanes;        // Color Planes
    WORD              deBitCount;      // Bits per pixel
    DWORD             deBytesInRes;    // How many bytes in this resource?
    DWORD             deImageOffset;   // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;

typedef struct tagICONDIR
{
    WORD              idReserved;      // Reserved (must be 0)
    WORD              idType;          // Resource Type (1 for icons)
    WORD              idCount;         // How many images?
    LPICONDIRENTRY    idEntries;       // An entry for each image.
} ICONDIR, *LPICONDIR;

这包括数据结构(如上)和第一处理阶段

/// <summary>
/// Creates an icon from indicated icon bytes.
/// </summary>
/// <param name="pbIconBytes">The icon bytes.</param>
/// <param name="wByteCount">The number of icon bytes.</param>
/// <param name="lWidth">The requested icon width in pixel.</param>
/// <param name="lHeight">The requested icon height in pixel.</param>
/// <returns>The handle to the GDI icon object on success, or <c>NULL</c> otherwise.
/// The caller is responsible to free the GDI object with <c>DestroyIcon()</c>.</returns>
/// <remarks>For details see: "https://docs.microsoft.com/en-us/previous-versions/
///                            ms997538(v=msdn.10)?redirectedfrom=MSDN"</remarks>
__declspec(dllexport) HICON __cdecl Utils_CreateIconFromBytes(BYTE* pbIconBytes,
                                                              WORD wByteCount,
                                                              LONG lWidth, LONG lHeight)
{
    if (pbIconBytes == NULL)
    {
        Console::WriteText(Console::__ERROR,
                           L"Utils_CreateIconFromBits: The 'pbIconBytes' argument"
                           L" must not be NULL!\n");
        return NULL;
    }
    if (wByteCount <= 22)
    {
        Console::WriteText(Console::__ERROR,
                           L"Utils_CreateIconFromBits: The 'wByteCount' argument"
                           L" must be at least 22!\n");
        return NULL;
    }
    if (lWidth <= 0)
    {
        Console::WriteText(Console::__ERROR,
                           L"Utils_CreateIconFromBits: The 'lWidth' argument"
                           L" must be a valid width - at least 1!\n");
        return NULL;
    }
    if (lHeight <= 0)
    {
        Console::WriteText(Console::__ERROR,
                           L"Utils_CreateIconFromBits: The 'lHeight' argument"
                           L" must be a valid height - at least 1!\n");
        return NULL;
    }

    // --------------------------------------------------------------------------
    // ICON DIR
    // --------------------------------------------------------------------------

    ICONDIR aIconDir       = {0};

    aIconDir.idReserved    = (WORD)pbIconBytes[0] + (WORD)pbIconBytes[1] * (WORD)256;

    aIconDir.idType        = (WORD)pbIconBytes[2] + (WORD)pbIconBytes[3] * (WORD)256;
    if (aIconDir.idType   != (WORD)1 && aIconDir.idType    != (WORD)2)
    {
        Console::WriteText(Console::__ERROR,
                           L"Utils_CreateIconFromBits: The icon header's content"
                           L" type is not equal to '1' (for icon) or '2' (for cursor)!\n");
        return NULL;
    }

    aIconDir.idCount       = (WORD)pbIconBytes[4] + (WORD)pbIconBytes[5] * (WORD)256;
    if (aIconDir.idCount  == (WORD)0)
    {
        Console::WriteText(Console::__ERROR,
                           L"Utils_CreateIconFromBits: The icon bits contains '0' images!\n");
        return NULL;
    }

    int n = 6;

    aIconDir.idEntries = new ICONDIRENTRY[aIconDir.idCount];

    for (WORD wCountIconEntries = 0; wCountIconEntries < aIconDir.idCount; wCountIconEntries++)
    {
        // --------------------------------------------------------------------------
        // ICON DIRENTRY
        // --------------------------------------------------------------------------

        LPICONDIRENTRY pIconDirEntry  = &aIconDir.idEntries[wCountIconEntries];

        pIconDirEntry->deWidth         = (BYTE)pbIconBytes[n + 0];
        if (pIconDirEntry->deWidth    == 0)
            pIconDirEntry->deWidth     = (BYTE)256;

        pIconDirEntry->deHeight        = (BYTE)pbIconBytes[n + 1];
        if (pIconDirEntry->deHeight   == 0)
            pIconDirEntry->deHeight    = (BYTE)256;

        pIconDirEntry->deColorCount    = (BYTE)pbIconBytes[n + 2];

        pIconDirEntry->deReserved      = (BYTE)pbIconBytes[n + 3];

        pIconDirEntry->dePlanes        = (WORD)pbIconBytes[n + 4];
        pIconDirEntry->dePlanes       += (WORD)pbIconBytes[n + 5] * (WORD)256;
        if (pIconDirEntry->dePlanes   != (WORD)0 && pIconDirEntry->dePlanes   != (WORD)1)
        {
            Console::WriteText(Console::__ERROR,
                               L"Utils_CreateIconFromBits: The icon header's"
                           L" number of planes is not equal to '1'!\n");
            return NULL;
        }

        pIconDirEntry->deBitCount      = (WORD)pbIconBytes[n + 6];
        pIconDirEntry->deBitCount     += (WORD)pbIconBytes[n + 7] * (WORD)256;
        if (pIconDirEntry->deBitCount == (WORD)0)
        {
            Console::WriteText(Console::__ERROR,
                               L"Utils_CreateIconFromBits: The icon header's"
                           L" bits per pixel is '0'!\n");
            return NULL;
        }
        if (pIconDirEntry->deBitCount != (WORD)4 &&
            pIconDirEntry->deBitCount != (WORD)8 && pIconDirEntry->deBitCount != (WORD)32)
        {
            Console::WriteText(Console::__ERROR,
                               L"Utils_CreateIconFromBits: The icon header's"
                           L" bits per pixel is not equal to '4', '8' or '32'!\n");
            return NULL;
        }

        pIconDirEntry->deBytesInRes   = (DWORD)pbIconBytes[n +  8];
        pIconDirEntry->deBytesInRes  += (DWORD)pbIconBytes[n +  9] * (DWORD)256;
        pIconDirEntry->deBytesInRes  += (DWORD)pbIconBytes[n + 10] * (DWORD)65536;
        pIconDirEntry->deBytesInRes  += (DWORD)pbIconBytes[n + 11] * (DWORD)16777216;
        if (pIconDirEntry->deBytesInRes == (WORD)0)
        {
            Console::WriteText(Console::__ERROR,
                               L"Utils_CreateIconFromBits: The icon header's"
                           L" size of the bitmap is '0'!\n");
            return NULL;
        }

        pIconDirEntry->deImageOffset  = (DWORD)pbIconBytes[n + 12];
        pIconDirEntry->deImageOffset += (DWORD)pbIconBytes[n + 13] * (DWORD)256;
        pIconDirEntry->deImageOffset += (DWORD)pbIconBytes[n + 14] * (DWORD)65536;
        pIconDirEntry->deImageOffset += (DWORD)pbIconBytes[n + 15] * (DWORD)16777216;
        if (pIconDirEntry->deImageOffset == (WORD)0)
        {
            Console::WriteText(Console::__ERROR,
                               L"Utils_CreateIconFromBits: The icon header's"
                           L" offset of the bitmap is '0'!\n");
            return NULL;
        }

        n += 16;
    }

    WORD wPreferredIconEntry = -1;
    for (WORD wCountIconEntries = 0; wCountIconEntries < aIconDir.idCount; wCountIconEntries++)
    {
        LPICONDIRENTRY pIconDirEntry  = &aIconDir.idEntries[wCountIconEntries];

        if ((LONG)pIconDirEntry->deWidth == lWidth && (LONG)pIconDirEntry->deHeight == lHeight)
        {
            wPreferredIconEntry = wCountIconEntries;
            break;
        }
    }
    if (wPreferredIconEntry < 0)
    {
        Console::WriteText(Console::__ERROR,
                           L"Utils_CreateIconFromBits: The icon doesn't include"
                           L" an icon image of the requested width and height!\n");
        return NULL;
    }

    LPICONDIRENTRY pIDEntry  = &aIconDir.idEntries[wPreferredIconEntry];
    HICON          hIcon     = Utils_CreateIconFromBytesSub(pbIconBytes, n, pIDEntry);

    delete aIconDir.idEntries;

    return hIcon;
}

此函数中的处理是众所周知的。 比较不为人知的处理步骤在 CreateIconFromBytesSub() 中执行。

HICON Utils_CreateIconFromBitsSub(BYTE* pbIconBytes, LONG lIconBytesBaseOffset,
                                  LPICONDIRENTRY pIDEntry)
{
    // --------------------------------------------------------------------------
    // ICON IMAGE
    // --------------------------------------------------------------------------

    ICONIMAGE aIconImage = {0};
    LPBITMAPINFOHEADER bmiHeader = &aIconImage.iiHeader;
    LONG n = lIconBytesBaseOffset;

    bmiHeader->biSize           = (DWORD)pbIconBytes[n +  0];
    bmiHeader->biSize          += (DWORD)pbIconBytes[n +  1] * (DWORD)256;
    bmiHeader->biSize          += (DWORD)pbIconBytes[n +  2] * (DWORD)65536;
    bmiHeader->biSize          += (DWORD)pbIconBytes[n +  3] * (DWORD)16777216;

    bmiHeader->biWidth          =  (LONG)pbIconBytes[n +  4];
    bmiHeader->biWidth         +=  (LONG)pbIconBytes[n +  5] * (LONG)256;
    bmiHeader->biWidth         +=  (LONG)pbIconBytes[n +  6] * (LONG)65536;
    bmiHeader->biWidth         +=  (LONG)pbIconBytes[n +  7] * (LONG)16777216;

    bmiHeader->biHeight         =  (LONG)pbIconBytes[n +  8];
    bmiHeader->biHeight        +=  (LONG)pbIconBytes[n +  9] * (LONG)256;
    bmiHeader->biHeight        +=  (LONG)pbIconBytes[n + 10] * (LONG)65536;
    bmiHeader->biHeight        +=  (LONG)pbIconBytes[n + 11] * (LONG)16777216;
    bmiHeader->biHeight        /=  2; // By convention.

    bmiHeader->biPlanes         =  (WORD)pbIconBytes[n + 12];
    bmiHeader->biPlanes        +=  (WORD)pbIconBytes[n + 13] * (WORD)256;

    bmiHeader->biBitCount       =  (WORD)pbIconBytes[n + 14];
    bmiHeader->biBitCount      +=  (WORD)pbIconBytes[n + 15] * (WORD)256;

    bmiHeader->biCompression    = (DWORD)pbIconBytes[n + 16];
    bmiHeader->biCompression   += (DWORD)pbIconBytes[n + 17] * (DWORD)256;
    bmiHeader->biCompression   += (DWORD)pbIconBytes[n + 18] * (DWORD)65536;
    bmiHeader->biCompression   += (DWORD)pbIconBytes[n + 19] * (DWORD)16777216;

    bmiHeader->biSizeImage      = (DWORD)pbIconBytes[n + 20];
    bmiHeader->biSizeImage     += (DWORD)pbIconBytes[n + 21] * (DWORD)256;
    bmiHeader->biSizeImage     += (DWORD)pbIconBytes[n + 22] * (DWORD)65536;
    bmiHeader->biSizeImage     += (DWORD)pbIconBytes[n + 23] * (DWORD)16777216;

    bmiHeader->biXPelsPerMeter  =  (LONG)pbIconBytes[n + 24];
    bmiHeader->biXPelsPerMeter +=  (LONG)pbIconBytes[n + 25] * (LONG)256;
    bmiHeader->biXPelsPerMeter +=  (LONG)pbIconBytes[n + 26] * (LONG)65536;
    bmiHeader->biXPelsPerMeter +=  (LONG)pbIconBytes[n + 27] * (LONG)16777216;

    bmiHeader->biYPelsPerMeter  =  (LONG)pbIconBytes[n + 28];
    bmiHeader->biYPelsPerMeter +=  (LONG)pbIconBytes[n + 29] * (LONG)256;
    bmiHeader->biYPelsPerMeter +=  (LONG)pbIconBytes[n + 30] * (LONG)65536;
    bmiHeader->biYPelsPerMeter +=  (LONG)pbIconBytes[n + 31] * (LONG)16777216;

    bmiHeader->biClrUsed        = (DWORD)pbIconBytes[n + 32];
    bmiHeader->biClrUsed       += (DWORD)pbIconBytes[n + 33] * (DWORD)256;
    bmiHeader->biClrUsed       += (DWORD)pbIconBytes[n + 34] * (DWORD)65536;
    bmiHeader->biClrUsed       += (DWORD)pbIconBytes[n + 35] * (DWORD)16777216;

    bmiHeader->biClrImportant   = (DWORD)pbIconBytes[n + 36];
    bmiHeader->biClrImportant  += (DWORD)pbIconBytes[n + 37] * (DWORD)256;
    bmiHeader->biClrImportant  += (DWORD)pbIconBytes[n + 38] * (DWORD)65536;
    bmiHeader->biClrImportant  += (DWORD)pbIconBytes[n + 39] * (DWORD)16777216;

    if (bmiHeader->biSize != 40)
    {
        Console::WriteText(Console::__ERROR, L"Utils_CreateIconFromBits: The icon header"
                           L" size is not equal to '40'!\n");
        return NULL;
    }

    if (bmiHeader->biPlanes != 1)
    {
        Console::WriteText(Console::__ERROR, L"Utils_CreateIconFromBits: The icon header's"
                           L" number of planes is not equal to '1'!\n");
        return NULL;
    }

    if (bmiHeader->biCompression != 0)
    {
        Console::WriteText(Console::__ERROR, L"Utils_CreateIconFromBits: The icon header's"
                           L" compression is not equal to '0'!\n");
        return NULL;
    }

    if (bmiHeader->biSizeImage != (DWORD)(bmiHeader->biWidth * bmiHeader->biHeight) *
                                          bmiHeader->biPlanes * bmiHeader->biBitCount / 8)
    {
        Console::WriteText(Console::__ERROR, L"Utils_CreateIconFromBits: The image size"
                           L" of bitmap '1' is not equal to the calculated size!\n");
        return NULL;
    }

    n += 40;

    // --------------------------------------------------------------------------
    // COLOR PALETTE
    // --------------------------------------------------------------------------

    COLORREF* aColorPalette = (COLORREF*)new COLORREF[pIDEntry->deColorCount];
    for (DWORD i = 0; i < pIDEntry->deColorCount; i++)
    {
        DWORD color = 0;
        color  = (DWORD)pbIconBytes[n + i * 4 + 0];
        color += (DWORD)pbIconBytes[n + i * 4 + 1] * (DWORD)256;
        color += (DWORD)pbIconBytes[n + i * 4 + 2] * (DWORD)65536;
        color += (DWORD)pbIconBytes[n + i * 4 + 3] * (DWORD)16777216;
        aColorPalette[i] = color;
    }

    n += sizeof(COLORREF) * pIDEntry->deColorCount;

    // --------------------------------------------------------------------------
    // XOR BITMAP
    // --------------------------------------------------------------------------

    aIconImage.iiXOR = (BYTE*)new COLORREF[bmiHeader->biWidth * bmiHeader->biHeight];
    for (LONG rowXOR = 0; rowXOR < bmiHeader->biHeight; rowXOR++)
    {
        for (LONG colXOR = 0; colXOR < bmiHeader->biWidth; colXOR++)
        {
            LONG sourcePos = rowXOR * bmiHeader->biWidth + colXOR;
            LONG targetPos = (bmiHeader->biHeight - 1 - rowXOR) * bmiHeader->biWidth + colXOR;

            if (bmiHeader->biBitCount == 4)
            {
                // Every BYTE stores the color index of two pixel.
                BYTE palIndex  = pbIconBytes[n + sourcePos / 2];
                COLORREF color;
                if (colXOR % 2 == 0)
                    color = aColorPalette[(palIndex & 0xf0) / 16];
                else
                    color = aColorPalette[(palIndex & 0x0f)     ];

                ((COLORREF*)aIconImage.iiXOR)[targetPos] = color;
            }
        }
    }

    n += bmiHeader->biSizeImage;

    // --------------------------------------------------------------------------
    // AND BITMAP
    // --------------------------------------------------------------------------

    aIconImage.iiAND = (BYTE*)new BYTE[bmiHeader->biSizeImage / 2];
    for (DWORD i = 0; i < bmiHeader->biSizeImage / 2; i++)
        aIconImage.iiAND[i] = pbIconBytes[n + i];

    LONG sourceOffset = 0;
    for (LONG rowAND = 0; rowAND < bmiHeader->biHeight; rowAND++)
    {
        // The mask is BIT-wise. Every BYTE holds the mask for 8 BITs.
        for (LONG colAND = 0; colAND < bmiHeader->biWidth; colAND += 8)
        {
            BYTE mask  = pbIconBytes[n + sourceOffset];
            sourceOffset++;

            LONG targetPos = (bmiHeader->biHeight - 1 - rowAND) *
                             bmiHeader->biWidth / 8 + colAND / 8;
            aIconImage.iiAND[targetPos] = mask;
        }
        while (sourceOffset % 4 != 0)
            sourceOffset++;
    }

    // --------------------------------------------------------------------------
    // ICON
    // --------------------------------------------------------------------------

    HICON hIcon        = NULL;

    Utils_InspectBitmapBytes (&(pbIconBytes[pIDEntry->deImageOffset]), pIDEntry->deBytesInRes);

    ICONINFO ii = {0};
    ii.fIcon = TRUE;
    ii.xHotspot = 0;
    ii.yHotspot = 0;
    ii.hbmColor = ::CreateBitmap(bmiHeader->biWidth,  bmiHeader->biHeight,
                                 bmiHeader->biPlanes, 32, aIconImage.iiXOR);
    ii.hbmMask  = ::CreateBitmap(bmiHeader->biWidth, bmiHeader->biHeight,
                                 1, 1, aIconImage.iiAND);

    hIcon = ::CreateIconIndirect(&ii);

    ::DeleteObject(ii.hbmMask);
    ::DeleteObject(ii.hbmColor);
    delete aColorPalette;
    delete aIconImage.iiXOR;
    delete aIconImage.iiAND;

    return hIcon;
}

就是这样!

关注点

除了优秀的 Microsoft 关于 .ico 格式的在线文章 之外,我学到了什么?

  • BITMAPINFOHEADER.biHeight 的值必须除以 2
  • 颜色表紧跟在 BITMAPINFOHEADER 数据之后。
  • 颜色表的长度已经由 ICONDIRENTRY.deColorCount 知道。
  • ICONIMAGE.iiXOR 数据紧跟在颜色表之后,然后是 ICONIMAGE.iiAND 数据。

历史

  • 2019年10月22日:初始技巧
© . All rights reserved.