美文网首页
圆角绘制

圆角绘制

作者: Harely | 来源:发表于2020-03-07 22:05 被阅读0次

离屏渲染

离屏渲染定义:指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作;

上下文切换:上下文切换,不管是在GPU渲染过程中,还是一直所熟悉的进程切换,上下文切换在哪里都是一个相当耗时的操作。首先我要保存当前屏幕渲染环境,然后切换到一个新的绘制环境,申请绘制资源,初始化环境,然后开始一个绘制,绘制完毕后销毁这个绘制环境,如需要切换到On-Screen Rendering或者再开始一个新的离屏渲染重复之前的操作。 下图描述了一次mask的渲染操作。

离屏渲染流程
  一次mask发生了两次离屏渲染和一次主屏渲染。即使忽略昂贵的上下文切换,一次mask需要渲染三次才能在屏幕上显示,这已经是普通视图显示3陪耗时,若再加上下文环境切换,一次mask就是普通渲染的30倍以上耗时操作。问我这个30倍以上这个数据怎么的出来的?当我在cell的UIImageView的实例增加到150个,并去掉圆角的时候,帧数才跌至28帧每秒。虽然不是甚准确,但至少反映mask这个耗时是无mask操作的耗时的数十倍的。

离屏渲染消耗性能的原因:

  • 需要创建新的缓冲区,离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);
  • 等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕。

离屏渲染触发的场景:

  • 圆角(同时设置layer.masksToBounds = YES、layer.cornerRadius大于0);
  • 图层蒙版;
  • 阴影:layer.shadowXXX,如果设置了layer.shadowPath就不会产生离屏渲染;
  • 遮罩:layer.mask;
  • 光栅化:layer.shouldRasterize = YES;



离屏渲染检测

  • 真机检测
    在Xcode打开离屏渲染检查机制,如下图:


    打开检测
  • 模拟器检测


    打开检测
离屏渲染了 模拟器Debug检测

切圆角Code

  • 性能最好的
    OC代码
- (void) good {
    
    UIImageView * imageview = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 150, 150)];
    imageview.layer.cornerRadius = 75;
    imageview.center = self.view.center;
    imageview.image = [UIImage imageNamed:@"fire.png"];
    //开始对imageView进行画图, true表示透明 false表示不透明
    UIGraphicsBeginImageContextWithOptions(imageview.bounds.size, true, 0);
    //填充与背景相同的颜色
    [UIColor.systemGroupedBackgroundColor setFill];
    UIRectFill(imageview.bounds);
    //使用贝塞尔曲线画出一个圆形图
    [[UIBezierPath bezierPathWithOvalInRect:imageview.bounds] addClip];
    [imageview drawRect:imageview.bounds];
    imageview.image = UIGraphicsGetImageFromCurrentImageContext();
    //结束画图
    UIGraphicsEndImageContext();
    [self.view addSubview:imageview];
}

Swift代码

public func good() {
    let imageview = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 150))
    imageview.center = view.center
    imageview.image = UIImage(named: "fire.png")
    // 开始对imageView进行画图
    UIGraphicsBeginImageContextWithOptions(imageview.bounds.size, true, 0);
    UIColor.white.setFill()
    UIRectFill(imageview.bounds)
    // 实例化一个圆形的路径
    let path = UIBezierPath(ovalIn: imageview.bounds)
    //  进行路劲裁切   后续的绘图都会出现在圆形内  外部的都被干掉
    path.addClip()
    imageview.draw(imageview.bounds)
    //  取到结果
    imageview.image = UIGraphicsGetImageFromCurrentImageContext()
    // 关闭上下文
    UIGraphicsEndImageContext()
    view.addSubview(imageview)

}

效果:


模拟器测试
  • 不同位置角度切割
//上述方法中的corner参数设置需要变化的角
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
    UIRectCornerTopLeft     = 1 << 0,    //上左
    UIRectCornerTopRight    = 1 << 1,    //上右
    UIRectCornerBottomLeft  = 1 << 2,    //下左
    UIRectCornerBottomRight = 1 << 3,    //下右
    UIRectCornerAllCorners  = ~0UL
};

- (void) test {
    UIImageView *view = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"paCong.jpeg"]];
    view.backgroundColor = UIColor.blackColor;
    view.frame =  CGRectMake(100, 150, 214, 114);
    
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(60,60)];
    CAShapeLayer *layer = [[CAShapeLayer alloc] init];
    layer.frame = view.bounds;
    layer.path = path.CGPath;
    view.layer.mask = layer;
    
    [self.view addSubview:view];
}
不同位置圆角切割
  • 图片绘制圆角
    UIImage+DrawCorner.m
#import "UIImage+DrawCorner.h"

@implementation UIImage (DrawCorner)

//绘制图片圆角
- (UIImage *)drawCornerInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius
{
    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];
    UIGraphicsBeginImageContextWithOptions(rect.size, false, [UIScreen mainScreen].scale);
    CGContextAddPath(UIGraphicsGetCurrentContext(), bezierPath.CGPath);
    CGContextClip(UIGraphicsGetCurrentContext());
    [self drawInRect:rect];
    
    CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathFillStroke);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return image;
}


- (UIImage *)drawCornerPicture{
//    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(200, 400, 200, 200)];
//    imageView.layer.cornerRadius = 100;
//    imageView.backgroundColor = UIColor.purpleColor;
//    imageView.image = [UIImage imageNamed:@"fire.png"];
    // 开启图片上下文
    // UIGraphicsBeginImageContext(imageView.bounds.size);
    // 一般使用下面的方法
    CGSize size = CGSizeMake(200, 200);
    CGRect rect = CGRectMake(0, 0, 200, 200);
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    // 绘制贝塞尔曲线
    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:100];
    // 按绘制的贝塞尔曲线剪切
    [bezierPath addClip];
    // 画图
    [self drawInRect:rect];
    // 获取上下文中的图片
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭图片上下文
    UIGraphicsEndImageContext();
    return image;
//    imageView.frame = CGRectMake(200, 400, 200, 200);
}

@end

调用

- (void)creatControl
{
    CGFloat margin = 20.f;
    CGFloat controlW = (KMainW - margin * 3) * 0.5;
    
    NSArray *titleArray = @[@"离屏渲染", @"非离屏渲染"];
    for (int i = 0; i < titleArray.count; i++) {
        ///第一排View
        CGFloat controlX = margin + (controlW + margin) * I;
        //UILabel
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(controlX, 30, controlW, 40)];
        label.layer.backgroundColor = [[UIColor grayColor] CGColor];
        label.text = titleArray[I];
        label.textAlignment = NSTextAlignmentCenter;
        label.layer.cornerRadius = label.bounds.size.height * 0.5;
        label.layer.masksToBounds = i == 0 ? YES : NO;
        [self.view addSubview:label];
 
        
        ///第二排View
        //UIView
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(controlX, CGRectGetMaxY(label.frame) + margin, controlW, 40)];
        view.backgroundColor = [UIColor grayColor];
        view.layer.cornerRadius = view.bounds.size.height * 0.5;
        view.layer.masksToBounds = i == 0 ? YES : NO;
        [self.view addSubview:view];
        //UIView若未添加子控件,设置view.layer.masksToBounds = YES;也不会造成离屏渲染
        UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(30, 0, controlW - 60, 40)];
        subView.backgroundColor = [UIColor redColor];
        [view addSubview:subView];
 
        
        ///第三排View
        //UITextView
        UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(controlX, CGRectGetMaxY(view.frame) + margin, controlW, 40)];
        textView.userInteractionEnabled = NO;
        textView.backgroundColor = [UIColor grayColor];
        if (i == 0) {
            /*
             这里换了一种实现方法,用UIBezierPath赋值layer.mask,两种方式都会造成离屏渲染
             textView.layer.cornerRadius = textView.bounds.size.height * 0.5;
             textView.layer.masksToBounds = YES;
             */
            UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:textView.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(textView.bounds.size.height * 0.5, textView.bounds.size.height * 0.5)];
            CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
            maskLayer.frame = textView.bounds;
            maskLayer.path = path.CGPath;
            textView.layer.mask = maskLayer;
        }else {
            textView.layer.cornerRadius = textView.bounds.size.height * 0.5;
            textView.layer.masksToBounds = NO;
        }
        [self.view addSubview:textView];
        
        
        ///第四排View
        //UIButton
        UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(controlX, CGRectGetMaxY(textView.frame) + margin, controlW, controlW)];
        button.backgroundColor = UIColor.purpleColor;
        if (i == 0) {
            [button setImage:[UIImage imageNamed:@"fire.png"] forState:UIControlStateNormal];
            button.layer.cornerRadius = button.bounds.size.width * 0.5;
            button.layer.masksToBounds = YES;
        }else {
            [button setImage:[[UIImage imageNamed:@"fire.png"] drawCornerInRect:button.bounds cornerRadius:button.bounds.size.width * 0.5] forState:UIControlStateNormal];
            button.layer.cornerRadius = button.bounds.size.width * 0.5;

        }
        [self.view addSubview:button];


        
        ///第五排View
        //UIImageView设置圆角
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(controlX, CGRectGetMaxY(button.frame) + margin, controlW, controlW)];
        imageView.backgroundColor = UIColor.blueColor;
        if (i == 0) {
            [imageView setImage:[UIImage imageNamed:@"fire.png"]];
            imageView.layer.cornerRadius = imageView.bounds.size.width * 0.5;
            imageView.layer.masksToBounds = YES;
        }else {
            [imageView setImage:[[UIImage imageNamed:@"fire.png"] drawCornerInRect:imageView.bounds cornerRadius:imageView.bounds.size.width * 0.5]];
        }
        [self.view addSubview:imageView];
        
        //UIImageView若未添加子控件,设置imageView.layer.masksToBounds = YES;也不会造成离屏渲染
        UIView *subImageView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, controlW, controlW)];
        subImageView.backgroundColor = [UIColor redColor];
        subImageView.layer.cornerRadius = imageView.bounds.size.width * 0.5;
        [imageView addSubview:subImageView];
        
        
        
        ///第六排View
        //UIImageView设置阴影
        CGFloat imgW = 70.f;
        CGFloat imgPadding = (KMainW - imgW * 4 - margin * 2) / 3;
        UIImageView *shadowImgView = [[UIImageView alloc] initWithFrame:CGRectMake(margin + (imgW + imgPadding) * 2 * i, CGRectGetMaxY(imageView.frame) + margin, imgW, imgW)];
        shadowImgView.backgroundColor = UIColor.cyanColor;
        [shadowImgView setImage:[UIImage imageNamed:@"fire.png"]];
        shadowImgView.layer.shadowColor = [UIColor redColor].CGColor;
        shadowImgView.layer.shadowOpacity = 0.8f;
        shadowImgView.layer.shadowOffset = CGSizeMake(5, 5);
        shadowImgView.layer.shadowRadius = 5.f;
        if (i == 1) {
            UIBezierPath *path = [UIBezierPath bezierPathWithRect:shadowImgView.bounds];
            shadowImgView.layer.shadowPath = path.CGPath;
        }
        [self.view addSubview:shadowImgView];
        
        //UIImageView设置阴影+圆角
        UIImageView *shadowCorImgView = [[UIImageView alloc] initWithFrame:CGRectMake(margin + imgW + imgPadding + (imgW + imgPadding) * 2 * i, CGRectGetMinY(shadowImgView.frame), imgW, imgW)];
        shadowCorImgView.backgroundColor = UIColor.cyanColor;
        [shadowCorImgView setImage:[[UIImage imageNamed:@"fire.png"] drawCornerInRect:imageView.bounds cornerRadius:imageView.bounds.size.width * 0.5]];
        shadowCorImgView.layer.shadowColor = [UIColor redColor].CGColor;
        shadowCorImgView.layer.shadowOpacity = 0.8f;
        shadowCorImgView.layer.shadowOffset = CGSizeMake(0, 0);
        shadowCorImgView.layer.shadowRadius = 5.f;
        if (i == 1) {
            UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:shadowCorImgView.bounds cornerRadius:shadowCorImgView.bounds.size.height * 0.5];
            shadowCorImgView.layer.shadowPath = path.CGPath;
        }
        [self.view addSubview:shadowCorImgView];
    }
}
效果图: 效果如上

参考资料:
阴影,圆角,避免离屏渲染us
切指定圆角,防止离屏渲染 us
实现UIView任意圆角+绘制阴影 us

相关文章

网友评论

      本文标题:圆角绘制

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