An ICO file starts with a 6-byte header, ICONDIR, and one 16-byte
ICONDIRENTRY for each image. Every number is little-endian. A
cursor file has the same layout, with the hotspot in
place of two of the fields.
00000000 00 00 01 00 01 00 10 10 10 00 01 00 04 00 28 01
00000010 00 00 16 00 00 00
ICONDIR
The first six bytes of the file.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 2 | u16 | idReserved | Reserved. Must be 0. |
| 2 | 2 | u16 | idType | The type of file: see below. |
| 4 | 2 | u16 | idCount | The number of images, and so of directory entries that follow. |
idType | File |
|---|---|
1 | An icon, .ico |
2 | A cursor, .cur |
Any other type is not an icon or cursor file. Together with the two zero bytes before it, the type is the only signature the format has.
ICONDIRENTRY
One entry for each image, straight after ICONDIR, starting at offset 6.
Offsets in this table are from the start of the entry.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 1 | u8 | bWidth | The image’s width in pixels. 0 means 256. |
| 1 | 1 | u8 | bHeight | The image’s height in pixels. 0 means 256. This is the height of the picture and is not doubled. |
| 2 | 1 | u8 | bColorCount | The number of colours in the image’s colour table: see the rule below. |
| 3 | 1 | u8 | bReserved | Reserved. Must be 0. |
| 4 | 2 | u16 | wPlanes | Colour planes. Almost always 1; Wikipedia gives 0 or 1 as the valid values, and a planar image has more. In a cursor this field is wXHotspot. |
| 6 | 2 | u16 | wBitCount | Bits per pixel in each plane, so the image’s depth is wBitCount × wPlanes. It can be 0, which leaves the depth to the image’s own header. In a cursor this field is wYHotspot. |
| 8 | 4 | u32 | dwBytesInRes | The size of the image in bytes. |
| 12 | 4 | u32 | dwImageOffset | The offset of the image from the start of the file. |
bColorCount by depth
The field holds the number of colours in the image. Raymond Chen gives the rule in terms
of both fields: if wBitCount × wPlanes is 8 or more,
bColorCount is 0.
wBitCount × wPlanes | bColorCount |
|---|---|
| 1 | 2 |
| 4 | 16 |
| 8 and above | 0 |
The field is one byte, so 256 colours cannot be written. A common fault is a count of 0 on images of few colours, such as 16-colour ones. Windows XP and later detect that and correct it, though Chen notes the correction is slightly wrong for planar images, which are rare. A wrong count can make Windows choose a poorer image.
The image data usually follows the directory in the order of the entries, but a reader must use the offsets, and nothing requires the images to be contiguous. The image’s own header or PNG, not the directory, gives its true size and depth, so a reader should check the two against each other.
Which image is used
Microsoft’s article on icons gives these rules for choosing an image from a group:
- The image closest in size to the one wanted is chosen.
- Among images of that size, the one matching the display’s colour depth is chosen.
- If none matches, the one with the greatest depth that does not exceed the display’s is chosen.
- If all exceed it, the one with the lowest depth is chosen.
- All depths of 8 bits or more count as equal, so a 256-colour image and a 16-bit image of the same size are interchangeable and the first one found wins.
- On an 8-bit display, a 16-colour image is preferred over a 256-colour one.
Icons inside programs
Windows programs and DLLs (PE and
NE files) hold icons as resources rather than as
.ico files. The directory becomes an RT_GROUP_ICON resource, a
GRPICONDIR, and each image becomes its own RT_ICON resource, with
the same bytes as an image in an ICO file. Only the last field of the entry differs: an
image identifier replaces the file offset, and the entry is 14 bytes, packed on 2-byte
boundaries.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 1 | u8 | bWidth | As in ICONDIRENTRY. |
| 1 | 1 | u8 | bHeight | As in ICONDIRENTRY. |
| 2 | 1 | u8 | bColorCount | As in ICONDIRENTRY. |
| 3 | 1 | u8 | bReserved | Reserved. |
| 4 | 2 | u16 | wPlanes | As in ICONDIRENTRY. |
| 6 | 2 | u16 | wBitCount | As in ICONDIRENTRY. |
| 8 | 4 | u32 | dwBytesInRes | The size of the RT_ICON resource. |
| 12 | 2 | u16 | nID | The identifier of the RT_ICON resource that holds the image. |
The six-byte header is the same as ICONDIR, with idType 1 for
icons. Cursor groups differ a little more.