iOS --- CoreGraphics中三种绘图context切换方式的区别

来源:互联网 发布:qq三国张飞和关羽js 编辑:程序博客网 时间:2024/06/06 16:31

CoreGraphics的使用过程中,经常会遇到绘图context切换的操作,一般使用用到CGContextSaveGState/CGContextRestoreGState,UIGraphicsPushContext/UIGraphicsPopContext,UIGraphicsBeginImageContext/UIGraphicsEndImageContext这三对方法。
它们的区别如下:

CGContextSaveGState/CGContextRestoreGState

CGContextSaveGState/CGContextRestoreGState用于记录和恢复已存储的绘图context。

[[UIColor redColor] setStroke]; //画笔红色CGContextSaveGState(UIGraphicsGetCurrentContext()); //记录上下文的当前状态[[UIColor blackColor] setStroke]; //画笔黑色CGContextRestoreGState(UIGraphicsGetCurrentContext()); //恢复之前记录的上下文状态UIRectFill(CGRectMake(10, 10, 100, 100)); //绘制红色矩形

UIGraphicsPushContext/UIGraphicsPopContext

UIGraphicsPushContext/UIGraphicsPopContext的作用就完全不同了,而是完全地切换绘图context。使用场景是:

  1. 当前正在使用CoreGraphics绘制图形A,想要使用UIKit绘制完全不同的图形B,此时就希望保存当前绘图context及已绘制内容。
  2. 使用UIGraphicsPushContext切换到一个全新的绘图context。
  3. 使用UIKit绘制图形B。
  4. 使用UIGraphicsPopContext恢复之前的绘图context,继续使用CoreGraphics绘制图形A。

先看一段代码:

const CGFloat Scale = [UIScreen mainScreen].scale;GLsizei width = view.layer.bounds.size.width * Scale;GLsizei height = view.layer.bounds.size.height * Scale;GLubyte *imageData = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();CGContextRef context = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);CGContextScaleCTM(context, Scale, Scale);UIGraphicsPushContext(context);{[view drawViewHierarchyInRect:view.layer.bounds afterScreenUpdates:NO];}UIGraphicsPopContext();CGContextRelease(context);CGColorSpaceRelease(colorSpace); free(texturePixelBuffer);

以上代码的步骤为:

  1. 初始化一块内存区域,用于存储图像纹理texture。建立绘图context。
  2. 因此时未能获取到所需要的图像UIImage,所以只能采用UIKit的截图方式,调用UIView的drawViewHierarchyInRect:afterScreenUpdates:从该view中截取图像。
  3. 截图完毕后,恢复到之前的绘图context。

注意步骤2,因为必须调用UIKit中的绘图方法,所以才需要用到UIGraphicsPushContext/UIGraphicsPopContext。类似的UIKit方法还有

[self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; // 将当前layer渲染到image context中// 绘制贝塞尔曲线UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100));[[UIColor blueColor] setFill];[p fill];

这两种方法也是iOS中常见的截屏方法,但截取的图像有时未必足够清晰。

UIGraphicsBeginImageContext/UIGraphicsEndImageContext

如果想在切换绘图context后,继续使用CoreGraphics绘图(而非UIKit),则不需要使用UIGraphicsPushContext/UIGraphicsPopContext。因为CoreGraphics已将绘图context视为参数。使用场景是:

  1. 当前正在绘制图形A。
  2. 使用UIGraphicsBeginImageContext将旧的绘图context入栈,创建新的绘图context并使用。
  3. 绘制图形B。
  4. 结束绘制图形B之后,使用UIGraphicsEndImageContext恢复到之前的绘图context,继续绘制图形A。

例如,使用如下方法即可在一个绘图步骤中,切换到一个新的绘图context,完成截取view中图像的操作,然后再恢复之前的绘图步骤。
因新的绘图操作是使用UIGraphicsGetImageFromCurrentImageContext()截图,所以仍是CoreGraphics操作,因此不需要使用UIGraphicsPushContext/UIGraphicsPopContext。

// 绘图中UIGraphicsBeginImageContext(CGSizeMake(200, 200));[self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES];UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();imageView.image = snapshot;// 继续绘图

类似的使用方法还有:

// 绘图中UIGraphicsBeginImageContext(CGSizeMake(200, 200));CGContextRef newContext = UIGraphicsGetCurrentContext();CGContextAddEllipseInRect(newContext, CGRectMake(0, 0, 100, 100));CGContextSetFillColorWithColor(newContext, [UIColor blueColor].CGColor);CGContextFillPath(newContext);UIGraphicsEndImageContext();// 继续绘图

注意,绘图context切换的关键是:要看切换新的绘图context后,是要继续使用CoreGraphics绘制图形,还是要使用UIKit。

1 0
原创粉丝点击