原文:http://noark9.github.io/2013/12/28/cocoa-drawing-guide-study-part-1/git
cocoa drawing由AppKit提供而且也兼容其余的模式:
Quartz,OpenGL,Core Image,Core Video,Quartz Composer,PDF Kit,QuickTime
基于Quartz,因此AppKit提供了Quartz相关的功能github
cocoa drawin基于Quartz能够利用硬件资源进行渲染,而且使用的是打印机的模式。所以不一样的绘图顺序也会获得不一样的结果。canvas
绘图环境决定了绘图最终的结果,canvas决定了绘图内容在哪里,绘图设置控制绘图的大小,颜色,质量,方向等。安全
绘图上下文能够说是绘制的位置,封装了绘图所须要的各类信息。
那么绘图的目标能够是下面的东西多线程
NSGraphicsContext也管理了绘图目标的状态,这些状态会影响如何绘图,好比闲的宽度,颜色,填充色,绘图状态能够保存在当前图形上下文的栈上,全部的改变均可以经过回滚绘图状态来撤销。
cocoa管理的一些属性和Quartz不一样,好比颜色使用NSColor,大部分的基于路径的绘图使用NSBezierPathapp
Coordinate System是cocoa支持使用Quartz应用的,使用浮点数,绘图代码绘制在用户的协调空间,在渲染到具体设备的时候,转换到设备的Coordinate Space。Coordinate Systemide
User Coordinate Space使用具体的值,每个单位是1/72英寸,但并不表明是72dpi。不使用像素或是dpi,你只须要考虑大小,而显示由cocoa去关心。Device Coordinate Space使用设备的解析度单位,cocoa负责Device Coordinate Space和User Coordinate Space的转换。函数
变形操做2d协调空间,变形使用一个变形的数学矩阵,获取变形属性如何修改协调,cocoa中变形是NSAffineTransform
类
那么包括下面的东西:布局
能够用各类各样的效果组合获得有趣的结果,另外使用变形操做比你直接操做原数据要快字体
绘图以前须要指定颜色和颜色空间,NSColor
和NSColorSpace
NSPoint
,点,有一个x,y坐标NSSize
,size包含宽,高NSRect
,矩形包含一个叫origin
的NSPoint
和一个叫size
的NSSize
,origin
是左上角的点位置,size
是宽和高
能够用NSBezierPath
画一些基本形状
贝塞尔路径对象,保存着矢量的路径信息,保证数据小而且分辨率独立。你可使用简单形状建立路径或是与其余基本形状组合更复杂的路径。
由NSImage
提供图片,NSImageRep是图片的表示类,图片能够从文件加载或者在线(on the fly?),支持BMP,GIF,JPEG,JPEG2000,PNG,TIFF,从EPS,PDF,PICT数据获得的图片和CoreImage图片
cocoa提供了先进的文件系统绘制文字,例如:
cocoa中基本上全部的绘图都在view里面完成,View对象表示一个窗口上可视的一部分。一个视图用来显示一些可视的内容,也能够包括多个子视图。
NSView
是全部视图的基础,cocoa有一些基础的视图,text view,split view等,cocoa控件也是基于NSView
的。
能够基于基础的视图和控件建立自定义的视图,cocoa经过drawRect:
消息告诉你视图须要如何绘制,实现drawRect:
方法实现自定义绘图。
1 2 3 4 5 |
- (void)drawRect:(NSRect)rect
{
// Custome draw code
}
|
drawRect:
调用时,cocoa将绘画焦点锁定到你的view,保存绘图转贴,调整当前的变形矩阵适应你的视图的方向,调整从view中截取的矩形,咱们只须要完成绘制就能够了。
打印和建立PDF,EPS的话,在这里就不看了,实际用到的时候看吧。
在自定义视图中实现drawRect:
方法可使用路径,文字,图形或者Cocoa,Quartz,OpenGL等东西
向View发送setNeedsDisplayInRect:
或者setNeedsDisplay:
消息,告诉View部分或者所有的内容已经失效而且须要更新,在下一个更新循环的时候cocoa会响应后发送一个drawRect:
消息给View进行更新。
使用Core Animation,设置Timer,或者使用NSAnimation或者NSViewAnimation类时在必定帧率产生的通知。接收到消息时,让view中部分或者所有失效来进行强制更新。能够参考Core Animation Programming Guide,以及后面会说道的NSTimer和使用Core Animation Objects。
NSView中的inLiveResize方法判断是否正在发生改变大小的事件。为了保证你的View可以如预期同样,那么尽可能少的进行绘制,能够参考Drawing Performance Guidelines。
标识当前绘图的上下文是设备,屏幕,仍是文件,Coordinate System,边界等一些图形属性。基本上不须要人工建立一个图形上下文。Cocoa应用中基本上全部的画布都使用NSGraphicsContext。(OpenGL的话,使用NSOpenGLContext)
drawRect:
调用的以前,cocoa就会对当前的绘图上下文先有必定的处理:
1 保存当前图形状态,保证能够undo
1 添加一个适当的变形,保证方向跟如今View的方向是一致的
1 在可视范围内截取区域,防止内容被其余的视图(Straying into other views)渲染
你的绘图将会发送到Quartz Compositor和其余在窗口中的视图合并起来。
在你的drawRect:
结束后,cocoa回复绘图上下文为下一个view绘制作准备。
能够这样获取
NSGraphicsContext *context = [NSGraphicsContext currentContext];
使用saveGraphicsState
保存当前的图形状态
使用restoreGraphicsState
弹出当前的状态并恢复到上一个保存的状态
这两个方法,调用须要成对
这两个方法的类方法,做用于当前的图形上下文
这两个方法的实例方法,做用于特定的图形上下文
映射view的Coordinate System和目标设备的Coordinate System用的。cocoa在调用drawRect:
以前会修改CTM,可使用一个NSAffineTransform
对象修改CTM的朝向,缩放,旋转当前的Coordinate System。
截取区域描述了用于调用绘制函数的画布区域,cocoa在调用drawRect:
以前会修改截取区域为可视区域,可使用NSBezierPath
对象来进一步设置可视区域。
设置路径的宽度,默认是1.0,可使用NSBezierPath
对象修改这个值。
线合并描述了线是如何合并的,默认是NSMiterLineJoinStyle
,可使用NSBezierPath
对象修改这个值。
描述一个路径的开闭,默认是NSButtLineCapStyle
,可使用NSBezierPath
对象修改这个值。
描述线的断开模式,也包括开始的模式,这个属性没有默认值,表示是实线,能够修改NSBezierPath
对象的这个样式。
定义线在何时合并成一个。只有Line join style使用了NSMiterLineJoinStyle
的时候起效。miter的长度已经被线的宽度除后,若是超过了miter limit的话,那么就用斜面,默认是10.0,可使用NSBezierPath
对象修改这个值。
描述了其实是那一部分曲线被渲染了。数值越小,那么曲线越平滑,也会有跟复杂的运算,这个值在不一样的设备渲染时影响很是小。默认值是0.6,可使用NSBezierPath
对象修改这个值。
使用NSColor设置,渲染路径的颜色。
使用NSColor设置,渲染路径所包含区域的颜色(填充色)。
使用NSShadow描述所渲染内容的阴影。
描述颜色映射,cocoa不支持,须要使用Quartz。
使用NSFont设置
描述文字的字符空间。
描述文字如何进行渲染(这个属性并非由Cocoa直接支持的)
描述渲染时图形插值处理,使用NSGraphicsContext
类进行设置。
描述合成过程(cocoa中基于Quartz blend模式来进行支持,可是使用了不要同的使用方法和行为),使用NSGraphicsContext
类进行设置,一些渲染方法可以提供额外的设置。
设置一个全局的alpha值,用于叠加在使用的颜色的alpha值上。cocoa不直接支持这个属性,经过Quartz中的CGContextSetAlpha
函数设置。
设置抗锯齿,使用NSGraphicsContext
设置。
打印原本是不考虑了,不过既然这里单独提到了一节,那我也就顺便一块儿看下了。
通常啊,cocoa的绘图上下文,有两种画布,屏幕上的和打印的。在屏幕上的cocoa提供一个绘图上下文做给有view更新绘制或者响应用户打印文档时使用。可是下面几种状况是须要手工建立图形上下文的:
使用NSGraphicContext
的类方法,能够建立使用屏幕做为画布的图形上下文对象,这些方法没法建立打印的画布,cocoa把全部的打印都经过cocoa printing system(cocoa 打印系统)来处理这些任务并为你提供绘图上下文。所以,咱们须要使用NSPrintOperation
建立打印任务,而且你的view至少要提供最少的打印支持。
能够用示例方法isDrawingToScreen
或者类方法currentContextDrawingToScreen
判断画布的类型。打印类型的画布能够用attributes
方法获取画布的更多信息。
NSGraphicsContext
在cocoa里面是Quartz绘图上下文CGContextRef
数据的一个转化。转化和使用很容易。
在drawRect:
方法中能够用NSGraphicsContext
的方法直接修改大部分绘图状态属性,可是还有一些属性须要借助其余的一些对象。
保存和回复当前绘图状态是消耗很是大的动做,尽可能在要立刻撤销一些操做的时候,或者没有修改的时候用,好比重置截取区域的时候。
cocoa提供了不少的颜色空间,NSColor默认支持RGB,CMYK,灰度,也能够用ICC和ColorSync描述来支持自定义颜色空间。使用NSColor对象的setStroke
和setFill
方法来设置描边和填充的颜色。文字的颜色不使用Fill或是Storke的,须要对文字专门进行设置。
路径属性包括了不少,上面提过一些,这里说下设置路径属性有两种,一种是全局的属性经过NSBezierPath
类的类方法来设置,例如setDefaultLineWidth:
,另外一种是为某个路径对象设置,使用类方法设置,例如setLineWidth:
。
cocoa的字符串对象和core text系统都支持,使用属性修改字符串的表现。NSAttributedString
对象经过选择部分范围进行设置属性,NSString
直接设置整个字符串的属性。
用NSFont
对象的set font family和size方法能够设置图形状态中的全局字体设置,能够在Quartz绘制字符串的时候使用。
嘛,这个么,用官方的图最好说明问题了
Compositing operations in Cocoa
嗯,上面一堆图,基本能说明问题了设置的名字基本都是NSCompositeXXXXXXXXXX
之类的
接下来是这些模式的一个数学模型(我仍是懒惰的截图了),R是结果,S是源,D是目标,Sa是源的alpha值,Da是目标的alpha值,alpha值的范围是0~1。
Mathematical equations for compositing colors
截取区域是用来肯定对view中的那一部分进行修改,调用drawRect:
以前cocoa会根据可视范围设置截取区域,能够用NSBezierPath
从新限制绘图区域。简单点的矩形能够用NSRectClip
函数建立,NSRectClipList
建立一组矩形,NSBezierPath
用来建立更复杂的图形。使用addClip
方法添加结果形状到当前的截取区域。
根据不一样的环绕规则,建立出来的截取区域也将不一样,以下:
Clipping paths and winding rules
下面代码建立上面图形的截取区域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// If you plan to do more drawing later, it's a good idea // to save the graphics state before clipping. [NSGraphicsContext saveGraphicsState]; // Create the path and add the shapes NSBezierPath* clipPath = [NSBezierPath bezierPath]; [clipPath appendBezierPathWithRect:NSMakeRect(0.0, 0.0, 100.0, 100.0)]; [clipPath appendBezierPathWithOvalInRect:NSMakeRect(50.0, 50.0, 100.0, 100.0)]; // Add the path to the clip shape. [clipPath addClip]; // Draw the image. [NSGraphicsContext restoreGraphicsState]; |
可使用setClip
方法设置截取区域,可是这个方法将会替换原来的截取区域,
这个效果么,你们应该都知道了,能够用NSGraphicContext
中的setShouldAntialias:
方法设置。
若是在view和打印的画布,能够用cocoa提供的绘图上下文。不过若是你要作其余类型的绘图,那么就须要本身建立了。
若是须要在常规的更新周期以外绘制图形,那么须要单独的建立一个图形上下文。这样能够在另一个线程中,在一个屏幕外的窗口或是位图上进行绘图,以后再拷贝结果,使用NSGraphicsContext
类中的方法能够为特定的窗口或是位图建立绘图上下文。
使用graphicsContextWithWindow:
方法为一个特定的窗口建立绘图上下文,当时若是那个窗口有不少子视图,那就很麻烦了,你须要一个一个的去设置,这个建议使用在建立屏幕外的缓冲区的时候用。
这里说下,OSX的大部分窗口已经使用了双缓冲机制,不须要淡腾的再去用屏幕外的窗口和位图去刷新ui,费时费力。
在10.4之前, 须要经过截取view或是window,来实现位图绘制。10.4之后能够经过graphicsContextWithBitmapImageRep:
方法为一个NSBitmapImageRep
对象设置绘图上下文,绘图将会直接渲染到位图上。
不一样与以前的那个PDF和PostScript不能那么直接的建立,他们都要经过cocoa打印系统。
最快捷的方法是用NSView对象的dataWithPDFInsideRect:
和dataWithEPSInsideRect:
方法自动的用view中的绘图代码建立打印任务,输出PDF和EPS。可使用NSPrintOperation
类手工建立打印任务,用runOperation
方法开始打印队列。
打印过程当中,cocoa调用view上的一些方法来处理布局和绘制,实现这些方法能够支持PDF和EPS的打印。
AppKit为全部窗口和线程的合并管理一个绘图上下文。对应一个窗口,每一个线程都有一个绘图上下文,也可使用另外一个线程来进行绘图,可是这样会有一些警告。
正常的窗口更新循环中,因此的绘图请求都会发送到应用程序的主线程中。能够经过调用setNeedsDisplay:
或者setNeedsDisplayInRect:
方法触发重绘事件,这些方法不能在非主线程中调用。
若是必定要在非主线程中更新窗口那么须要本身建立,配置那个窗口的绘图上下文。
建立位图是另外一种多线程绘图的方式,由于位图的画,是本身做为容器的,因此能够安全的在非主线程中建立。