ICO文件格式的演化(一):单色图标

原文:http://blogs.msdn.com/b/oldnewthing/archive/2010/10/18/10077133.aspx

这个礼拜我将花时间来说一下ICO文件格式的演化。首先图标资源的格式和图标文件的格式是不同的,这个我改日再说。

ICO文件有一个固定的文件头:

typedef struct ICONDIR {
    WORD          idReserved;
    WORD          idType;
    WORD          idCount;
    ICONDIRENTRY  idEntries[];
} ICONHEADER;

idReserved必须为0,idType必须为1。idCount表示这个图标里有多少个图像。ICO文件其实是由一组图像组成的。理论上来说,每个图像应该是同样的内容,但是不同的大小和颜色深度。当然,如果你要让一个16x16的图像和32x32的图像看起来完全不像,那也是可以的,不过用户可能会觉得很怪。

idCount之后是一个ICONDIRECTORY结构的数组。当然,数组的长度由idCount指定。

struct IconDirectoryEntry {

    BYTE  bWidth;

    BYTE  bHeight;

    BYTE  bColorCount;

    BYTE  bReserved;

    WORD  wPlanes;

    WORD  wBitCount;

    DWORD dwBytesInRes;

    DWORD dwImageOffset;

};

bWidth和bHeight表示了图像的大小。一开始呢,只支持1到255的大小。从Windows95(NT 4.0)开始,用0来表示大小是256。

wBitCount和wPlanes用来描述图像的颜色深度。对单色图标来说,它们都为1。bReserved必须为0。dwBytesInRes和dwImageOffset用来描述实际图像数据的位置(相对于ICO文件的开始)和数据字节数。

然后,还有个悲剧的bColorCount。它被假定的认为等于图像的颜色数量,也就是说:

bColorCount = 1 << (wBitCount * wPlanes)

如果 wBitCount * wPlanes 大于等于8,则bColorCount为0。

在现实情况中,很多人懒得填写bColorCount的值,即使是4色或16色的图标,也把它设为0。从Windows XP开始,Windows会检测这个常见的错误,但是对于planar位图来说,这个自动纠错的机制还是有些问题的。幸运的是,几乎没人使用planar位图了。但是你还是不应该依赖于Windows提供的自动纠错机制,而正确的填写bColorCount的值。错误的bColorCount意味着,由于提供了错误的颜色深度信息,Windows可能会在ico文件中选择一个不怎么好的图像。

我将假定单色图标出现在彩色图标之前,当然这不是事实,只是让故事更好说一些。

单色图标由两个位图组成,分别被称为AND(或mask)和XOR(或image,或者当说到彩色图标的时候,color)。绘制图标分为两个步骤:首先mask和screen作“与”操作,然后和image作“异或”操作。也就是说,

pixel = (screen AND mask) XOR image

为mask和image选择适当的值,可以覆盖所有单色blt的操作。

mask image 结果 操作
0 0 (screen AND 0) XOR 0 = 0 黑色
0 1 (screen AND 0) XOR 1 = 1 白色
1 0 (screen AND 1) XOR 0 = screen 不变
1 1 (screen AND 1) XOR 1 = NOT screen 反色

概念上来说,mask指定了image的像素是否拷贝到屏幕上。mask中黑色的像素表示要将image中相应的像素拷贝到屏幕上。

mask和image位图在物理上是保存为单个但是双倍高度的DIB。首先是image位图,然后是mask。(由于DIB自下而上的保存格式,所以如果你实际的观察一个位图,mask在上面而image在下面)。(译注:bmp文件格式使用数学坐标系,也就是说(0,0)在左下角。bmp文件中的数据最开始的数据是最下面一行像素。)

按照格式,每个图标的图像保存为BITMAPINFO结构的形式(根据其中BITMAPINFOHEADER来决定有没有调色板数据),然后是image的像素数据和mask的像素数据。其中biCompression(译注:BITMAPINFOHEADER中的字段,后面的biWidth和biHeight都是)必须为BI_RGB。由于是双倍高度的位图,所以biWidth为图像的宽度,biHeight为图像高度的两倍。例如:16x16的图标,则宽度为16,高度为16 * 2 = 32。

单色图标就这么多了。下次说彩色图标。

那么,对于下面的故事,你应该可以了解原因了。

一位顾客联系了ms的shell team,说他们随便怎么努力,windows就是不在他们的ico文件中选择一个他们想要的图像。Windows不知怎么,就是使用一个颜色数比较少的图标。例如:在32bpp的显示模式下,Windows总是选择16色(4bpp)的图像,即使有32bpp的图像。

仔细看一下这个ICO文件可以发现,IconDirectoryEntry中的bColorCount都被设置成了1,无论实际图像的颜色深度是什么。这个ICO文件的“目录”说“有一些单色的图像。3个48x48的单色图像,3个32x32的图像,3个16x16的图像” 得到了这些信息,Windows认为“在这些选择中,我应该使用一个单色的图标”。Windows从中随机的选择一个,然后解析到了位图的数据,发现“哦,怎么回事,它实际上是个16色图标。好吧,我仍然能加载它”

总之,这个ICO文件没有被正确的创建。用一个16进制编辑器中修正每个IconDirectoryEntry结构,图标文件总算是如预期得工作了。这个顾客感谢了我们的努力,并说会把这个问题提交给他们的图形设计部门的。

你可能感兴趣的:(文件)