路径定义一个或多个形状或子路径。 子路径可以由直线,曲线或两者组成。 它可以打开或关闭。 子路径可以是简单的形状,例如线,圆,矩形或星形,或更复杂的形状,例如山脉的轮廓或抽象涂鸦。 图3-1显示了您可以创建的一些路径。 直线(在图的左上方)是虚线; 线条也可以是实心的。 波形路径(在中间顶部)由几条曲线组成并且是开放路径。 同心圆被填充,但不被描边。 加利福尼亚州是一条封闭的路径,由许多曲线和线组成,路径既描边(stroked)又填充。 星星说明了两种填充路径的选项,您将在本章后面阅读。
3-1 Quartz支持基于路径的绘图在本章中,您将了解构成路径的构建块,如何描绘和绘制路径以及影响路径外观的参数。
路径创建和路径绘画
路径创建和路径绘制是单独的任务。 首先创建一个路径。 当你想渲染一个路径时,你要求Quartz绘制它。 如图3-1所示,您可以选择划出路径,填充路径,或者同时划出和填充路径。 您还可以使用路径来限制路径边界内的其他对象的绘制,实际上是剪裁区域。
图3-2显示了已绘制并包含两个子路径的路径。 左边的子路径是矩形,右边的子路径是由直线和曲线组成的抽象形状。 每个子路径都已填充,其轮廓已划线。
3-2 包含两个形状或子路径的路径图3-3显示了独立绘制的多条路径。 每个路径包含随机生成的曲线,其中一些填充和其他描边。 绘图通过剪裁区域限制为圆形区域。
3-3 剪切区域约束绘图构件块
子路径由线,弧和曲线构成。 Quartz还提供了方便的功能,通过单个函数调用来添加矩形和椭圆。 点也是路径的基本构建块,因为点定义了形状的开始和结束位置。
Points
点是指定用户空间中的位置的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的径向角度创建一个完整的圆。 图3-4显示了独立绘制的多个路径。 每个路径包含随机生成的圆; 一些填充和其他描边。
3-4 多路径; 每个路径包含随机生成的圆函数CGContextAddArcToPoint是理想的,当你想要圆角的矩形。 Quartz使用您提供的端点创建两条切线。 您还提供了Quartz切割圆弧的圆的半径。 弧的中心点是两个半径的交点,每个半径垂直于两条切线中的一条。 圆弧的每个端点是切线之一上的切点,如图3-5所示。 圆的红色部分是实际绘制的。
3-5 用两条切线和半径定义圆弧如果当前路径已经包含子路径,Quartz将从当前点到弧的起点添加一条直线段。 如果当前路径为空,Quartz将在弧的起点处创建一个新的子路径,而不添加初始直线段。
Curves
二次和三次贝塞尔曲线是可以指定任何数量的有趣曲线形状的代数曲线。 通过对起点和终点以及一个或多个控制点应用多项式公式来计算这些曲线上的点。 以这种方式定义的形状是矢量图形的基础。 公式比位数组更紧凑地存储,并且具有可以以任何分辨率重新创建曲线的优点。
图3-6显示了通过独立绘制多个路径创建的各种曲线。 每个路径包含随机生成的曲线; 一些填充和其他描边。
提供二次和三次贝塞尔曲线的多项式公式以及关于如何从公式生成曲线的细节,在描述计算机图形的许多数学文本和在线资源中讨论。 这里不讨论这些细节。
您使用函数CGContextAddCurveToPoint从当前点附加一个三次贝塞尔曲线,使用控制点和您指定的端点。 图3-7显示了从图中所示的当前点,控制点和终点产生的三次贝塞尔曲线。 两个控制点的位置确定曲线的几何形状。 如果控制点都在起点和终点之上,则曲线向上拱起。 如果控制点都在起点和终点以下,则曲线向下弯曲。
3-7 三次贝塞尔曲线使用两个控制点您可以通过调用函数CGContextAddQuadCurveToPoint,并指定控制点和端点,从当前点追加二次Bézier曲线。 图3-8显示了使用相同的端点但是使用不同的控制点产生的两条曲线。 控制点确定曲线弯曲的方向。 使用二次Bézier曲线创建尽可能多的有趣形状是不可能的,因为二次曲线只使用一个控制点。 例如,不能使用单个控制点创建交叉。
3-8 二次Bézier曲线使用一个控制点Closing a Subpath
要关闭当前子路径,应用程序应调用CGContextClosePath。 此函数将从当前点到子路径的起始点添加一个线段,并关闭子路径。 在子路径起点处结束的线,弧和曲线实际上不关闭子路径。 您必须显式调用CGContextClosePath以关闭子路径。
一些Quartz函数将路径的子路径视为由应用程序关闭。 这些命令处理每个子路径,就像您的应用程序调用CGContextClosePath关闭它,隐式地添加一个线段到子路径的起始点。
关闭子路径后,如果您的应用程序进行额外的调用以向路径添加线,弧或曲线,Quartz将从您刚关闭的子路径的起点开始新的子路径。
Ellipses
椭圆本质上是一个被挤压的圆。 通过定义两个聚焦点,然后绘制所有位于一定距离处的点,从而将从椭圆上的任何点到一个焦点的距离与从该相同点到另一个焦点的距离相加总是相同的值来创建一个 。 图3-9显示了独立绘制的多条路径。 每个路径包含随机生成的椭圆; 一些填充和其他描边。
3-9 多路径; 每个路径包含随机生成的椭圆您可以通过调用函数CGContextAddEllipseInRect将椭圆添加到当前路径。 您提供了一个定义椭圆边界的矩形。 Quartz使用一系列Bézier曲线近似椭圆。 椭圆的中心是矩形的中心。 如果矩形的宽度和高度相等(即,正方形),则椭圆是圆形的,半径等于矩形的宽度(或高度)的一半。 如果矩形的宽度和高度不相等,则它们定义椭圆的长轴和短轴。
添加到路径的椭圆以移动到操作开始,并以闭合子路径操作结束,所有移动以顺时针方向定向。
Rectangles
您可以通过调用函数CGContextAddRect将矩形添加到当前路径。 您提供了一个包含矩形的原点及其宽度和高度的CGRect结构。
添加到路径的矩形以移动到操作开始,并以闭合子路径操作结束,所有移动以逆时针方向定向。
您可以通过调用函数CGContextAddRects并提供一个CGRect结构数组来向当前路径添加许多矩形。 图3-10显示了独立绘制的多条路径。 每个路径包含一个随机生成的矩形; 一些填充和其他描边。
3-10 多路径; 每个路径包含随机生成的矩形创建路径
当你想在图形上下文中构造一个路径时,你通过调用函数CGContextBeginPath来发送Quartz信号。 接下来,通过调用函数CGContextMoveToPoint,为路径中的第一个形状或子路径设置起点。 建立第一个点后,您可以向路径中添加线,弧和曲线,并记住以下几点:
- 在开始新路径之前,请调用函数CGContextBeginPath。
- 从当前点开始绘制线,弧和曲线。 空路径没有当前点; 您必须调用CGContextMoveToPoint以设置第一个子路径的起点或调用一个为您隐式执行此操作的便利函数。
- 当要关闭某个路径中的当前子路径时,请调用函数CGContextClosePath将段连接到子路径的起始点。 后续路径调用开始一个新的子路径,即使您没有显式设置新的起点。
- 当绘制弧时,Quartz在当前点和弧的起点之间绘制一条线。
- 添加椭圆和矩形的Quartz例程向路径中添加一个新的封闭子路径。
- 您必须调用绘制函数来填充或描边路径,因为创建路径不会绘制路径。 有关详细信息,请参阅绘制路径。
绘制路径后,将从图形上下文中刷新。 你可能不想失去你的路径这么容易,特别是如果它描绘了一个复杂的场景,你想一次又一次使用。 因此,Quartz为创建可重用路径提供了两种数据类型:CGPathRef和CGMutablePathRef。 您可以调用函数CGPathCreateMutable创建一个可变的CGPath对象,您可以添加线,弧,曲线和矩形。 Quartz提供了一组与“构件块”中讨论的功能并行的CGPath函数。 路径函数对CGPath对象而不是图形上下文进行操作。 这些功能是:
- CGPathCreateMutable,它替换了CGContextBeginPath
- CGPathMoveToPoint,它替换CGContextMoveToPoint
- CGPathAddLineToPoint,它替换CGContextAddLineToPoint
- CGPathAddCurveToPoint,它替换CGContextAddCurveToPoint
- CGPathAddEllipseInRect,它替换CGContextAddEllipseInRect
- CGPathAddArc,它替换CGContextAddArc
- CGPathAddRect,它将替换CGContextAddRect
- CGPathCloseSubpath,它替换CGContextClosePath
有关路径函数的完整列表,请参阅Quartz 2D Reference Collection。
当您想将路径附加到图形上下文时,可以调用函数CGContextAddPath。 路径停留在图形上下文中,直到Quartz绘制它。 您可以通过调用CGContextAddPath再次添加路径。
注:您可以通过调用函数CGContextReplacePathWithStrokedPath,将图形上下文中的路径替换为路径的描边版本。
绘画路径
您可以通过描边或填充或两者来绘制当前路径。描边画一条跨越路径的线。 填充绘制路径中包含的区域。 Quartz有一些函数可以让你描绘一个路径,填充一个路径,或者描边和填充一个路径。 描边线(宽度,颜色等),填充颜色和Quartz用于计算填充区域的方法的特性都是图形状态的一部分(请参见图形状态)。
Parameters That Affect Stroking
您可以通过修改表3-1中列出的参数来影响路径的描边。 这些参数是图形状态的一部分,这意味着为参数设置的值将影响所有后续的描边,直到将参数设置为另一个值。
影响Quartz如何描述当前路径的参数线宽是线的总宽度,以用户空间的单位表示。 线跨过路径,在任一侧具有一半的总宽度。
线连接指定Quartz如何绘制连接的线段之间的连接。 Quartz支持表3-2中描述的线连接样式。 默认样式为miter join。
线连接样式线条帽指定CGContextStrokePath用于绘制线的端点的方法。 Quartz支持表3-3中描述的线帽样式。 默认样式为对接帽。
线帽样式闭合子路径将开始点视为连接的线段之间的连接点; 使用所选的线连接方法渲染起点。 相反,如果通过添加连接到起点的线段来关闭路径,则使用所选的line-cap方法绘制路径的两端。
线图案允许您沿着描边路径绘制分段线。 通过将虚线数组和虚线阶段指定为CGContextSetLineDash的参数,可以控制沿线的虚线段的大小和位置:
void CGContextSetLineDash (
CGContextRef ctx,
CGFloat phase,
const CGFloat lengths[],
size_t count
);
length参数的元素指定了破折号的宽度,在线的着色和未涂漆段之间交替。 phase参数指定破折号模式的起点。 图3-11显示了一些行破折号模式。
3-11 线破折号模式的示例笔画颜色空间确定了Quartz对笔触颜色值的解释方式。 您还可以指定封装颜色和颜色空间的Quartz颜色(CGColorRef数据类型)。 有关设置颜色空间和颜色的详细信息,请参阅颜色和颜色空间。
Functions for Stroking a Path
Quartz提供了表3-4中显示的用于描画当前路径的函数。 一些是用于描边矩形或椭圆的便利函数。
描边路径的函数函数CGContextStrokeLineSegments等价于以下代码:
CGContextBeginPath (context);
for (k = 0; k < count; k += 2) {
CGContextMoveToPoint(context, s[k].x, s[k].y);
CGContextAddLineToPoint(context, s[k+1].x, s[k+1].y);
}
CGContextStrokePath(context);
当您调用CGContextStrokeLineSegments时,您将线段指定为一个点数组,组织为对。 每对包括线段的起点,后面是线段的终点。 例如,阵列中的第一点指定第一行的开始位置,第二点指定第一行的结束位置,第三点指定第二行的开始位置,等等。
Filling a Path
当你填充当前路径时,Quartz的行为就像路径中包含的每个子路径都被关闭一样。然后使用这些闭合的子路径并计算要填充的像素。有两种方法可以计算填充面积。简单路径(如椭圆形和矩形)具有明确定义的区域。但是,如果路径由重叠段组成,或者路径包含多个子路径,例如图3-12中所示的同心圆,则有两个规则可用于确定填充区域。
默认填充规则称为非零匝数规则。要确定是否应该绘制特定点,请从该点开始,绘制超出图形边界的线。从计数0开始,每当路径段从左到右穿过该线时将计数加1,并且每当路径段从右到左穿过该线时减1。如果结果为0,则该点不被绘制。否则,点被绘。绘制路径段的方向会影响结果。图3-12显示了使用非零匝数规则填充的两组内圆和外圆。当在相同方向绘制每个圆时,填充两个圆。当在相反方向绘制圆时,内圆不被填充。
您可以选择使用偶数规则。要确定是否应该绘制特定点,请从该点开始,绘制超出图形边界的线。计算线路交叉的路径段数。如果结果是奇数,点被绘。如果结果是偶数,点不绘。绘制路径段的方向不会影响结果。如图3-12所示,每个圆圈绘制的方向无关紧要,填充将始终如图所示。
3-12 使用不同的填充规则填充的同心圆Quartz提供了表3-5中显示的用于填充当前路径的函数。 一些是用于描边矩形或椭圆的便利函数。
填充路径的函数Setting Blend Modes
混合模式指定Quartz如何在背景上应用涂料。 Quartz默认使用正常混合模式,它使用以下公式将前景绘制与背景绘制相结合:
result = (alpha * foreground) + (1 - alpha) * background
颜色和颜色空间提供了颜色的alpha分量的详细讨论,它指定了颜色的不透明度。对于本节中的示例,您可以假设颜色完全不透明(alpha值= 1.0)。对于不透明颜色,当使用正常混合模式绘制时,在背景上绘制的任何内容都会遮蔽背景。
您可以通过调用函数CGContextSetBlendMode设置混合模式以实现各种效果,传递适当的混合模式常量。请记住,混合模式是图形状态的一部分。如果在更改混合模式之前使用函数CGContextSaveGState,则调用函数CGContextRestoreGState会将混合模式重置为正常。
本节的其余部分显示在图3-14所示的矩形上绘制如图3-13所示的矩形的结果。在每种情况下(图3-15到图3-30),使用正常混合模式绘制背景矩形。然后通过用适当的常数调用函数CGContextSetBlendMode来改变混合模式。最后,绘制前景矩形。
3-13 在前景绘的长方形 3-14 在背景中绘的长方形注:您也可以使用混合模式来合成两个图像,或者在已经绘制到图形上下文的任何内容上合成图像。 将混合模式与图像配合使用可提供有关如何使用混合模式合成图像的信息,并显示将混合模式应用于两个图像的结果。
Normal Blend Mode
因为正常混合模式是默认混合模式,所以使用常量kCGBlendModeNormal调用函数CGContextSetBlendMode仅在使用其他混合模式常量之后将混合模式重置为默认模式。 图3-15显示了使用正常混合模式绘制图3-13和图3-14的结果。
3-15 使用正常混合模式绘制的矩形Multiply Blend Mode
乘法混合模式指定将前景图像样本与背景图像样本相乘。 所得到的颜色至少与两种贡献的样品颜色中的任一种一样暗。 图3-16显示了使用乘法混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeMultiply调用函数CGContextSetBlendMode。
3-16 使用乘法混合模式绘制的矩形Screen Blend Mode
屏幕混合模式指定将前景图像样本的倒数乘以背景图像样本的倒数。 所得到的颜色至少与两种贡献的样品颜色中的任一种一样轻。 图3-17显示了使用屏幕混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeScreen调用函数CGContextSetBlendMode。
3-17 使用屏幕混合模式绘制的矩形Overlay Blend Mode
覆盖混合模式指定根据背景颜色,将前景图像样本与背景图像样本相乘或筛选。 背景颜色与前景颜色混合以反映背景的亮度或暗度。 图3-18显示了使用覆盖混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeOverlay调用函数CGContextSetBlendMode。
3-18 使用重叠混合模式绘制的矩形Darken Blend Mode
指定通过选择较暗的样本(来自前景图像或背景)创建合成图像样本。 背景图像样本被更暗的任何前景图像样本替换。 否则,背景图像样本保持不变。 图3-19显示了使用暗混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeDarken调用函数CGContextSetBlendMode。
3-19 使用暗混合模式绘制的矩形Lighten Blend Mode
指定通过选择较亮的样本(从前景或背景)创建复合图像样本。 结果是背景图像样本被更轻的任何前景图像样本替换。 否则,背景图像样本保持不变。 图3-20显示了使用lighten混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeLighten调用函数CGContextSetBlendMode。
3-20 使用淡化混合模式绘制的矩形Color Dodge Blend Mode
指定使背景图像样本变亮以反映前景图像样本。 指定黑色的前景图像样本值不会产生变化。 图3-21显示了使用颜色闪避混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeColorDodge调用函数CGContextSetBlendMode。
3-21 使用颜色闪避混合模式绘制的矩形Color Burn Blend Mode
指定使背景图像样本变暗以反映前景图像样本。 指定白色的前景图像样本值不会产生变化。 图3-22显示了使用彩色混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeColorBurn调用函数CGContextSetBlendMode。
3-22 使用颜色混合模式绘制的矩形Soft Light Blend Mode
指定根据前景图像样本颜色使颜色变深或变亮。 如果前景图像样本颜色比50%灰色更亮,则背景被减轻,类似于躲避。 如果前景图像样本颜色比50%灰色深,则背景变暗,类似于燃烧。 如果前景图像样本颜色等于50%灰色,则背景不会改变。 等于纯黑色或纯白色的图像样本会产生较暗或较亮的区域,但不会产生纯黑色或白色。 整体效果类似于通过在前景图像上发射漫射聚光灯所实现的效果。 使用此向场景添加高光。 图3-23显示了使用柔光混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeSoftLight调用函数CGContextSetBlendMode。
3-23 使用柔光混合模式绘制的矩形Hard Light Blend Mode
指定乘法或屏幕颜色,具体取决于前景图像样本颜色。 如果前景图像样本颜色比50%灰色更亮,则背景被减轻,类似于筛选。 如果前景图像样本颜色比50%灰色深,则背景变暗,类似于乘法。 如果前景图像样本颜色等于50%灰色,则前景图像不改变。 等于纯黑色或纯白色的图像样本导致纯黑色或白色。 整体效果类似于通过在前景图像上照射严酷的聚光灯所实现的效果。 使用此向场景添加高光。 图3-24显示了使用硬光混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeHardLight调用函数CGContextSetBlendMode。
3-24 使用硬光混合模式绘制的矩形Difference Blend Mode
指定从背景图像样本颜色中减去前景图像样本颜色,或反之,根据哪个样本具有较大的亮度值。 黑色的前景图像样本值不变; 白色反转背景颜色值。 图3-25显示了使用差分混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeDifference调用函数CGContextSetBlendMode。
3-25 使用差异混合模式绘制的矩形Exclusion Blend Mode
指定与kCGBlendModeDifference产生的效果类似的效果,但对比度较低。 黑色的前景图像样本值不会产生变化; 白色反转背景颜色值。 图3-26显示了使用排除混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeExclusion调用函数CGContextSetBlendMode。
3-26 使用排除混合模式绘制的矩形Hue Blend Mode
指定使用背景的亮度和饱和度值与前景图像的色调。 图3-27显示了使用色调混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeHue调用函数CGContextSetBlendMode。
3-27 使用色相混合模式绘制的矩形Saturation Blend Mode
指定使用背景的亮度和色调值与前景图像的饱和度。 没有饱和度的背景区域(即,纯灰色区域)不产生变化。 图3-28显示了使用饱和混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeSaturation调用函数CGContextSetBlendMode。
3-28 使用饱和度混合模式绘制的矩形Color Blend Mode
指定使用背景的亮度值与前景图像的色相和饱和度值。 此模式保留图像中的灰度级。 您可以使用此模式对单色图像进行着色或对彩色图像进行着色。 图3-29显示了使用颜色混合模式绘制图3-13的图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeColor调用函数CGContextSetBlendMode。
3-29 使用颜色混合模式绘制的矩形Luminosity Blend Mode
指定使用背景的色相和饱和度与前景图像的亮度。 此模式创建的效果与kCGBlendModeColor创建的效果相反。 图3-30显示了使用光度混合模式绘制图3-13和图3-14的结果。 要使用此混合模式,请使用常量kCGBlendModeLuminosity调用函数CGContextSetBlendMode。
3-30 使用亮度混合模式绘制的矩形剪切路径
当前剪裁区域是从用作掩码的路径创建的,允许您阻止不想绘制的页面部分。例如,如果您有一个非常大的位图图像,并且只想显示它的一小部分,您可以设置剪切区域,只显示您要显示的部分。
当你绘画时,Quartz只在剪贴区域内绘画。在剪切区域的闭合子路径内发生的绘制是可见的;在剪切区域的闭合子路径外发生的绘制不是。
当最初创建图形上下文时,剪切区域包括上下文的所有可绘制区域(例如,PDF上下文的媒体框)。通过设置当前路径然后使用裁剪函数代替绘图函数来更改裁剪区域。剪切函数使当前路径的填充区域与现有剪切区域相交。因此,您可以交叉裁剪区域,缩小图片的可见区域,但不能增加裁剪区域的面积。
剪切区域是图形状态的一部分。要将剪裁区域恢复到先前的状态,您可以在剪裁之前保存图形状态,并在完成剪切绘制后恢复图形状态。
清单3-1显示了一个以圆形形状设置剪切区域的代码片段。此代码导致绘图被剪切,类似于图3-3所示。 (有关另一个示例,请参阅“渐变”一章中的“剪辑上下文”。)
// 设置圆形剪裁区域
CGContextBeginPath (context);
CGContextAddArc (context, w/2, h/2, ((w>h) ? h : w)/2, 0, 2*PI, 0);
CGContextClosePath (context);
CGContextClip (context);
剪切图形上下文的函数
网友评论