iOS减包实战:Compress PNG Files作用分析

减包这个词大家应该都不陌生,在减包过程中,图片资源的优化这项应该是必经之路了,毕竟在包大小中,图片资源占的比重是很大,而且是可优化空间较大的一项,而在Xcode中有一个build setting就叫做"Compress PNG Files",翻译一下就是“压缩PNG文件”,看上去简直是完美,有木有!

然而,当把这个配置改为YES之后,打包之后发现ipa的大小不但没有减小,反而增大了。说好的Compress呢!!!

这就必须要好好研究下这个"Compress PNG Files"到底是干啥的了!先找到/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/iphoneos-optimize这个脚本,其中有如下一段:

 Xcode就是执行了这条命令来进行图片优化的,command line tools中也自带了这么一个命令行工具,通过这个工具,我们可以看看图片经过所谓的Compress之后发生了什么变化。

以如下这张LUT图为例,为什么是这张图片呢,一是因为这种图片在图像处理的App中特别常见,是用来做滤镜调色时使用的,二是因为这种类型的图片差别特别明显。

从结果上来看,经过这个“优化”之后,图片大小从207KB,变成了750KB,整整大了500多K,为了排除偶然性,又选择了另一张图片处理

结果还是增大了非常多,说好的Compress呢!

接下来就要详细的分析下为什么会出现这种情况了。

首先先简单介绍一下PNG这种图片格式,PNG由文件标志和多个数据块组成,文件标志是固定的如下图所示:

数据块的格式如下:

名称

字节数

说明

Length (长度)

4字节

指定数据块中数据域的长度,其长度不超过(231 -1)字节

Chunk Type Code (数据块类型码)

4字节

数据块类型码由ASCII字母(A-Z和a-z)组成

Chunk Data (数据块数据)

可变长度

存储按照Chunk Type Code指定的数据

CRC (循环冗余检测)

4字节

存储用来检测是否有错误的循环冗余码

PNG中常见的数据块类型如下:

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

图像结束数据

最后一个数据块

还是用之前那张图片作为例子来看,用pngcheck工具看一下文件信息:

这个图片包含了IHDR、tEXt、iTXt、IDAT、IEND等五个数据块,其中比较关键的数据块是IHDR、IDAT、IEND三个,而当我们同pngcheck查看处理之后图片的话,就会出现如下提示:

这里我们就会发现,这里的Compress PNG Files做的并不是单纯的压缩数据,而是把文件格式也做了修改,当我们去查阅相关资料的时候,可以发现其实Apple是将png图片转换成了一种CgBI格式:

These modifications cause the generated images to be invalid as per the current version of the PNG standard.

  • extra critical chunk (CgBI)
  • byteswapped (RGBA -> BGRA) pixel data, presumably for high-speed direct blitting to the framebuffer
  • zlib header, footer, and CRC removed from the IDAT chunk
  • premultiplied alpha (color' = color * alpha / 255)

明显的改动就是在IHDR块之前插入了CgBI块来表示这种格式,同时修改了IDAT块中的数据,原因就是在iPhone中,图像是以BGRA格式在内存中处理的,到这里就可以发现,其实这个所谓的Compress PNG Files,最主要的目的并不是压缩图片的大小,而是将图片转换成iPhone能更方便处理的格式,加快处理速度。

接下来就要解释下为什么LUT这种图片的大小会变化这么大了,首先要看下Compress后的PNG的数据格式,这里通过hexdump可以查询到各个数据块的关键字

这里推荐一个查看CgBI文件数据的命令行工具,pngdefry,这个工具可以用来查看CgBI文件信息,同时可以用来将CgBI文件还原成png文件,当然这里还原之后的png文件和原始文件还是会有区别的,这里后续会有提到。

可以发现,这处理增加了CgBI数据块之外,还增加了一个iDot数据块,这里数据块是Apple自定义的数据块,在网上也没有找到相关的文档,所以暂时无法分析其作用,变化最大的就是IDAT数据块了,IDAT数据块中存放的是实际的图像数据,由于png本身就是一种压缩格式,所以IDAT数据块的数据本身就是经过压缩的,其大小取决于Filter方式、zlib的压缩方式等等。

关于filter方式http://www.libpng.org/pub/png/book/chapter09.html这篇文章讲的非常详细,有兴趣的同学可以看下。而Apple默认的filter方式就是filter0,也就是None这种方式,不同的filter方式对数据的压缩性是有很大影响的,同样是这张图片,使用不用的filter方式处理之后的大小如下:

不同的filter处理之后的图,放到实际工程中使用的话,效果上我测试下来是没有什么区别的,也就是说这几种类型,iPhone都可以解码,不过可以发现,即使是最小的文件也是217KB,还是比源文件207KB要大一些,这又是为什么呢?

xcrun -sdk iphoneos pngcrush -revert-iphone-optimizations lutf0.png lut_revert.png

通过上面的命令,我们可以将图片还原成正常的png格式,然而还原出来的png图片是246KB,比原来的207KB还是大了不少,通过pngcheck,我们可以发现原因:

因为CgBI的IDAT是BGRA格式的,所以不管之前的IDAT是否有Alpha通道,在处理的时候,都会增加alpha通道,其次就是因为每一行数据的filter不同,apple处理的时候,默认每一行都使用相同的filter,而原始文件则可以通过更好的算法,对不同的数据行使用不同的filter,为后面的数据压缩提供更容易压缩的数据。

以上就是对Compress PNG Files这个编译选项的一些分析,当然并不是所有图片经过处理都会变得巨大,也有些图片会变小的,所以当Compress PNG Files这个属性已经开启,并且不确定关闭会不会对现有工程产生大量影响的时候,可以通过如下操作将这张图片排除出compress的方法,尤其是对于LUT图片:

修改为:

参考文献:

【1】http://iphonedevwiki.net/index.php/CgBI_file_format

【2】https://www.cnblogs.com/lidabo/p/3701197.html

作者简介:ash, 天天P图iOS工程师


文章后记:
天天P图是由腾讯公司开发的业内领先的图像处理,相机美拍的APP。欢迎扫码或搜索关注我们的微信公众号:“天天P图攻城狮”,那上面将陆续公开分享我们的技术实践,期待一起交流学习!

加入我们:
天天P图技术团队长期招聘:
(1) AND / iOS 开发工程师 (2) 图像处理算法工程师 
期待对我们感兴趣或者有推荐的技术牛人加入我们(base 上海)!联系方式:ttpic_dev@qq.com

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
iOS减包实战:Compress PNG Files作用分析
减包这个词大家应该都不陌生,在减包过程中,图片资源的优化这项应该是必经之路了。
<<上一篇
下一篇>>