本文翻译自苹果官方文档:原文地址
Quartz 2D 概述
Quartz 2D 是一个二维的绘画引擎,在Mac OS X和iOS环境下可以使用,它在内核之外。你可以使用Quartz 2D 的API来完成各种各样的操作,例如:基于路径的绘图、透明度绘图、阴影绘图、透明图层、颜色管理、抗锯齿渲染、PDF操作等。Quartz 2D能够尽可能的发挥出图形硬件的性能。
页(The Page)
Quartz 2D 使用画家模型来成像。在这个模型中,每次连续的绘图操作都将一个叫做“油漆”的图层应用到一个通常叫做“页”的输出“画布”上。在“页”上的“油漆”能够被后续的的绘图操作所覆盖。只有你进行额外的绘图操作,原先在“页”上的物体才会被修改。这个模型允许你使用少量的基本元素来构建复杂精细的图像。
图 1-1 展示了画家模型的工作原理。按照从左往右的顺序绘制图形,可以看到结果是不一样的,因此绘制的顺序在这个模型中是十分重要的。
这个“页”或许是一个真正的纸张(输出设备是打印机);也或许是一个虚拟的纸张(输出设备是一个PDF文件);还可能是一个位图图像。准确的说,这个“页”的本质取决于你使用的绘图上下文的类型。
绘图地点:图形上下文(The Graphics Context)
图形上下文(graphics context)是一个未开源的数据类型(CGContextRef),它包含了Quartz需要绘图到输出设备(如:PDF文件、bitmap、显示器上的一个窗口等等)的信息。在图形上下文中的信息,不仅包含绘图需要的参数,还包含了特定设备的绘图信息。所有Quartz需要的信息都包含在一个图形上下文中。
你可以把一个图形上下文看做绘图的地方,就像 图 1-2展示的那样。当你使用Quartz绘图时,所有指定设备的特征都包含在你使用的那个特定的图形上下文中。换句话说,你可以仅仅为绘图这一系列操作提供不同的图形上下文就可以在不同的设备上绘制出相同的内容。
下列这些图形上下文在你的应用程序用可以使用:
- 位图图形上下文允许你在位图(bitmap)中绘制RGB颜色、CMYK颜色或者灰阶(grayscale)。位图是像素的矩形阵列(或栅格),每个像素表示图像中的点。位图图像也称为采样图像。请参阅创建位图图形上下文。
- PDF图形上下文允许你创建PDF文件。在一个PDF文件中,你的绘图操作将被当做一系列命令被保留。PDF文件和位图有以下明显的不同点:
- PDF文件可能拥有多个page(不像位图只有一个)。
- 当您从不同设备上的PDF文件中绘制页面时,生成的映像将针对该设备的显示特性进行优化。
- 被绘制的PDF文件天生就可以在不损失细节的情况下无限放大和缩小,而位图图像的分辨率直接影响感官效果。
- 详见创建PDF图形上下文。
- 窗口(window)图形上下文是一个你可以在窗口中绘制的图形上下文。注意,因为Quartz 2D是一个图形引擎而不是窗口管理系统,你可以使用应用程序的框架来获取一个窗口的图形上下文。详见:在Mac OS X下创建窗口图形上下文。
- 图层上下文(CGLayerRef)是一个和其他图形上下文关联的一个离屏绘制的目标。它是为了在将图层绘制到创建它的图形上下文的最佳性能而设计的。在离屏绘制中,图层上下文是一个比位图图形上下文更好的选择。详见:核心图层绘制。
- 当你在Mac OS X中想要打印时,可以把要打印的内容发送到一个叫做PostScript 图形上下文中,这个PostScript 图形上下文在printing框架中。详见:获取打印的图形上下文。
Quartz 2D 中的数据类型
Quartz 2D 的 API 定义了大量的不透明数据类型。因为这些 API 是Core Graphics 框架中的一部分,所以这些数据类型按照惯例都是以“CG”开头的。
为了实现指定的绘图输出,你的应用程序运行Quartz 2D,并使用这些闭源的数据类型来创造对象。图 1-3 展示了一系列你能够使用Quartz 2D提供的三种数据类型来实现的不同效果。例如:
- 你能够通过创建PDF page对象,应用旋转变换,并将其绘制到图形上下文中来实现旋转并展示一个PDF page的效果。
- 你能够通过创建一个样章对象并定义构成图案的形状来进行相应的绘制。
- 你可以通过创建阴影对象来填充具有轴向或者镜像的渐变区域。
Quartz 2D中包含的闭源数据类型如下:
- CGPathRef,用于你要填充或者描边的矢量图形。详见:路径。
- CGImageRef,通常代表根据您提供的样本数据来表示位图图像和位图图像遮罩。详见:位图图像和图像遮罩。
- CGLayerRef,通常代表一个可以重复使用的或者离屏绘制的图层。详见:核心图形图层绘制。
- CGPatternRef,用于重复绘制。详见:样章。
- CGShadingRef和CGGradientRef,用于绘制渐变。详见:渐变。
- CGFunctionRef,用于定义具有任意数量的浮点类型的参数的回调函数。当你在创建一个渐变时会用到这种数据类型,详见:渐变。
- CGColorRef和CGColorSpaceRef,用来告知Quartz如何解析颜色。详见:颜色和色域。
- CGImageSourceRef和CGImageDestinationRef,用来从Quartz中导入或导出数据。详见:Quartz 2D的数据管理和Image I/O 编程指导。
- CGFontRef,用于绘制文字。详见:文字(Text)。
- CGPDFDictionaryRef,CGPDFObjectRef,CGPDFPageRef,CGPDFStream,CGPDFStringRef和CGPDFArrayRef,这些类提供了获取PDF元数据的方法。详见:PDF文档的创建、预览和转换。
- CGPDFSacnnerRef和CGPDFContenStreamRef,用来解析PDF元数据。详见:PDF文档解析。
- CGPSConverterRef,被用来将PostScript转换成PDF,iOS中不可用。详见:PostScript 转换。
图形状态(Graphics States)
Quartz绘制的结果是根据存储在当前图形状态(Graphics States)中的参数决定的。图形状态(Graphics States)中所包含的参数在相应的情况下会被用于绘制过程,这些参数的值决定了Quartz如何去渲染绘图的结果。例如:当你使用一个函数去设置了填充颜色(fill color),那么你就修改了存储在当前图形状态(Graphics States)中一个参数的值。其他一些经常使用的参数有:线宽、当前的位置、字体的大小等等。
图形上下文(Graphics Context)使用栈的方式来存储图形状态(Graphics States)。当Quartz创建图形上下文时,这个栈是空的。当你保存保存图形状态(Graphics States)时,Quartz将当前图形状态的复制压入到栈中。当你重置图形状态时,Quartz将栈顶的图形状态推出。推出之后栈中的图形状态成为了当前的图形状态。
如果想要保存当前的图形状态,那么可以使用CGContextSaveGState函数;如果要重置之前的图形状态,那么可以使用CGContextRestoreGState。
需要注意的是,不是所有当前绘画涉及的内容都属于图形状态的。例如:当前的路径(path)就不是图形状态中的内容,因此,当你保存图形状态时并不会保存这些不属于图形状态的内容。表 1-1 列举了当调用保存状态方法时会保存的图形状态参数:
表 1-1 图形状态相关参数
参数名 | 详细讲述章节 |
---|---|
当前变换矩阵(Current transformation matrix 也即CTM) | 变换 |
剪辑区域(Clipping area) | 路径 |
线:宽度、连接、线帽、虚线、连接线的斜接限制(miter limit) | 路径 |
曲线的平直度 | 路径 |
抗锯齿设置 | 图形上下文 |
颜色:填充和描边设置 | 颜色和色域 |
Alpha值(透明度) | 颜色和色域 |
渲染意图 | 颜色和色域 |
色域:填充和描边设置 | 颜色和色域 |
文字:字体、大小、间距、绘制模式 | 文字(Text) |
混色模式 | 路径、位图图像与图像遮罩 |
Quartz 2D 的坐标系
如图1-4 所示,这个坐标系定义了位置的范围,用它可以描述需要被绘制图形的大小和位置。你可以在用户空间坐标系统(user-space coordinate system)中指定位置和大小,简称为用户空间(user space)。这个坐标系被定义为浮点值。
因为不同的设备具有不同的底层绘图方式,所以图形的位置和大小必须由这些独立的设备单独定义和管理。例如:一个屏幕可能能够展示超过96个像素每英寸,而一个打印机能够展示超过300个像素每英寸。如果以设备的级别来定义坐标系,那么绘制到该坐标系的图形不能在视觉效果一致的情况下重新绘制到其他的设备上,它不是太大就是太小。
Quartz用一个单独的坐标系使得设备变得独立。利用当前变换矩阵(CTM)能够将用户空间(user-space)的坐标系映射成当前输出设备的坐标系。矩阵是数学上的一个结构,用它来描述一系列相等关系是非常有效率的。当前变换矩阵(CTM)是一个特殊的矩阵,叫做仿射变换,仿射变换将一个坐标系的很多点映射到另一个坐标系的其他对应的点(通过平移、旋转、缩放等操作)。
当前转换矩阵(CTM)还有一个次要的目的:可以使用它改变对象的绘制。例如:绘制一个倾斜45°的盒子,你仅仅需要在绘制之前旋转当前的坐标系(CTM),Quartz 绘制的输出就自动使用了旋转之后的坐标系。
一个点在用户空间(user space)下用一个坐标对表示 (x,y) ,其中x代表水平(左和右)的距离,y代表距离垂直(上和下)的距离。坐标系的原点是(0,0),它在"页"的左下角,如 图1-4 所示。在Quartz默认的坐标系中,x从左向右时增加,y从下向上时增加。
在某些技术条件下所使用的图形上下文的坐标系和Quartz默认的坐标系是不同的。对于Quartz而言,这些坐标系是被修改过的,因此当使用Quartz来进行一些绘图操作时,就应该对此进行相应的补偿。最常见的补偿方式就是:将原点由左下角移动到左上角,并且y轴的正半轴方向向下延伸。下面是一些使用到上述坐标系的地方:
- 在 MAC OS X 中,当一个NSView的子类重写了它的isFlipped方法并返回YES时。
- 在iOS中,使用UIView获取到的绘图上下文。
- 在iOS中,使用UIGraphicsBeginImageContextWithOptions方法获取到的绘图上下文。
UIKit之所以返回给Quartz的坐标系与Quartz默认的坐标系不一致,是因为UIKit默认使用另一个不同的坐标系。为了使得创建的Quartz上下文符合这个默认的坐标系,UIKit对其应用了变换。如果你想使用相同的坐标系来绘制UIView和一个PDF图形上下文(由Quartz创建并使用Quartz默认的坐标系),你可以对PDF的图形上下文进行相应的变换(将原点转换到左上角,将y轴以scale的方式乘以-1)。
使用缩放(scale)的方式使y轴的方向改变后同时也改变了Quartz中一些默认的东西。例如:如果你在这样的图形上下文中调用CGContextDrawImage来绘制图片时,它会被这个变换所修改。同样,路径绘制中所接受的参数(如弧的顺时针、逆时针方向)也可能会被修改。如 图1-5 所示,在默认的坐标系中是顺时针绘制的,但是应用上述变换后,绘制的方向就变成了逆时针。
在Quartz绘制时,这些变换的应用完全取决于你的应用程序。例如,如果你想将一个图片或者PDF正确的绘制到图形上下文中,你的应用程序需要暂时调整该图形上下文的CTM。在iOS中如果你使用一个UIImage对象来包装你创建的一个CGImage对象,你不需要修改CTM,UIImage自动修改了UIKit对坐标系应用的变换。
重点:上述讨论对于你想直接在iOS上使用Quartz是十分重要的,但是它还不足够让你彻底理解。在iOS 3.2和之后的版本中,当UIKit为你的应用程序创建图形上下文时,它同时也附加了额外的操作使得这个图形上下文能够符合UIKit默认的坐标系标准。特别地,不受CTM影响的样章(Patterns)和阴影(shadows)被单独调整,使得它们的约定与UIKit的坐标系匹配。在这种情况下,您的应用程序不能使用等效的CTM机制来更改由Quartz创建的上下文以匹配UIKit提供的上下文的行为;您的应用程序必须意识到它正在什么样的上下文中进行绘制,并调整其行为以符合上下文的期望。
内存管理:对象的归属(Object Ownership)
Quartz使用了Core Foundation 内存管理模型,即对象使用了引用计数。当创建时,这些对象具有1的引用计数。你可以通过调用函数增加对象的引用计数来保留这个对象,也可以调用相应的函数较少对象的引用计数来释放对象。当对象的引用计数减少到0时,这个对象就会被释放,这个模型提供了安全的对象共享方式。
你应当记住这三个简单的规则:
- 如果你创建或者拷贝(copy)了一个对象,那么你就拥有它,因此你必须要释放(release)它。也就是说,如果你使用带有"Create"或者"Copy"这样名字的方法获得了这个对象,当你不需要使用时你就必须释放它(release),否则就会造成内存泄露。
- 如果你没有使用带有"Create"或者"Copy"这样名字的方法获得了一个对象,那么你就没有拥有这个对象的引用,因此不能释放这个对象。这个对象在将来会被它的拥有者所释放。
- 如果你没有拥有一个对象,但是你需要让他在你的使用周期内存在时,你必须retain它,当你不用时还要release它。你可以使用Quartz提供的方法来retain和release一个对象。例如:你得到一个CGColorspace对象,你可以使用CGColorSpaceRetain 方法和 CGColorSpaceRelease 方法来retain和release这个你需要的对象。同样,你也可以使用 Core Foundation 提供的 CFReatin 和 CFRelease 方法,但是你一定要注意不要对这些方法传入NULL。
上一章:Core Graphics(Quartz 2D)编程简介
下一章:图形上下文(Graphics Context)