站内搜索: 请输入搜索关键词

当前页面: 开发资料首页J2ME 专题图片压缩[比市面上的压缩省30%以上]

图片压缩[比市面上的压缩省30%以上]

摘要: 图片压缩[比市面上的压缩省30%以上]
<tr><td>
http:///tech/article955.html
[转贴自迷你手游]
[]
写在前面的话
这里仅仅作为讨论,具体的实现各位朋友可以自行去实现,我这里提供一个思路,也是我目前我实现了的,经过实践验证的。
预备知识


  PNG格式
 
      PNG是20世纪90年代中期开始开发的图像文件存储格式,其目的是企图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。流式网络图形格式(Portable Network Graphic Format,PNG)名称来源于非官方的“PNG's Not GIF”,是一种位图文件(bitmap file)存储格式,读成“ping”。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。PNG使用从LZ77派生的无损数据压缩算法。
PNG文件格式保留GIF文件格式的下列特性:

[]使用彩色查找表或者叫做调色板可支持256种颜色的彩色图像。
流式读/写性能(streamability):图像文件格式允许连续读出和写入图像数据,这个特性很适合于在通信过程中生成和显示图像。
逐次逼近显示(progressive display):这种特性可使在通信链路上传输图像文件的同时就在终端上显示图像,把整个轮廓显示出来之后逐步显示图像的细节,也就是先用低分辨率显示图像,然后逐步提高它的分辨率。
透明性(transparency):这个性能可使图像中某些部分不显示出来,用来创建一些有特色的图像。
辅助信息(ancillary information):这个特性可用来在图像文件中存储一些文本注释信息。
独立于计算机软硬件环境。
使用无损压缩。
PNG文件格式中要增加下列GIF文件格式所没有的特性:

每个像素为48位的真彩色图像。
每个像素为16位的灰度图像。
可为灰度图和真彩色图添加α通道。
添加图像的γ信息。
使用循环冗余码(cyclic redundancy code,CRC)检测损害的文件。
加快图像显示的逐次逼近显示方式。
标准的读/写工具包。
可在一个文件中存储多幅图像。
文件结构

PNG图像格式文件(或者称为数据流)由一个8字节的PNG文件署名(PNG file signature)域和按照特定结构组织的3个以上的数据块(chunk)组成。

PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。

(1) PNG文件署名域

8字节的PNG文件署名域用来识别该文件是不是PNG文件。该域的值是:

十进制数 137 80 78 71 13 10 26 10
十六进制数 89 50 4e 47 0d 0a 1a 0a

 

(2) 数据块的结构

每个数据块都由表6-07所示的的4个域组成。

[]表6-07 PNG文件数据块的结构

[]名称
字节数
说明

Length(长度) 4字节 指定数据块中数据域的长度,其长度不超过
(231-1)字节
Chunk Type Code(数据块类型码) 4字节 数据块类型码由ASCII字母(A-Z和a-z)组成
Chunk Data(数据块数据) 可变长度 存储按照Chunk Type Code指定的数据
CRC(循环冗余检测) 4字节 存储用来检测是否有错误的循环冗余码

 

在表6-07中,CRC(cyclic redundancy check)域中的值是对Chunk Type Code域和Chunk Data域中的数据进行计算得到的。CRC具体算法定义在ISO 3309和ITU-T V.42中,其值按下面的CRC码生成多项式进行计算:
[]
[]x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1

数据块结构

1. 关键数据块

关键数据块中的4个标准数据块是:
[]
(1) 文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。

文件头数据块由13字节组成,它的格式如表6-08所示。
[]
表6-08 PNG文件头键数据块的结构

域的名称
[] 字节数
说明

Width 4 bytes 图像宽度,以像素为单位
[]Height 4 bytes 图像高度,以像素为单位
Bit depth 1 byte 图像深度:
[]索引彩色图像:1,2,4或8
灰度图像:1,2,4,8或16
真彩色图像:8或16
ColorType 1 byte 颜色类型:
0:灰度图像, 1,2,4,8或16
2:真彩色图像,8或16
[]3:索引彩色图像,1,2,4或8
4:带α通道数据的灰度图像,8或16
6:带α通道数据的真彩色图像,8或16

[]Compression method 1 byte 压缩方法(LZ77派生算法)
Filter method 1 byte 滤波器方法
Interlace method 1 byte 隔行扫描方法:
[]0:非隔行扫描
[]
1: Adam7(由Adam M. Costello开发的7
遍隔行扫描方法)


 

(2) 调色板数据块PLTE(palette chunk):它包含有与索引彩色图像((indexed-color image))相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。真彩色的PNG数据流也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。调色板数据块结构如表6-09所示。

表6-09 调色板数据块结构
[]
域的名称
字节数
说明

Red 1 byte 0 = 黑,255 = 红
[]Green ">
0 = 黑,255 = 绿

Blue 1 byte 0 = 黑,255 = 蓝

 

调色板实际是一个彩色索引查找表,它的表项数目可以是1~256中的一个数,每个表项有3字节,因此调色板数据块所包含的最大字节数为768。

(3) 图像数据块IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。

(4) 图像结束数据IEND(image trailer chunk):它用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部。

除了表示数据块开始的IHDR必须放在最前面, 表示PNG文件结束的IEND数据块放在最后面之外,其他数据块的存放顺序没有限制。

2. 辅助数据块

PNG文件格式规范制定的10个辅助数据块是:

(1) 背景颜色数据块bKGD(background color)。

(2) 基色和白色度数据块cHRM(primary chromaticities and white point)。所谓白色度是指当R=G=B=最大值时在显示器上产生的白色度。

(3) 图像γ数据块gAMA(image gamma)。

(4) 图像直方图数据块hIST(image histogram)。

(5) 物理像素尺寸数据块pHYs(physical pixel dimensions)。

(6) 样本有效位数据块sBIT(significant bits)。

(7) 文本信息数据块tEXt(textual data)。

(8) 图像最后修改时间数据块tIME (image last-modification time)。
[]
(9) 图像透明数据块tRNS (transparency)。

(10) 压缩文本数据块zTXt (compressed textual data)。
[]
3. 数据块摘要

关键数据块、辅助数据块和专用公共数据块(special-purpose public chunks)综合在表6-10中。

表6-10 PNG文件格式中的数据块

[]数据块符号
数据块名称
[] 多数据块
可选否
位置限制
[]
IHDR 文件头数据块 否

第一块
cHRM 基色和白色点数据块 否

在PLTE和IDAT之前
gAMA 图像γ数据块 否

在PLTE和IDAT之前
sBIT 样本有效位数据块 否

在PLTE和IDAT之前
PLTE 调色板数据块 否

在IDAT之前
bKGD 背景颜色数据块 否

在PLTE之后IDAT之前
hIST 图像直方图数据块 否

在PLTE之后IDAT之前
tRNS 图像透明数据块 否

在PLTE之后IDAT之前
oFFs (专用公共数据块) 否

[] 在IDAT之前
pHYs 物理像素尺寸数据块 否
[] 是
在IDAT之前
sCAL (专用公共数据块) 否

[] 在IDAT之前
IDAT 图像数据块 是

与其他IDAT连续
[]tIME 图像最后修改时间数据块 否

无限制
tEXt 文本信息数据块 是

无限制
zTXt 压缩文本数据块 是
[] 是
无限制
fRAc (专用公共数据块) 是

无限制
gIFg (专用公共数据块) 是

无限制
gIFt (专用公共数据块) 是

无限制
gIFx (专用公共数据块) 是

无限制
IEND 图像结束数据 否

最后一个数据块

 

tEXt和zTXt数据块中的标准关键字:

Title
图像名称或者标题

Author
图像作者名

Description
图像说明

Copyright
版权声明

CreationTime
原图创作时间

Software
创作图像使用的软件

Disclaimer
[] 弃权

Warning
图像内容警告

Source
创作图像使用的设备

Comment
各种注释



目前流行的图片处理方式
1,使用工具对图片进行优化

www.minisoyo.com/forum/thread.jspa?threadID=234&tstart=25

通过以上的link,使用Xatio来对图片进行图片优化是一个很不错的选择,主要是对bit depth等参数进行优化。这是一种比较基本对图片处理的方式。我想各位朋友应该都知道这种方法。
2,多张小图片拼成一张大图
这里我用了GameLoft的最新游戏Combat的图片来说明,该帖子可以从
www.minisoyo.com/forum/thread.jspa?threadID=311&tstart=0
[]
[]查看。
这是其中一张图片
540) this.width = 540'>


这样做的好处是多张小图片可以共用一个调色板,共同的描述信息等等,所以图片的大小能节省不少。但是这样就会增加对图片处理的代码量,但是我相信各位朋友对这种处理已经轻车熟路了。
[]3,只存储图片的像素数据。
目前GameLoft公司就是采用这个方式。有心的朋友可能会觉得奇怪,这样存储图片不久会增加文件的大小吗?的确如果只是以象素点的格式存储的话,其数据大小的确大过一个完整的png原始图片。但是png是经过压缩的,而只存储图片的象素点数据的文件是没有经过压缩的,当打成jar的时候,就会被压缩,这时候大小就能区别出来了。
4,图片压缩的另一个思路。
这也就是这次我想说的一种图片压缩的思路,不能说新思路,而是我个人经验和个人实践得出来的。有什么不对的地方欢迎大家讨论。
想上面说到的第2点--多张图片拼成一张大图,我们可以不可以做一个这样大胆的假设,我们能不能把一个游戏中用到所有用到图片都拼成一张大图呢。这时候应该有朋友会说这是不可能的,因为Kjava中的Image.createImage(int width, int height)这个方法中的width
和height只能在一定范围内,不然就会报内存溢出,例如40中超过两百多*两百多就会导致内存溢出。
但是是不是就这样就能阻碍我们的这种想法呢。答案当然是否定的了。在这里我在罗嗦以下,多张小图拼成一张大图最关键的地方是公用了
一个调色板。那么只要我们只要自己构建一个调色板,然后所有游戏用到的图片的象素点都根据这个自制调色板作索引,那么实质上就是公用一个调色板了,现在需要存储图片(所有)信息的结构就变为

[]调色板 | 第一张图片的索引表 | 第二章图片的索引表 | ...

这样我们就很清晰的看出来,这就是多张小图片拼成一张大图片原理的反映。然后我们把这些信息通过字节流存到文件中去即可。
这时候朋友会问,这样的文件,这里我们假设存为的文件名字为1.pak,怎么在游戏中用到这个1.pak,也就是解释还原成j2me中可用的数据,也就是第3点钟的图片各个象素点。呵呵,先不要着急,下面我会说明的。

好了,上面说了压缩的一个路径,但是形成以上1.pak的关键是怎么形成这个调色板以及怎么建立每一个图片对这个自制调色板的索引。
这个算法我自己已经实现了一个工具来形成这个1.pak,可以对一个目录下的所有图片形成这个1.pak文件。大概的思路我想结合图片压缩的另一个途径来一起说。

接着上一步的压缩,我们都知道目前很流行的Nokia40和Nokia60,一般来说该设备支持的颜色为4096色,超出这个颜色范围的颜色就会被取舍。
也就是超出这个范围的颜色是没有意义的。所以一开始这个调色板的大小就是4096色。所以在美工绘制图片的时候我们需要给于美工一个确定的调色板(在这里我们假设为4096色),在PS上有说怎么导入一个调色板。这个调色板也可以通过工具来产生。美工根据这个调色板来绘制图片,那么画出来的图就保证在范围之内的,当然各位朋友可以通过图片优化工具来,但是为什么这里不用,还有一个原因,就是要控制美工绘制图片所用的色数。在这里就要考验美工的功底,能用尽可能少的色数绘制出一个游戏所用的图片,而且看起来不错。色数越少,之后的压缩比越高,这是一个优势。

现在结合上一个途径一起来说说怎么形成1.pak这个文件,实际上就是要通过程序来确定每一个图片的在调色板中的索引。这时候有些朋友会问这个调色板4096色是不是会太大,当然不会,这个调色板是一个最小集合,也就是所有图片用到颜色值的一个交集。所以能保证其最简。接下来就是就是建立图片对应调色板索引了,如下图所示:

540) this.width = 540'>

把这些信息存起来就是1.pak文件了。这里面保证有两个优势了,公用一个调色板,而且这个调色板是最简的。第二就是只要保存索引即可,这个可以用byte来记录。

呵呵,以上还没有完。下面还有一个途径,也是一个很有效的图片压缩途径。我们都知道一个象素点的格式如下,这里我用32位色来打个比方,格式如下:

alpha | red | green | blue
-- 8 -- 8 -- 8 -----8 --


我们都知道游戏中很少用到半透明效果,用的是大部分是透明或者不透明,也就是说我们可以一个值来表示透明。有关于实现半透明的,我们也可以用算法来实现
有兴趣的朋友可以看看以下link:

www.minisoyo.com/forum/thread.jspa?messageID=737ˡ

所以我们可以来一个更狠的,我们不要Alpha部分,那么调色板是不是就一下子节省了25%了。呵呵。都是在调色板上做文章。

好了,图片压缩的三个途径大概就过了一下。那么下一个问题就是,这样的压缩是好,但是我们在程序中怎么用,那么就需要我们作一个还原的工作,所以这样的压缩就是牺牲计算和内存为代价的,也就是在游戏初始化的时候对一次性加载,也就是设计模式中的饿汉模式,而不用懒汉模式。而且目前大部门现在的游戏都是采用前者的方式,是为了为了游戏在绘制的时候顺畅点。呵呵,废话了一下。
[]
那么还原的基本流程大概就是这样的。
先读取出调色板信息,注意这里的调色板时没有Alpha部分的,所以我们还需要还原为具有alpha部分的像素。这里我们申明两个变量

public static int TRANSPARENT = 0;
[]
public static int OPAQUE = 15;

读取的调色板值通过这两个值就可以还原出具有alpha部分的像素了。下面举个例子:

[]palette[index] = b + (g << 4) + (r << 8) + (TRANSPARENT << 12);

那么接下来我们就需要读取各个图片在调色板的索引了,然后根据这个索引找到对于得象素点,这个实现很简单,这里我就不多说了。


[]呵呵,基本上讲完了,这时候得到就是GameLoft那种存储格式读取出来的格式了,然后通过Nokia的特有drawPixels来绘制出来。当然也能通过我之前说的自己实现drawPixels来实现通用性。



经过实践证明,以上压缩方式能比一般压缩方法节省30%-40%。图片的公用颜色更多的话,更能节省!。

有兴趣的朋友可以去实现一下,该算法涉及到诸多工具和算法,我这里就不贴出来了。所以这里不用顶了
http:///tech/article955.html
</td></tr></table></td> </tr> <tr> <td background="/pic/split.gif" height=1></td> </tr> <tr> <td class="tdMargin1">
↑返回目录
前一篇: 开发易于移植的J2ME游戏
后一篇: J2ME游戏开发学习之用setClip分割图片