赞
踩
场景很简单,上传图片前压缩图片,节省流量和发图时间。最近看了看 iOS 的静态图片压缩,这里记个笔记。本人之前没学过 iOS 和 Swift,本文是一篇入门文章,描述不到位之处请大家多多批评斧正。 ̄ω ̄=
我把它放在本文的第一位,因为接下来的代码展示都要用到它。
我选择的是 iOS 自带的 Image I/O,好处是速度快、内存占用小、主流图片格式硬解,Image and Graphics Best Practices - WWDC 2018 和 Image Resizing Techniques 中有介绍。
根据原图的尺寸推测出预期缩略图的尺寸,这个与业务场景强相关,开源的实现比如 Luban,是模仿微信的缩略图尺寸计算逻辑。
首先获取原图尺寸:
let
然后根据原图尺寸区分几种情况,这里给个 Example,真正用的话可以根据 Luban 改改:
计算出预期缩略图的尺寸(thumbWidth, thumbHeight)之后,创建缩略图 CGImage:
let
快速看了下选项 kCGImageSourceSubsampleFactor 的注释,它能令解码后返回的图片的长宽缩小为原图的 1/n,n ∈ { 2, 4, 8 }。
有点像 libjpeg 支持的 IDCT Scaling,即 jpeg_decompress_struct 里的 scale_num / scale_denom。Skia(Android 用的就是它)的 SkJpegCodec 有提到,libjpeg-turbo 目前对其中的 1/4 和 1/2 有 SIMD 加速。
咳咳,跑远了,要注意一点,用 JPEG 的 IDCT scaling 虽然快,但会导致图像质量变差,我并没有尝试 kCGImageSourceSubsampleFactor 这个选项,不确定它会不会令图像质量变差,有兴趣的同学可以试试。 // TODO 之后补上
就顺便一提,如果想获得微信的缩略图尺寸计算逻辑,可以先从日志入手,自己发几张图,从微信打的日志定位到具体的业务代码。微信日志是非对称加密的。一种方法是修改 xlog 库,拿一台 root 的 Android 手机,把 libwechatxlog.so pull 出来,修改成忽略公钥,再把它 push 回去,替换掉所有 libwechatxlog.so 出现的地方,这样就得到了一个日志不加密的微信,之后 jadx 打开,在代码里搜索相关日志即可。另一种方法是在 Java 层加 hook,不提。
这节主要是针对 JPEG 格式写的,JPEG 在编码时可以选择质量,iOS (AppleJPEG) 可以指定 0..1.0,而 libjpeg 是 0..100,不要以为它俩之间就是 1:100 的关系,实际我用工具 identify 估算了一下,iOS (AppleJPEG) 的 0.38 约对应于 libjpeg 的 60,0.44 约对应于 70。
这里我们选择 0.38 作为质量参数:
var
实际场景中,这个值可以受下发的配置和网络状态等环境变量影响,比如我 WIFI 下指定 0.44,4G 下指定 0.38。
透明色由 alpha channel 决定。一般 alpha channel 存在每一个像素里,当然还有其他结构的存储方式,比如 Indexed PNG 会把它写到独立的 tRNS 段。判断图片是否包含 alpha channel 可以用 CGImage Source:
let
或 CGImage 的 alphaInfo 字段:
let
来判断。这里提一下 CGImage 的 alphaInfo 字段:
public
其他字段没啥好说的,premultipliedLast 和 premultipliedFirst 代表“预乘 Alpha”,可以让图片的加载消耗变少,一般在游戏等 CPU (GPU) 密集的场景有用,介绍见 Premultiplied alpha。
压缩成 JPEG 还是 PNG 也就一个参数的区别:
if
另外我发现还有一个参数 kUTTypeJPEG2000,这个新的 JPEG 格式支持透明色,但推广不起来(估计是不向前兼容的原因)。
以下手段本人还在研究,到时来填坑!!!
在 iOS 11 或以上,照片格式已经是 HEIF,iOS 11 或以上也已经支持 HEIF 格式的编解码:
if
在本文的有损压缩的场景下,HEIF 跟 JPEG 相比,虽然编码耗时长,但输出图片的体积会小很多,网上有许多比较,不提。
由于 HEIF 比同等体积的 JPEG 更为清晰,质量参数可以给大点。
比如 iPhone XS Max 的截屏图片的 color depth 都是 16-bit,降到 8-bit 图片体积会小很多。
let
if
6. PNG 有没有有损压缩?有,但是不适合本文的场景。TinyPNG 就是有损压缩,开源的有 https://github.com/kornelski/pngquant,思想都是 quantization + indexed。完全可以用 pngquant 自己撸一个不输于 TinyPNG。注意因为出来的图是 indexed,编码时记得 NoFilter。
7. 已经很久很久没有写过博客了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。