Home > Software engineering >  CreateIconIndirect - interpretation of pixel data
CreateIconIndirect - interpretation of pixel data

Time:12-06

I would like to create an icon manually with CreateIconIndirect as follows:

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 ) );

According to MSDN ( https://docs.microsoft.com/en-us/previous-versions/dd183376(v=vs.85) ) each pixel's data consists of 4 bytes: blue, green, red, and an unused byte.

I made some experiments with the data by changing the values of byMask and byColor, then doing a screen shot and reading the exact RGB value in MS Paint. (Each time I placed Notepad directly behind the application's windows to have a constant background for eventual transparency / alpha channel effects.)

First I changed only the top left corner, while the rest of the data was 0x00 for the mask and 0xff for the color. The result: most of the icon was white (as expected) and the top left pixel had the following colors:

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

After that I changed each pixel in both bitmaps to the same value. (And checked with MS Paint's fill tool that the picture has only one color.)

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)

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

I don't understand the following (letters refer to rows in the above table):

  • If the 4. byte is ignored, and not an alpha channel, why is pixel (a) not ff0000 like (b)?
  • If I change other pixels only, why does the first one change: (c) vs (a)?
  • Why is the result of all rows marked with (d) the same, why does the mask have no effect here?

Is there a bug in my code? Am I looking at the wrong MSDN page?

CodePudding user response:

OK, I figured it. For historical reasons, there are two types of 32 bit/pixel bitmaps: those that use 3 bytes for the 3 colors, and the last one is unused (usually set to 0), and those that use the fourth byte to represent the alpha channel.

The catch is that the type is not indicated by any field but Windows takes it as an old format bitmap (without alpha) if all 4th bytes are zero, and treats it as a new format (with alpha) one if any fourth byte differs from zero. So this will result in a fully black icon:

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

But if I add this line to the above, the whole icon becomes transparent*:

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

* Except for the pixel in row 7 column 7, which will be only 255/256 transparent.

  • Related