离屏渲染

2020-07-07  本文已影响0人  逃避不面对

屏幕显示图像的原理:

高中物理应该学过显示器是如何显示图像的:需要显示的图像经过电子枪以极快的速度进行一行一行的扫描,发射出来的电子撞击在屏幕的荧光剂上,荧光粉发光,就呈现了一帧画面,随后电子枪回到初始位置,因为人眼有视觉暂留效果,就形成了我们看到的图片或视频。

GPU屏幕渲染有两种方式:

(1)On-Screen Rendering (当前屏幕渲染) 

指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行。GPU渲染过后,直接将结果放到帧缓存区,通过视频控制器读取后,显示到屏幕上。

(2)Off-Screen Rendering (离屏渲染)

指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区offscreenBuffer进行渲染操作。

相比于当前屏幕渲染,离屏渲染的代价是很高的

1.创建新缓冲区

要想进行离屏渲染,首先要创建一个新的缓冲区。存储空间大小的上限是屏幕像素大小的2.5倍。

2.上下文切换

离屏渲染的过程,需要多次切换上下文环境:先从当前屏幕切换到离屏,当离屏渲染结束后,需要将渲染结果显示到屏幕上,又需要将上下文环境切换到当前屏幕。

那么既然离屏渲染这么消耗性能,为什么又要使用离屏渲染呢?

1.不得不使用离屏渲染:比如使用了特殊的渲染效果,所以必须用离屏缓冲区(offscreenBuffer)来保存中间状态,然后进行合成、裁切等操作后,才显示到屏幕上。这种情况不得不使用离屏渲染。

2.效率优势:对于某些需要多次使用的效果,提前渲染到离屏缓冲区,使用的时候直接拿,通过复用来提升效率。

造成离屏渲染的常见情况:

- 为图层设置遮罩(layer.mask)

- 设置裁切:将图层的layer.masksToBounds / view.clipsToBounds属性设置为true

- 设置组透明:将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0

- 为图层设置阴影(layer.shadow *)。

- 为图层设置layer.shouldRasterize=true

- 具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的图层

- 绘制的文本的layer(任何种类,包括UILabel,CATextLayer,Core Text等)。

- 使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。

关于离屏渲染的优化:

1.圆角的优化

a.系统的优化

iOS 9.0 之前UIimageView跟UIButton设置圆角都会触发离屏渲染。

iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。

b.使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100,100,100,100)];

imageView.image = [UIImage imageNamed:@"myImg"];

//开始对imageView进行画图

UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO,1.0);

//使用贝塞尔曲线画出一个圆形图

[[UIBezierPath bezierPathWithRoundedRect:imageView.boundscornerRadius:imageView.frame.size.width]addClip];

[imageView drawRect:imageView.bounds];

imageView.image=UIGraphicsGetImageFromCurrentImageContext();

//结束画图

UIGraphicsEndImageContext();

[self.view addSubview:imageView];

c.使用CAShapeLayer和UIBezierPath设置圆角

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];

imageView.image = [UIImage imageNamed:@"myImg"];

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];

//设置大小

maskLayer.frame = imageView.bounds;

//设置图形样子

maskLayer.path = maskPath.CGPath;

imageView.layer.mask = maskLayer;

[self.view addSubview:imageView];

CAShapeLayer动画渲染直接提交到手机的GPU当中,相较于view的drawRect方法使用CPU渲染而言,其效率极高,能大大优化内存使用情况。推荐使用方案2

d.从图的角度:美工出圆角图,美工出圆角遮罩图、用户上传的图片由服务器切好圆角图

2.阴影优化shadow

imageView.layer.shadowColor=[UIColorgrayColor].CGColor;

imageView.layer.shadowOpacity=1.0;

imageView.layer.shadowRadius=2.0;

UIBezierPath *path=[UIBezierPathbezierPathWithRect:imageView.frame];

imageView.layer.shadowPath=path.CGPath;

其他的优化:

尽量使用不包含透明(alpha)通道的图片资源

尽量设置layer的大小值为整形值

使用异步进行layer渲染

参考内容:https://www.jianshu.com/p/cff0d1b3c915

上一篇下一篇

猜你喜欢

热点阅读