Icon合并(1) - 文件结构

要合并icon,就要明白ICON文件的格式。

首先是文件头。由于一个ico文件可以包含多个icon,为了表示的方便,就在文件头部用了一个目录结构 ICONDIR:

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
} ICONDIR, *LPICONDIR

前三项分别是保留区,类型标识和icon个数。第四项开始就是一个ICONDIRENTRY的数组,每个数组元素将对应一个icon。当然,icon数据不是存储在这个ICONDIRENTRY里面的。下面就是ICONDIRENTRY的结构:

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;

可见,在这个ICONDIRENTRY中定义了icon的一些基本信息,dwImageOffset指向真正的icon数据,它标记了icon数据在此图标文件中的偏移位置。icon数据区的大小记录在dwBytesInRes中。

那么,icon图像数据又是怎么存储的呢?它由下面的结构表示,ICONIMAGE :

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;

每个icon图像有一个和bmp文件一样的DIB头,有一张颜色表,然后就是图像数据的掩码。再来一个个分析这些结构,下面是 BITMAPINFOHEADER:

typedef struct tagBITMAPINFOHEADER{
    DWORD  biSize;
    LONG      biWidth;
    LONG      biHeight;
    WORD     biPlanes;
    WORD     biBitCount;
    DWORD   biCompression;
    DWORD   biSizeImage;
    LONG      biXPelsPerMeter;
    LONG      biYPelsPerMeter;
    DWORD  biClrUsed;
    DWORD  biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER; 

在icon数据中的BITMAPINFOHEADER只用到了其中的 biSize,biWidth,biHeight,biPlanes,biBitCount,biSizeImage,其它的成员必须赋值为0。其中 biSize表示了该结构体需要的字节数,一般就是40(但也有例外,可能采用BITMAPV4HEADER或者BITMAPV5HEADER)。 biHeight在这里是像素高度的两倍,因为是XOR掩码区和AND掩码区高度之和。biBitCount取值范围是 0,1,4,8,16,24,32。我处理256色的icon,这里取值就是8。biSizeImage表示掩码数据区的大小(XOR和AND)。有了BITMAPINFOHEADER信息,就可以定位颜色表,地址为

icColors= ((LPSTR)pIconImage + (WORD)(pIconImage ->icHeader.biSize))

然后再来看颜色表的格式,RGBQUAD:

typedef struct tagRGBQUAD {
    BYTE rgbBlue;
    BYTE rgbGreen;
    BYTE rgbRed;
    BYTE rgbReserved;
} RGBQUAD; 

颜色表的大小取决于icon文件中所用到的颜色数(icHeader.biBitCount),对于24位或者32位色的图像就不需要用到颜色表了。

定位了颜色表并且知道了颜色表的大小就可以算出XOR掩码和AND掩码的地址。掩码的应用规则如下:

AND掩码 XOR掩码 规则
0 n 显示icColors[n]指定的颜色
1 0 透明
1 非0 屏幕反色

一个完整的icon文件结构如下:

--------------------Begin Of File
IconDir
    idReserved
    idType
    idCount
    idEntries[0] //ICONDIRENTRY
        bWidth
        bHeight
        bColorCount
        bReserved
        wPlanes
        wBitCount
        dwBytesInRes
        dwImageOffset
    ...
    ...
    idEntries[idCount-1] //ICONDIRENTRY
        bWidth
        bHeight
        bColorCount
        bReserved
        wPlanes
        wBitCount
        dwBytesInRes
        dwImageOffset
--------------------IconDir结束,图片数据开头
IconImage 1
    icHeader
        biSize
        biWidth
        biHeight
        biPlanes
        biBitCount
        biCompression
        biSizeImage
        biXPelsPerMeter
        biYPelsPerMeter
        biClrUsed
        biClrImportant
    icColors[256]   //假设是32x32的256色的icon
    icXOR[1024] //32x32
    icAND[128]  //32x32/8每个像素之要1bit
...
...
IconImage idCount-1
    icHeader
        biSize
        biWidth
        biHeight
        biPlanes
        biBitCount
        biCompression
        biSizeImage
        biXPelsPerMeter
        biYPelsPerMeter
        biClrUsed
        biClrImportant
    icColors[256]   //假设是32x32的256色的icon
    icXOR[1024] //32x32
    icAND[128]  //32x32/8每个像素之要1bit
-------------------End Of File

(to be continued)

Comments