Core Graphics也称为Quartz 2D,是可用于iOS,tvOS和macOS应用程序开发的高级二维绘图引擎。Quartz 2D提供低级、轻量的2D渲染,无论显示或打印设备如何,其输出保真度均无与伦比。Quartz 2D与分辨率和设备无关。
Quartz 2D API易于使用,并提供对强大功能的访问,例如透明层、基于路径的绘图、离屏渲染、高级颜色管理、抗锯齿渲染以及PDF文档的创建、显示和解析。Quartz 2D会尽可能利用图形硬件的功能。
在Mac OS X中,Quartz 2D可以与所有其他图形技术和成像技术(Core Image,Core Video,OpenGL和QuickTime)一起配合使用。可以从QuickTime图形导入器中使用QuickTime函数GraphicsImportCreateCGImage
在Quartz中创建图像,有关详细信息请参看QuickTime Framework Reference。Mac OS X中的Moving Data Between Quartz 2D and Core Image描述了如何向Core Image提供图像,Core Image是一个支持图像处理的框架。
同样,在iOS中,Quartz 2D可使用所有可用的图形技术和动画技术,例如Core Animation,OpenGL ES和UIKit。
可以使用Quartz 2D技术来执行以下任务:
- 在应用程序中提供图形编辑功能;
- 创建或者显示位图图像(bitmap image);
- 处理PDF文档;
位图(Bitmap),又称栅格图或点阵图,是使用像素阵列(Pixel-array/Dot-matrix点阵)来表示的图像。
页面(The Page)
Quartz 2D使用painter’s model进行成像。 在painter’s model中,每个连续的绘画操作都会应用一层“涂料”到输出“画布”上,这通常被称为一个页面(page)。 可以通过其他绘图操作覆盖更多的涂料来修改页面上的涂料。 在页面上绘制的对象只能通过覆盖更多的涂料来修改。 这种模式使我们可以从少量强大的原函数中构建极其复杂的图像。
下图显示了painter’s model是如何工作的。为了获得下图上半部分中的Result图像,首先绘制左侧的形状,然后绘制实心形状。实心形状覆盖了第一个形状,除了第一个形状的周边之外,其他所有颜色都被遮盖了。下图下半部分中的Result图像则以相反的顺序绘制形状,其是首先绘制实心形状。 如您所见,在painter’s model中,绘图顺序很重要。
页面可能是一张真实的纸(如果输出设备是打印机),也可能是一张虚拟的纸(如果输出设备是PDF文件),甚至可能是位图图像。页面的确切本质取决于我们使用的特定图形上下文。
绘图目标:图形上下文
图形上下文是不透明的数据类型(CGContextRef
),其封装了Quartz用于将图像绘制到输出设备的信息,例如PDF文件,位图或显示器上的窗口。 图形上下文中的信息包含图形绘图参数和页面上涂料的特定于设备的表示。Quartz中的所有对象都被绘制到或包含在图形上下文中。
可以将图形上下文视为绘图目标,如下图所示。当使用Quartz进行绘制时,所有特定于设备的特征都包含在使用的特定类型的图形上下文中。换句话说,只需向相同的Quartz绘制程序提供不同的图形上下文,就可以将同一图像绘制到不同的设备。Quartz会执行任何特定于设备的计算。
有以下这些图形上下文可用于我们的应用程序:
位图图形上下文可以将RGB颜色,CMYK颜色或者灰度绘制到位图中。位图是一个像素矩形阵列(或栅格),每个像素代表图像中的一个点。位图图像也被称为采样图像。请参看Creating a Bitmap Graphics Context。
-
PDF图形上下文可以创建PDF文件。在PDF文件中,图形被保存为一系列命令。PDF文件和位图之间存在一些重大差异:
- 与位图不同,PDF文件可能包含多个页面。
- 在其他设备上从PDF文件绘制页面时,针对该设备的显示特性,优化了生成的图像。
- PDF文件本质上与分辨率是无关的,可以无限增大或减小PDF文件的绘制尺寸,而无需牺牲图像细节。 用户感知的位图图像质量与要查看位图的分辨率有关。
窗口图形上下文是可用于在窗口中绘图。注意,由于Quartz 2D是图形引擎而不是窗口管理系统,因此可以使用一种应用程序框架来获取窗口的图形上下文。有关详细信息,请参看Creating a Window Graphics Context in Mac OS X。
图层上下文(
CGLayerRef
)是与另一个图形上下文关联的屏幕外绘图目标。 将图层绘制到创建该图层的图形上下文中可以获得最佳性能。与位图图形上下文相比,图层上下文可能是用于屏幕外绘制的更好选择。请参看Core Graphics Layer Drawing。如果要在Mac OS X中进行打印,则将内容发送到由打印框架管理的PostScript图形上下文。有关更多信息,请参看Obtaining a Graphics Context for Printing。
Quartz 2D不透明数据类型
除了图形上下文外,Quartz 2D API还定义了各种不透明的数据类型。因为该API是Core Graphics框架的一部分,所以数据类型和对其进行操作的函数都使用CG前缀。
Quartz 2D使用不透明数据类型来创建对象,应用程序可以使用这些对象来实现特定的绘画输出。 下图显示了将绘图操作应用于Quartz 2D提供的三个对象时可以实现的各种结果。例如:
- 可以通过创建PDF页面对象,将旋转操作应用到图形上下文中并要求Quartz 2D将PDF页面对象绘制到该图形上下文中,来旋转并显示PDF页面。
- 可以通过创建图案对象,定义构成图案的形状以及在将图案绘制到图形上下文时将图案用作涂料,来绘制图案。
- 可以通过创建阴影对象,提供确定阴影中每个点颜色的函数,然后要求Quartz 2D将阴影用作填充颜色,来用轴向或径向阴影填充区域。
Quartz 2D中可用的不透明数据类型包含以下这些:
-
CGPathRef
,用于矢量图形来创建填充和描边的路径,请参看Paths。 -
CGImageRef
,用于根据提供的样本数据来表示位图图像和位图图像蒙版,请参看Bitmap Images and Image Masks。 -
CGLayerRef
,用于表示可用于重复绘制(例如用于背景或图案)和屏幕外绘制的绘画图层,请参看Core Graphics Layer Drawing。 -
CGPatternRef
,用于重复绘制,请参看Patterns。 -
CGShadingRef
和CGGradientRef
,用于绘制渐变,请参看Gradients。 -
CGFunctionRef
,用于定义采用任意数量的浮点参数的回调函数,当为阴影创建渐变时使用此数据类型,请参看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。
图形状态
Quartz根据current graphics state中的参数修改绘图操作的结果。图形状态包含会被应用到绘图例程中去的参数,绘制到图形上下文的例程会参考图形状态以确定如何呈现其结果。例如,当调用一个函数来设置填充颜色时,您正在修改存储在当前图形状态中的填充颜色参数值。当前图形状态的其他常用元素包括线宽,当前位置和文本字体大小。
图形上下文包含一个图形状态堆栈。当Quartz创建图形上下文时,堆栈为空。当我们保存图形状态时,Quartz将当前图形状态的副本压入堆栈。 当我们恢复图形状态时,Quartz将图形状态弹出堆栈顶部。被弹出的状态变为当前图形状态。
要保存当前图形状态,请使用CGContextSaveGState
函数将当前图形状态的副本推入堆栈。要恢复以前保存的图形状态,请使用CGContextRestoreGState
函数将当前图形状态替换为堆栈顶部的图形状态。
请注意,并非当前绘图环境的所有方面都是图形状态的元素。例如,当前路径不被视为图形状态的一部分,因此,当您调用函数CGContextSaveGState
时,不会保存当前路径。调用此函数时,保存的图形状态参数包括以下这些:
- Current transformation matrix (CTM)
- Clipping area
- Line: width, join, cap, dash, miter limit
- Accuracy of curve estimation (flatness)
- Anti-aliasing setting
- Color: fill and stroke settings
- Alpha value (transparency)
- Rendering intent
- Color space: fill and stroke settings
- Text: font, font size, character spacing, text drawing mode
- Blend mode
Quartz 2D 坐标系
下图所示的坐标系定义了用于表示要在页面上绘制的对象的位置和大小的位置范围。可以在用户空间坐标系中,或更简单地,在用户空间中指定图形的位置和大小。坐标被定义为浮点值。
由于不同的设备具有不同的基础成像功能,因此必须以与设备无关的方式定义图形的位置和大小。例如,屏幕显示设备可能能够显示每英寸不超过96像素,而打印机可能能够显示每英寸300像素。如果在设备级别(在此示例中为96像素或300像素)定义坐标系,则在该空间中绘制的对象将无法在没有可见畸变的情况下在其他设备上复制。它们将显得太大或太小。
Quartz使用current transformation matrix或CTM将一个独立的坐标系(用户空间)映射到输出设备的坐标系(设备空间)来实现设备独立性。矩阵是用于有效描述一组相关方程的数学构造。current transformation matrix是一种特殊类型的矩阵,被称为仿射变换,其通过应用平移,旋转和缩放操作(移动,旋转和调整坐标系大小的计算),将点从一个坐标空间映射到另一个坐标空间。
current transformation matrix还有第二个用处:改变对象的绘制方式。例如,要绘制旋转45度的方框,请在绘制方框之前旋转页面(CTM)的坐标系。Quartz使用旋转后的坐标系绘制到输出设备。
用户空间中的一个点由坐标(x,y)表示,其中x代表该点在水平轴(左和右)上的位置,y代表该点在垂直轴(上和下)上的位置。用户坐标空间的原点是点(0,0)。原点位于页面的左下角,如下图所示。在Quartz的默认坐标系中,x轴上的值随着页面左侧向右侧的移动而增加,y轴上的值随着页面底部向顶部的移动而增加。
某些技术使用与Quartz不同的默认坐标系来设置其图形上下文。相对于Quartz,此类坐标系是已被修改过的坐标系,在执行某些Quartz绘图操作时,必须对这类坐标系进行补偿。最常见的修改过的坐标系将原点放置在上下文的左上角,并将y轴更改为指向页面的底部。您可能会在以下几个地方看到使用此特定坐标系:
- 在Mac OS X中,
NSView
的子类重写其isFlipped
方法来返回YES
。 - 在iOS中,
UIView
返回的绘图上下文(drawing context)。 - 在iOS中,通过调用
UIGraphicsBeginImageContextWithOptions
函数创建的绘图上下文。
UIKit返回具有修改过的坐标系的Quartz绘图上下文的原因是UIKit使用了不同的默认坐标约定。它将transform(变换)应用于它创建的Quartz上下文,以便它们符合其约定。如果您的应用程序希望使用相同的绘图例程来绘制UIView对象和PDF图形上下文(由Quartz创建并使用默认坐标系),则需要应用transform,以便PDF图形上下文收到相同的修改过的坐标系。为了做到这点,需要应用一个将原点平移到PDF上下文的左上角并以-1来缩放y坐标的transform。
使用缩放变换取反y坐标会更改Quartz绘图中的某些约定。例如,如果调用CGContextDrawImage
将图像绘制到上下文中,则在将图像绘制到目标时,图像会通过transform进行修改。类似地,路径绘制例程接受指定在默认坐标系中沿顺时针或者逆时针方向绘制圆弧的参数。如果修改了坐标系,则结果也会被修改,就像图像在镜子中反射一样。在下图中,将相同的参数传递给Quartz会导致在默认坐标系中是一个顺时针圆弧,以及在y坐标被transform取反后的坐标系中是一个逆时针圆弧。
您的应用程序可以对创建应用了变换的上下文的任何Quartz调用进行调整。例如,如果您希望将图像或PDF正确地绘制到图形上下文中,则您的应用程序可能需要临时调整图形上下文的CTM。在iOS中,如果使用UIImage
对象包装您创建的CGImage
对象,则无需修改CTM。UIImage
对象会自动补偿UIKit应用的修改后的坐标系。
重要:如果打算编写直接针对iOS上Quartz的应用程序,那么上面的讨论对于理解是至关重要的,但这还不够。在iOS 3.2和更高版本上,当UIKit为应用程序创建绘图上下文时,它还会对该上下文进行其他更改以匹配默认的UIKIt约定。特别是,不受CTM影响的图案和阴影会分别进行调整,以使它们的约定与UIKit的坐标系匹配。在这种情况下,应用程序无法使用CTM的等效机制来更改Quartz创建的上下文以匹配UIKit提供的上下文的行为。应用程序必须识别要绘制的上下文属于哪种类型,并调整其行为以符合上下文的期望。
内存管理:对象所有权
Quartz使用对对象进行引用计数的Core Foundation内存管理模式,创建Core Foundation对象后,其开始的引用计数为1。可以通过调用保留对象的函数来增加引用计数,并通过调用释放对象的函数来减少引用计数。当引用计数减为0时,对象会被释放。该模式允许对象安全共享对其他对象的引用。
需要牢记以下一些简单的规则:
- 如果我们创建或复制了对象,则说明我们是对象的所有者,因此必须由我们负责释放它。也就是说,如果我们从名称中带有“Create”或“Copy”单词的函数中获取了对象,则在完成处理后必须释放该对象。否则,会导致内存泄漏。
- 如果我们从名称中不包含“Create”或“Copy”单词的函数中获取了对象,则我们不具有对该对象的引用,也不用我们手动去释放该对象。该对象将在将来的某个时候由其所有者释放。
- 如果我们不拥有对任何对象的引用,但又需要保留它,则我们必须保留它,并在完成使用后将其释放。可以使用特定于对象的Quartz 2D函数来保留和释放该对象。例如,如果收到对
CGColorspace
对象的引用,则可以使用CGColorSpaceRetain
和CGColorSpaceRelease
函数根据需要保留和释放该对象。也可以使用Core Foundation函数CFRetain
和CFRelease
,但是必须注意不要将NULL
传递给这些函数。