700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > iOS代码质量要求_图片压缩(iOS)

iOS代码质量要求_图片压缩(iOS)

时间:2022-09-01 13:52:57

相关推荐

iOS代码质量要求_图片压缩(iOS)

场景很简单,上传图片前压缩图片,节省流量和发图时间。最近看了看 iOS 的静态图片压缩,这里记个笔记。本人之前没学过 iOS 和 Swift,本文是一篇入门文章,描述不到位之处请大家多多批评斧正。 ̄ω ̄=

从这几个方向入手:

选择图片处理库减小图片尺寸降低图片质量改变图片格式降低像素中每个 color component 的 depth

选择图片处理库

我把它放在本文的第一位,因为接下来的代码展示都要用到它。

我选择的是 iOS 自带的 Image I/O,好处是速度快、内存占用小、主流图片格式硬解,Image and Graphics Best Practices - WWDC 和 Image Resizing Techniques 中有介绍。

减小图片尺寸

根据原图的尺寸推测出预期缩略图的尺寸,这个与业务场景强相关,开源的实现比如 Luban,是模仿微信的缩略图尺寸计算逻辑。

首先获取原图尺寸:

let

然后根据原图尺寸区分几种情况,这里给个 Example,真正用的话可以根据 Luban 改改:

短边与长边比例小于1/3的被算作长图,单独开一条分支。(比例小于 x/x 被算作超长图)正常比例的图尺寸较小的,截图等:可视最长边长度,尺寸等比缩小到长边为 1280尺寸较大的,照片等:可视最短边长度,尺寸等比缩小到短边为 1080 或 1280

计算出预期缩略图的尺寸(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。

改变图片格式

如果想做得简单点,编码格式就两种:JPEG 和 PNG。无透明色选 JPEG,有透明色选 PNG。因为 PNG 格式是无损压缩,一般比 JPEG 格式的图片大,也要更清晰。

透明色由 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 格式支持透明色,但推广不起来(估计是不向前兼容的原因)。

以下手段本人还在研究,到时来填坑!!!

如果想做得好点复杂点,比如上传的图片全部编码成 WEBP 或者更好的 HEIF(没错它比 WEBP 还小),那可能需要客户端自己引入新的编码库,还需要服务端支持转码成 JPEG 或 PNG,以便用户下载编辑。好在 HEIF 那么出名,已经有人给我们准备好库了:libheif-rs(突然放出 Rust crate

在 iOS 11 或以上,照片格式已经是 HEIF,iOS 11 或以上也已经支持 HEIF 格式的编解码:

if

在本文的有损压缩的场景下,HEIF 跟 JPEG 相比,虽然编码耗时长,但输出图片的体积会小很多,网上有许多比较,不提。

由于 HEIF 比同等体积的 JPEG 更为清晰,质量参数可以给大点。

如果想把握未来,可以研究研究同事给我墙裂推荐的 AV1,这坑不填。

降低像素中每个 color component 的 depth

比如 iPhone XS Max 的截屏图片的 color depth 都是 16-bit,降到 8-bit 图片体积会小很多。

let

多说几句

很多图片会存一些“元信息”、“元数据”,像拍摄的地理位置、时间、thumbnail 等等,但压缩图片场景需要的一般就两个:color profile 和 orientation。color profile 防止偏色,iPhone 产出的图片一般带有 Display P3,是要写到压缩后的图片里的,这个 Image I/O 会默认自动 copy;指定了 kCGImageSourceCreateThumbnailWithTransform 也会帮你做旋转对称。而大部分多余的信息和数据(地理位置、时间、thumbnail)会被去掉。这块基本不用担心。处理图片要小心内存不够用 didReceiveMemoryWarning。好的采样算法(比如 Lanczos)会带来更好的图像质量,但一般会消耗会更多的 CPU。目前看来没啥必要。要怎么知道 JPEG 图片的质量?很遗憾,质量并不是元信息,要猜测 JPEG 图片质量的话可以参考 ImageMagick。要避免重复压缩。加上个预期的缩略图的大小。

if

6. PNG 有没有有损压缩?有,但是不适合本文的场景。TinyPNG 就是有损压缩,开源的有 /kornelski/pngquant,思想都是 quantization + indexed。完全可以用 pngquant 自己撸一个不输于 TinyPNG。注意因为出来的图是 indexed,编码时记得 NoFilter。

7. 已经很久很久没有写过博客了。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。