iOS-图片压缩

移动端开发经常会遇到图片压缩的问题,场景有更新头像,发帖等。今天我们就了解一下推荐

图片格式基础

  • 点阵图:也叫位图。用像素为单位,像素保存颜色信息,排列像素实现显示。
  • 矢量图:记录元素形状和颜色的算法,显示时展示算法运算的结果。

颜色

表示颜色时,有两种形式,一种为索引色(Index Color),一种为直接色(Direct Color)

  • 索引色:用一个数字索引代表一种颜色,在图像信息中存储数字到颜色的映射关系表(调色盘 Palette)。每个像素保存该像素颜色对应的数字索引。一般调色盘只能存储有限种类的颜色,通常为 256 种。所以每个像素的数字占用 1 字节(8 bit)大小。
  • 直接色:用四个数字来代表一种颜色,数字分别对应颜色中红色,绿色,蓝色,透明度(RGBA)。每个像素保存这四个纬度的信息来代表该像素的颜色。根据色彩深度(每个像素存储颜色信息的 bit 数不同),最多可以支持的颜色种类也不同,常见的有 8 位(R3+G3+B2)、16 位(R5+G6+B5)、24 位(R8+G8+B8)、32 位(A8+R8+G8+B8)。所以每个像素占用 1~4 字节大小。

移动端常用图片格式

图片格式中一般分为静态图和动态图

静态图

  • JPG:是支持 JPEG( 一种有损压缩方法)标准中最常用的图片格式。采用点阵图。常见的是使用 24 位的颜色深度的直接色(不支持透明)。

  • PNG:是支持无损压缩的图片格式。采用点阵图。PNG 有 5 种颜色选项:索引色、灰度、灰度透明、真彩色(24 位直接色)、真彩色透明(32 位直接色)。

  • WebP:是同时支持有损压缩和无所压缩的的图片格式。采用点阵图。支持 32 位直接色。移动端支持情况如下:

系统 原生 WebView 浏览器
iOS 第三方库支持 不支持 支持
Android 4.3后支持完整功能 支持 支持

动态图

  • GIF:是支持无损压缩的图片格式。采用点阵图。使用索引色,并有 1 位透明度通道(透明与否)。
  • APNG:基于 PNG 格式扩展的格式,加入动态图支持。采用点阵图。使用 32 位直接色。但没有被官方 PNG 接纳。移动端支持情况如下:
系统 原生 WebView 浏览器
iOS 支持 支持 支持
Android 第三方库支持 不支持 不支持
  • Animated Webp:Webp 的动图形式,实际上是文件中打包了多个单帧 Webp,在 libwebp 0.4 后开始支持。移动端支持情况如下:
系统 原生 WebView 浏览器
iOS 第三方库支持 不支持 不支持
Android 第三方库支持 不支持 不支持

而由于一般项目需要兼容三端(iOS、Android、Web 的关系),最简单就是支持 JPG、PNG、GIF 这三种通用的格式。所以本文暂不讨论其余图片格式的压缩。

移动端系统图片处理架构

根据我的了解,画了一下 iOS&Android 图片处理架构。iOS 这边,也是可以直接调用底层一点的框架的。

iOS 的 ImageIO

本文 iOS 端处理图片主要用 ImageIO 框架,使用的原因主要是静态图动态图 API 调用保持一致,且不会因为 UIImage 转换时会丢失一部分数据的信息。

ImageIO 主要提供了图片编解码功能,封装了一套 C 语言接口。在 Swift 中不需要对 C 对象进行内存管理,会比 Objective-C 中使用方便不少,但 api 结果返回都是 Optional(实际上非空),需要用 guard/if,或者 !进行转换。

解码

1. 创建 CGImageSource
CGImageSource 相当于 ImageIO 数据来源的抽象类。通用的使用方式 CGImageSourceCreateWithDataProvider: 需要提供一个 DataProvider,可以指定文件、URL、Data 等输入。也有通过传入 CFData 来进行创建的便捷方法 CGImageSourceCreateWithData:。方法的第二个参数 options 传入一个字典进行配置。根据 Apple 在 WWDC 2018 上的 Image and Graphics Best Practices 上的例子,当不需要解码仅需要创建 CGImageSource 的时候,应该将 kCGImageSourceShouldCache 设为 false。

2. 解码得到 CGImage
CGImageSourceCreateImageAtIndex: 或者 CGImageSourceCreateThumbnailAtIndex: 来获取生成的 CGImage,这里参数的 Index 就是第几帧图片,静态图传入 0 即可。

编码

1. 创建 CGImageDestination
CGImageDestination 相当于ImageIO数据输出的抽象类。通用的使用方式 CGImageDestinationCreateWithDataConsumer:需要提供一个 DataConsumer,可以置顶 URL、Data 等输入。也有通过传入 CFData 来进行创建的便捷方法 CGImageDestinationCreateWithData:,输出会写入到传入的Data中。方法还需要提供图片类型,图片帧数。

2. 添加 CGImage
添加 CGImage 使用CGImageDestinationAddImage:方法,动图的话,按顺序多次调用就行了。

而且还有一个特别的 CGImageDestinationAddImageFromSource: 方法,添加的其实是一个CGImageSource,有什么用呢,通过options 参数,达到改变图像设置的作用。比如改变 JPG 的压缩参数,用上这个功能后,就不需要转换成更顶层的对象(比如 UIImage),减少了转换时的编解码的损耗,达到性能更优的目的。

3. 进行编码
调用 CGImageDestinationFinalize:,表示开始编码,完成后会返回一个Bool值,并将数据写入 CGImageDestination 提供的 DataConsumer 中。

压缩思路分析

位图占用的空间大小,其实就是像素数量 x 单像素占用空间 x 帧数。所以减小图片空间大小,其实就从这三个方向下手。其中单像素占用空间,在直接色的情况下,主要和色彩深度相关。在实际项目中,改变色彩深度会导致图片颜色和原图没有保持完全一致,笔者并不建议对色彩深度进行更改。而像素数量就是平时非常常用的图片分辨率缩放。除此之外,JPG 格式还有特有的通过指定压缩系数来进行有损压缩。

JPG:压缩系数 + 分辨率缩放 + 色彩深度降低
PNG: 分辨率缩放 + 降低色彩深度
GIF:减少帧数 + 每帧分辨率缩放 + 减小调色盘

https://nemocdz.github.io/post/%E6%B5%85%E8%B0%88%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%9B%BE%E7%89%87%E5%8E%8B%E7%BC%A9/#%E5%9B%BE%E7%89%87%E6%A0%BC%E5%BC%8F%E5%9F%BA%E7%A1%80

目的

减少图片占用内存空间

质量压缩

图片的质量压缩 降低图片质量(大小)。
原理
通过算法扣掉(同化)了 图片中的一些某个点附近相近的像素,达到降低质量 减少 文件大小的目的。

他其实只能 实现对 file 的影响,对加载这个图片出来的bitmap 内存是无法节省的 ,还是那么大。 因为 bitmap 在内存中的大小是按照 像素 计算的 ,也就是width * height ,对于质量压缩,并不会改变图片的真实的像素(像素大小不会变)。

尺寸压缩

就是按照一定的倍数对图片减少单位尺寸的像素值。
原理
通过减少单位尺寸的像素值,真正意义上的降低像素值。