因为编写一个图标编辑程序,分析了一下图标的文件格式,颇有一些心得,写出
来与各位兄弟共享。(笔者注:以下所说的图标均为调色板模式的图标,真彩图标会特别注
明)
一、从图标的显示原理说起
每个图标都是由两个单独的位图组成的。如果该图标是屏蔽背景色的话,那么,第一个
位图是由黑色背景(相关的颜色位全为 0)与彩色图标图案组成的,该位图将与当前屏幕显
示通过异或操作(XOR)结合起来,故称其为XOR位图;第二个位图是由白色背景(相关的颜
色位全为1)与黑色图标图案(相关的颜色位全为0)组成的,该位图将与当前屏幕显示通过
与操作(AND)结合起来,故称其为AND位图。因此,图标的显示是通过两个步骤完成的:
1.当前屏幕显示与AND位图通过AND操作结合起来;
2.当前屏幕显示与XOR位图通过XOR操作结合起来。
大家知道,1与任何数值AND操作的结果将维持原数值,而0与任何数值AND操作的结果则
是0,因此在步骤1中,AND位图中的白色(1)与屏幕显示经过AND操作后被原色彩屏蔽,而黑
色(0)则将原色彩屏蔽。步骤1结束后,屏幕上将留下一个黑色的图标图案。在随后的步骤
2中,由于0与任何数值异或的结果都将是原数值,因此,XOR位图与屏幕显示经过异或操作后,
位图和屏幕中的黑色部分都将被各自对应的彩色部分屏蔽。步骤2结束后,一个形状不规则的
图标图案就出现在屏幕上了,这就是图标显示的原理。
二、图标的图形
图标的图形,实际上就是位图格式的图片,是一个标准的位图格式。Windows只要从BMP
信息头中获取信息就足够了,它据以解释在其后出现的数据应该如何处理。如果是调色板模
式,其后的数据包含有调色板和像素点颜色索引,如果是真彩色,其后的数据直接就是像素
点的 RGB 颜色值。
而位图文件是由文件头、BMP 信息头、调色板、数据区(又称位图点阵)等几个部分组
成的。知道了这个情况,我们可以简单地把上面叙述的图标图形结构理解为位图信息。这样,
我们就有可能根本不需要真的去画一幅图,而只需要对关键数据进行程序填充就可以了,所
编写的代码,直接按图标格式的要求,可把一个只要尺寸不大于255×255像素的任意位图,
封装成标准格式的图标(真实的位图宽高尺寸保持不变,所以可以做出最大255×255的图标
来)。
三、图标的数据结构
图标文件的数据结构可分为6部分,其数据结构如下:
-----------------------------------------------------------------------
顺号 名称 长度(单位:字节) 说明
-----------------------------------------------------------------------
1 文件头 6
2 图象信息块 16 有多少个图像,则信息块也有多少个
3 BMP 信息头 40
4 XOR 位图调色板 16色=64,256色=1024 真彩图标从此开始像素点的 RGB 值
5 XOR 位图 以调色板的索引值形式存放
6 AND位图
-----------------------------------------------------------------------
说明:
1.上表中的3-6部分合称图像数据块,其中5、6部分又合称为图像数据区(也称位图点阵)
2.不论何种规格的图标,其1-3部分的字节长度是固定的。
3.请注意第 2部分的图象信息块的说明,“如果有多少个图像”是什么意思呢?原来,在一
个图标文件中,有可能存放几个图像(windows图标最多可有8个图像),但每个图像都有
自己的调色板、XOR 位图和 AND 位图,且它们的图像大小也可能不相同。以16色有3个图
像为例,其数据结构如下:
-----------------------------------------------------------------
顺号 名称 长度 说明
-----------------------------------------------------------------
1 文件头 6
2 图象1的图象信息块 16
3 图象2的图象信息块 16
4 图象3的图象信息块 16
5 图象1的图象数据块 不定 其中BMP信息头和调色板固定为40和64字节
6 图象2的图象数据块 不定 同上
7 图象3的图象数据块 不定 同上
------------------------------------------------------------------
下面以内含1个图像、16色、16×16规格的图标为例详述(偏移量括号前为10进制,括
号内为16进制):
----------------------------------------------------------------------------
偏移量 字节数 描述 典型值(16进制)
----------------------------------------------------------------------------
(一、文件头6字节)
000(000)2 保留的字节 00 00
002(002)2 资源类型 01 00 (01为图标,02为光标)
004(004)2 图象个数 01 00
(二、图像信息块16字节)
006(006)1 图标宽度 10
007(007)1 图标高度 10
008(008)1 颜色计数 10(02=单色, 00≥256色)
009(009)1 未用 00
010(00A)4 保留的 00 00 00 00
014(00E)4 图象数据块的长度 28 01 00 00(10进制=296)
018(012)4 图象数据块相对于文件头部的偏移量 16 00 00 00(10进制=22)
(三、BMP信息头40字节)
022(016)4 BMP 信息头结构长度 28 00 00 00(10进制=40)
026(01A)4 图像宽度 10 00 00 00
030(01E)4 图像高度(XOR图高度+AND图高度) 20 00 00 00
034(022)2 位面板数 01 00
036(024)2 每象素所占位数 04 00
038(026)4 象素数据的压缩类型 00 00 00 00(表示未压缩)
042(02A)4 图象数据的长度 C0 00 00 00(10进制=192)
046(02E)16 未用 16个00
(四、XOR位图的调色板64字节)
062(03E)1 蓝色分量
063(03F)1 绿色分量
064(040)1 红色分量
065(041)1 未用 00
……
(五、XOR位图的数据)
126(07E)128 XOR位图
(六、单色AND位图的数据)
254(0FE)64 AND位图
-----------------------------------------------------------------------------
说明:
1.上表中有关长度的数据,遵循“低位在前,高位在后”的原则。例如偏移量为00E的图象数
据块的长度是28 01 00 00,在计算时要把它们反过来,变成00 00 01 28,实际就是16进
制的128即10进制的296。
2.偏移量为024的“每象素所占位数”是指每个象素在XOR位图中所占的位数(1个字节=8位)
,04表示每个象素占4位,换言之,就是说XOR位图中每个字节可表示2个象素。该数据如为
01 00,则表示单色(黑白位图),04 00为16色,08 00为256色,18 00为24位真彩。
3.XOR位图每字节对应2个像素(16色)或1个像素(256色),它的排列规律是:倒向(最末行的
像素在最前,由此类推),行内像素按从左至右的顺序,字节高位表示靠左的像素。AND位
图每字节对应8个象素(每位对应1个象素),排列规律与XOR位图相同。
4.真彩图标没有调色板,而是在第 3部分 BMP 信息头的后面直接存放像素的 RGB 值,24位
真彩图标的每 3个字节对应1个像素值,其中每个字节分别对应该像素值的R、G、B值。像
素排列规律与调色板模式的图标相同
下面是各种图标的调色板、图像数据的长度及总长度,括号内是偏移量:
单位:字节
------------------------------------------------------
图标规格 调色板 XOR位图 AND位图 总长度
------------------------------------------------------
16 色16×16象素 (62) 64 (126) 128 (254) 64 318
16 色24×24象素 (62) 64 (126) 288 (414) 96 510
16 色32×32象素 (62) 64 (126) 512 (638) 128 766
16 色48×48象素 (62) 64 (126) 1152 (1278)384 1662
256色16×16象素 (62)1024 (1086) 256 (1342) 64 1406
256色24×24象素 (62)1024 (1086) 576 (1662) 96 1758
256色32×32象素 (62)1024 (1086)1024 (2110)128 2238
256色48×48象素 (62)1024 (1086)2304 (3390)384 3774
------------------------------------------------------
说明:
1.XOR位图每字节对应2个像素(16色)或1个像素(256色)
2.AND位图每4字节为一组(48×48图标的AND位图每8个字节为一组),组内每个颜色位对
应1个象素。注意:
①16×16图标每组的后2个字节不用,所以实际上是2字节(16位)对应一行16个象素
②24×24图标每组的最后1个字节不用,所以实际上是3字节(24位)对应一行24个象素
③32×32图标每组4个字节32位对应一行32个象素
④48×48图标每组的后2个字节不用,所以实际上是6字节(48位)对应一行48个象素
3.为什么AND位图中会有不用的字节呢,这不是浪费吗?原来,Windows有一个规定,每一条
扫描线(位图中的一行数据信息叫做一条扫描线)必须结束于一个32位的边界。也就是说,
一条扫描线的位长度(按位计算)必须能整除32,或字节长度必须能整除4,也就是说,如
果只有8位,那么拿空白的24位来补充,如果只有48位,那么也就拿空白的16位来补充。
4.要使背景透明,XOR位图的背景必须为黑色(对应的颜色位=0),图案为彩色,而 AND位
图则背景必须为白色(对应的颜色位=1),图案为黑色(对应的颜色位=0)
5.XOR位图的彩色图形信息中存储的并不是颜色值,而是与调色板对应的索引值,从 0 开始
编号。
四、图标的颜色
调色板模式的图标,其颜色是由调色板确定的,而调色板是可以自行定义的。比如16色
图标,其调色板只要有16种颜色就行。下面是以 QBasic 的16种颜色作为调色板时,XOR 位
图调色板的16色索引值与 QBColor 的颜色值对照:
----------------------------------------------------
颜色 索引值 QBasic 调色板中的值(最后一个字节未用)
----------------------------------------------------
黑色 0 0 00 00 00 00
深红 1 4 00 00 80 00
深绿 2 2 00 80 00 00
深黄 3 6 00 80 80 00
深蓝 4 1 80 00 00 00
深紫 5 5 80 00 80 00
深青 6 3 80 80 00 00
深灰 7 8 80 80 80 00
浅灰 8 7 C0 C0 C0 00
红色 9 C(12) 00 00 FF 00
绿色 A(10) A(10) 00 FF 00 00
黄色 B(11) E(14) 00 FF FF 00
蓝色 C(12) 9 FF 00 00 00
紫色 D(13) D(13) FF 00 FF 00
青色 E(14) B(11) FF FF 00 00
白色 F(15) F(15) FF FF FF 00
----------------------------------------------------
怎么个对应法呢?举个例来说,比如 XOR位图中某个字节值为81,那就表示该字节所对应的2个象素,左边的象素为浅灰色而右边的象素为深红色。
五、一个具体的例子
有一个16色16×16象素的图标,该图标是一个红色的大“口”字形,其全部数据如下:
------------------------------------------------------
偏移量 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
------------------------------------------------------
000 00 00 01 00 01 00 10 10 10 00 00 00 00 00 28 01
010 00 00 16 00 00 00 28 00 00 00 10 00 00 00 20 00
020 00 00 01 00 04 00 00 00 00 00 C0 00 00 00 00 00
030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
040 00 00 00 00 80 00 00 80 00 00 00 80 80 00 80 00
050 00 00 80 00 80 00 80 80 00 00 80 80 80 00 C0 C0
060 C0 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00
070 00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 99 99
080 99 99 99 99 99 99 90 00 00 00 00 00 00 09 90 00
090 00 00 00 00 00 09 90 00 00 00 00 00 00 09 90 00
0A0 00 00 00 00 00 09 90 00 00 00 00 00 00 09 90 00
0B0 00 00 00 00 00 09 90 00 00 00 00 00 00 09 90 00
0C0 00 00 00 00 00 09 90 00 00 00 00 00 00 09 90 00
0D0 00 00 00 00 00 09 90 00 00 00 00 00 00 09 90 00
0E0 00 00 00 00 00 09 90 00 00 00 00 00 00 09 90 00
0F0 00 00 00 00 00 09 99 99 99 99 99 99 99 99 00 00
100 00 00 7F FE 00 00 7F FE 00 00 7F FE 00 00 7F FE
110 00 00 7F FE 00 00 7F FE 00 00 7F FE 00 00 7F FE
120 00 00 7F FE 00 00 7F FE 00 00 7F FE 00 00 7F FE
130 00 00 7F FE 00 00 7F FE 00 00 00 00 00 00
------------------------------------------------------
下面对位图部分的数据加以说明:
1.XOR位图最末一行的数据在07E-085这8个字节中,由于是一条红线,所以索引值均为9
2.XOR位图倒数第二行的数据在086-08D这8个字节中,该行的首尾2个象素为红色,其余14个
象素为背景色,所以086字节所对应象素的颜色索引值分别为9、0,而08D字节所对应象素
的颜色索引值分别为0、9,因为要屏蔽背景色,所以除了红色外,其余均为黑色,黑色在
调色板中的索引值为0。其余行的数据也照此分析。
3.AND位图最末一行的数据在0FE-101这4个字节中,后2个字节没用,前2个字节为00 00,
因为要屏蔽背景色,AND位图的图象位必须为0(黑色),而不管这图象原来是什么颜色,
所以,这一行是红线,相应的颜色位就全是0了。
4.AND位图倒数第二行的数据在102-105这 4个字节中,前2个字节对应该行的16个象素,其
值为7F FE。先看7F,其二进制为 01111111,因为该行首个象素为红色,所以对应的最高
位为0(黑色),跟着的7个象素都是背景色,而在AND位图中,要屏蔽背景色,那么背景色
的对应位应该是白色,白色在单色中表示为1,所以这8个象素所表示的16进制值就是7F了。
再看FE,其二进制值为 11111110,它对应的该行后8个象素,而该行最后1个象素是红色,
所以对应的最低位为0(黑色),其它象素是背景色,所以对应的位都是1(白色)。
5.其它行的数据,诸位兄弟可以比照上述的方法自行分析。
============================对上面进行数据结构定义====================================================
//2字节对齐
#pragma pack (2)
typedef struct ICON_DIR_ENTRY // 16字节长度
{
BYTE bWidth; //宽度
BYTE bHeight; //高度
BYTE bColorCount; //颜色数
BYTE bReserved; //保留字,必须为0
WORD wPlanes; //调色板
WORD wBitCount; //每个像素的位数
DWORD dwBytesInRes; //资源长度,即图象数据块的长度
DWORD dwImageOffset; //资源在文件偏移,即图象数据块相对于文件头部的偏移量
}ICON_DIR_ENTRY, *PICON_DIR_ENTRY;
struct PE_ICON_DIR_ENTRY
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
WORD wID; //图标ID
};
typedef struct ICON_FILE_HEADER //文件头6字节
{
WORD idreserved; //ICO文件头,保留的字节
WORD idtype; //资源类型,(01为图标,02为光标)
WORD idcount; //图象个数,Windows图标中最多有8个
}ICON_FILE_HEADER, *PICON_FILE_HEADER;
#pragma pack()