众所周知,GIM是PSP平台的一种标准图片格式,关于GIM格式,这次在这里就不多说了,要把它说清楚也是一项比较浩大的工程。基本上可以把GIM文件看作是一个图片集,可以存放各种形式的图形数据,包括色盘-索引图、真彩图以及下面要说的DXT图。
DXT是S3 Graphics, Ltd.为了他们的Savage 3D加速卡开发的一系列有损图形压缩算法,最早叫做S3TC,包括DXT1, DXT2, DXT3, DXT4, DXT5,后来也统称为DXTn或者DXTC。随着被收录在微软的DirectX 6.0中,DXT逐渐被各软硬件厂商所采用。
下面分别简单讨论一下DXTn的算法:
DXT中,图片是被拆分成一个个4-pixel x 4-pixel的小块(英文称为texel)来看待的,当图片宽度或者高度不足4 pixels时,补足对齐之后再处理。在所有的DXTn中,每个4x4的块,都存放有两个16-bit的ARGB5650的颜色值(C0和C1),以及4x4的2-bit的颜色索引。而在DXT2 – DXT5中,相对于DXT1,每个4x4的块还额外使用了64 bits来存放alpha信息。因此,DXT1中,每个4x4的块用8个字节来表示,而DXT2 - DXT5中,每个4x4的块用16个字节来表示。可以看出,在宽高固定的情况下,DXTn存储的图形数据大小也是固定的。
可以看到,颜色索引使用了2 bits,可以表示4种颜色,因此还需要根据存放的2个颜色值插值出另外两个颜色。通用的公式如下:
C2 = 2 / 3 * C0 + 1 / 3 * C1
C3 = 1 / 3 * C0 + 2 / 3 * C1
公式1
但是,虽然DXT1没有额外保存alpha信息,它本身是支持1-bit的alpha的,这个支持是通过将C3固定为透明色来实现的。根据标准的S3TC算法,判断DXT1中某个块是否含有透明色,是通过比较C0和C1来进行的。
当C0 > C1时,表示没有透明色,C2和C3根据前面的公式1进行插值算出。
当C0 <= C1时,表示存在透明色,此时C3表示透明,C2的插值公式变为:
C2 = 1 / 2 * C0 + 1 / 2 * C1
公式2
要注意的是,这种判断标准并不是一个强制标准,听说有一些dxt压缩软件(如nvidia的dxt tool),为了获得更好的插值效果,是有可能会不按大小顺序来排列C0和C1的,只有在压缩的时候让用户选择是否保存alpha,此时,只有压缩者自己知道是否存在alpha,而无论是微软的DDS还是PSP上的GIM,都不存在这样的标志来指明这个信息。。。orz 不过据我见过的GIM中的DXT1来说,都还是遵循前面的判断标准的。
下面再来说说DXT2和DXT3,DXT2和DXT3的存储结构是一模一样的,唯一不同的地方是,DXT2中存储的颜色值是经过alpha预乘的(premultiplied),而DXT3的没有。以下以DXT3来进行讨论。
DXT3的色盘和索引部分与上面讲述的相同,而且因为DXT3中一定保存alpha信息,所以只需要用到公式1来做插值。DXT3相对DXT1,额外使用了64 bits来保存alpha信息,即一个4x4的4-bit的alpha表,可以表示16级的透明度。每一个alpha分别对应该块中相应的像素点。
对于DXT4和DXT5来说,情况和前面一样,DXT4和DXT5唯一的不同之处也只在于DXT4的颜色值是经过alpha预乘的。下面以DXT5为例。
DXT5和DXT3一样,也额外使用了64 bits来存储alpha信息,但是存储的结构确不尽相同。DXT5中,存放有两个8-bit的alpha值(a0和a1)和4x4的3-bit的alpha索引。同前面一样,因为3 bits可以索引8个alpha值,因此需要插值出另外6个alpha。而这里的插值算法,也分为两种:
当a0 > a1时:
a2 = 6 / 7 * a0 + 1 / 7 * a1
a3 = 5 / 7 * a0 + 2 / 7 * a1
a4 = 4 / 7 * a0 + 3 / 7 * a1
a5 = 3 / 7 * a0 + 4 / 7 * a1
a6 = 2 / 7 * a0 + 5 / 7 * a1
a7 = 1 / 7 * a0 + 6 / 7 * a1
当a0 <= a1时:
a2 = 4 / 5 * a0 + 1 / 5 * a1
a3 = 3 / 5 * a0 + 2 / 5 * a1
a4 = 2 / 5 * a0 + 3 / 5 * a1
a5 = 1 / 5 * a0 + 4 / 5 * a1
a6 = 0
a7 = 255
实际应用中,DXT1、DXT3和DXT5使用较多,DXT2和DXT4甚少被使用。而在PSP的GIM中,只存在DXT1、DXT3和DXT5(类型编码分别是8, 9, 10),不支持DXT2和DXT4。P.S. GIM中还支持三种所谓的DXT1Ext、DXT3Ext和DXT5Ext的格式(类型编码分别是264, 265, 266),因为没有找到相关资料,所以在这里就不进行讨论了。
下面举几个例子来看看GIM中的DXT1和DXT3。(DXT5的GIM还没见过,不过应该和DXT3差别不大,alpha存储结构不一样罢了)
DXT1:
来看这样一组数据:56 7E 78 F8 A2 99 A0 60 3F BF B7 95 41 71 60 58
这是从某个GIM中提取出来的DXT数据,因为是DXT1,所以这里其实是两个4x4块的数据,下面以前面的一个块来分析。
a.56 7E 78 F8 b.A2 99 c.A0 60
数据包括三个部分,其中,a是颜色索引,2 bits一个像素,共4x4个像素;b和c是两个颜色值,即C0和C1。
索引部分,其二进制数据,2 bits为一组,如下:
01 01 01 10 01 11 11 10 01 11 10 00 11 11 10 00
根据以上数据可以得到该图块各像素的颜色索引信息,如下:
|
01 |
01 |
01 |
10 |
|
01 |
11 |
11 |
10 |
|
01 |
11 |
10 |
00 |
|
11 |
11 |
10 |
00 |
颜色值部分,C0和C1分别为0x99A2和0x60A0,其二进制数据,16 bits一组,按ARGB5650划分为:
C0:10011 001101 00010
C1:01100 000101 00000
C0的R、G、B分别为19、13、2,映射到256色系里分别是156、53、16
C1的R、G、B分别为12、5、0,映射到256色系里分别是99、20、0
因为此处C0 > C1,因此使用256色系里的C0和C1利用公式1分别对R、G、B三色进行插值,可得到:
C2的R、G、B分别为(256色系) 137、42、11
C3的R、G、B分别为(256色系) 118、31、5
色盘如下:
|
00 |
01 |
10 |
11 |
通过索色,便可得到图块的最终显示结果:
|
01 |
01 |
01 |
10 |
|
01 |
11 |
11 |
10 |
|
01 |
11 |
10 |
00 |
|
11 |
11 |
10 |
00 |
DXT3:
来看这样一组数据:55 55 55 55 E0 FF DF FF 70 29 60 EF 00 FC 00 F7
这是从某个GIM中提取出来的DXT数据,因为是DXT3,所以这里是一个4x4块的数据。
前面8个字节55 55 55 55 E0 FF DF FF,与前面描述的DXT1的格式是一样的,此处就不再重复说明了。最终的显示结果为:
|
01 |
01 |
01 |
01 |
|
01 |
01 |
01 |
01 |
|
01 |
01 |
01 |
01 |
|
01 |
01 |
01 |
01 |
其实,这个GIM文件中的DXT数据,每个4x4的块,前8个字节都是一样的,那么难道整幅图就是一片单色吗?当然不是,别忘了还有后面8个字节存储着alpha信息呢,对,这幅图完全是依靠alpha来进行显示的,必须混合alpha,然后配合背景色才能看到最终的效果。
alpha信息是4x4的4-bit数据,得到图块的alpha信息:
|
7 |
0 |
2 |
9 |
|
6 |
0 |
14 |
15 |
|
0 |
0 |
15 |
12 |
|
0 |
0 |
15 |
7 |
因为是4-bit的16级alpha,所以要在真彩环境中显示的话,还要映射为256级的值,然后带入到图块中和前景色、背景色做混合就能看到最终结果了,此处过程省略,直接给出背景色为黑色时的最终结果(近似):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

#1