OpenGL 三 - 001、iOS 离屏渲染 切圆角一定会触发离屏渲染吗?
正式开始前,我们可以先看一下下面几段代码:
1 //1.按钮存在背景图片 ==========》 触发了离屏渲染 2 UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom]; 3 btn1.frame = CGRectMake(100, 30, 100, 100); 4 btn1.layer.cornerRadius = 50; 5 [self.view addSubview:btn1]; 6 7 [btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal]; 8 btn1.clipsToBounds = YES; 9 10 //2.按钮不存在背景图片 11 UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom]; 12 btn2.frame = CGRectMake(100, 180, 100, 100); 13 btn2.layer.cornerRadius = 50; 14 btn2.backgroundColor = [UIColor blueColor]; 15 [self.view addSubview:btn2]; 16 btn2.clipsToBounds = YES; 17 18 //3.UIImageView 设置了图片+背景色; ==========》 触发了离屏渲染 19 UIImageView *img1 = [[UIImageView alloc]init]; 20 img1.frame = CGRectMake(100, 320, 100, 100); 21 img1.backgroundColor = [UIColor blueColor]; 22 [self.view addSubview:img1]; 23 img1.layer.cornerRadius = 50; 24 img1.layer.masksToBounds = YES; 25 img1.image = [UIImage imageNamed:@"btn.png"]; 26 27 //4.UIImageView 只设置了图片,无背景色; 28 UIImageView *img2 = [[UIImageView alloc]init]; 29 img2.frame = CGRectMake(100, 480, 100, 100); 30 [self.view addSubview:img2]; 31 img2.layer.cornerRadius = 50; 32 img2.layer.masksToBounds = YES; 33 img2.image = [UIImage imageNamed:@"btn.png"];
//5.UIView 多个贴在 self.view 上
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 600, 100, 100)];
view1.backgroundColor = [UIColor blackColor];
view1.layer.cornerRadius = 50;
view1.clipsToBounds = YES;
[self.view addSubview:view1];
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(100, 600, 60, 60)];
view2.backgroundColor = [UIColor whiteColor];
view2.layer.cornerRadius = 30;
view2.clipsToBounds = YES;
[self.view addSubview:view2];
运行,开启模拟器的查看离屏渲染触发功能:可看到 1 和 3 两段代码触发了离屏渲染()。他们都开了圆角,却并没有都触发离屏渲染,这是为什么呢?
一、渲染简图 - 普通 & 离屏
普通渲染:普通情况下,我们的APP的渲染流程是,CPU/GPU 合作,不断的将需要渲染的内容数据存到帧缓存区(Frame Buffer),显示则是不断地从帧缓存区取出数据进行显示。多个view 每个都独立 一层一层贴 在主view上。
离屏渲染:触发离屏渲染时,数据不会直接存到帧缓存区,而是先放在 另外开辟了一块缓存区空间 --> 离屏缓存区(offscreen Buffer),在不断地将数据存到离屏缓存区,之后将几个图层进行合并叠加,然后通过帧缓存区,再进行显示。一个view上多个图层。
二、离屏渲染
1)为什么离屏渲染呢?例子:我们要显示一个 button --> 背景粉色、有颗球的背景图、圆角4 --> 这些图层是不能一个个单独的显示在屏幕,需要三个叠加,那么叠加处理前 就要有个地方先存放它们 --> offscreen Buffer --> 三个都处理好了,然后一起叠加、处理 --> 到帧缓存区 --> 显示。 (这里的图层叠加并非是一层层独立 view 的叠加,而是它们三个在同一层layer上)
张图:
“不能单独的显示在屏幕”是设么意思呢?在脑海里想象一下,你想要做幅画,它要长成下面描述的样子,首先一层绿色的树背景、背景上有棵大树干、树根旁有只猫,最后要把这个图裁成圆形的。我们是不是要至少画三个元素(绿色、树、猫)合在一起才能呈现出这幅画,然后一剪刀将其裁成的圆。画纸便是我们的一层 view。“一张画纸上画多种元素” 区别于 “每张画纸画一种元素多张画纸叠加”。
你可能要说那他为什么非要触发离屏渲染呢?离屏渲染又要消耗我们的性能,我不可以直接一层一层的 view 摆放在屏幕上吗?当然可以,但这个的前提是你要向 UI 要张圆形的图片嘛,我们暂不考虑这个,没有现成的图的情况下我们是需要自己裁的。
2)离屏渲染为什么会引起性能问题呢?
1、要额外存储空间,大量的离屏渲染会使内存压力会比较大
2、offscreen Buffer --> frame Buffer 这个过程是需要一定时间的
3、离屏缓存区的空间是有他的大小限制的:屏幕的2.5倍(像素点)
3)离屏渲染的大量使用,会造成性能问题,可能会引起掉帧卡顿现象,那么为什么要用离屏渲染呢?
1、不得不使用:我们所需要的的一些特殊效果特效,并不能一次性一个图层就得到想要的结果的,必须要多个图层一起实现,不得不。
2、效率:特殊效果的复用。多次出现的一个动画特效,渲染结果保存在离屏缓存区里面,用的时候直接取。
到这里,我们再考虑下为什么切圆角没有都触发离屏渲染呢?
图层只有一层时(一个颜色or一张背景图...),我们对其渲染切圆角后直接就可以将其显示在屏幕中了,不需要有另外的图片或者其他什么和他混在一起的效果图,那么何必将其放在离屏缓存区消耗空间呢。
三、触发离屏渲染的常见场景
1)系统自动触发:
1、裁剪layer:例:切圆角 -- view.layer.masksToBounds = YES;(等同于 view.clipsToBounds = YES; )// 触发离屏渲染原因
圆角效果的几点实现:
a、按上面代码直接切
b、贝塞尔曲线画圆角
c、找UI要切好的图
2、阴影
3、高斯模糊
4、带透明度的图层的重合: 重合部分的颜色是必须要混合计算的
苹果毛玻璃效果(高斯模糊)的渲染流程图:
例子:对一张图片进行模糊处理:
渲染内容-Render Content(存offscreen Buffer) --> 抓取捕获图片内容-capture content(存) --> 垂直模糊-vertical blur(存) --> 水平模糊-horizonal blur(存) --> 合成compositiong Pass(四合一) --> 显示
2)主动触发:我们在复用时
光栅化:shouldRasterize --> When the value of this property is YES, the layer is rendered as a bitmap in its local coordinate space and then composited to the destination with any other content
当设为YES时会开启离屏渲染。
shouldRasterize 光栅化使用建议:
1、如果layer不能被复用,不要开启
2、layer不是静态的,要时时变化频繁修改,开启离屏渲染反而影响效率
3、离屏渲染缓存内容是有时间限制的,当存的内容在100ms内没有被复用,便会被清理掉,无法复用
4、离屏渲染空间限制,2.5倍的屏幕,超过屏幕像素大小的2.5倍后,也不能继续复用了