CreateIconIndirect - 像素数据的解释

CreateIconIndirect - interpretation of pixel data

我想使用 CreateIconIndirect 手动创建一个图标,如下所示:

HDC hDC = ::CreateCompatibleDC( nullptr );

BITMAPINFO bmiMask = {};
bmiMask.bmiHeader.biSize = sizeof( bmiMask.bmiHeader );
bmiMask.bmiHeader.biWidth = 16;
bmiMask.bmiHeader.biHeight = -16; // starts with top row
bmiMask.bmiHeader.biPlanes = 1;
bmiMask.bmiHeader.biBitCount = 32;
BYTE *byMask = nullptr;
HBITMAP hbmMask = ::CreateDIBSection( hDC, &bmiMask, DIB_RGB_COLORS,
                                      reinterpret_cast< void** >( &byMask ),
                                      nullptr, 0 );
BYTE bgraMask[] = { 0x00, 0x00, 0x00, 0x00 };
for( int i = 0; i < 16 * 16; i++ )
    for( int j = 0; j < 4; j++ )
        byMask[ i * 4 + j ] = bgraMask[ j ];
byMask[ 0 ] = 0x00; byMask[ 1 ] = 0x00; byMask[ 2 ] = 0x00; byMask[ 3 ] = 0x00;

BITMAPINFO bmiColor = {};
bmiColor.bmiHeader.biSize = sizeof( bmiColor.bmiHeader );
bmiColor.bmiHeader.biWidth = 16;
bmiColor.bmiHeader.biHeight = -16; // starts with top row
bmiColor.bmiHeader.biPlanes = 1;
bmiColor.bmiHeader.biBitCount = 32;
BYTE *byColor = nullptr;
HBITMAP hbmColor = ::CreateDIBSection( hDC, &bmiColor, DIB_RGB_COLORS,
                                       reinterpret_cast< void** >( &byColor ),
                                       nullptr, 0 );
BYTE bgraColor[] = { 0xff, 0xff, 0xff, 0xff };
for( int i = 0; i < 16 * 16; i++ )
    for( int j = 0; j < 4; j++ )
        byColor[ i * 4 + j ] = bgraColor[ j ];
byColor[ 0 ] = 0x00; byColor[ 1 ] = 0x00; byColor[ 2 ] = 0x00; byColor[ 3 ] = 0x00;

ICONINFO ii = {};
ii.fIcon = TRUE;
ii.xHotspot = ii.yHotspot = 0;
ii.hbmMask = hbmMask;
ii.hbmColor = hbmColor;
HICON hIcon = ::CreateIconIndirect( &ii );
::SendMessage( hwndDialog, WM_SETICON, ICON_SMALL,
               reinterpret_cast< LPARAM >( hIcon ) );

根据 MSDN (https://docs.microsoft.com/en-us/previous-versions/dd183376(v=vs.85)),每个像素的数据由 4 个字节组成:蓝色、绿色、红色和一个未使用的字节。

我通过更改 byMask 和 byColor 的值对数据进行了一些实验,然后进行屏幕截图并在 MS Paint 中读取准确的 RGB 值。 (每次我将记事本直接放在应用程序 windows 的后面,以获得最终透明/alpha 通道效果的恒定背景。)

首先,我只更改了左上角,其余数据为掩码 0x00 和颜色 0xff。结果:大部分图标是白色的(正如预期的那样),左上角的像素具有以下颜色:

MR MA CR CA OR OG OB

00 00 00 ff 00 00 00
00 00 00 00 d3 e9 fe
00 00 ff 00 d3 e9 fe (a)
ff 00 00 00 d3 e9 fe
ff ff 00 00 d3 e9 fe
00 ff 00 00 d3 e9 fe
00 00 ff ff ff 00 00 (b)
ff 00 ff ff ff 00 00
ff ff ff ff ff 00 00
00 ff ff ff ff 00 00

之后,我将两个位图中的每个像素都更改为相同的值。 (并且用MS Paint的填充工具检查过图片只有一种颜色。)

MR MA CR CA OR OG OB

00 00 00 ff 00 00 00
00 00 00 00 00 00 00
00 00 ff 00 ff 00 00 (c)
ff 00 00 00 00 00 00
ff ff 00 00 00 00 00
00 ff 00 00 00 00 00
00 00 ff ff ff 00 00 (d)
ff 00 ff ff ff 00 00 (d)
ff ff ff ff ff 00 00 (d)
00 ff ff ff ff 00 00 (d)

缩写:M=mask C=Color O=Observation R=red G=green B=blue A=alpha/4.byte

我不明白以下内容(字母指的是上面table中的行):

我的代码中有错误吗?我是不是看错了 MSDN 页面?

好的,我想通了。由于历史原因,32bit/pixel位图有两种:一种是3种颜色用3个字节,最后一个未使用(通常设置为0),另一种是用第四个字节表示Alpha 通道。

要注意的是,该类型未由任何字段指示,但 Windows 如果所有第 4 个字节均为零,则将其视为旧格式位图(无 alpha),并将其视为新格式(具有alpha) 如果任何第四个字节不为零则为 1。所以这将导致一个全黑的图标:

for( int i = 0; i < 16 * 16; i++ )
    for( int j = 0; j < 4; j++ )
        byColor[ i * 4 + j ] = 0x00;

但是如果我在上面加上这一行,整个图标就变透明了*:

byColor[ 7 * 16 + 7 + 0 ] = 0x01;

*除了第7行第7列的像素,将只有255/256透明。