Core Graphics, 也称作Quartz 2D,是用在macOSiOStvOS上的绘制引擎。Quartz 2D提供轻量化的,底层的2D渲染,无论显示器还是打印设备,输出保真度都无与伦比。Quartz 2D是不必依赖于输出设备和设备分辨率。
Quartz 2D使用画布,绘制对象的方式模式成像——一个绘制操作就是一层“”画,绘制在画布上。画布也叫做Page,通过附加绘图操作叠加更多paint,可以修改页面上的paint。在页面上绘制的对象不能被修改,除非覆盖更多的paint
绘制对象:图形上下文 The graphics context
The Graphics Context 是一种不透明的数据类型(CGContextRef)封装了需要用来绘制图片到设备上的各种信息,比如 bitmap,比如pdf文件,或者一个用于显示的窗口。图形上下文中的信息包括图形绘制参数和页面上绘画的设备特定表示。 Quartz中的所有对象都被绘制或包含在图形上下文中。
可以把 图形上下文 想像成一个绘制目标。当使用quartz绘制的时候,使用Quartz进行绘制时,所有设备特定的特征都包含在你使用的特定类型的图形上下文中。换句话说,你可以简单地通过为相同的Quartz绘图例程序列提供不同的图形上下文来将相同的图像绘制到不同的设备上。
以下是App可用的Graphics Context:
  • Bitmap graphics context,允许你将RGB颜色,CMYK颜色或灰度绘制到位图中。位图是像素的矩形阵列(或光栅),每个像素代表图像中的一个点。位图图像也称为采样图像。
  • PDF graphics context,可以使用此context创建PDF文件。PDF文件的绘制,被存储为一组绘制命令。此context和bitmap的不同主要有:
  1. pdf是基于矢量的,bitmap是基于像素的。
  2. pdf包含多页数据
  3. 你从不同设备上的PDF文件中绘制页面时,生成的图像将针对该设备的显示特性进行优化。
  4. DF文件本质上是独立于分辨率的 – 它们被绘制的大小可以无限增加或减少,而不会牺牲图像细节。位图图像的用户感知质量与打算查看位图的分辨率相关联。
  • Window graphics context,可以绘制到window上的context。因为Quartz是绘制引擎吧,不是window管理系统,所以,你使用其中一个应用程序框架来获取窗口的图形上下文。
  • Layer context (CGLayerRef),一个与graphics context相关的屏幕外绘制目标。将layer绘制到创建它的图形上下文时,它被设计为获得最佳性能。对于离屏绘图而言,layer context可能比bitmap context更好。
  • 如果想要在macOS上打印,把打印内容发送给负责打印的framework的PostScript context实现打印功能。
Quartz 2D 不透明数据类型 Quartz 2D Opaque Data Types
除了图形上下文之外,Quartz 2D API还定义了各种不透明的数据类型。因为API是Core Graphics框架的一部分,所以对它们进行操作的数据类型和例程使用CG前缀。
Quartz 2D根据应用程序运行的不透明数据类型创建对象,以实现特定的绘图输出。以下是当你将绘图操作应用于Quartz 2D提供的三个对象时可以实现的各种结果。例如:
  • 你可以通过创建PDF页面对象来旋转和显示PDF页面,将旋转操作应用于图形上下文,并要求Quartz 2D将页面绘制到图形上下文中。
  • 你可以通过创建图案对象来绘制图案,定义构成模式的形状,并设置Quartz 2D以在绘制到图形上下文时将该图案用作绘制。
  • 你可以通过创建阴影对象来填充具有轴向或径向阴影的区域,提供一个函数来确定阴影中每个点的颜色,然后要求Quartz 2D使用阴影作为填充颜色。
在quartz 2d里的不透明数据类型主要有:
  • CGPathRef,用户绘制矢量图像的的路径,可以填充或者stroke。
  • CGImageRef,用于根据你提供的样本数据表示位图图像和位图图像蒙版
  • CGLayerRef,用于表示可用于重复绘图(例如用于背景或图案)和用于屏外绘图的绘图层
  • CGPatternRef,用户重复的绘制
  • CGShadingRef/CGGradientRef,用户绘制阴影和渐变
  • CGFunctionRef,用于定义采用任意数量的浮点参数的回调函数。在为阴影创建渐变时使用此数据类型。
  • CGColorRef/CGColorSpaceRef ,用于通知Quartz如何解释颜色
  • CImageSourceRef/CGImageDestinationRef,用于在Quartz里移入和移除数据
  • CGFontRef,用于绘制文字
  • CGPDFDictionaryRef, CGPDFObjectRef, CGPDFPageRef, CGPDFStream, CGPDFStringRef, and CGPDFArrayRef,用于访问PDF metaData
  • CGPDFScannerRef and CGPDFContentStreamRef, 解析PDF metaData
  • CGPSConverterRef,用于把PostScript转换为PDF,iOS上不支持。
图形状态 Graphics State
Quartz 通过修改the current graphics state的参数来修改绘制操作的结果。Graphics State 包含很多参数(对于Graphics State是实参,对于drawing routines是形参)(会另外以arguments形参形式带给绘制例程的)。绘制到图形上下文的例程会查询Graphics State以确定如何呈现其结果。 例如,当你调用函数来设置填充颜色时,你正在修改存储在当前图形状态中的值。当前图形状态的其他常用元素包括线宽,当前位置和文本字体大小。
图形上下文包含一个图形状态的stack。当Quartz创建一个图形上下文时,栈是空的。保存图形状态时,Quartz会将当前图形状态的副本push入堆栈。当你恢复Graphics State时,Quartz会将Graphics State pop出栈。pop的state成为the current Graphics State。
为了保存当前的图形状态,使用函数CGContextSaveGState将当前图形状态的副本推送到堆栈上。要恢复以前保存的图形状态,请使用函数CGContextRestoreGState将当前图形状态替换为堆栈顶部的图形状态。
Graphics state中的参数有:
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 的坐标系 Quartz 2D Coordinate Systems
和上篇的Cocoa 绘制指南官方文档笔记 Cocoa Drawing Guide一样,为了处理不同设备的不同dpi问题(如iphone的96像素每inch,如某打印机 300像素每inch),Quartz 使用了 The current transformation matrix(CTM)来自动处理这些坐标转换。CTM的一种特殊类型的矩阵,称为affine transform,通过应用平移,旋转和缩放操作(移动,旋转和调整坐标系统的计算)将点从一个坐标空间映射到另一个坐标空间。
当前的转换矩阵有一个次要目的:它允许你对如何绘制对象进行转换。例如,要绘制一个旋转45度的方框,可以在绘制方框之前旋转页面的坐标系(CTM)。使用旋转的坐标系将Quartz绘制到输出设备。
在Quartz的默认坐标系中,x轴从页面的左侧向右侧移动时增加。随着从底部向页面顶部移动,y轴的值会增加。bottom-left坐标系。一些技术使用不同于Quartz使用的默认坐标系统来设置它们的图形上下文。相对于Quartz,这样的坐标系是一个修改后的坐标系,并且在执行一些Quartz绘图操作时必须进行补偿。最常见的修改后的坐标系将原点放置在上下文的左上角,并将y轴更改为指向页面底部。使用这种修改的坐标系(modified coordinate system)的有:
UIKit使用modified coordinate system返回Quartz绘图上下文的原因是UIKit使用不同的默认坐标约定;它将转换应用到它创建的Quartz上下文,以便它们符合其约定。如果你的应用程序想要使用相同的绘图原则来同时绘制UIView对象和PDF图形上下文(由Quartz创建并使用默认坐标系统),则需要应用转换,以便PDF图形上下文接收相同的修改坐标系统。为此,请应用一个转换,将原点转换为PDF上下文的左上角,并将y坐标缩放-1。
使用缩放转换来给y转换负值坐标会改变Quartz绘图中的一些约定。例如,如果你调用CGContextDrawImage将图像绘制到上下文中,则该图像将在绘制到目标中时由其进行修改。同样,路径绘制原则接受参数,该参数指定是否在默认坐标系中以顺时针或逆时针方向绘制圆弧。如果修改了坐标系,结果也会被修改,就好像图像被镜像反射一样。
应用程序调整它对应用了变换的上下文所作的任何Quartz调用,取决于你自己。例如,如果要将图像或PDF正确绘制到graphics contect,应用程序可能需要暂时调整图形上下文的CTM。在iOS中,如果你使用UIImage对象来包装你创建的CGImage对象,则不需要修改CTM。 UIImage对象会自动补偿由UIKit应用的已修改坐标系。
重要: 上面的讨论对于理解你是否打算编写直接在iOS上定位Quartz的应用程序是很重要的,但这还不够。在iOS 3.2及更高版本中,当UIKit为你的app创建drawing context时,它还会对上下文进行其他更改以匹配默认的UIKIt约定。特别是,不受CTM影响的图案和阴影会分别进行调整,以便它们的约定与UIKit的坐标系相匹配。在这种情况下,没有与CTM等价的机制,你的应用程序可以使用它来更改由Quartz创建的上下文以匹配UIKit提供的上下文的行为;你的应用程序必须认识到它正在绘制什么样的上下文,并调整其行为以符合上下文的期望。
注意内存管理:对象的所有者
Quartz使用Core Foundation的内存管理模型,使用引用计数控制。创建时,Core Foundation对象以引用计数1开始。你可以通过调用保留该对象的函数来增加引用计数,并通过调用释放该对象的函数来减少引用计数。当引用计数递减到0时,该对象被释放。该模型允许对象安全地共享对其他对象的引用。
  • 如果你创建或复制对象,则你拥有它,因此你必须释放它。也就是说,一般来说,如果你从名称中带有创建复制这个词的函数中获得一个对象,则必须在完成该对象时释放该对象。否则,会导致内存泄漏
  • 如果你从名称中不包含单词创建复制的函数获取对象,则你不拥有对该对象的引用,并且不得释放它。该物体将在未来某个时候由其所有者发布。
  • 如果你不拥有一个对象,并且你需要保留它,你必须保留它并在完成时释放它。你使用特定于对象的Quartz 2D函数来保留和释放该对象。例如,如果你收到对CGColorspace对象的引用,则可以使用CGColorSpaceRetainCGColorSpaceRelease函数根据需要保留和释放该对象。你也可以使用Core Foundation函数CFRetainCFRelease,但是你必须小心,不要将NULL传递给这些函数。
Graphics Context
图形上下文表示一个绘图目的地。它包含绘图参数和绘图系统执行任何后续绘图命令所需的所有设备特定信息。图形上下文定义了基本的绘图属性,例如绘图时使用的颜色,剪裁区域,线宽和样式信息,字体信息,合成选项等等。
你可以通过使用Quartz上下文创建函数或使用iOS OS中的某个Mac OS X框架或UIKit框架提供的更高级别的函数来获取图形上下文。 Quartz提供各种风格的Quartz图形上下文的功能,包括位图和PDF,你可以使用它来创建自定义内容。
本章介绍如何为各种绘图目标创建图形上下文。图形上下文在代码中由数据类型CGContextRef表示,该数据类型是不透明的数据类型。获取图形上下文后,可以使用Quartz 2D函数绘制上下文,在上下文上执行操作(例如转换),并更改图形状态参数(如线宽和填充颜色)。
iOS上绘制Graphics Context
要在iOS应用程序中绘制屏幕内容, 需要设置一个UIView对象并实现其drawRect:方法来执行绘制。视图的drawRect:方法在屏幕上可见并且其内容需要更新时调用。在调用你的自定义drawRect:方法之前,视图对象会自动配置其绘图环境,以便你的代码可以立即开始绘制。作为此配置的一部分,UIView对象为当前绘图环境创建图形上下文(CGContextRef不透明类型)。你可以通过调用UIKit函数UIGraphicsGetCurrentContext在drawRect:方法中获得此图形上下文。
UIKit中使用的默认坐标系与Quartz使用的坐标系不同。在UIKit中,原点位于左上角,正值指向下方。 UIView对象通过将原点转换到视图的左上角并通过将其与-1相乘来反转y轴来修改Quartz图形上下文的CTM以匹配UIKit约定。
macOS上绘制Graphics Context
在Mac OS X中绘图时,你需要创建适合你所用框架的窗口图形上下文。 Quartz 2D API本身不提供获取Windows图形上下文的函数。相反,你使用Cocoa框架来获取在Cocoa中创建的窗口的上下文。你可以使用以下代码行从Cocoa应用程序的drawRect:例程中获取Quartz图形上下文:
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
currentContext方法返回当前线程的NSGraphicsContext实例。 graphicsPort方法返回由接收器表示的底层,特定于平台的图形上下文,这是一个Quartz图形上下文。
创建PDF绘制上下文
当你创建PDF图形上下文并绘制到该上下文时,Quartz会将你的绘图记录为写入文件的一系列PDF绘图命令。你提供PDF输出的位置和默认媒体框 – 指定页面边界的矩形。
iOS注意:iOS中的PDF图形上下文使用Quartz提供的默认坐标系,而不应用转换来匹配UIKit坐标系。如果你的应用程序计划在PDF图形上下文和由UIView对象提供的图形上下文之间共享绘图代码,则应用程序应该修改PDF图形上下文的CTM以修改坐标系。
使用 CGPDFContextCreateWithURL, CGPDFContextCreate等方法创建pdf context
创建一个 Bitmap graphics context
bitmap graphics context接受一个内存缓冲区(包含了bitmap的存储空间)指针。当绘制一个bitmap图形上下文时,这个内存缓冲区更新。在你释放图形上下文之后,你可以使用指定的像素格式完全更新位图。
注: bitmap图形上下文有时用于屏幕外绘制。在决定为此使用bitmap图形上下文之前,请参阅Core Graphics Layer Drawing CGLayer对象(CGLayerRef)针对屏外绘图进行了优化,因为只要可能,Quartz会在显卡上缓存图层。
iOS注意:iOS应用程序应该使用函数UIGraphicsBeginImageContextWithOptions,而不是使用这里描述的底层Quartz函数。如果应用程序使用Quartz创建离屏位图,则位图图形上下文使用的坐标系是默认的Quartz坐标系。相比之下,如果你的应用程序通过调用函数UIGraphicsBeginImageContextWithOptions来创建图像上下文,则UIKit将相同的转换应用于上下文的坐标系统,就像它对UIView对象的图形上下文所做的那样。这使你的应用程序可以使用相同的绘图代码,而无需担心不同的坐标系。虽然你的应用程序可以手动调整坐标转换矩阵以获得正确的结果,但在实践中,这样做没有性能优势。
使用CGBitmapContextCreate 创建一个位图图形上下文(iOS应该使用UIGraphicsBeginImageContextWithOptions用于转换CTM),这个方法有以下参数:
•data
•width 单位:像素
•height. 单位:像素
•bitsPerComponent
•bytesPerRow
•colorspace
•bitmapInfo
关于Paths

路径定义了一个或多个形状或子路径。子路径可以由直线,曲线或两者组成。它可以打开或关闭。子路径可以是简单的形状,例如线条,圆形,矩形或星形,也可以是更复杂的形状,如山脉或抽象涂鸦的轮廓。

构建Path的各种block
子路径由线条,弧线和曲线构建而成。 Quartz还提供了方便的函数来添加一个函数调用的矩形和椭圆。点也是重要的路径构建块,因为点定义了形状的起始位置和结束位置。

Points

Point是指定用户空间中位置的x和y坐标。你可以调用函数CGContextMoveToPoint来指定新子路径的起始位置。 Quartz会跟踪当前点,这是用于路径构建的最后一个位置。例如,如果你调用函数CGContextMoveToPoint在(10,10)处设置位置,则会将当前点移动到(10,10)。如果你画一条50单位长的水平线,则线上的最后一点,即(60,10)成为当前点。直线,圆弧和曲线总是从当前点开始绘制。大多数情况下,你通过传递Quartz函数的两个浮点值来指定x和y坐标来指定点。某些函数要求你传递一个CGPoint数据结构,该结构包含两个浮点值。

Lines

一条线由其端点定义。它的起点总是被假定为当前点,所以当你创建一条线时,你只能指定它的端点。你可以使用函数CGContextAddLineToPoint将单行附加到子路径。通过调用函数CGContextAddLines,可以将一系列连接线添加到路径中。你传递这个函数的一组点。第一点必须是第一条线的起点;剩余的点是端点。 Quartz在第一个点开始一个新的子路径,并将一条直线段连接到每个端点。

Arcs 弧线

圆弧是圆形段。 Quartz提供了两个创建弧的函数。函数CGContextAddArc从圆中创建一个曲线段。你可以指定圆的中心,半径和径向角(以弧度表示)。你可以通过指定2 pi的径向角度来创建一个完整的圆。
CGContextAddArcToPoint 用于创建圆角矩形 ,Quartz只用你提供的结束点创建两条切线,你也需要提供圆的半径来让quartz裁切弧线。圆弧的中心点是两个半径的交点,每个半径都垂直于两条切线中的一条。圆弧的每个端点都是其中一条切线上的切点,如图3-5所示。圆的红色部分是实际绘制的。
如果当前路径已经包含子路径,则Quartz将从当前点到弧的起点追加一条直线段。如果当前路径为空,则Quartz会在圆弧的起点处创建一个新的子路径,并且不会添加最初的直线段。

曲线

二次和三次Bézier曲线是代数曲线,可以指定任意数量的有趣的曲线形状。通过将多项式应用于起点和终点以及一个或多个控制点来计算这些曲线上的点。以这种方式定义的形状是矢量图形的基础。一个公式比一系列位更紧凑,并且具有曲线可以在任何分辨率下重新创建的优点。
给出二次和三次Bézier曲线的多项式以及如何从公式中生成曲线的细节在很多描述计算机图形的数学文本和在线资源中讨论。这些细节不在这里讨论。
使用CGContextAddCurveToPoint函数可以使用控制点和指定的端点从当前点追加三次Bézier曲线。图3-7显示了由图中显示的当前点,控制点和端点产生的三次Bézier曲线。两个控制点的位置决定曲线的几何形状。如果控制点都在起点和终点之上,则曲线向上拱起。如果控制点都低于起点和终点,则曲线向下拱起。
你可以通过调用函数CGContextAddQuadCurveToPoint并指定控制点和端点,从当前点追加二次Bézier曲线。图3-8显示了使用相同端点但控制点不同的两条曲线。控制点确定曲线的方向。用二次Bézier曲线创建尽可能多的有趣形状,因为二次曲线只使用一个控制点。例如,使用单个控制点创建分频器是不可能的。

关闭Subpath

要关闭当前子路径,应用程序应调用CGContextClosePath。此函数添加从当前点到子路径起点的线段并关闭子路径。以子路径起点为终点的直线,圆弧和曲线实际上并不关闭子路径。你必须显式调用CGContextClosePath来关闭子路径。一些Quartz函数将路径的子路径视为由应用程序关闭。这些命令对待每个子路径,就像应用程序调用CGContextClosePath关闭它一样,隐式地将一个线段添加到子路径的起始点。在关闭一个子路径后,如果你的应用程序进行了额外的调用来将线,弧或曲线添加到路径中,Quartz将从刚刚关闭的子路径的起点开始一个新的子路径。

椭圆

椭圆本质上是一个压扁的圆。你可以通过定义两个焦点创建一个,然后绘制位于距离上的所有点,以便将椭圆上的任意点与一个焦点之间的距离与从该相同点到另一个焦点的距离相加始终是相同的值。
你可以通过调用CGContextAddEllipseInRect函数向当前路径添加一个椭圆。你提供了一个定义椭圆边界的矩形。quartz 用一系列贝塞尔曲线模拟椭圆。椭圆的中心是矩形的中心。如果矩形的宽度和高度相等(即正方形),则椭圆是圆形的,其半径等于矩形宽度(或高度)的一半。如果矩形的宽度和高度不相等,则它们定义椭圆的长轴和短轴。添加到路径中的椭圆开始于移至操作并以关闭子路径操作结束,所有移动都以顺时针方向。

矩形

你可以通过调用CGContextAddRect函数向当前路径添加一个矩形。你提供了一个CGRect结构,其中包含矩形的原点及其宽度和高度。添加到路径的矩形以移动操作开始,以结束子路径操作结束,所有移动均以逆时针方向定向。通过调用函数CGContextAddRects并提供CGRect结构数组,可以将多个矩形添加到当前路径。
创建一个Path
当你想在图形上下文中构建一个路径时,你可以通过调用CGContextBeginPath函数来发信号通知Quartz。接下来,通过调用函数CGContextMoveToPoint来设置路径中第一个形状或子路径的起点。在建立第一个点后,可以将线条,弧线和曲线添加到路径中,请注意以下事项:
  • 在开始一个新路径之前,调用函数CGContextBeginPath。
  • 直线,圆弧和曲线从当前点开始绘制。 空路径没有当前点; 你必须调用CGContextMoveToPoint来为第一个子路径设置起始点,或者调用一个为你隐式执行此操作的便利函数。
  • 如果要关闭路径中的当前子路径,请调用函数CGContextClosePath将一个段连接到子路径的起始点。 后续路径调用会开始一个新的子路径,即使你没有明确设置新的起点。
  • 绘制弧线时,Quartz会在当前点与弧线的起点之间绘制一条线。
  • 添加椭圆和矩形的Quartz例程为路径添加一个新的封闭子路径。
  • 你必须调用绘画函数来填充或描边路径,因为创建路径不会绘制路径。 有关详细信息,请参见绘制路径。
绘制路径后,将从图形上下文中刷新路径。你可能不想轻易失去自己的路径,特别是如果它描绘了一个想要反复使用的复杂场景。出于这个原因,Quartz提供了两种用于创建可重用路径的数据类型 – CGPathRef和CGMutablePathRef。你可以调用CGPathCreateMutable函数来创建可添加线条,弧线,曲线和矩形的可变CGPath对象。 Quartz提供了一组CGPath函数,它们与Building Block中讨论的函数并行。路径函数在CGPath对象上运行,而不是在图形上下文中运行。这些功能是:
  • CGPathCreateMutable, which replacesCGContextBeginPath
  • CGPathMoveToPoint, which replaces CGContextMoveToPoint
  • CGPathAddLineToPoint, which replaces CGContextAddLineToPoint
  • CGPathAddCurveToPoint, which replaces CGContextAddCurveToPoint
  • CGPathAddEllipseInRect, which replaces CGContextAddEllipseInRect
  • CGPathAddArc, which replaces CGContextAddArc
  • CGPathAddRect, which replaces CGContextAddRect
  • CGPathCloseSubpath, which replaces CGContextClosePath
当你想把路径附加到图形上下文时,你可以调用函数CGContextAddPath。路径保持在图形上下文中,直到Quartz描绘它。你可以通过调用CGContextAddPath再次添加路径。
注意:你可以通过调用函数CGContextReplacePathWithStrokedPath,将图形上下文中的路径替换为路径的描边版本。
画一个当前的Path
你可以通过描画或填充或both来画当前路径。stroke一条横跨path的线。填充绘制路径中包含的区域。 Quartz的功能可以让你描画一条路径,填充一条路径,或者同时描画和填充一条路径。描边线(宽度,颜色等)的特征,填充颜色和Quartz用来计算填充区域的方法都是图形状态的一部分。
设置Blend Modes,在OSX中,叫做Compositing operation组合操作
混合模式指定Quartz如何在背景上应用绘画。 Quartz默认使用普通混合模式,它使用以下公式将前景绘画与背景绘画相结合:
result = (alpha * foreground) + (1 – alpha) * background
Color and Color Spaces 供了对颜色的alpha分量的详细讨论,该颜色指定了颜色的不透明度。对于本节中的示例,你可以假定颜色是完全不透明的(alpha值= 1.0)。对于不透明的颜色,当使用普通混合模式进行绘制时,任何在背景上绘制的内容都会完全遮挡背景。
可以通过调用函数CGContextSetBlendMode来设置混合模式以获得各种效果,并传递适当的混合模式常量。请记住,混合模式是graphics state的一部分。如果在更改混合模式之前使用函数CGContextSaveGState,则调用函数CGContextRestoreGState会将混合模式重置为normal。
Path的裁切
当前裁剪区域是从用作遮罩的路径创建的,允许你阻止不想绘制的页面部分。例如,如果你有非常大的位图图像并且只想显示其中的一小部分,则可以将剪辑区域设置为仅显示要显示的部分。
在绘制时,Quartz只在裁剪区域内绘制绘画。在裁剪区域的闭合子路径内发生的绘图是可见的;绘图发生在裁剪区域的闭合子路径之外不是。
当图形上下文最初创建时,剪贴区域包括上下文的所有可绘制区域(例如,PDF上下文的媒体框)。你可以通过设置当前路径并使用裁剪功能而不是绘图功能来改变裁剪区域。裁剪功能将当前路径的填充区域与现有裁剪区域相交。因此,你可以交叉裁剪区域,缩小图片的可见区域,但不能增加裁剪区域的面积。
剪辑区域是图形状态的一部分。要将裁剪区域恢复到以前的状态,可以在裁剪之前保存图形状态,并在裁剪完成后恢复图形状态。
Quartz 2d提供了几个作用不同的裁切方法:
CGContextClip
CGContextEOClip
CGContextClipToRect
CGContextClipToRects
CGContextClipToMask
变换Transforms
Quartz 2D绘图模型定义了两个完全独立的坐标空间:代表文档页面的用户空间和代表设备本机分辨率的设备空间。用户空间坐标是与设备空间中像素分辨率无关的浮点数。当你想要打印或显示文档时,Quartz会将用户空间坐标映射到设备空间坐标。因此,你不必重写应用程序或编写额外的代码来调整应用程序的输出,以便在不同设备上实现最佳显示效果。你可以通过在当前转换矩阵或CTM上进行操作来修改默认用户空间。创建图形上下文后,CTM是单位矩阵。你可以使用Quartz转换函数来修改CTM,并因此修改用户空间中的绘图。
修改CTM Current Transformation Matrix
通过修改CTM来旋转,缩放,在绘制一个图片前变换页。在转换CTM前,需要存储graphics state,这样在绘制后可以恢复上次的状态。也可以用affine transform 连接CTM。
修改CTM的方法有(前提是你有一个可用的graphics context,可以是current graphics context,也可以是自己创建的其他context)。
CGContextTranslateCTM (myContext, w/4, 0);
CGContextScaleCTM (myContext, .25, .5);
CGContextRotateCTM (myContext, radians ( 22.));
创建一个CTM
Quartz 操作中的affine transform functions 作用于 矩阵,而不是CTM本身,你可以使用这些方法构建一个矩阵(通过调用CGContextConcatCTM应用于CTM)。affine transform functions对CGAffineTransform的数据结构进行操作或者返回。你可以构建简单或者负责的affine transforms,而且可以重用。
affine transforms functions 执行和CTM一样的操作函数:translation, rotation, scaling, and concatenation。这些函数主要有:
Function Use
CGAffineTransformMakeTranslation 构建一个新的变换矩阵,指明在origin point基础上如何移动
CGAffineTransformTranslate 应用一个已存在的translation affine transform
CGAffineTransformMakeRotation 构建一个新的变换矩阵,指明如何旋转当前坐标系
CGAffineTransformRotate 应用一个已存在的rotation affine transform
CGAffineTransformMakeScale 构建一个新的变换矩阵,指明如何缩放
CGAffineTransformScale 应用一个已存在的scale affine transform
Quartz还提供了一个反转矩阵的仿射变换函数CGAffineTransformInvert。反转通常用于提供转换对象内点的逆向转换。当需要恢复已由矩阵转换的值时,反转可能很有用:反转矩阵,并将该值乘以反转矩阵,结果为原始值。你通常不需要反转转换,因为你可以通过保存和恢复graphics state来反转转换CTM的效果。
在某些情况下,你可能不想改变整个空间,但只是一个点或一个大小。你可以通过调用函数CGPointApplyAffineTransform来对CGPoint结构进行操作。通过调用函数CGSizeApplyAffineTransform来操作CGSize结构。你可以通过调用函数CGRectApplyAffineTransform来对CGRect结构进行操作。该函数返回包含传递给它的矩形的转换角点的最小矩形。如果对矩形进行操作的仿射变换仅执行缩放和平移操作,则返回的矩形与从四个变换角构成的矩形重合。
你可以通过调用CGAffineTransformMake函数来创建一个新的 affine transform,但不同于其他形成新的 affine transform的函数,这个函数需要你提供矩阵条目。要有效使用此功能,你需要了解矩阵数学。
你可以通过调用函数CGAffineTransformEqualToTransform来确定一个 affine transform是否与另一个相等。如果传递给它的两个变换相等,则此函数返回true,否则返回false。函数CGAffineTransformIsIdentity是检查变换是否为标识变换的有用函数。标识转换不执行变化,缩放或旋转。将该变换应用于输入坐标总是返回输入坐标。 Quartz常量CGAffineTransformIdentity表示标识转换。
可以获取 user space to device space transform
通常,当你使用Quartz 2D进行绘制时,只能在用户空间中工作。 Quartz负责为用户和设备空间之间的转换。如果你的应用程序需要获取Quartz用于在用户和设备空间之间进行转换的仿射变换,则可以调用函数CGContextGetUserSpaceToDeviceSpaceTransform。
Quartz提供了许多便利功能来转换用户空间和设备空间之间的以下几何形状。你可能会发现这些函数比应用函数CGContextGetUserSpaceToDeviceSpaceTransform返回的仿射变换更易于使用。
points。函数CGContextConvertPointToDeviceSpace和CGContextConvertPointToUserSpace将CGPoint数据类型从一个空间转换为另一个空间。
sizes。函数CGContextConvertSizeToDeviceSpace和CGContextConvertSizeToUserSpace将CGSize数据类型从一个空间转换为另一个空间。
Rects。函数CGContextConvertRectToDeviceSpace和CGContextConvertRectToUserSpace将CGRect数据类型从一个空间转换为另一个空间。
矩阵背后的数学
和线性代数密切相关,关于向量在矩阵中映射到另一个位置的过程,建议回去翻线性代数。
关于图案的绘制 Patterns
一个图案是一组重复的绘制一个 graphics context 的操作。 可以和使用color一样使用 patterns。当你绘制图案的时候,quartz 分离页面到一组(set)图案cell,每个图案cell有自己的大小,使用你提供的回调函数绘制每个cell。
Pattern的原理
PAttern图案的基本组成是 pattern cell,你可以指定Quartz距水平和垂直方向的距离。你可以为每个方向指定不同的间距值。如果使间距小于图案单元的宽度或高度,则图案单元格会重叠。

绘制图案单元时,Quartz使用Pattern space作为坐标系。Pattern space是一种抽象空间,通过你在创建Pattern(Pattern matrix)时指定的转换矩阵映射到默认用户空间。

注意:Pattern space与用户空间是分开的。无论当前转换矩阵的状态如何,未转换的Pattern space都映射到基本(未转换的)用户空间。当你将变换应用于Pattern space时,Quartz仅将变换应用于Pattern spacePattern space的默认约定是底层图形上下文的默认约定。默认情况下,Quartz使用坐标系,其中正x值表示向右位移,正y值表示向上位移。但是,由UIKit创建的图形上下文使用不同的约定,其中正y值表示向下位移。虽然此惯例通常通过将变换连接到坐标系上应用于图形上下文,但在这种情况下,Quartz也会修改要匹配的Pattern空间的默认约定。

Pattern如何工作

Pattern的操作与颜色类似,因为你可以设置填充或笔触Pattern,然后调用绘画功能。 Quartz使用你设置的Pattern作为“绘画”。例如,如果你想用纯色绘制一个填充的矩形,你首先调用一个函数,比如CGContextSetFillColor来设置填充颜色。然后调用函数CGContextFillRect,用你指定的颜色绘制填充的矩形。要使用Pattern进行绘制,首先调用函数CGContextSetFillPattern来设置Pattern。然后调用CGContextFillRect实际上用你指定的Pattern绘制填充的矩形。绘画与颜色和图案的区别在于你必须定义图案。你将Pattern和颜色信息提供给函数CGContextSetFillPattern。你将看到如何在绘画彩色图案和绘画模板图案中创建,设置和绘制图案。
以下是Quartz如何在幕后使用你提供的Pattern进行绘制的示例。当你使用Pattern填充或描边时,Quartz概念性地执行以下任务来绘制每个Pattern单元格:
保存图形状态。
将当前转换矩阵转换为Pattern单元的原点。
将CTM与Pattern矩阵连接起来。
剪辑到图案单元的边界矩形。
调用绘图回调来绘制图案单元格。
恢复图形状态。
Quartz负责所有的平铺工作,重复渲染图形单元到绘图空间,直到整个空间被绘制完毕。你可以用图案填充或描边。Pattern单元格可以是你指定的任何大小。如果你想看到图案,你应该确保图案单元适合绘图空间。例如,如果你的图案单元格是10个单位的8个单位,并且你使用该图案对宽度为2个单位的线条进行描边,则该图案单元格将被裁剪,因为它的宽度为10个单位。在这种情况下,你可能无法识别该Pattern。
可以绘制带有颜色的colored图案,可以绘制图案Stencil模板,绘制大函数都在 你提供的回调函数里完成。
关于阴影
阴影有三个元素:
一个x偏移量,它指定阴影偏离图像水平方向多远。
y偏移量,用于指定阴影距图像偏移量在垂直方向上的距离。
一个模糊值,用于指定图像是否具有硬边缘
阴影如何工作
Quartz中的阴影是graphic state的一部分。 你可以调用函数CGContextSetShadow,传递图形上下文,偏移值和模糊值。 设置阴影后,你绘制的任何对象都会绘制阴影,并在设备RGB颜色空间中使用具有1/3 alpha值的黑色。 换句话说,使用设置为{0,0,0,1.0 / 3.0}的RGBA值绘制阴影。
通过调用函数CGContextSetShadowWithColor,传递图形上下文,偏移值,模糊值和CGColor对象,可以绘制彩色阴影。 为颜色提供的值取决于要绘制的颜色空间。
如果在调用CGContextSetShadow或CGContextSetShadowWithColor之前保存图形状态,则可以通过恢复图形状态来关闭阴影。 你还可以通过将阴影颜色设置为NULL来禁用阴影。
关于渐变 Gradients
Quartz提供了两种不透明的数据类型用于创建渐变 – CGShadingRef和CGGradientRef。你可以使用这两种方法创建轴向或径向渐变。渐变是从一种颜色到另一种颜色的填充。
axial gradient(也称为linear gradient)沿两个定义的端点之间的轴线变化。位于与轴垂直的线上的所有点具有相同的颜色值。
radial gradient是沿两个限定端部之间的轴线径向变化的填充物,其通常是两个圆形。如果点位于中心点位于轴上的圆的圆周上,则点共享相同的颜色值。梯度的圆形部分的半径由端部圆的半径限定;每个中间圆的半径从一端到另一端线性地变化。
关于透明layer
透明层由两个或两个以上的对象组成,它们组合在一起形成一个复合图形。合成的合成物被当作一个单一的对象。
透明layer如何工作
透明层与许多流行的图形应用程序中可用的图层相似。层是独立的实体。 Quartz为每个上下文维护一个透明图层堆栈,透明图层可以嵌套。但是因为图层总是堆栈的一部分,所以不能独立操纵它们。
你可以通过调用函数CGContextBeginTransparencyLayer来指示透明图层的开始,该函数将图形上下文和CFDictionary对象作为参数。字典允许你提供选项来指定关于图层的附加信息,但由于Quartz 2D API尚未使用该字典,因此传递NULL。在此调用之后,图形状态参数保持不变,除了alpha(设置为1),shadow(关闭),blend模式(设置为normal)以及其他影响最终组合的参数。
在开始透明图层之后,你可以执行想要在该图层中显示的任何图形。指定上下文中的绘图操作将绘制为完全透明的背景。此背景被视为来自上下文的单独目标缓冲区。
当你完成绘图时,你可以调用函数CGContextEndTransparencyLayer。 Quartz使用上下文的全局alpha值和阴影状态并且考虑上下文的 剪辑区域将结果合成到上下文中。
简单的说就是把graphics context绘制在一个自身维护的一个透明层stack的一个透明layer上。用于解决绘制图形组合后,阴影问题。估计的过程是,每次绘制一个图形,从透明层栈中pop一个透明层,绘制在这个透明层上,透明层对象push回栈。
Quartz 2d中的数据管理
管理数据是每个图形应用程序需要执行的任务。对于Quartz,数据管理是指向Quartz 2D例程提供数据或从Quartz 2D例程接收数据。一些Quartz 2D例程将数据移动到Quartz中,例如从文件或应用程序的另一部分获取图像或PDF数据的Quartz 2D例程。其他例程接受Quartz数据,例如将图像或PDF数据写入文件或将数据提供给应用程序的另一部分。
Quartz可以识别三大类数据源和目标:
URL。其位置可以被指定为URL的数据可以充当数据的提供者或接收者。使用Core Foundation数据类型CFURLRef将URL传递给Quartz函数。
CFData。 Core Foundation数据类型CFDataRef和CFMutableDataRef是让简单分配的缓冲区承担Core Foundation对象行为的数据对象。 CFData与cocoa 的对应的NSData类别“桥接”;如果你在Cocoa框架中使用Quartz 2D,你可以传递一个NSData对象给任何带有CFData对象的Quartz函数。
raw data原始数据。你可以提供一个指向任何类型数据的指针,以及一组用于处理数据的基本内存管理的回调。
数据本身,无论是由URL,CFData对象还是数据缓冲区表示,都可以是图像数据或PDF数据。图像数据可以使用任何类型的文件格式。 Quartz理解大多数常见的图像文件格式。一些Quartz数据管理功能专门用于图像数据,一些仅使用PDF数据,另一些则更通用,可用于PDF或图像数据。
位图和图片蒙板 Bitmap Images and Image Masks
这里内容较多,将会单独发一篇博客记录讨论。
Core Graphics Layer Drawing
CALayer对象(CGLayerRef数据类型)允许你的app使用layer来绘制。
layers 在以下场景适用:
  • 你计划使用的高质量离线绘制。例如: 你可能创建了一个场景并且想要重复使用相同的背景。绘制背景场景到一个layer,然后在任何你需要的地方绘制他。这样做的好处是你不需要在海岛color space或者设备依赖信息就可以绘制。
  • 重复的绘制。例如:你可能想要创建一个pattern图案,包含相同的item,重复的画啊画。把这个item画到一个layer上,然后不同的重复绘制这个layer。任何重复绘制的Quartz对象,包括CGPath,CGShading,CGPDFPage——绘制到一个CGLayer可以带来性能好处。注意,alyer不只是为了屏幕上的绘制,注意,layer不仅仅是屏幕上的绘画;你可以将它用于不面向屏幕的图形上下文,比如PDF图形上下文。
  • 可以缓存。虽然你可以使用图层来达到这个目的,但你不需要这样做,因为Quartz Compositor不需要缓冲你的layer。如果你必须绘制到缓冲区,请使用图层而不是位图图形上下文。
CGLayer对象和透明图层与CGContext函数创建的CGPath对象和路径并行。对于CGLayer或CGPath对象,你绘制到抽象目标,然后可以将完整的绘画绘制到另一个目标,例如显示或PDF。当你绘制透明图层或使用绘制路径的CGContext函数时,你可以直接绘制到由图形上下文表示的目标,没有对组装绘制输出的抽象中间目标。CALayer绘制到一个抽象的中间层,可重复使用。而透明层绘制,直接绘制graphics context到目标。
layer drawing 如何工作 How Layer Drawing works
一个Layer,由CGLayerRef 数据类型表示。该对象为性能优化良好设计。当可能的时候,Quartz缓存一个CGlayer对象,使用一种机制适应到他关联的Quartz graphics context上。例如,一个关联显卡的graphics context 可能在显卡上缓存这个layer,这让绘制这个layer内容的速度比画一个构建与bitmap graphics context的小图片要快很多。因为这个原因,layer是非常好的离屏绘制选择,相对bitmap graphics context来说。
所有的quartz 绘制操作画到一个graphics context上。这个上下文提供了一个抽象的目标,让你无需考虑这个目标的细节。比如她的分辨率。你在user space工作,Quartz 来执行必要的转换来绘到正确的目标。当你使用一个CGLayer对象绘制的时候,你也绘制了一个graphics context。
所有的layer 绘制都开始于一个你创建的CGLayer对象的 graphics context,你可以用 CGLayerCreateWithContext创建他。这个用于创建CGLayer对象的context是一个典型的window graphics context。Quartz 创建一个layer,这个layer有了所有的graphics context的特征:分辨率,color space,graphic state settings。你可以给这个layer设置一个size(如果你不想用这个graphics context上下文里的size的话)。
在你能挥着这个layer之前,你必须获取和这个layer关联的graphics context. 这个graphics context和用于创建layer的graphics context有一样的特点。只要用于创建layer的图形上下文是一个窗口图形上下文,那么CGLayer图形上下文就会被缓存到GPU上,如果可能的话。
你可以绘制到layer的图形上下文中,就像你将绘制到任何图形上下文一样,将layer的图形上下文传递给绘图函数。
当你准备好使用这个layer的内容,你使用CGContextDrawLayerInRect or CGContextDrawLayerAtPoint 来绘制layer到一个 graphics context。通常情况下,你将会绘制与你用来创建layer对象的相同图形上下文,但你不需要这样做。你可以绘制这个layer到任何graphics context,请记住,图层绘图具有用于创建图层对象的图形上下文的特征,这可能会施加某些限制,比如:性能,分辨率等。举例:一个和屏幕关联的layer可能在显示硬件上被缓存了。如果目标上下文是一个打印输出或者一个PDF上下文,它可能需要从图形硬件获取到内存,从而导致性能低下。
提示:如果要合成图形的某些部分以实现遮蔽一组对象等效果,请使用透明度图层。 (当你想要脱离屏幕或需要重复绘制相同的东西时使用CGLayer对象。
绘制一个layer
你需要执行以下部分中描述的任务以使用CGLayer对象进行绘制:
1,创建一个使用现有图形上下文初始化的CGLayer对象
2,获取图层的图形上下文
3,绘制到CGLayer图形上下文
4,将图层绘制到目标图形上下文
PDF文件的创建,访问和变换
PDF文件存储矢量的,非分辨率依赖的数据,包括图形,文字和图片。绘制命令以一组连续的命令绘制。
访问一个PDF文件的主要工作是:获取一个pdf文件,或者一页pdf的 pdf graphics context,并且绘制。
创建pdf文件主要通过quartz 2d绘制到graphics context中。
PDF文件的解析
Quartz提供了让你检查PDF文档结构和内容流的功能。检查文档结构可让你阅读文档目录中的条目以及与每个条目相关的内容。通过递归遍历目录,你可以检查整个文档。
一个PDF内容流正如其名字所暗示的那样 – 一个连续的数据流,例如’BT 12 / F71 Tf(绘制该文本)Tj。 。 。 ‘PDF操作符及其描述符与实际的PDF内容混合在一起。检查内容流需要你顺序访问它。
end

留下评论

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.