离屏渲染
探索之前可以先了解一些CALayer的基础
我们都知道使用系统提供的设置圆角方法也会触发离屏渲染
view.layer.cornerRadius = 5
view.layer.masksToBounds = true
为什么用圆角讲起,应为这个方法是我们平时经常会用到的,比较好理解一点。
上面两行代码是一起才会引发的离屏渲染,还是某一行引起的呢?
view.layer.cornerRadius = 5
这行代码做了什么事?文档中cornerRadius属性中说明
Setting the radius to a value greater than 0.0 causes the layer to begin drawing rounded corners on its background. By default, the corner radius does not apply to the image in the layer’s contents property; it applies only to the background color and border of the layer. However, setting the masksToBounds property to YES causes the content to be clipped to the rounded corners.
从此说明中我们可以知道,view.layer.cornerRadius
只对背景颜色和前景框有效果,无法对contents
进行设置,再看CALayer的结构,如果contents
中有内容或内容的背景不是透明的话,还需要设置这部分的内容,否则没有效果。所以才需要修改 view.layer.masksToBounds
为true
(在 UIView 上对应的属性是clipsToBounds)。
之前有文章指出是修改view.layer.masksToBounds
为true
才是触发离屏渲染的原因,而非修改cornerRadius。
对此我们通过简单的代码来进行测试
首先得打开模拟器的离屏渲染测试
image.png
1.圆角触发离屏渲染测试
第一次测试:不设置masksToBounds
或clipsToBounds
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
// 设置背景色
view1.backgroundColor = UIColor.blueColor;
// 设置边框宽度和颜色
view1.layer.borderWidth = 2.0;
view1.layer.borderColor = UIColor.redColor.CGColor;
// 设置圆角
view1.layer.cornerRadius = 100.0;
view1.center = self.view.center;
[self.view addSubview:view1];
结果我们发现确实没有发生离屏渲染:
image.png
第二次测试:
同时设置masksToBounds
和 cornerRadius
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
// 设置背景色
view1.backgroundColor = UIColor.blueColor;
// 设置边框宽度和颜色
view1.layer.borderWidth = 2.0;
view1.layer.borderColor = UIColor.redColor.CGColor;
// 设置圆角
view1.layer.cornerRadius = 100.0;
// 设置裁剪
view1.clipsToBounds = YES;
view1.center = self.view.center;
[self.view addSubview:view1];
结果还是没有发生离屏渲染:
image.png
这是为什么呢?
原来我们还没设置contents
,在CALayer的基础中有说过contents
它必须是一个CGImage
才能显示。
下面我们将给contents
赋值一个CGImage
//设置图片
view1.layer.contents = (__bridge id)[UIImage imageNamed:@"zhu24.jpg"].CGImage;
结果触发了离屏渲染
image.png
因此可以知道当我们同时设置masksToBounds
和 cornerRadius
,并且contents
是有内容的时候,会触发离屏渲染。
但并没有解决我们的疑问
下面我们测试只开启masksToBounds
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
// 设置背景色
view1.backgroundColor = UIColor.blueColor;
// 设置边框宽度和颜色
view1.layer.borderWidth = 2.0;
view1.layer.borderColor = UIColor.redColor.CGColor;
//设置图片
view1.layer.contents = (__bridge id)[UIImage imageNamed:@"zhu24.jpg"].CGImage;
// 设置裁剪
view1.clipsToBounds = YES;
view1.center = self.view.center;
[self.view addSubview:view1];
结果是没有触发离屏渲染的
image.png
可能也是因为view1
的图形压根就没有发生改变,所以没有触发也是正常现象。
不过从中我们可以知道这两个属性单独作用时都不是引发离屏渲染的原因,他俩合体(masksToBounds = true&&cornerRadius>0
)才是。
最后测试一下当CALayer中只有contents
有内容,背景和边框为空
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
//设置图片
view1.layer.contents = (__bridge id)[UIImage imageNamed:@"zhu24.jpg"].CGImage;
// 设置圆角
view1.layer.cornerRadius = 100.0;
// 设置裁剪
view1.clipsToBounds = YES;
view1.center = self.view.center;
[self.view addSubview:view1];
结果没有触发离屏渲染
image.png
所以触发离屏渲染还需要有多个图层。
2. 离屏渲染的逻辑
- 正常我们将图像渲染到屏幕上是遵循
油画算法:先绘制场景中的离观察者较远的物体,再绘制较近的物体。
image.png
当subLayer1绘制到屏幕后会立即从帧缓冲区
(Offscreen Buffer)移除,从而节省空间。
- 当我们应用
cornerRadius
和maskToBounds
进行圆角+裁剪时
image.png
绘制后并不会立即从帧缓冲区
移除,而是依次在帧缓冲区
中保存,等待圆角+裁剪处理,即引发了 离屏渲染 。
常见的几种触发离屏渲染的情况
- 使⽤了 mask 的 layer (layer.mask)
- 需要进⾏裁剪的 layer (layer.masksToBounds / view.clipsToBounds)
- 设置了组透明度为 YES,并且透明度不为 1 的 layer (layer.allowsGroupOpacity/
layer.opacity)- 添加了投影的 layer (layer.shadow*)
- 采⽤了光栅化的 layer (layer.shouldRasterize)
- 绘制了⽂字的 layer (UILabel, CATextLayer, Core Text 等)
网友评论