Swift教程

离屏渲染原理及案例解析

本文主要是介绍离屏渲染原理及案例解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、模拟器开启离屏渲染检测

二、离屏渲染原理

APP -> FrameBuffer -> Display

APP -> offscreenBuffer -> FrameBuffer -> Display

mask layer 渲染完毕,不能单独立即显示,要等待 contents layer 渲染完毕进行混合,等待过程中,先存在 离屏渲染缓冲区。
等待内容都渲染完毕,传递到 帧缓冲区,等待显示。

app 进行额外的渲染和合并 -> offscreenBuffer 组合 -> FrameBuffer -> Display

离屏渲染缺点:

  1. 额外的存储空间;
  2. offscreenBuffer 传递到 FrameBuffer 需要时间 最终可能导致 掉帧

离屏渲染空间限制:屏幕的 2.5 倍

使用离屏渲染的原因

  1. 特殊效果
    需要使用额外的 offscreenBuffer 保存中间状态,不得不使用。
    系统自动触发:圆角、阴影、高斯模糊等
  2. 效率优势
    效果会多次渲染 -> 提前渲染保存在 offscreenBuffer -> 可以实现复用

一、高斯模糊 触发离屏渲染

1、获取图像 -> Render Content
2、缩放处理 -> Capture Content
3、水平毛玻璃 -> Horizontal Blur Content
4、垂直毛玻璃 -> Vertical Blur Content
5、合成 -> Compositing Content
6、输出到 FrameBuffer

由于两个 帧缓冲区 空间有限,所以不能存在 帧缓冲区。
处理过程中的 content 存储在 offscreenBuffer。

二、开启光栅化(shouldRasterize) 触发离屏渲染

开启光栅化,layer 渲染为位图后, 会保存在缓冲区,可以复用在其他内容。

不建议开启的场景:

  1. layer 不能被复用
  2. layer 不是静态的,需要被频繁修改。比如处于动画之中,开启会影响效率。
  3. 离屏渲染缓存有时间限制,100ms 没被复用,就会被丢弃。
  4. 离屏渲染缓存空间有限,超过 2.5 倍屏幕像素大小的话,也会失效。

layer.cornerRadius 属性与离屏渲染之间的解读

layer 渲染的时候分为三层

Apple 文档

文档显示:

layer.cornerRadius 只会设置 backgroundColor 和 border 的圆角。不会设置 contents 的圆角。
除非同时设置了 layer.masksToBounds 为 YES(对应 view.clipsToBounds)。

简单理解:

layer.cornerRadius 会修改 backgroundColor 和 border 两个图层。
layer.masksToBounds 会让上述切圆角扩张到修改 contents 图层,子视图的图层也在这里。 此时,绘制完 backgroundColor 层后,需要暂存到 offscreenBuffer,等待 contents 层渲染完毕,再进行 裁剪操作,然后输出到 FrameBuffer。这就是就是离屏渲染。

三、触发离屏渲染的案例

  • 案例一
    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn1.frame = CGRectMake(100, 30, 100, 100);
    btn1.layer.cornerRadius = 50;
    [self.view addSubview:btn1];
    
    btn1.clipsToBounds = YES;
    [btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
复制代码

解析
设置 image 后,触发离屏渲染。
如果不设置 image, contents = nil,系统进行了优化, 不用切圆角,因此直接对 backgroundColor 层操作后,输出到 FrameBuffer 即可。

  • 案例二
    // UIImageView 设置了切圆角,设置了背景色,设置了图片
    UIImageView *img1 = [[UIImageView alloc]init];
    img1.frame = CGRectMake(100, 320, 100, 100);
    [self.view addSubview:img1];
    
    img1.layer.cornerRadius = 50;
    img1.layer.masksToBounds = YES;
    
    img1.backgroundColor = [UIColor blueColor];
    img1.image = [UIImage imageNamed:@"btn.png"];
复制代码

解析
如果不设置 backgroundColor,不会触发离屏渲染。
因为 backgroundColor 图层为空,不要渲染,因此,只需要对 contents 层操作后,输出到 FrameBuffer 即可。

  • 案例三
    // UIImageView 设置了切圆角,没有设置背景色,但是加了一个子视图
    UIImageView *img2 = [[UIImageView alloc]init];
    img2.frame = CGRectMake(100, 480, 100, 100);
    [self.view addSubview:img2];
    
    img2.layer.cornerRadius = 50;
    img2.layer.masksToBounds = YES;
    
    UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
    subView.backgroundColor = [UIColor redColor];
    [img2 addSubview:subView];
复制代码

解析
案例二中,如果没有背景色,img2.layer.contents 直接是位图,可以直接裁剪,输出到 FrameBuffer。
但是案例三中,img2.layer.contents 其实是子视图的渲染出来的位图,如果要给 contents 加圆角,则必须等待子视图渲染完成,存到 offscreenBuffer,然后再对缓冲区中的渲染结果进行裁剪,因此产生了离屏渲染。

‼️️ 注意

案例二中,UIImageView 不设置背景色,只设置 image,就不会触发背景色。
那为什么在案例一中,UIButton 只是设置了 image,没有设置背景色,却触发了离屏渲染呢?
其实是因为 UIButton 设置 image 的时候,是把 image 添加到了 UIButton 的一个 UIImageView 子视图上,就变成了案例三的这种情况,会触发离屏渲染。

四、离屏渲染原理总结

离屏渲染本质:要等待对多个非空图层的渲染结果进行操作,过程中的图层需要储存起来,这个存储位置就是 offscreenBuffer(离屏渲染缓冲区),整个过程就叫做离屏渲染

五、iOS 不触发离屏渲染设置圆角

方案一

    // UIImageView 设置了切圆角,不设置背景色
    imageView.backgroundColor = [UIColor clearColor];
    imageView.layer.cornerRadius = 50;
    imageView.layer.masksToBounds = YES;
复制代码

方案二

UI 切图带圆角

方案三

使用 贝塞尔曲线,裁剪 UIImage,绘制一个带圆角的 UIImage。

方案五

增加一个带圆角,中间镂空的切图,盖在需要圆角的视图上面。

六、iOS 触发离屏渲染的常见案例

  1. 使用了 mask 的 layer (layer.mask)
  2. 需要进行裁剪的 layer (layer.masksToBounds / view.clipsToBounds)
  3. 设置了组透明度为 YES,并且透明度不为 1 的 layer (layer.allowsGroupOpacity/ layer.opacity)
  4. 添加了投影的 layer (layer.shadow*)
  5. 采用了光栅化的 layer (layer.shouldRasterize)
  6. 绘制了文字的 layer (UILabel, CATextLayer, Core Text 等)
这篇关于离屏渲染原理及案例解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!