[Back to WIN-OS2 SWAG index]  [Back to Main SWAG index]  [Original]

Some days ago someone asked me about the icon file format. I will publish
these messages just once. So put it in your archives please :-)
(I don't save my own messages, I never did :).

TIconHeader = record
  idReserved: Word; (* Always set to 0 *)
  idType: Word;     (* Always set to 1 *)
  idCount: Word;    (* Number of icon images *)
  (* immediately followed by idCount TIconDirEntries *)

A .ICO file can contain several icon images. Each image can differ in size,
resolution and color format.

TIconDirEntry = record
  bWidth: Byte;          (* Width *)
  bHeight: Byte;         (* Height *)
  bColorCount: Byte;     (* Nr. of colors used, see below *)
  bReserved: Byte;       (* not used, 0 *)
  wPlanes: Word;         (* not used, 0 *)
  wBitCount: Word;       (* not used, 0 *)
  dwBytesInRes: Longint; (* total number of bytes in images *)
  dwImageOffset: Longint;(* location of image from the beginning of file *)

bColorCount refers to the number of palette entries in the image data. For the
most common icon type (32x32 pixels, 16 colors) the first three fields would be
32, 32 and 16 resp.

dwBytesInRes is the total number of bytes in the image data (including palette
data!). dwImageOffset is an offset (from the beginning of the file) to the
image data.

Image data

The icon file stores image data in the so-called device independent bitmap
(DIB) format. It's supposed to be fairly device independent :-). The actual
pixel data is preceded by two "structures": the bitmap info header and the
palette data.

First the bitmap info header (pasted from the BP WinSDK help, comments are
TBitmapInfoHeader = record
  biSize: Longint;    (* sizeof(TBitmapInfoHeader *)
  biWidth: Longint;   (* width of bitmap *)
  biHeight: Longint;  (* height of bitmap, see notes *)
  biPlanes: Word;     (* planes, always 1 *)
  biBitCount: Word;   (* number of color bits *)
  biCompression: Longint; (* compression used, 0 *)
  biSizeImage: Longint;   (* size of the pixel data, see notes *)
  biXPelsPerMeter: Longint; (* not used, 0 *)
  biYPelsPerMeter: Longint; (* not used, 0 *)
  biClrUsed: Longint;       (* nr of colors used, set to 0 *)
  biClrImportant: Longint;  (* important colors, set to 0 *)

biBitcount contains the number of color bits per pixel. If it's 4, it means a
pixel can have 16 colors (2 shl 4). biBitcount also refers to how a pixel is
"coded" in the pixel data. If biBitcount is 4 a pixel takes 4 bits of an image
byte. If it's 8 (256 possible colors), a pixel is a byte.

As you perhaps know, icons are created by combining two bitmap masks: the
XOR-mask and the monochrome AND-mask. To draw an icon you first copy the mask
to the screen with an AND operator, then you copy the XOR-mask with an XOR

Therefore the biHeight field is set to 2 * TIconDirEntry.bHeight and
biSizeImage is set to the size of the AND plus the XOR mask (ofcourse in
bytes). For example, for a 32x32 16 color icon biHeight would be 64 and
biSizeImage 512 + 128.

OK. How do you determine the size of the masks? 

XOR mask: (TIconDirEntry.bWidth * TIconDirEntry.bHeight * biBitCount) / 8
AND mask: (TIconDirEntry.bWidth * TIconDirEntry.bHeight) / 8

The palette data

The bitmap info header is followed by a table with palette entries. The actual
pixel data refer to this table. So when a pixel has color 13, look at palette
entry number 13 to get the actual RGB color for the pixel. At this moment
Windows just supports 2 (monochrome), 16 and 256 colors, so there's always a
palette table.

An entry in the palette table has the following format:

TRGBQuad = record
  rgbBlue: Byte;      (* blue component of color *)
  rgbGreen: Byte;     (* green component of color *)
  rgbRed: Byte;       (* red component of color *)
  rgbReserved: Byte;  (* reserved, 0 *)

For Windows programmers: this record is *not* equivalent to a TColorRef (or
COLORREF). If you forget this, you'll get some odd colored icons :-).

An icon has TIconDirEntry.bColorCount palette entries. So there are
sizeof(TRGBQuad) * TIconDirEntry.bColorCount bytes in the palette.

The monochrome AND mask does _not_ have a palette table. A 1 bit in the AND
mask's pixel data means a _white_ pixel on-screen. A 0 bit means a black pixel.

On the other hand, the pixel data of monochrome icons _are_ preceded by a
palette table.

Pixel data

Now to the real nitty-gritty. Immediately after the palette data, the pixel
data follow. Remember: the pixel data contain both the XOR and AND bits.

There's something odd with the storage of the mask bits: both mask data are
stored in bottom up format. For example: in a 32 x 32 16 color icon the first
32 nibbles belong actually to the last pixel row. The second 32 nibbles form
the last - 1 pixel row. And so on.
Retrieving pixels is a little bit hard. Just keep track of the biBitCount
value. Write different functions for monochrome, 16 and 256 color icons or keep
a running bit mask ready.
In Windows creating an icon from .ICO file is rather easy, call the
CreateDIBitmap function twice (for the AND mask/XOR mask). Then call
GetBitmapBits to retrieve the bitmap data.
Under DOS you have to do a little more: e.g. translation of RGB colors.

[Back to WIN-OS2 SWAG index]  [Back to Main SWAG index]  [Original]