经常看到有人问:“为什么安卓版微信发的图片这么草率!比iphone差远了!”。不仅是微信,很多应用安卓版的画质都远低于iPhone版。到底是怎么回事?我们团队一开始也纠结于这个问题,花了很长时间,走了很多弯路。直到***才发现这是谷歌犯的一个“小”错误,一直错到今天。Google的错误在于:libjpeg。libjpeg是一个广泛使用的开源JPEG图像库(参考http://en.wikipedia.org/wiki/Libjpeg),Android也依赖libjpeg来压缩图像。通过查看源码,我们会发现Android并不是直接封装了libjpeg,而是基于另一个开源项目Skia(http://en.wikipedia.org/wiki/Skia_Graphics_Engine)作为图像处理引擎。Skia是谷歌自己维护的一个大型综合引擎,其中实现了各种图像处理功能,广泛应用于谷歌自己和其他公司的产品(如:Chrome、Firefox、Android等)。Skia对libjpeg进行了很好的封装,基于这个引擎,可以很方便的为操作系统和浏览器开发图像处理功能。ibjpeg在压缩图片的时候,有一个参数叫做optimize_coding。关于这个参数,libjpeg.doc有如下解释:booleanoptimize_codingTRUE使压缩器为图像计算最优霍夫曼编码表。这需要额外传递数据,因此会花费大量空间和时间。默认值为FALSE,它告诉压缩器使用提供的或默认的Huffman表。在大多数情况下,与默认表格相比,最佳表格仅节省几个百分点的文件大小。请注意,当这是TRUE时,您根本不需要提供Huffman表,并且您提供的任何内容都将被覆盖。这段话大概的意思是,如果你把optimize_coding设置为TRUE,那么在图像压缩的过程中,会根据图像数据计算出Huffman表(图像压缩中的Huffman表,请自行查阅相关资料),因为这个计算会显着消耗空间和时间,默认值设置为FALSE。这个解释乍一看没有问题,而且libjpeg的代码经过了十多年的考验,健壮高效。但是很多人忽略了这个解释是十多年前写的,空间和时间的消耗对于当时的计算设备来说可能是巨大的,但是在今天看来,这应该不再是这样了。问题,相反,我们应该考虑图片的质量(越来越好的显示技术)和图片的大小(越来越依赖云服务)。谷歌的Skia项目工程师最终并没有设置这个参数。optimize_coding在Skia中默认等于FALSE,表示图片质量更差,图片文件更大,压缩图片所耗费的时间和空间实际上是相反的,可以忽略不计。那么,这个参数的影响有多大呢?根据我们实测,使用同一张原图,分别设置optimize_coding=TRUE和FALSE进行压缩,达到相近的画质(使用Photoshop放大到像素级进行逐块比较),图片尺寸时FALSE大约是5-5-10次。换句话说,如果我们在FALSE和TRUE时要压缩同样大小的JPEG图片,FALSE的质量会大大不如TRUE(虽然质量很难量化,我们不妨说是5-差10倍)。我们还比较了Android和iOS(均使用标准的JPEG压缩方法)。两个系统都没有提供设置optimize_coding的接口(通过阅读源码,我们已经知道Android为FALSE,iOS未知)。当压缩同一张原图时,结果是一样的,iOS胜出。如果要接近质量,文件大小会相差5-10倍,而如果要压缩相同大小的文件,安卓的压缩质量简直惨不忍睹。结果表明苹果非常清楚optimize_coding参数和哈夫曼表的含义。这里需要指出的是,苹果使用的哈夫曼表算法和libjpeg(还有我们后来自己采用的libjpeg-turbo)是不一样的,从像素层面就能看出区别。Apple似乎在libjpeg的基础上进行了进一步的优化,压缩后的图像细节更加柔和流畅。在上面的实验中,我们尝试了多张原始图片和各种压缩比,测试结果都差不多。如果你有兴趣,不妨亲自尝试一下。最后我们决定不使用Android系统原生的JPEG压缩方式,而是基于libjpeg-turbo编译一个Android原生的库,专门用来压缩图片。这样一来,在我们的产品中,仅仅1/5的图像尺寸就可以让用户得到不逊色甚至更好的图像质量。对我们团队来说,花很长的时间,到处转转,是非常值得的。(使用libjpeg-turbo也有性能上的好处,这里就不赘述了)***,附上我们团队在github上的开源项目地址,供参考:https://github.com/bither
