iOS 图片加载与处理
一、图像从文件到屏幕的过程
图片显示分为三步:加载、解码、渲染
CPU 和 GPU 在渲染过程中的分工是什么?
图片渲染原理CPU(中央处理器):计算frame、解压缩图片、将需要绘制的纹理图片通过数据总线交给GPU。
GPU(图形处理器):定点的计算与变换、像素点的填充计算和纹理混合、渲染到帧缓冲区。
图片显示到屏幕上是CPU和GPU共同完成的。
二、图片加载的工作流程
以 +(UIImage *)imageWithContentOfFile 为例:
图片加载的过程图片解码是一个非常耗时的CPU操作,并且默认是在主线程进行的,所以当图片较多时对性能会造成很大的影响,特别是在快速滑动的列表上。
三、为什么要解压缩
既然是耗时操作,那么是否可以避免解压缩直接绘制图片到屏幕上,答案是否定的。图片解压缩的过程其实就是将图片的二进制数据转换成像素数据的过程,先了解一下位图, 什么是位图呢?
图像分为位图和矢量图。
矢量图是根据几何特性来绘制图形,矢量可以是一个点或一条线,文件占用空间较小,可自由无限制的重新组合 。
位图也称点阵图像,位图使用我们称为像素的一格一格的小点来描绘图像。
位图是一个像素数组, 每一个元素代表图片中的一个点. 我们在程序中使用的PNG和JPEG 图片就是位图.
事实上,不管是 JPEG 还是 PNG 图片,都是一种压缩的位图图形格式。只不过 PNG 图片是无损压缩,并且支持 alpha 通道,而 JPEG 图片则是有损压缩,可以指定 0-100% 的压缩比.
在将磁盘中的图片渲染到屏幕之前,必须先要得到图片的原始像素数据,才能执行后续的绘制操作,这就是为什么需要对图片解压缩的原因。
四、强制解压缩的原理
图片的解压缩不可避免,但是又不想让它在主线程执行,会影响应用的响应性,就要有更好的解决方案。
当未解压缩的图片将要渲染到屏幕时,系统会在主线程对图片进行解压缩,而如果图片已经解压缩了,系统就不会再对图片进行解压缩。因此,也就有了业内的解决方案,在子线程提前对图片进行强制解压缩。
而强制解压缩的原理就是对图片进行重新绘制,得到一张新的解压缩后的位图。其中,用到的最核心的函数是 CGBitmapContextCreate
CG_EXTERN CGContextRef __nullable CGBitmapContextCreate(void * __nullable data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef __nullable space, uint32_t bitmapInfo) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
1)像素格式
位图其实就是一个像素数组,而像素格式则是用来描述每个像素的组成格式,它包括以下信息:
Bits per component :一个像素中每个独立的颜色分量使用的 bit 数;
Bits per pixel : 一个像素使用的总 bit 数;
Bytes per row : 位图中的每一行使用的字节数。
对于位图来说,像素格式并不是随意组合的,目前只支持以下有限的17种特定组合:官方文档
2)位图的布局信息
像素格式是用来描述每个像素的组成格式的,比如每个像素使用的总bit数。而要想确保Quartz能够正确解析这些bit所代表的含义,还需要提供位图的布局信息 CGBitmapInfo:
CGBitmapInfo包含了三方面信息:alpha的信息、颜色分量是否为浮点数、像素格式的字节顺序。
CGImageAlphaInfoCGImageAlphaInfo包含的信息:
(1)是否包含 alpha ;
(2)如果包含 alpha ,那么 alpha 信息所处的位置,在像素的最低有效位,比如 RGBA ,还是最高有效位,比如 ARGB ;
(3)如果包含 alpha ,那么每个颜色分量是否已经乘以 alpha 的值,这种做法可以加速图片的渲染时间,因为它避免了渲染时的额外乘法运算。比如,对于 RGB 颜色空间,用已经乘以 alpha 的数据来渲染图片,每个像素都可以避免 3 次乘法运算,红色乘以 alpha ,绿色乘以 alpha 和蓝色乘以 alpha 。
3)参数选择
函数的参数具体的选择,官方文档有推荐,如图所示:
当没有alpha信息时,选用 kCGImageAlphaNoneSkipFirst,当有alpha通道时选择 kCGImageAlphaPremultipliedFirst,字节顺序采用 kCGBitmapByteOrder32Host。
4)开源库的实现
下图为各开源库图片解码时参数的选择,其中YYKit 和 SDWebImage 采用相同的参数。
五、图片缩略技术
将Data Buffers解码到 Image Buffers是一个CPU密集型的操作。同时它的大小是和与原始图像大小成比例,和 View 的大小无关。
如果一个浏览照片的应用展示多张照片时,没有经过任何处理,就直接读取图片,然后来展示。那 Decode 时,将会占用极大的内存和 CPU。而我们展示的图片的 View 的大小,其实是完全用不到这么大的原始图像的
图像是每个应用程序不可缺少的一部分。尤其面对高精度大图片的处理时,方式不当可能会出现OOM。
首先要知道 什么是压缩:
“压” 指文件体积变小,但是像素不变,长宽尺寸不变,那么质量可能下降
“缩” 指文件的尺寸变小,也就是像素数减少,而长宽尺寸变小,文件体积同样会减小。
1、图像的压处理:
图片的压处理,我们可以使用UIImageJPEGRepresentation或UIImagePNGRepresentation方法实现
2、图像的缩处理:
原图大小为15.4MB,尤其测试条件单一,结果仅供参考。
3、超大图片处理
CATiledLayer 分片显示, iOS大图展示的解决方法
六、性能优化
1、降采样
2、子线程解码和降采样
3、使用Image Asset Catalogs
4、合理的图片格式和尺寸的选用
5、UITableViewDataSourcePrefetching
6、分片显示
参考文章:
https://www.jianshu.com/p/de7b6aede888
https://juejin.im/post/5b1a7c2c5188257d5a30c820
https://www.jianshu.com/p/943dbe3ed608
https://honglu.me/2016/09/02/一张图片引发的深思/
https://longtimenoc.com/archives/ios如何避免图像解压缩的时间开销
http://blog.leichunfeng.com/blog/2017/02/20/talking-about-the-decompression-of-the-image-in-ios/
https://blog.csdn.net/y4x5m0nivsrjay3x92c/article/details/80327202
http://www.cocoachina.com/articles/25174