Abstract
This article describes, in detail, the format and use of icons in 32-bit Windows. The following topics are covered; the format of icon resources in ICO, DLL and EXE files, the format of icon images in memory, Windows' use of icons, Windows' selection of an icon image from an icon resource, and the APIs provided to manipulate icon images. To follow the discussion, the reader should be familiar with Device Independent Bitmaps (DIBs) and their format. For more information about DIBs, please refer to the following sources:
Win32 SDK On-line help for the BITMAPINFO structure
Knowledge Base article Q81498 SAMPLE: DIBs and Their Uses
Knowledge Base article Q94326 SAMPLE: 16 and 32 Bits-Per-Pel Bitmap Formats
Sample code dealing with some of the topics in this article is available in the Win32 SDK sample tree in the IconPro project.
Disclaimer Internal details discussed in this article are subject to change without notice in future versions of Windows.
Introduction
Icons are a varied lot—they come in many sizes and color depths. A single icon resource—an ICO file, or an icon resource in an EXE or DLL file—can contain multiple icon images, each with a different size and/or color depth. Windows 95 and future versions of Windows NT (collectively referred to as "Windows" from here on) extract the appropriate size/color depth image from the resource depending on the context of the icon's use. Windows also provides a collection of APIs for accessing and displaying icons and icon images.
What's in an Icon?
An icon resource can contain multiple icon images. For example, one icon resource—in this case, a single .ICO file—can contain images in several sizes and color depths:
16 x 16 16 colors 32 x 32 16 colors 72 x 72 256 colors
The ICO File
An Icon file, which usually has the ICO extension, contains one icon resource. Given that an icon resource can contain multiple images, it is no surprise that the file begins with an icon directory:
typedef struct
{
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource Type (1 for icons)
WORD idCount; // How many images?
ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
} ICONDIR, *LPICONDIR;
The idCount member indicates how many images are present in the icon resource. The size of the idEntries array is determined by idCount. There exists one ICONDIRENTRY for each icon image in the file, providing details about its location in the file, size and color depth. The ICONDIRENTRY structure is defined as:
typedef struct
{
BYTE bWidth; // Width, in pixels, of the image
BYTE bHeight; // Height, in pixels, of the image
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved ( must be 0)
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // How many bytes in this resource?
DWORD dwImageOffset; // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;
For each ICONDIRENTRY, the file contains an icon image. The dwBytesInRes member indicates the size of the image data. This image data can be found dwImageOffset bytes from the beginning of the file, and is stored in the following format:
typdef struct
{
BITMAPINFOHEADER icHeader; // DIB header
RGBQUAD icColors[1]; // Color table
BYTE icXOR[1]; // DIB bits for XOR mask
BYTE icAND[1]; // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;
The icHeader member has the form of a DIB BITMAPINFOHEADER. Only the following members are used: biSize, biWidth, biHeight, biPlanes, biBitCount, biSizeImage. All other members must be 0. The biHeight member specifies the combined height of the XOR and AND masks. The members of icHeader define the contents and sizes of the other elements of the ICONIMAGE structure in the same way that the BITMAPINFOHEADER structure defines a CF_DIB format DIB.
The icColors member is an array of RGBQUADs. The number of elements in this array is determined by examining the icHeader member.
The icXOR member contains the DIB bits for the XOR mask of the image. The number of bytes in this array is determined by examining the icHeader member. The XOR mask is the color portion of the image and is applied to the destination using the XOR operation after the application of the AND mask.
The icAND member contains the bits for the monochrome AND mask. The number of bytes in this array is determined by examining the icHeader member, and assuming 1bpp. The dimensions of this bitmap must be the same as the dimensions of the XOR mask. The AND mask is applied to the destination using the AND operation, to preserve or remove destination pixels before applying the XOR mask.
Note The biHeight member of the icHeader structure represents the combined height of the XOR and AND masks. Remember to divide this number by two before using it to perform calculations for either of the XOR or AND masks. Also remember that the AND mask is a monochrome DIB, with a color depth of 1 bpp.
The following is an incomplete code fragment for reading an .ICO file:
// We need an ICONDIR to hold the data
pIconDir = malloc( sizeof( ICONDIR ) );
// Read the Reserved word
ReadFile( hFile, &(pIconDir->idReserved), sizeof( WORD ), &dwBytesRead, NULL );
// Read the Type word - make sure it is 1 for icons
ReadFile( hFile, &(pIconDir->idType), sizeof( WORD ), &dwBytesRead, NULL );
// Read the count - how many images in this file?
ReadFile( hFile, &(pIconDir->idCount), sizeof( WORD ), &dwBytesRead, NULL );
// Reallocate IconDir so that idEntries has enough room for idCount elements
pIconDir = realloc( pIconDir, ( sizeof( WORD ) * 3 ) +
( sizeof( ICONDIRENTRY ) * pIconDir->idCount ) );
// Read the ICONDIRENTRY elements
ReadFile( hFile, pIconDir->idEntries, pIconDir->idCount * sizeof(ICONDIRENTRY),
&dwBytesRead, NULL );
// Loop through and read in each image
for(i=0;i<pIconDir->idCount;i++)
{
// Allocate memory to hold the image
pIconImage = malloc( pIconDir->idEntries[i].dwBytesInRes );
// Seek to the location in the file that has the image
SetFilePointer( hFile, pIconDir->idEntries[i].dwImageOffset,
NULL, FILE_BEGIN );
// Read the image data
ReadFile( hFile, pIconImage, pIconDir->idEntries[i].dwBytesInRes,
&dwBytesRead, NULL );
// Here, pIconImage is an ICONIMAGE structure. Party on it :)
// Then, free the associated memory
free( pIconImage );
}
// Clean up the ICONDIR memory
free( pIconDir );
Complete code can be found in the Icons.C module of IconPro, in a function named ReadIconFromICOFile().