然而,当把这个配置改为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.
明显的改动就是在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
文章后记: 天天P图是由腾讯公司开发的业内领先的图像处理,相机美拍的APP。欢迎扫码或搜索关注我们的微信公众号:“天天P图攻城狮”,那上面将陆续公开分享我们的技术实践,期待一起交流学习!
加入我们: 天天P图技术团队长期招聘: (1) AND / iOS 开发工程师 (2) 图像处理算法工程师 期待对我们感兴趣或者有推荐的技术牛人加入我们(base 上海)!联系方式:ttpic_dev@qq.com