图片渲染基础知识总结

2018-04-08  本文已影响54人  woniu

凡仁心之发,必一鼓作气,尽吾力之所能为。稍有转念,则疑心生,私心亦生。疑心生则计较多,而出纳吝矣;私心生则好恶偏,而轻重乖矣。------------------------------------曾国藩

想到好的事就立刻去做,别犹豫!

首先,我们提出我们的疑问,什么是GPU?什么是离屏渲染?GPU与CPU渲染有什么不同等等?下面就让我们带着这些疑问来一一解答。

GPU 概念:

GPU英文全称Graphic Processing Unit,中文翻译为“图形处理器”。GPU是相对于CPU的一个概念,GPU是显示卡的“心脏”,也就相当于CPU在电脑中的作用,它决定了该显卡的档次和大部分性能,同时也是2D显示卡和3D显示卡的区别依据。

CPU与GPU的不同:

CPU擅长逻辑控制,串行的运算。和通用类型数据运算不同,GPU擅长的是大规模并发计算,这也正是密码破解等所需要的。所以GPU除了图像处理,也越来越多的参与到计算当中来。

OpenGL中,GPU屏幕渲染有两种方式:

1、On-Screen Rendering (当前屏幕渲染)
指的是GPU的渲染操作是在当前用户现实的屏幕缓冲区中进行。

当前屏幕渲染不需要额外创建新的缓存,也不需要开启新的上下文,相对于离屏渲染性能更好。但是受当前屏幕渲染的局限因素限制(只有自身上下文、屏幕缓存有限等),当前屏幕渲染有些情况下的渲染解决不了的,就使用到离屏渲染。

2、Off-Screen Rendering (离屏渲染)
指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。

离屏渲染的代价很高,想要进行离屏渲染,首选要创建一个新的缓冲区,屏幕渲染会有一个上下文环境的一个概念,离屏渲染的整个过程需要切换上下文环境,先从当前屏幕切换到离屏,等结束后,又要将上下文环境切换回来。这也是为什么会消耗性能的原因了。

特殊的“离屏渲染”:CPU渲染

如果我们重写了drawRect方法,并且使用任何Core Graphics的技术进行了绘制操作,就涉及到了CPU渲染。整个渲染过程由CPU在App内同步地完成,渲染得到的bitmap(位图)最后再交由GPU用于显示。

离屏渲染场景

1、为图层设置遮罩(layer.mask)
2、将图层的layer.masksToBounds / view.clipsToBounds属性设置为true
3、将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0
4、为图层设置阴影(layer.shadow )。
5、为图层设置layer.shouldRasterize=true
6、具有layer.cornerRadius,layer.edgeAntialiasingMask,
* layer.allowsEdgeAntialiasing的图层
7、文本(任何种类,包括UILabel,CATextLayer,Core Text等)。
8、使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。

优化方案:

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

圆角优化

方案一:使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角
要点:使用Core Graphics框架与贝塞尔曲线可以实现不在view的drawRect(继承于CoreGraphics走的是CPU,消耗的性能较大)方法中画出一些想要的图形。

    UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; 
    imageView.image = [UIImage imageNamed:@"1"];
    //开始对imageView进行画图
    UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
    //使用贝塞尔曲线画出一个圆形图
    [[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
    [imageView drawRect:imageView.bounds]; 
    imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    //结束画图
    UIGraphicsEndImageContext();
    [self.view addSubview:imageView];

方案二:使用CAShapeLayer和UIBezierPath设置圆角
要点:CAShapeLayer动画渲染直接提交到手机的GPU当中,相较于view的drawRect方法使用CPU渲染而言,其效率极高,能大大优化内存使用情况。

    UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
    imageView.image = [UIImage imageNamed:@"1"];
    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];

shadow优化

方案一:如果图层是个简单的几何图形或者圆角图形,我们可以通过设置shadowPath来优化性能,能大幅提高性能。
方案二:还可以通过设置shouldRasterize属性值为YES来强制开启离屏渲染。其实就是光栅化(Rasterization)。当一个图像混合了多个图层,每次移动时,每一帧都要重新合成这些图层,十分消耗性能。当我们开启光栅化后,会在首次产生一个位图缓存,当再次使用时候就会复用这个缓存。但是如果图层发生改变的时候就会重新产生位图缓存。
注意:一般不用于位图缓存,复用的cell反而降低性能。

知识点来源:
https://www.zhihu.com/question/19903344
https://www.zhihu.com/question/19903344

上一篇 下一篇

猜你喜欢

热点阅读