美文网首页
图片渲染基础知识总结

图片渲染基础知识总结

作者: woniu | 来源:发表于2018-04-08 18:09 被阅读54次

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

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

    首先,我们提出我们的疑问,什么是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

    相关文章

      网友评论

          本文标题:图片渲染基础知识总结

          本文链接:https://www.haomeiwen.com/subject/vhemhftx.html