iOS 图片加载速率优化

iOS 从磁盘加载一张图片,使用 UIImageVIew 显示在屏幕上,需要经过以下步骤:

  1. 从磁盘拷贝数据到内核缓冲区
  2. 从内核缓冲区复制数据到用户空间
  3. 生成 UIImageView,把图像数据赋值给 UIImageView
  4. 如果图像数据为未解码的 PNG/JPG,解码为位图数据
  5. CATransaction 捕获到 UIImageView layer 树的变化
  6. 主线程 Runloop 提交 CATransaction,开始进行图像渲染
    1. 如果数据没有字节对齐,Core Animation 会再拷贝一份数据,进行字节对齐。
    2. GPU 处理位图数据,进行渲染。
Read more   2021/4/29 posted in  图形图像

关于图像加载字节对齐的优化

字节对齐

字节对齐是对基本数据类型的地址做了一些限制,即某种数据类型对象的地址必须是其值的整数倍。例如,处理器从内存中读取一个8个字节的数据,那么数据地址必须是8的整数倍。

对齐是为了提高读取的性能。因为处理器读取内存中的数据不是一个一个字节读取的,而是一块一块读取的一般叫做cache lines。如果一个不对齐的数据放在了2个数据块中,那么处理器可能要执行两次内存访问。当这种不对齐的数据非常多的时候,就会影响到读取性能了。这样可能会牺牲一些储存空间,但是对提升了内存的性能,对现代计算机来说是更好的选择。

Read more   2021/4/29 posted in  图形图像

iOS 图片解码的几种方式

SDWebImage 使用的图片解压缩方案

Read more   2021/4/29 posted in  图形图像

PNG 图片的压缩原理

图片压缩是个很大的话题。本文从具体的工具进行初探其中的原理。

本文探索的工具有:

TinyPNG

看一下 TinyPNG 官网上是如何介绍的:

它是如何工作的?

好问题! 当您上传一张PNG(Portable Network Graphics)图片时,图片中相似的颜色会被合并。 这种技术被称作“量化(quantization)”。 通过减少颜色数量,可以将24位PNG文件转换成更小的8位索引颜色图像。 所有不必要的元数据也被丢弃。 结果是支持100%透明度的更好的PNG文件。 两者兼得!

Read more   2021/3/22 posted in  图形图像

图片解码到渲染

以 iOS 中的流程为例进行探究

渲染

混合

平时我们写代码的时候,往往会给不同的CALayer添加不同的颜色,不同的透明度,我们最后看到是所有这些层CALayer混合出的结果。

每个像素都包含了 R G B 和 A,GPU 要计算每个像素混合出来的 RGB 值,假设在正常混合模式下,并且像素对齐的两个 CALayer,计算的混合公式如下:

 R = S + D*(1 - Sa)

The blend mode constants introduced in OS X v10.5 represent the Porter-Duff blend modes. The symbols in the equations for these blend modes are:
* R is the premultiplied result
* S is the source color, and includes alpha
* D is the destination color, and includes alpha
* Ra, Sa, and Da are the alpha components of R, S, and D

苹果的 CGBlendMode 的文档中有对其中每个参数进行解释:

/* Blend modes.

.
.
.

    /* Available in Mac OS X 10.5 & later. R, S, and D are, respectively,
       premultiplied result, source, and destination colors with alpha; Ra,
       Sa, and Da are the alpha components of these colors.

       The Porter-Duff "source over" mode is called `kCGBlendModeNormal':
         R = S + D*(1 - Sa)

       Note that the Porter-Duff "XOR" mode is only titularly related to the
       classical bitmap XOR operation (which is unsupported by
       CoreGraphics). */

    kCGBlendModeClear,                  /* R = 0 */
    kCGBlendModeCopy,                   /* R = S */
    kCGBlendModeSourceIn,               /* R = S*Da */
    kCGBlendModeSourceOut,              /* R = S*(1 - Da) */
    kCGBlendModeSourceAtop,             /* R = S*Da + D*(1 - Sa) */
    kCGBlendModeDestinationOver,        /* R = S*(1 - Da) + D */
    kCGBlendModeDestinationIn,          /* R = D*Sa */
    kCGBlendModeDestinationOut,         /* R = D*(1 - Sa) */
    kCGBlendModeDestinationAtop,        /* R = S*(1 - Da) + D*Sa */
    kCGBlendModeXOR,                    /* R = S*(1 - Da) + D*(1 - Sa) */
    kCGBlendModePlusDarker,             /* R = MAX(0, (1 - D) + (1 - S)) */
    kCGBlendModePlusLighter             /* R = MIN(1, S + D) */
};

像素对齐

像素对齐就是视图上像素和屏幕上的物理像素完美对齐。
在像素对齐的情况下,只需要把所有 layer 上的单个像素进行混合计算即可。当时如果像素不对齐,GPU 就需要进行 Anti-aliasing 反抗锯齿计算,GPU 负担加重。

造成像素不对齐的情况:

  1. 图片大小和UIImageView大小不符合2倍3倍关系
  2. 边缘像素不对齐,即起始坐标不是整数,可以使用CGRectIntegral()方法去除小数位。

「优化点」开发中尽量避免以上情况

不透明

R = S + D * ( 1 – Sa )

如果 Sa 的值为1,也就是源色对应的像素不透明。那么得到 R=S, 这样GPU 就无需进行混合计算,只需要拷贝最上层layer。

开发中需要做的就是设置 opaque 属性。

加载没有 alpha 通道的图片,opaque 会自动设置为YES。
但是如果是一个每个像素alpha值都为100%的图片,尽管此图不透明但是Core Animation依然会假定是否存在alpha值不为100%的像素。

「优化点」开发中关注图像 alpha 通道的取舍

解码

Core Animation准备阶段,会对图片进行解码操作,即把压缩的图像解码成位图数据。

这是一个很消耗CPU的事情。系统是在图片将要渲染到屏幕之前再进行解码,而且默认是在主线程中进行的。所以我们可以将解码放在子线程中进行,解码实现 e.g.

NSString *picPath = [[NSBundle mainBundle] pathForResource:@"tests" ofType:@"png"];
NSData *imageData = [NSData dataWithContentsOfFile:picPath];//读取未解码图片数据

CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData((__bridge CFTypeRef)imageData, NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSourceRef, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CFRelease(imageSourceRef);
size_t width = CGImageGetWidth(imageRef);//获取图片宽度
size_t height = CGImageGetHeight(imageRef);//获取图片高度
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);

size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);//每个颜色组件占的bit数
size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);//每个像素占几bit
size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);//位图数据每行占多少bit
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);

CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef);
CFRelease(imageRef);
CFDataRef dataRef = CGDataProviderCopyData(dataProvider);//获得解码后数据
CGDataProviderRef newProvider = CGDataProviderCreateWithCFData(dataRef);
CFRelease(dataRef);

CGImageRef newImageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, newProvider, NULL, false, kCGRenderingIntentDefault);
CFRelease(newProvider);

UIImage *image = [UIImage imageWithCGImage:newImageRef scale:2.0 orientation:UIImageOrientationUp];
CFRelease(newImageRef);

其他的图片解码方式 iOS 图片解码的几种方式 中继续了解

字节对齐

字节对齐是对基本数据类型的地址做了一些限制,即某种数据类型对象的地址必须是其值的整数倍。例如,处理器从内存中读取一个8个字节的数据,那么数据地址必须是8的整数倍。

对齐是为了提高读取的性能。因为处理器读取内存中的数据不是一个一个字节读取的,而是一块一块读取的一般叫做cache lines。如果一个不对齐的数据放在了2个数据块中,那么处理器可能要执行两次内存访问。当这种不对齐的数据非常多的时候,就会影响到读取性能了。这样可能会牺牲一些储存空间,但是对提升了内存的性能,对现代计算机来说是更好的选择。

在iOS中,如果这个图像的数据没有字节对齐,那么Core Animation会自动拷贝一份数据做对齐处理。这里我们可以提前做好字节对齐。


Reference

iOS-图片高级处理(二、图片的编码解码)

探讨iOS 中图片的解压缩到渲染过程

iOS中的图片解码

谈谈 iOS 中图片的解压缩

2021/2/19 posted in  图形图像