转载请带上出处, 谢谢.
Quartz 2D是一个二维图形绘制引擎, 它支持iOS环境和Mac OS X非内核下的所有应用环境. 你可以使用 Quartz 2D API 来实现许多的功能, 例如基本路径的绘制, 透明层, 描影, 绘制阴影, 透明层, 色彩管理, 抗锯齿渲染, PDF 文档生成和 PDF 元数据访问. 在需要的时候, Quartz 2D 还可以利用图形硬件的能力.
在 Mac OS X中, Quartz 2D 可以与其他图形图像技术混合使用, 例如Core Image, Core Video, OpenGL和QuickTime. 例如, 通过使用 QuickTime的GraphicsImportCreateCGImage函数,可以用 Quartz从一个 QuickTime图形导入器中创建一个图像. 详情可以查看QuickTime Framework Reference . 移动Quartz 2D和Mac OS X中的Core Image之间的数据交换描述了如何能提供一个图像来转换成Core Image. (Core Image是一个支持图像处理的框架. )
同样地, 在IOS中, Quartz 2D与所有可用的图形和动画的技术可以混合使用,如Core Animation,OpenGL ES和UIKit类.
The Page
Quartz 2D在图像中使用了绘画者模型(painter’s model). 在绘画者模型中, 每个连续的绘制操作都是将一个绘制层(a layer of 'paint')放置于一个画布('canvas'),我们通常称这个画布为(Page). Page上的绘图可以通过添加绘图操作来叠加更多绘图来改变. 一个Page上的图形对象除了通过叠加更多的绘图来改变外别无他法. 这个模型允许你可以通过非常多小的图元来构建超级复杂的图形.
图 1-1 展示了绘画者模型如何工作. 在这个图的上半部分, 左边的图像先被绘制上去然后再是实心的图像. 结果除了一点点边界实心的图案把第一个图案完全遮盖的看不到了. 在这个图的下半部分就是将反过来绘制, 让实心的图案先绘制. 正如你所看到的, 在绘画者模式中绘制的顺序是非常重要的.
Figure 1-1 绘画者模型
Page可能是一张真的纸 (如果输出设备是打印机); 它也有可能是一张虚拟的纸 (如果输出设备是PDF文件); 它还可以是bitmap图像. 这根据实际使用的graphics content 而定.
Drawing Destinations: The Graphics Context
Graphics Context是一个不透明数据类型 (CGContextRef) , 用于封装Quartz绘制图像到输出设备的信息, 例如 PDF 文件, bitmap 或者显示器的窗口上. Graphics Context中的信息包括在Page中的图像的图形绘制参数和设备相关的表现形式. Quartz中的所有对象都是绘制或者包含到一个Graphics Context中.
你可以将Graphics Context当成一个绘制目标, 如图 1-2所示.当你使用 Quartz 绘图时, 所有设备相关的特性都包含在你所使用的Graphics Context中. 换句话说, 你可以简单的将同一图形画到不同的设备上, 只需要提供不同的 Graphics Context 然后用一样的顺序进行Quartz绘图. 你不用进行任何设备相关的计算; Quartz都替你做好了.
图 1-2 Quartz 绘制目标
这些Graphics Context可以在你的应用程序中使用:
• bitmap graphics context 允许你将RGB Colors,CMYK Colors,或灰度(grayscale)加到位图(bitmap)中. 位图(bitmap)是一个有像素组成的矩形阵列(rectangular array)(或栅格(raster)),每个像素都表示图像中的一个点. 位图图像(bitmap image)也称为样本图像(sampled image) . 更多详情参见Creating a Bitmap Graphics Context.
• PDF graphics context 允许你创建 PDF 文件. 在 PDF 文件中, 你的绘图保留为命令的序列. PDF文件和位图之间有一些显着的差异:
• PDF 文件不像位图(bitmap)可能有且不止一张Page.
• 当你在不同的设备上绘制PDF 的 Page时, 结果图像会根据当前的设备来进行显示特性优化.
• PDF 文件的分辨率独立性质—绘制的图案大小可以无限的放大或者缩小而不牺牲图像细节. 而用户感知的位图质量取决于当前正在被浏览的位图的分辨率 .
更多详情参见 Creating a PDF Graphics Context.
• window graphics context 是一个可以在窗口上绘制的Graphics Context. 请注意. 因为Quartz 2D 是一个图形引擎而不是窗口管理系统, 你使用一个应用程序框架来获得一个窗口的Graphics Context. 详情查看 Creating a Window Graphics Context in Mac OS X .
• layer context (CGLayerRef) 是一个与其他 Graphics Context有关联的一个离屏绘制目标. 当给Graphics Context 绘制层的时候为了最佳的性能而被创建出来, 对于离屏绘制来说layer context是比bitmap graphics context更好的选择. 详情参见 Core Graphics Layer Drawing.
• 当你想在 Mac OS X 中打印时, 你需要将你的内容发给 PostScript graphics context , 它由printing framework管理. 更多详情参见 Obtaining a Graphics Context for Printing.
Quartz 2D 不透明数据类型
除了 Graphics Contexts 外 Quartz 2D API 还定义了许多不透明数据类型. 由于这些API是 Core Graphics framework 的一部分, 所以这些数据类型和在这些之上的程序都是用CG开头的.
Quartz 2D 使用这些不透明数据类型来创建对象. 通过操作这些对象来获得特定的绘制图形. 图 1-3 展示了3种使用Quartz 2D的绘制操作能获得的图像. 比如:
• 你可以通过创建一个PDF Page对象来旋转和展示它, 对 graphics context 应用旋转操作, 并且要求 Quartz 2D将 page 绘制到 graphics context 中.
• 你可以通过创建一个pattern对象来画一个图案, 定义构成pattern的形状, 并且在绘制到Graphics Context是设置 Quartz 2D 使用这个pattern来绘制.
• 您可以通过创建一个Shading对象来填充一个轴向或径向的渐晕效应区域, 提供了一个确定渐晕上每个点颜色并且要求 Quartz 2D 使用这些颜色来填充Shading的函数.
图 1-3 Quartz 2D 中的不透明数据类型是原生绘图的基础
Quartz 2D 中的不透明数据类型包括以下这些 :
• CGPathRef, 用于矢量图,可以创建路径,并进行填充(fill)或描画(stroke).参见Paths
• CGImageRef, 用于表示位图图像 (bitmap images) 和表示基于你提供的采样数据的位图图像遮罩 (bitmap image masks) . 参见 Bitmap Images and Image Masks.
• CGLayerRef, 用于表示能够被重复绘制的 (如背景或模式(Pattern)) 和离屏绘制的绘制层. 参见 Core Graphics Layer Drawing
• CGPatternRef, 用于重复绘制. 参见 Patterns.
• CGShadingRef 和 CGGradientRef, 用于绘制渐变. 参见 Gradients.
• CGFunctionRef, 用于定义包含一个随机的浮点值参数的回调函数 当你为shading创建gradients时使用这个数据类型. 参见 Gradients.
• CGColorRef 和 CGColorSpaceRef, 用于告诉 Quartz 怎么理解颜色. 参见 Color and Color Spaces.
• CGImageSourceRef 和 CGImageDestinationRef, 用于在 Quartz 中移动数据. 参见 Data Management in Quartz 2D 和 Image I/O Programming Guide.
• CGFontRef, 用于绘制文字, 参见 Text.
• CGPDFDictionaryRef, CGPDFObjectRef, CGPDFPageRef, CGPDFStream, CGPDFStringRef, 和 CGPDFArrayRef, 提供了 PDF 元数据的访问. 参见 PDF Document Creation, Viewing, and Transforming.
• CGPDFScannerRef 和 CGPDFContentStreamRef, 用于解析 PDF 元数据. 参见 PDF Document Parsing.
• CGPSConverterRef, 用于将 PostScript 转换成 PDF. 在 iOS 中不可用. 参见 PostScript Conversion.
Graphics States
Quartz 根据当前图形状态(current graphics state)中的参数来修改绘制操作的结果. 图形状态包含用于绘制程序的参数. 在 graphics context 上绘制的程序根据这些图形状态来确定怎么去渲染结果. 例如, 当你调用函数来填充颜色时, 你就改变了出存在当前图形状态中的值. 当前图形状态的其他常用元素还包括线宽、当前位置和文本字体大小.
graphics context 包含了一个图形状态栈. 当 Quartz 创建一个 graphics context 时, 这个栈是空的. 当你保存图形状态时, Quartz 将当前图形状态的一个副本压入栈中. 当你还原图形状态时, Quartz 将栈顶的图形状态弹出栈. 栈顶的状态就变成当前的图像状态.
要保存当前的图形状态, 可使用 CGContextSaveGState 函数来将当前图形状态的副本压入栈中. 要还原以前保存的图形状态, 可以使用 CGContextRestoreGState 函数来用栈顶的状态替换当前的图形状态.
请注意, 当前绘图环境下的所有方面并不都是图形状态的元素. 例如, 当前路径 (current path)就不是图形状态的一部分所以当你调用 CGContextSaveGState 时它并不会保存. 当你调用这个方法时, 图形状态会被保存的情况(即与图形状态相关联的参数)参见 Table 1-1.
Table 1-1 与图形状态相关联的参数
参数 | 参见 |
---|---|
Current transformation matrix(当前转换矩阵) (CTM) | Transforms |
Clipping area(裁剪区域) | Paths |
Line: width, join, cap, dash, miter limit(线) | Paths |
Accuracy of curve estimation(曲线平滑度) (flatness) | Paths |
Anti-aliasing setting(抗锯齿设置) | Graphics Contexts |
Color: fill and stroke settings(颜色设置) | Color and Color Spaces |
Alpha value(透明度) (transparency) | Color and Color Spaces |
Rendering intent(再现意图) | Color and Color Spaces |
Color space: fill and stroke settings(颜色空间) | Color and Color Spaces |
Text: font, font size, character spacing, text drawing mode(文本) | Text |
Blend mode(混合模式) | Paths 和 Bitmap Images and Image Masks |
Quartz 2D 坐标系
如 图 1-4 所示的一个坐标系, 定义了被绘制到Page上的对象的位置及大小范围. 你在用户空间坐标系统 (user-space coordinate system) 指定图像的位置和大小, 或者简单点, 用户空间 (user space) . 坐标值用浮点数来定义.
图 1-4 Quartz 坐标系
由于不同的设备成像能力也不同, 所有图像的大小和位置必须以设备无关的方式定义. 例如, 一个屏幕显示的设备可以显示每英寸不超过96个像素, 虽然打印机可能能够显示每英寸300像素. 如果在设备级别上定义坐标系 (在这个例子中为96或者300), 则在一个设备上绘制的图像无法在没有明显失真的情况下在其他设备显示. 他们会显得太大或者太小.
Quartz 使用当前转换矩阵 (current transformation matrix)或者说 CTM 将用户空间 (user space) —将其映射到输出设备的坐标系统—设备空间 (device space) 这种方式来解决设备的依赖. 矩阵是用来有效描述了一组相关方程的数学结构. 当前变换矩阵是一种特殊类型的矩阵被称为仿射变换 (affine transform) . 通过平移(translation)、旋转(rotation)、缩放(scale)操作将点从一个坐标空间映射到另外一个坐标空间 (calculations that move, rotate, and resize a coordinate system).
当前转换矩阵还有另一个目的: 它允许你通过旋转来决定对象怎么被绘制. 例如 , 绘制一个旋转了45度的盒子, 我们可以在绘制盒子之前旋转Page(the CTM)的坐标系统。Quartz使用旋转过的坐标系统来将盒子绘制到输出设备中.
在我们的坐标系中1个点被表示为一对坐标 (x,y), Quartz中默认的坐标系统是:沿着x轴从左到右坐标值逐渐增大, 沿着y轴从下到上坐标值逐渐增大, (0, 0)表示坐标原点. 原点位于 page 的左下角, 如 图 1-4 所示.
一些技术在设置他们的 graphics contexts 时使用了不同于 Quartz 的默认坐标系. 相对于 Quartz 来说, 这些坐标系统是修改的坐标系统(modified coordinate system),当在这些坐标系统中显示Quartz绘制的图形时,必须进行转换(补偿). 最常见的一种修改的坐标系统是原点位于左上角, 而沿着y轴从上到下坐标值逐渐增大. 我们可以在如下一些地方见到这种坐标系统:
• 在Mac OS X中,重写过isFlipped方法以返回yes的NSView类的子类
• 在IOS中,由UIView返回的绘图上下文
• 在IOS中,通过调用UIGraphicsBeginImageContextWithOptions函数创建的绘图上下文 .
UIKit 以修改的坐标系统返回Quartz绘图上下文的原因是因为UIKit中使用不同的默认坐标; 它适用与Quartz的变换. 如果应用程序想以相同的绘制程序在一个UIView对象和PDF Graphics Context上进行绘制 (都是用Quartz创建并且使用默认坐标系), 你需要做一个变换以使 PDF Graphics Contexts 接收到相同的坐标系. 要达到这一目的, 只需要对PDF的上下文的原点做一个平移(移到左上角)和用-1对y坐标值进行缩放. 例如 如果你调用 CGContextDrawImage 把一张图片绘制进上下文, 当它被绘制到目标时, 这个图片已经被变换修改了. Similarly, 路径绘制程序接受一个在默认坐标系中顺时针或者逆时针画圆的参数. 如果坐标系被修改了, 那么就过也被修改了, 就像图片被镜子反射一样. 在 ** 图1-5 中** , passing the same parameters into Quartz results in a clockwise arc in the default coordinate system and a counterclockwise arc after the y-coordinate is negated by the transform.(不懂- -)
图 1-5 通过修改坐标系统创建镜像图像.
你的应用程序负责调整 Quartz 调用以确保有一个转换应用到上下文中. 例如, 如果你想要一个图片或PDF正确的绘制到一个Graphics Context中, 你的应用程序可能需要临时调整Graphics Context的CTM. 在iOS中, 如果你使用UIImage对象来包裹创建的CGImage对象, 你可以不需要去修改 CTM. The UIImage 对象将自动为修改的坐标系进行补偿来适用 UIKit.
Important: 以上内容只是讨论一些必要的内容让你了解, 如果你打算直接在iOS上开发与Quartz相关的程序, 了解以上的讨论是有用的但不是必须的. 在iOS 3.2 和后续的版本中, 当 UIKit 为你的应用程序创建了一个绘图上下文时, 也对上下文进行了额外的修改来匹配 UIKit 的规定. 特别的, patterns 和 shadows, 都不被 CTM 所影响, 所以需要单独进行调整来匹配 UIKit 的坐标系. 在这种情况下, 没有一个等价的机制让你的应用程序能使用 CTM 将通过 Quartz 创建的上下文和UIKit提供的上下文所匹配; 所以你的程序必须认识到什么样的上下文正在进行绘制并且调整它的行为以匹配上下文的预期.
Memory Management: Object Ownership
Quartz 使用 Core Foundation 的内存管理模型, 对对象使用引用计数. 当对象呗创建时, Core Foundation 对象一开始的引用计数为1. 你可以通过调用方法来retain对象达到增加计数的目的, 并且你也可以通过调用方法来release对象来达到减少技术的目的. 当引用计数递减到0时, 这个对象就被释放了. 这个模型允许对象被其他对象安全的引用.
有几个简单的规则要记住:
• 如果你创建活拷贝了一个对象, 你将拥有它, 因此你必须释放他. 就是这样, 通常, 如果你通过带有 “Create” 或 “Copy” 的方法获得对象, 当你使用完毕后必须释放这些对象. 否则将导致内存泄露.
• 如果不是通过带有 “Create” 或 “Copy” 的方法获得对象, 你将不会拥有对象的引用, 你将不会拥有对象的引用, 不需要释放它, 这个对象会在未来的某个时刻由它的拥有者释放.
• 如果你不拥有一个对象而打算保持它, 你必须retain它并且在不需要的时候release掉它. 可以使用Quartz 2D的函数来指定retain和release一个对象. 例如, 如果你要引用一个 CGColorspace 对象, 则使用函数 CGColorSpaceRetain 和 CGColorSpaceRelease 来 retain 和 release 对象, 你也可以使用 Core Foundation 的 CFRetain 和 CFRelease 方法, 但是你必须要小心不要传递 NULL 给这些函数.