Core Graphic(二):context详解
来源:互联网 发布:南宁广电网络 编辑:程序博客网 时间:2024/04/27 21:51
上一篇介绍了CG的历史
context是Quartz的核心概念,在用CG进行画图的时候,必须和context打交道,所以必须要知道context是什么,干什么以及为什么要有context。
我们使用CG最基本的操作就是创建路径,路径是一个用数学描述的图形形状,路径可以是矩形的,圆形的,牛仔帽形甚至是一个泰姬陵的形状。路径内可以用颜色进行填充,每一个像素点都可以被设置成特定的颜色。路径也可以是一个轮廓。这个有点像是字帖。下面是三幅图,一副是一个帽子的轮廓,一副填充蓝色的帽子,另一副是填充黄色。
正如你所见,上面的轮廓还是挺复杂的。它可以被涂上任何颜色。也可以设置线条的样式,可以设置成粗线条也可以是细线条。线条的末端还能设置成矩形或者圆形。等等,还有很多的属性可以设置。
你查看CG的API后会发现,没有一个函数的参数是包含所有属性的。
CGStrokePath (path, fillColor, strokeColor, lineWidth, dashPattern, bloodType, endCap)
取而代之的,是下面这个函数
void CGContextStrokePath(CGContextRef c)
其他的属性是来自于哪里呢?都是从context中获得的。
context属性
context拥有许多全局的绘画状态。他们之间都是独立的。
- current state
- current fill and stroke colors
- line width and pattern
- line cap and join(miter)style
- alpha(transparency) ,antialiasing and blend mode
- shadows
- transformation matrix
- text attribuates(font, size, matrix)
- esoteric things like line flatness and interpolation quality
- and more
有非常多的属性。事实上CG的所有属性并没有被文档化,所以很多属性都是很隐性的。不同的conext(image,PDf,etc)拥有不同的属性。
当CG要开始画东西时,例如“fill a path”,它只会去获取需要的属性。同样的代码可能因为不同的context就会得到截然不同的效果。从某方面来说,这是非常强大的。但从另一方面来看,因为context持有的属性都是全局的,所以很容易将属性搞得一团糟。
如果你的代码是这样的:
draw orange square: set color to orange in the current context fill a rectagle
这个时候画出来的是一个橘色的矩形
draw red valentine: set color to red in the current context fill a valentine
ok,一个红心。这个时候你想将这个代码加到第一段代码中:
draw orange square: set color to orange in the current context draw red valentine fill a rectangle
这个时候画出来的矩形就变成了红色,为什么?因为画红心的代码中将当前的color设置成了红色。之前的橘色被红色冲掉了,如何避免这样的bug呢?
这里有两种方法:一种是将当前的颜色进行存储(橘色),然后画红心(红色),然后再将橘色重新赋值回去,画矩形。这种方式可以解决上述问题,但是这样做的坏处是,如果context的属性较多,层级较深,这样保存很容易混乱。当然你会想到一个方法就是获取当前context的属性,但是不好意思,CG没有提供getter方法来获取context属性。
context栈
对于上述的问题一个可行的方法就在改变全局属性之前保存整个context。保存context,调整线条属性,颜色属性,绘画。然后恢复context。CG提供了一个保存和恢复context的API。保存context其实就是保存全局属性。实际上,后端是用一个栈结构来保存这些属性的。
当保存context时,就会将当前所有的属性push到栈中。当恢复context时,就会pop出上次的属性,然后当前context也就恢复到上次的状态。下面就来演示下如何用这种方面修复上述的bug:
draw red valentine: save graphic state set color to red in the current context fill a valentine restore graphics state
然后,整个流程就像下面这样:
set color to orange in the current context save graphics state set colot to red in the current context fill a valenine restore graphic state fill a rectangle
下面用图示来演示上述代码:
Core Graphics API
到此为止,上面都是用伪代码在讲述,究竟CG的代码是什么样的呢?它们看上去有点点负责。Core graphics 风格和 Core Fundation API一样。其中比较让人头疼的是定义指针类型。但是在CG中,凡是“Ref”结尾的,例如CGContextRef活着CGColorRef。这些在实际的定义中就是指针类型的变量。
正确的形式
CGContextRef currentContext = ....; // actually a pointer
错误的形式
CGContextRef *currentContext = ...; // pointer to a pointer
在绘画的时候,很少自己去创建一个context,大多数情况下是获得当前的context:
CGContextRef context = UIGraphicsGetCurrentContext ();
在Mac中这样获取:
CGContextRef context = [NSGraphicsContext.currentContext graphicsPort] //before 10.10
CGContextRef context = [NSGraphicsContext.currentContext CGContext] //10.10
Draw
因为CG是一套C语言实现的面向对象API,函数中传入的第一个参数是“对象”。 例如,下面是画一个矩形轮廓的代码:
CGContextRef context = ...; // See above for the proper call for your platformCGRect bounds = someView.bounds;CGContextStrokeRect (context, bounds);
CGCotextStrokeRect将CGContextRef设置为第一个参数。这个函数的目的是画一个矩形轮廓。如果context是一个image类型,或者是要渲染当前屏幕的,则这个矩形就会去获取当前全局变量中的属性(线宽,颜色,图案等)。如果这个context是PDF,则就会生成特定的指令文件让PDf viewer来解析。
Context
GrafDemo是一个CG的Demo,它会讲CG的方方面面都做一个演示。代码在Github中。
GrafDemo包含了一个NSView,画了一个边框为蓝色,填充是绿色的圆圈。白色的背景边框是黑色。
上面有两个版本的图形,一个是正确的Context属性,另一个不是。注意那个错误的图形。白色背景的边框被设置成了蓝色。Demo中同时含有OC和Swift版本的。
获取当前view上的context很简单,下面是Mac中的获取方法:
- (CGContextRef) currentContext { return [NSGraphicsContext.currentContext graphicsPort];} // currentContext
在view的drawRect方法中,实现绘制图形背景线条的函数。 [code]
绘制背景和轮廓的函数也很简单:
- (void) drawSloppily { CGContextRef context = self.currentContext; CGContextSetRGBStrokeColor (context, 0.0, 0.0, 0.0, 1.0); // Black CGContextSetRGBFillColor (context, 1.0, 1.0, 1.0, 1.0); // White [self drawSloppyBackground]; [self drawSloppyContents]; [self drawSloppyBorder];} // drawSloppily
- (void) drawSloppyBackground { CGContextFillRect (self.currentContext, self.bounds);} // drawSloppyBackground- (void) drawSloppyBorder { CGContextStrokeRect (self.currentContext, self.bounds);} // drawSloppyBorder
下面的代码是在drawRect中调用的,但是,这里存在一个问题:
- (void) drawSloppyContents { CGContextRef context = self.currentContext; CGRect innerRect = CGRectInset (self.bounds, 20, 20); CGContextSetRGBFillColor (context, 0.0, 1.0, 0.0, 1.0); // Green CGContextFillEllipseInRect (context, innerRect); CGContextSetRGBStrokeColor (context, 0.0, 0.0, 1.0, 1.0); // Blue CGContextSetLineWidth (context, 6.0); CGContextStrokeEllipseInRect (context, innerRect);} // drawSloppyContents
注意,上述代码中,讲context中的一些属性进行了改变,这些都是全局属性。所以会影响后面的背景边框颜色。
Push&pull
修复上面bug的一个方法就是,在绘制矩形之前,保存context,绘制之后,恢复context。CGContextSaveGState将当前的contextpush到栈中。CGContextRestoreGState当栈顶的context恢复到当前的context中。
下面是一个修复后的代码版本:
- (void) drawNiceContents { CGContextRef context = self.currentContext; CGContextSaveGState (context); { CGRect innerRect = CGRectInset (self.bounds, 20, 20); CGContextSetLineWidth (context, 6.0); CGContextSetRGBFillColor (context, 0.0, 1.0, 0.0, 1.0); // Green CGContextFillEllipseInRect (context, innerRect); CGContextSetRGBStrokeColor (context, 0.0, 0.0, 1.0, 1.0); // Blue CGContextStrokeEllipseInRect (context, innerRect); } CGContextRestoreGState (context);} // drawNiceContents
代码中的{}符号,只是个人的编码风格,是为了让代码看上去更加有逻辑性,加不加对功能都没有任何影响。
Swift
CG的代码在OC和swift中使用几乎是一样的,唯一不同的是末尾不需要分号。下面是swift版本的代码:
func drawSloppyContents() { let innerRect = CGRectInset(bounds, 20.0, 20.0) CGContextSetRGBFillColor (currentContext, 0.0, 1.0, 0.0, 1.0) // Green CGContextFillEllipseInRect (currentContext, innerRect) CGContextSetRGBStrokeColor (currentContext, 0.0, 0.0, 1.0, 1.0) // Blue CGContextSetLineWidth (currentContext, 6.0) CGContextStrokeEllipseInRect (currentContext, innerRect)}
在OC中{}符号不适用于swift,所以这里换作函数闭包的形式来保持代码的逻辑性:
private func saveGState(drawStuff: () -> ()) -> () { CGContextSaveGState (currentContext) drawStuff() CGContextRestoreGState (currentContext)}
func drawNiceContents() { saveGState { let innerRect = CGRectInset(self.bounds, 20.0, 20.0) CGContextSetRGBFillColor (self.currentContext, 0.0, 1.0, 0.0, 1.0) // Green CGContextFillEllipseInRect (self.currentContext, innerRect) CGContextSetRGBStrokeColor (self.currentContext, 0.0, 0.0, 1.0, 1.0) // Blue CGContextSetLineWidth (self.currentContext, 6.0) CGContextStrokeEllipseInRect (self.currentContext, innerRect) }}
获取context
swift中获取context非常简单,也不需要用到Ref指针这些概念:
let context = UIGraphicsGetCurrentContext()
UIGraphicsGetCurrentContext()在OC中是返回一个CGContextRef类型的变量。
OSX 10.10中的获取方法:
let context = NSGraphicsContext.currentContext?.CGContext
10.9的获取就稍许负责一些,因为NSGraphicsContext中的 -graphicsPort返回的是一个Void*类型的变量,Swift就不能推断出类型了。下面是一种解决方案:
private var currentContext : CGContext? { get { // The 10.10 SDK provides a CGContext on NSGraphicsContext, but // that's not available to folks running 10.9, so perform this // violence to get a context via a void*. // iOS can just use UIGraphicsGetCurrentContext. let unsafeContextPointer = NSGraphicsContext.currentContext()?.graphicsPort if let contextPointer = unsafeContextPointer { let opaquePointer = COpaquePointer(contextPointer) let context: CGContextRef = Unmanaged.fromOpaque(opaquePointer).takeUnretainedValue() return context } else { return nil } }}
- Core Graphic(二):context详解
- Core Graphic(一):历史与介绍
- Spring核心组件详解(Bean、Context、Core)
- iphone Core Graphic入门教程
- 绘制字符串(core graphic)
- IOS-Core Graphic 画图
- iOS core graphic使用分析
- core graphic这个框架实例
- Android中Context详解 ---- 你所不知道的Context(二)
- TVSKIN源代码阅读日记(七)--- DEVICE CONTEXT之Graphic objects
- TVSKIN源代码阅读日记(八)--- DEVICE CONTEXT之Graphic Modes
- GLES2 Graphic Engine Practice(二)Android平台ndk编程
- Android 4.4 Graphic系统详解(2) VSYNC的生成
- Android 4.4 Graphic系统详解(3) VSYNC的处理
- Android 4.4 Graphic系统详解(2) VSYNC的生成
- Android 4.4 Graphic系统详解(4)HWUI概述
- Android 4.4 Graphic系统详解(2) VSYNC的生成
- Android 4.4 Graphic系统详解(4)HWUI概述
- fetch = FetchType.EAGER
- VC开发类似Circos的图表
- android选项卡
- CodeBlocks问题解决
- iOS 自动化测试 积累
- Core Graphic(二):context详解
- MyBatis Batch Update Exception 使用foreach 批量update 出错
- android ListView显示多个类型item 和 item中控件抢夺焦点解决办法
- 何为三层架构
- uAP 吞吐率测试
- 算法提高 最大乘积
- mac 查看隐藏文件
- 仿Mac菜单guide.js全部代码
- Bootstrap