Cocoa Drawing Guide学习part1——基础和图形上下文 (转)

原文: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

  • 基于path的绘图
  • 建立,加载,显示图片
  • 布局和显示文本
  • 建立,显示PDF
  • 半透明
  • 阴影
  • 色彩管理
  • 变形
  • 打印
  • 抗锯齿渲染
  • OpenGL

cocoa drawin基于Quartz能够利用硬件资源进行渲染,而且使用的是打印机的模式。所以不一样的绘图顺序也会获得不一样的结果。canvas

绘图环境

绘图环境决定了绘图最终的结果,canvas决定了绘图内容在哪里,绘图设置控制绘图的大小,颜色,质量,方向等。安全

Graphics Context - 绘图上下文

绘图上下文能够说是绘制的位置,封装了绘图所须要的各类信息。
那么绘图的目标能够是下面的东西多线程

  • Window(或者View)
  • 图片,包括了全部类型的Bitmap图片
  • 打印机
  • PDF,EPS文件
  • OpenGL界面

NSGraphicsContext也管理了绘图目标的状态,这些状态会影响如何绘图,好比闲的宽度,颜色,填充色,绘图状态能够保存在当前图形上下文的栈上,全部的改变均可以经过回滚绘图状态来撤销。
cocoa管理的一些属性和Quartz不一样,好比颜色使用NSColor,大部分的基于路径的绘图使用NSBezierPathapp

Coordinate System - 协调系统

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
那么包括下面的东西:布局

  • 移动
  • 缩放
  • 旋转

能够用各类各样的效果组合获得有趣的结果,另外使用变形操做比你直接操做原数据要快字体

颜色和颜色空间

绘图以前须要指定颜色和颜色空间,NSColorNSColorSpace

基础的绘图元素

NSPoint,点,有一个x,y坐标
NSSize,size包含宽,高
NSRect,矩形包含一个叫originNSPoint和一个叫sizeNSSizeorigin是左上角的点位置,size是宽和高

形状基本?(primitives)

能够用NSBezierPath画一些基本形状

  • 线
  • 矩形
  • 曲线
  • 贝塞尔曲线?

primitivesprimitives

贝塞尔路径对象,保存着矢量的路径信息,保证数据小而且分辨率独立。你可使用简单形状建立路径或是与其余基本形状组合更复杂的路径。

图片

NSImage提供图片,NSImageRep是图片的表示类,图片能够从文件加载或者在线(on the fly?),支持BMP,GIF,JPEG,JPEG2000,PNG,TIFF,从EPS,PDF,PICT数据获得的图片和CoreImage图片

文字

cocoa提供了先进的文件系统绘制文字,例如:

texttext

View和Drawing

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。

Graphics Context——图形上下文

标识当前绘图的上下文是设备,屏幕,仍是文件,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弹出当前的状态并恢复到上一个保存的状态
这两个方法,调用须要成对

这两个方法的类方法,做用于当前的图形上下文
这两个方法的实例方法,做用于特定的图形上下文

图形状态信息

  • Current transformation matrix(CTM)

映射view的Coordinate System和目标设备的Coordinate System用的。cocoa在调用drawRect:以前会修改CTM,可使用一个NSAffineTransform对象修改CTM的朝向,缩放,旋转当前的Coordinate System。

  • Clipping areas

截取区域描述了用于调用绘制函数的画布区域,cocoa在调用drawRect:以前会修改截取区域为可视区域,可使用NSBezierPath对象来进一步设置可视区域。

  • Line width

设置路径的宽度,默认是1.0,可使用NSBezierPath对象修改这个值。

  • Line join style

线合并描述了线是如何合并的,默认是NSMiterLineJoinStyle,可使用NSBezierPath对象修改这个值。

  • Line cap style

描述一个路径的开闭,默认是NSButtLineCapStyle,可使用NSBezierPath对象修改这个值。

  • Line dash style

描述线的断开模式,也包括开始的模式,这个属性没有默认值,表示是实线,能够修改NSBezierPath对象的这个样式。

  • Line miter limit

定义线在何时合并成一个。只有Line join style使用了NSMiterLineJoinStyle的时候起效。miter的长度已经被线的宽度除后,若是超过了miter limit的话,那么就用斜面,默认是10.0,可使用NSBezierPath对象修改这个值。

  • Flatness value

描述了其实是那一部分曲线被渲染了。数值越小,那么曲线越平滑,也会有跟复杂的运算,这个值在不一样的设备渲染时影响很是小。默认值是0.6,可使用NSBezierPath对象修改这个值。

  • Stroke color

使用NSColor设置,渲染路径的颜色。

  • Fill color

使用NSColor设置,渲染路径所包含区域的颜色(填充色)。

  • Shadow

使用NSShadow描述所渲染内容的阴影。

  • Rendering intent

描述颜色映射,cocoa不支持,须要使用Quartz。

  • Font name,Font Size

使用NSFont设置

  • Font character spacing

描述文字的字符空间。

  • Text drawing mode

描述文字如何进行渲染(这个属性并非由Cocoa直接支持的)

  • Image interpolation quality

描述渲染时图形插值处理,使用NSGraphicsContext类进行设置。

  • Compositing operation

描述合成过程(cocoa中基于Quartz blend模式来进行支持,可是使用了不要同的使用方法和行为),使用NSGraphicsContext类进行设置,一些渲染方法可以提供额外的设置。

  • Global alpha

设置一个全局的alpha值,用于叠加在使用的颜色的alpha值上。cocoa不直接支持这个属性,经过Quartz中的CGContextSetAlpha函数设置。

  • Anti-aliasing setting

设置抗锯齿,使用NSGraphicsContext设置。

屏幕画布和打印画布

打印原本是不考虑了,不过既然这里单独提到了一节,那我也就顺便一块儿看下了。

通常啊,cocoa的绘图上下文,有两种画布,屏幕上的和打印的。在屏幕上的cocoa提供一个绘图上下文做给有view更新绘制或者响应用户打印文档时使用。可是下面几种状况是须要手工建立图形上下文的:

  • 使用OpenGL渲染View的内容
  • 绘制屏幕外的位图
  • 建立PDF和EPS
  • 经过程序开始一个打印job

使用NSGraphicContext的类方法,能够建立使用屏幕做为画布的图形上下文对象,这些方法没法建立打印的画布,cocoa把全部的打印都经过cocoa printing system(cocoa 打印系统)来处理这些任务并为你提供绘图上下文。所以,咱们须要使用NSPrintOperation建立打印任务,而且你的view至少要提供最少的打印支持。

能够用示例方法isDrawingToScreen或者类方法currentContextDrawingToScreen判断画布的类型。打印类型的画布能够用attributes方法获取画布的更多信息。

绘图上下文和Quartz

NSGraphicsContext在cocoa里面是Quartz绘图上下文CGContextRef数据的一个转化。转化和使用很容易。

修改当前的绘图上下文状态

drawRect:方法中能够用NSGraphicsContext的方法直接修改大部分绘图状态属性,可是还有一些属性须要借助其余的一些对象。

保存和回复当前绘图状态是消耗很是大的动做,尽可能在要立刻撤销一些操做的时候,或者没有修改的时候用,好比重置截取区域的时候。

设置颜色和模式

cocoa提供了不少的颜色空间,NSColor默认支持RGB,CMYK,灰度,也能够用ICC和ColorSync描述来支持自定义颜色空间。使用NSColor对象的setStrokesetFill方法来设置描边和填充的颜色。文字的颜色不使用Fill或是Storke的,须要对文字专门进行设置。

设置路径(Path)属性

路径属性包括了不少,上面提过一些,这里说下设置路径属性有两种,一种是全局的属性经过NSBezierPath类的类方法来设置,例如setDefaultLineWidth:,另外一种是为某个路径对象设置,使用类方法设置,例如setLineWidth:

设置文字属性

cocoa的字符串对象和core text系统都支持,使用属性修改字符串的表现。NSAttributedString对象经过选择部分范围进行设置属性,NSString直接设置整个字符串的属性。

NSFont对象的set font family和size方法能够设置图形状态中的全局字体设置,能够在Quartz绘制字符串的时候使用。

设置图形合成选项

嘛,这个么,用官方的图最好说明问题了

Compositing operations in CocoaCompositing operations in Cocoa

嗯,上面一堆图,基本能说明问题了设置的名字基本都是NSCompositeXXXXXXXXXX之类的

接下来是这些模式的一个数学模型(我仍是懒惰的截图了),R是结果,S是源,D是目标,Sa是源的alpha值,Da是目标的alpha值,alpha值的范围是0~1。

Mathematical equations for compositing colorsMathematical equations for compositing colors

设置截取区域

截取区域是用来肯定对view中的那一部分进行修改,调用drawRect:以前cocoa会根据可视范围设置截取区域,能够用NSBezierPath从新限制绘图区域。简单点的矩形能够用NSRectClip函数建立,NSRectClipList建立一组矩形,NSBezierPath用来建立更复杂的图形。使用addClip方法添加结果形状到当前的截取区域。

根据不一样的环绕规则,建立出来的截取区域也将不一样,以下:

Clipping paths and winding rulesClipping 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类中的方法能够为特定的窗口或是位图建立绘图上下文。

在window上绘图

使用graphicsContextWithWindow:方法为一个特定的窗口建立绘图上下文,当时若是那个窗口有不少子视图,那就很麻烦了,你须要一个一个的去设置,这个建议使用在建立屏幕外的缓冲区的时候用。

这里说下,OSX的大部分窗口已经使用了双缓冲机制,不须要淡腾的再去用屏幕外的窗口和位图去刷新ui,费时费力。

在位图上绘图

在10.4之前, 须要经过截取view或是window,来实现位图绘制。10.4之后能够经过graphicsContextWithBitmapImageRep:方法为一个NSBitmapImageRep对象设置绘图上下文,绘图将会直接渲染到位图上。

建立一个PDF或是PostScript的绘图上下文

不一样与以前的那个PDF和PostScript不能那么直接的建立,他们都要经过cocoa打印系统。
最快捷的方法是用NSView对象的dataWithPDFInsideRect:dataWithEPSInsideRect:方法自动的用view中的绘图代码建立打印任务,输出PDF和EPS。可使用NSPrintOperation类手工建立打印任务,用runOperation方法开始打印队列。

打印过程当中,cocoa调用view上的一些方法来处理布局和绘制,实现这些方法能够支持PDF和EPS的打印。

线程和绘图上下文

AppKit为全部窗口和线程的合并管理一个绘图上下文。对应一个窗口,每一个线程都有一个绘图上下文,也可使用另外一个线程来进行绘图,可是这样会有一些警告。

正常的窗口更新循环中,因此的绘图请求都会发送到应用程序的主线程中。能够经过调用setNeedsDisplay:或者setNeedsDisplayInRect:方法触发重绘事件,这些方法不能在非主线程中调用。

若是必定要在非主线程中更新窗口那么须要本身建立,配置那个窗口的绘图上下文。

建立位图是另外一种多线程绘图的方式,由于位图的画,是本身做为容器的,因此能够安全的在非主线程中建立。

相关文章
相关标签/搜索