在OpenGL中,GPU有2种渲染方式
- On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作
- Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作
离屏渲染消耗性能的原因
-
需要创建新的缓冲区;
-
离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕。
哪些操作会触发离屏渲染?
- 光栅化,layer.shouldRasterize = YES;
- 遮罩,layer.mask;
- 圆角,同时设置layer.masksToBounds = YES、layer.cornerRadius大于0;
- 考虑通过CoreGraphics绘制裁剪圆角,或者叫美工提供圆角图片。
- 阴影,layer.shadowXXX;
-
如果设置了layer.shadowPath就不会产生离屏渲染。
官方的优化
-
iOS 9.0 之前UIimageView和UIButton设置圆角都会触发离屏渲染。
-
iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染,但是设置其他阴影效果之类的还是会触发离屏渲染。
开发者的优化
方法一:UIBezierPath和CAShapeLayer
// CAShapeLayer动画渲染直接提交到手机的GPU当中,内存消耗少,渲染速度快,能大大优化内存使用情况。
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.opacity = 0.5;
shapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:view.bounds].CGPath;
view.layer.mask = shapeLayer;
方法二:利用CoreGraphics画一个圆形上下文,然后把图片绘制上去,得到一个圆形的图片
- (UIImage *)drawCircleImage:(UIImage*)image {
CGFloat imgWidth = image.size.width;
CGFloat imgHeight = image.size.height;
CGFloat side = MIN(imageWidth, imageHeight);
CGFloat scale = [UIScreen mainScreen].scale;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(side, side), false, scale);
CGRect rect = CGRectMake(0, 0, side, side);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddPath(context, [UIBezierPath bezierPathWithOvalInRect: rect].CGPath);
CGContextClip(context);
CGFloat marginX = -(image.size.width - side) * 0.5;
CGFloat marginY = -(image.size.height - side) * 0.5;
[image drawInRect:CGRectMake(marginX, marginY, imageWidth, imageHeight)];
CGContextDrawPath(context, kCGPathFillStroke);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
方法三:使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角
CGRect rect = CGRectMake(100,100,100,100);
UIImageView *imageView = [[UIImageView alloc] initWithFrame: rect];
imageView.image = [UIImage imageNamed:@"myImg"];
// 开始对imageView进行画图
CGRect bounds = imageView.bounds;
UIGraphicsBeginImageContextWithOptions(bounds.size,NO,1.0);
// 使用贝塞尔曲线画出一个圆形图
CGFloat width = imageView.frame.size.width;
[[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:width] addClip];
[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
// 结束画图
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
方法四:AsyncDisplayKit(异步绘制框架)
点击查看AsyncDisplayKit项目
方法五:程序员+设计师
- 直接让设计师把图片切成圆角进行显示
- 尽量使用不包含透明(alpha)通道的图片资源
- 用户上传图片,可以让服务端处理圆角