一、什么是离屏渲染?
首先我们看下正常的图像显示流程:
GPU进⾏渲染->帧缓存区⾥ ->视频控制器->读取帧缓存区信息(位图) -> 数模转化(数字信号处->模 拟型号) ->(逐⾏扫描)显示;
但是,在上诉流程中还存在一种特殊情况,当需要显示的数据不能被视频控制器直接显示的时候,会在帧缓冲区外新开辟一个离屏缓冲区,后续GPU会复用离屏缓冲区的数据,计算完毕后再放入到帧缓冲区等待读取显示
一、那什么情况我们的代码会触发离屏渲染?
我们常会听说,设置圆角会触发离屏渲染。
但此种说法其实是不准确的。
我们先来看看苹果官方文档对于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 true causes the content to be clipped to the rounded corners.
意思:设置cornerRadius仅作用于background和border,不作用于layer’s contents ,除非设置masksToBounds
但设置了masksToBounds就会导致离屏渲染吗?
当然不是!!!
下面我们来看一些测试代码:
//1.按钮存在背景图片
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
btn1.frame = CGRectMake(100, 30, 100, 100);
btn1.layer.cornerRadius = 50;
[self.view addSubview:btn1];
[btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
btn1.clipsToBounds = YES;
//2.按钮不存在背景图片
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
btn2.frame = CGRectMake(100, 180, 100, 100);
btn2.layer.cornerRadius = 50;
btn2.backgroundColor = [UIColor blueColor];
[self.view addSubview:btn2];
btn2.clipsToBounds = YES;
//3.UIImageView 设置了图片+背景色;
UIImageView *img1 = [[UIImageView alloc]init];
img1.frame = CGRectMake(100, 320, 100, 100);
img1.backgroundColor = [UIColor blueColor];
[self.view addSubview:img1];
img1.layer.cornerRadius = 50;
img1.layer.masksToBounds = YES;
img1.image = [UIImage imageNamed:@"btn.png"];
//4.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;
img2.image = [UIImage imageNamed:@"btn.png"];
打开模拟器的离屏渲染检测工具:

通过demo我们可以看到,并不是所有的例子都发生了离屏渲染。

通过案例我们发现,当视图存在内容和背景时发生圆角裁剪,那么此时必定会产生离屏渲染。

三、解决办法
(1)后台绘制圆角图片,前台进行设置


(2)对于 contents 无内容或者内容的背景透明(无涉及到圆角以外的区域)的layer,直接设置layer的 backgroundColor 和 cornerRadius 属性来绘制圆角。
(3)使用混合图层,在layer上方叠加相应mask形状的半透明layer
sublayer.contents=(id)[UIImage imageNamed:@"xxx"].CGImage;
[view.layer addSublayer:sublayer];
(4)- (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius corners:(UIRectCorner)corners borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor borderLineJoin:(CGLineJoin)borderLineJoin此方法为YY_image处理圆角的方法,你可以去下载YY_image查看源码
其他情况触发离屏渲染以及解决办法:
- mask(遮罩)------>使用混合图层,在layer上方叠加相应mask形状的半透明layer
2.edge antialiasing(抗锯齿)----->不设置 allowsEdgeAntialiasing 属性为YES(默认为NO)
- allowsGroupOpacity(组不透明,开启CALayer的allowsGroupOpacity属性后,子 layer 在视觉上的透明度的上限是其父 layer 的opacity(对应UIView的alpha),并且从 iOS 7 以后默认全局开启了这个功能,这样做是为了让子视图与其容器视图保持同样的透明度。)------->关闭 allowsGroupOpacity 属性,按产品需求自己控制layer透明度
4.shadows(阴影)------>设置阴影后,设置CALayer的 shadowPath,view.layer.shadowPath=[UIBezierPath pathWithCGRect:view.bounds].CGPath;
CALayer离屏渲染终极解决方案:当视图内容是静态不变时,设置 shouldRasterize(光栅化)为YES(缓存离屏渲染的数据,当下次用到的时候直接拿,不需要开辟新的离屏缓冲区),此方案最为实用方便。view.layer.shouldRasterize = true;view.layer.rasterizationScale = view.layer.contentsScale;
shouldRasterize (光栅华使用建议):
1.如果layer不需要服用,则没有必要打开
2.如果layer不是静态的,需要被频繁修改,比如出于动画之中,则开启光栅华反而影响性能
3.离屏渲染缓存有时间限制,当超过100ms,内容没有被使用就会被丢弃,无法复用
4.离屏渲染缓存有空间限制,超过屏幕像素的2.5倍则失效,并无法使用
特别说明:当内容属于不常被复用的,不建议使用光栅化,开启了后反而会损耗性能
网友评论