读书人

png图片构造分析与加密解密原理[转]

发布时间: 2012-07-02 17:46:22 作者: rapoo

png图片结构分析与加密解密原理[转]

PNG文件格式分为PNG-24和PNG-8,其最大的区别是PNG-24是用24位来保存一个像素值,是真彩色,而PNG-8是用8位索引值来在调色盘 中索引一个颜色,因为一个索引值的最大上限为2的8次方既128,故调色盘中颜色数最多为128种,所以该文件格式又被叫做PNG-8 128仿色。PNG-24因为其图片容量过大,而且在Nokia和Moto等某些机型上创建图片失败和显示不正确等异常时有发生,有时还会严重拖慢显示速度,故并不常 用,CoCoMo认为这些异常和平台底层的图像解压不无关系。不过该格式最大的优点是可以保存Alpha通道,同事也曾有过利用该图片格式实现Alpha 混合的先例,想来随着技术的发展,手机硬件平台的提升,Alpha混合一定会被广泛的应用,到那时该格式的最大优势才会真正发挥。?

8 bit PNGs use an indexed color palette like GIF. If you want variable transparency, use 32bit PNGs (24 bit color, 8 bit alpha). If you don't care about transparency, use 24 bit PNGs.

?

PNG-8文件是目前广泛应用的PNG图像格式,其主要有六大块组成:

?1.PNG文件标志,为固定的64个字节:0x89504e47 0x0d0a1a0a

?2.文件头数据块IHDR(header chunk)

3.调色板数据块PLTE(palette chunk)

4.sBIT,tRNS块?等。。。

5.图像数据块IDAT(image data chunk)

6.图像结束数据IEND(image trailer chunk),固定的96个字节:0x00000000 0x49454e44 0xae426082

?这六大块按顺序排列,也就是说IDAT块永远是在PLTE块之后,期间也会有许多其他的区块用来描述信息,例如图像的最后修改时间是多少,图像的创建者是谁等,不过这些区块的信息对我们来说都是可有可无的描述信息,故压缩时一般先向这些区块开刀。

?

??数据块1-4:?

除了PNG文件标志,其中四大数据块和文件尾都是由统一的数据块文件结构描述的:????????

??????? Chunk Length: 4byte????????

? ? ? ? Chunk Type:???4byte????????

??????? Chunk Data:???Chunk Length的长度????????

??????? Chunk CRC:??? 4byte

?例如IHDR块的数据长度为13,既????????

?????? Chunk Length = 13 ????????

?????? Chunk Type = "IHDR"

?


IHDR块:

?用来描述图像的基本信息,其格式为:????????

图像宽:??? 4byte???????

图像高:??? 4byte???????

图像色深: 4byte???????

颜色类型:?1byte???????

压缩方法: 1byte???????

滤波方法: 1byte???????

扫描方法: 1byte?

曾经有人问过我,撒叫滤波方法和扫描方法,汗,说实话我也不知道,不过我们是在做手机游戏,不是在搞图形学不是嘛。


PLTE块:?

这个就是传说中放置调色盘数据的地方啦,其格式为:??????

???? 循环???????????

???????? RED:??? 1byte???????????

???????? GREEN:1byte???????????

???????? BLUE:? 1byte??????

???? END

?循环长度嘛,不就是Chunk Length / 3的长度嘛,而且Chunk Length一定为3的倍数。


tRNS块:?

这个块时有时无,主要是看你是否使用了透明色。该区块的格式为:???????

?????? 循环???????????

??????????? if(对应调色盘颜色非透明)????????????????

?????????????????? 0xFF:? 1byte???????????

??????????? else???????????????

?????????????????? 0x00:? 1byte??????

?????? END?

循环长度为调色盘的颜色数,相当于调色盘颜色表的一个对应表,标识该颜色是否透明,0xFF不透明,0x00透明。故如果用UltraEdit查看PNG文件的二进制编码,如果看到一大片FF,一般就是tRNS区块啦,因为一个PNG文件一般只有一个透明色。

?

??IDAT块:?这个就是存放图像数据的地方啦,这里要注意的是一个PNG文件可能有多个IDAT区块,而其他三大区块只可能有一个。?IDAT 区块是经过压缩的,所以数据不可读 ,压缩算法一般为LZ77滑动窗口算法,如果硬要看里面的数据的话,用zlib库也是可以的,CoCoMo当年就见过 Windows Mobile上的帝国时代巨变态的用zlib库压缩和解压该区块来进一步减少PNG文件大小,真是寸K寸金啊。


IEND块:?该区块虽然也按照数据块的结构,但Chunk Data是没有的,所以是固定的96个字节:0x00000000 0x49454e44 0xae426082

IEND数据块的长度总是0(00 00 00 00,除非人为加入信息),数据标识总是IEND(49 45 4E 44),因此,CRC码也总是AE 42 60 82。

?


PNG图像压缩:

??????? 了解了PNG的文件结构,压缩就有的放矢了。压缩有6个级别,可以根据需要选择。
Level1:读取PNG文件,将除六大块之外的所有区块都过滤掉
Level2:文件头是固定的0x89504e47 0x0d0a1a0a,文件尾是固定的0x00000000 0x49454e44 0xae426082,去掉!
Level3:每个区块的Chunk Type我们是否需要呢?很明显,我们自己写的压缩格式自己应该清楚是按照什么样的顺序,去掉!
Level4:每个区块的Chunk Length我们是否需要呢?
?????????? IHDR块:定长13个字节,明显不需要,去掉。
?????????? PLTE块:最多128个颜色,为撒要用4byte来记录区块长度而不是用1byte来记录颜色数呢?
?????????? tRNS块:既然有颜色数,tRNS又是调色盘颜色表的对应表,既数量与颜色数相同,为撒还需要呢?
?????????? IDAT块:我想这个是唯一需要4byte来记录长度的区块。
Level5:每个区块的Chunk CRC是否需要呢?
?????????? 因为计算CRC需要一些时间,但对于字节较少的区块一般可以忽略不计,所以对于这个问题还是由程序员自己决定吧。对于CRC的计算可以参看CoCoMo的另一篇Blog“PNG文件的CRC码计算”
Level6:每个区块我们是否要原封不动的保存期数据呢?
????????? IHDR块:除了宽、高、色深是需要的,后面那4byte的信息是固定的0x03000000
????????? PLTE块:为撒要用3byte来表示RGB而不是2byte的565格式?压缩方法可以参看CoCoMo的另一篇Blog“关于PNG图像压缩的一点感悟”
????????? tRNS块:我想tRNS块是冗余最多的区块了吧,大段大段的0xFF明显没有必要,一般的PNG文件只有一个透明色,为撒要用对应表的方法而不是一个索 引来记录到底哪个是透明色呢?由于颜色数最多128,所以只需1byte就可以代替tRNS那么多0xFF啦。
????????? IDAT块:么想法,如果你够变态,把zlib加进来吧!

PNG图像解压:
????????创建了自定义的文件,J2ME端读取后,就面临解压的问题了。我们可以利用此函数来创建Image:
static?Image
createImage(byte[]?imageData, int?imageOffset, int?imageLength)
???? 前提是传入的imageData与PNG未被压缩前的一致。因为PNG文件格式是固定的,所以读取自定义的压缩文件后,开始将那些默认的数据再添加进去,实现解压的目的。下面就开始解压之旅吧!
首先要创建一个ByteArrayOutputStream out,

1.写入文件头:
out.writeInt(0x89504e47);
out.writeInt(0x0d0a1a0a);

2.写入IHDR块
out.writeInt(13);
out.writeInt(0x49484452);? //0x49484452为Chunk Type "IHDR"
out.writeInt(width);
out.writeInt(height);
out.writeByte(depth);
out.writeInt(0x03000000);? //压缩时舍掉的4byte,默认0x03000000
out.writeInt(crc);
其他区块方法一致,故略过。。。

3.写入文件尾
out.writeInt(0x00000000);
out.writeInt(0x49454e44);
out.writeInt(0xae426082);

4.转换成数组,创建Image
byte[] pngBuffer = out.toByteArray();
Image image = Image.createImage(pngBuffer, 0, pngBuffer.length);

哈哈,大功告成。这里注意如果中途数据写入有错误,经常会出现创建Image失败的异常,而且非常不好调试,不过只要自定的压缩格式定下来后,对应的创建Image的函数只要写一次,以后基本不会出问题哈。

PNG图像加解密:
??????? 很多人都担心自己辛苦创作的漂亮的美术图片很easy就被别人拿到了,究其原因是由于PNG文件格式是固定的,稍微了解的人用UltraEdit很容易就 能找到IHDR,PLTE等标识了。CoCoMo就经常看GameLoft的图像文件,哈哈。一般是2byte的Length,然后紧接着图片数据,都放 在一个文件里,直接拷贝2进制然后粘贴到一个新文件里就是一幅图。后来的加密技术会把PNG分块,例如前100个字节一块,紧接着1K一块,最后剩余字节 一块,然后把块顺序打乱,用2byte来记录总长度,1byte记录顺序,但是这并没有从根本上消除IHDR,IEND这些显眼的定位标识,好像在对破解 者说:嘿,看,我就在这里!
?????? 现在了解了之前的压缩和解压技术,这个问题也就迎刃而解了,因为Chunk Length,Chunk Type和Chunk CRC这些东西都消失了,甚至连数据块本身的数据都修改了,我可以按照ImageWidth、ImageHeight、ImageDepth的顺序写数 据,也可以倒过来写。我想再牛的PNG分析器也是无能为力的吧,唯一可以定位的就只有IDAT区块了,不过就算得到该区块的数据,也应该是一张黑白图。

?

?

-----------------------------------------------------------------

-----------------------------------------------------------------

-----------------------------------------------------------------

?

附录

为了方便大家观看,我们将图像放大:

png图片构造分析与加密解密原理[转]

使用UltraEdit32打开该文件,如下:
00000000~00000007:

png图片构造分析与加密解密原理[转]

可以看到,选中的头8个字节即为PNG文件的标识。

?

?

接下来的地方就是IHDR数据块了:

00000008~00000020:

png图片构造分析与加密解密原理[转]

00 00 00 0D 说明IHDR头块长为1349 48 44 52 IHDR标识00 00 00 08 图像的宽,8像素00 00 00 08 图像的高,8像素04 色深,2^4=16,即这是一个16色的图像(也有可能颜色数不超过16,当然,如果颜色数不超过8,用03表示更合适)03 颜色类型,索引图像00 PNG Spec规定此处总为0(非0值为将来使用更好的压缩方法预留),表示使压缩方法(LZ77派生算法)00 同上00 非隔行扫描 36 21 A3 B8 CRC校验

?

?

00000021~0000002F:

png图片构造分析与加密解密原理[转]

可选数据块sBIT,颜色采样率,RGB都是256(2^8=256)

?

?

00000030~00000062:

png图片构造分析与加密解密原理[转]

这里是调色板信息

00 00 00 27 说明调色板数据长为39字节,既13个颜色数50 4C 54 45 PLTE标识FF FF 00 颜色0FF ED 00 颜色1…… ……09 00 B2 最后一个颜色,125F F5 BB DD CRC校验

?

?

00000063~000000C5:

png图片构造分析与加密解密原理[转]

这部分包含了pHYs、tExt两种类型的数据块共3块,由于并不太重要,因此也不再详细描述了。

?

000000C0~000000F8:

png图片构造分析与加密解密原理[转]

以上选中部分是IDAT数据块

00 00 00 27 数据长为39字节49 44 41 54 IDAT标识78 9C…… 压缩的数据,LZ77派生压缩方—A 12 06 A5 CRC校验

IDAT中压缩数据部分在后面会有详细的介绍。

?

?

000000F9~00000104:

png图片构造分析与加密解密原理[转]

IEND数据块,这部分正如上所说,通常都应该是 00 00 00 00 49 45 4E 44 AE 42 60 82

?

?

至此,我们已经能够从一个PNG文件中识别出各个数据块了。由于PNG中规定除关键数据块外,其它的辅助数据块都为可选部分,因此,有了这个标准后,我们可以通过删除所有的辅助数据块来减少PNG文件的大小。(当然,需要注意的是,PNG格式可以保存图像中的层、文字等信息,一旦删除了这些辅助数据块后,图像将失去原来的可编辑性。)

png图片构造分析与加密解密原理[转]

删除了辅助数据块后的PNG文件,现在文件大小为147字节,原文件大小为261字节,文件大小减少后,并不影响图像的内容。

其实,我们可以通过改变调色板的色值来完成一些又趣的事情,比如说实现云彩/水波的流动效果,实现图像的淡入淡出效果等等,在此,给出一个链接给大家看也许更直接:http://blog.csdn.net/flyingghost/archive/2005/01/13/251110.aspx ,我写此文也就是受此文的启发的。

如上说过,IDAT数据块是使用了LZ77压缩算法生成的,由于受限于手机处理器的能力,因此,如果我们在生成IDAT数据块时仍然使用LZ77压缩算法,将会使效率大打折扣,因此,为了效率,只能使用无压缩的LZ77算法,关于LZ77算法的具体实现,此文不打算深究,如果你对LZ77算法的JAVA实现有兴趣,可以参考以下两个站点:

http://jazzlib.sourceforge.net/ http://www.jcraft.com/jzlib/index.html

?

?

?

(调色板对照图)


至此,我们已经能够采用最快的时间将数组转换为PNG图片了
png图片构造分析与加密解密原理[转]

?

?

参考资料:

PNG文件格式白皮书:http://www.w3.org/TR/REC-png.html
为数不多的中文PNG格式说明:http://dev.gameres.com/Program/Visual/Other/PNGFormat.htm
RFC-1950(ZLIB Compressed Data Format Specification):ftp://ds.internic.net/rfc/rfc1950.txt
RFC-1950(DEFLATE Compressed Data Format Specification):ftp://ds.internic.net/rfc/rfc1951.txt
LZ77算法的JAVA实现:http://jazzlib.sourceforge.net/
LZ77算法的JAVA实现,包括J2ME版本:http://www.jcraft.com/jzlib/index.html

?

?

读书人网 >移动开发

热点推荐