Quartz-2D绘图之路径(Paths)详解

在上篇文章中,我们简单的理解了绘图上下文,今天我们来认识一下Quartz-2D中另一个重要的概念,路径(Paths)。

一、理解路径

路径定义了一个或多个形状,或是子路径。一个子路径可由直线,曲线,或者同时由两者构成。它可以是开放的,也可以是闭合的。一个子路径可以是简单的形状,如线、圆、矩形、星形;也可以是复杂的形状,如山脉的轮廓或者是涂鸦。图3-1显示了一些我们可以创建的路径。左上角的直线可以是虚线;直线也可以是实线。上边中间的路径是由多条曲线组成的开放路径。右上角的同心圆填充了颜色,但没有描边。左下角的加利福尼亚州是闭合路径,由许多曲线和直线构成,且对路径进行填充和描边。两个星形阐明了填充路径的两种方式,我们将详细描述。

二、创建及绘制路径

路径创建及路径绘制是两个独立的工作。首先我们创建路径。当我们需要渲染路径时,我们需要使用Quartz来绘制它。正如图3-1中所示,我们可以选择对路径进行描边,填充路径,或同时进行这两种操作。我们同样可以将其它对象绘制到路径所表示的范围内,即对对象进行裁减。
图3-2绘制了一个路径,该路径包含两个子路径。左边的子路径是一个矩形,右边的子路径是由直线和曲线组成的抽象形状。两个子路径都进行了填充及描边。

图3-3显示了多条独立绘制的路径。每个路径饮食随机生成的曲线,一些进行填充,另一些进行了描边。这些路径都包含在一个圆形裁减区域内。

三、构建块(Building Block)
子路径是由直线、弧和曲线构成的。Quartz同样也提供了简便的函数用于添加矩形或椭圆等形状。点也是路径最基本的构建块,因为点定义了形状的起始点与终止点。


点由x, y坐标值定义,用于在用户空间指定 一个位置。我们可以调用函数CGContextMoveToPoint来为新的子路径指定起始点。Quartz跟踪当前点,用于记录路径构建过程中最新的位置。例如,如果调用函数CGContextMoveToPoint并设置位置为(10, 10),即将当前点移动到位置(10, 10)。如果在水平位置绘制50个单位长度的直线,则直线的终点为(60, 10),该点变成当前点。直线、弧和曲线总是从当前点开始绘制。
通常我们通过传递(x, y)值给Quartz函数来指定一个点。一些函数需要我们传递一个CGPoint数据结构,该结构包含两个浮点值。

直线
直线由两个端点定义。起始点通常是当前点,所以创建直线时,我们只需要指定终止点。我们使用函数CGContextAddLineToPoint来添加一条直线到子路径中。
我们可以调用CGContextAddLines函数添加一系列相关的直线到子路径中。我们传递一个点数组给这个函数。第一个点必须是第一条直线的起始点;剩下的点是端点。Quartz从第一个点开始绘制一个新子路径,然后每两个相邻点连接成一条线段。


弧是圆弧段。Quartz提供了两个函数来创建弧。函数CGContextAddArc从圆中来创建一个曲线段。我们指定一个圆心,半径和放射角(以弧度为单位)。放射角为2 PI时,创建的是一个圆。图3-4显示了多个独立的路径。每个路径饮食一个自动生成的圆;一些是填充的,另一些是描边的。

函数CGContextAddArcToPoint用于为矩形创建内切弧的场景。Quartz使用我们提供的端点创建两条正切线。同样我们需要提供圆的半径。弧心是两条半径的交叉点,每条半径都与相应的正切线垂直。弧的两个端点是正切线的正切点,如图3-5所示。红色的部分是实际绘制的部分。

下面分别是画直线和圆弧的代码

直线:

 1   //    1. 获取一个与视图相关联的上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3
 4     //    2. 构建路径
 5     //    2.1 设置上下文路径起点
 6     CGContextMoveToPoint(context, 85, 85);
 7     //    2.2 增加路径内容……
 8     CGContextAddLineToPoint(context, 150, 150);
 9     CGContextAddLineToPoint(context, 250, 50);
10     //    3. 保存上下文状态
11     //    4. 设置上下文状态
12     //    4.1 设置边线颜色
13     CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);
14     //    4.2 设置线宽
15     CGContextSetLineWidth(context, 10);
16     //    4.3 设置线段连接样式
17     CGContextSetLineJoin(context, kCGLineJoinRound);
18     //    4.4 设置线段收尾样式
19     CGContextSetLineCap(context, kCGLineCapRound);
20     //    4.5 设置虚线样式
21     //    4. 绘制路径
22     CGContextDrawPath(context, kCGPathStroke);

圆弧:

    //    1. 获取一个与视图相关联的上下文
    CGContextRef context = UIGraphicsGetCurrentContext();

    [[UIColor redColor]set];
    // 2. 添加弧线
    // 2.1 上下文
    // 2.2 中心点坐标
    // 2.3 半径
    // 2.4 开始角度,结束角度,角度的单位是“弧度”
    CGContextAddArc(context, 160, 230, 100, M_PI, -M_PI_2, 0);

    // 绘制路径
    CGContextStrokePath(context);

曲线
二次与三次Bezier曲线是代数曲线,可以指定任意的曲线形状。曲线上的点通过一个应用于起始、终点及一个或多个控制点的多项式计算得出。这种方式定义的形状是向量图的基础。这个公式比将位数组更容易存储,并且曲线可以在任何分辨下重新创建。
图3-6显示了一些路径的曲线。每条路径包含一条随机生成的曲线;一些是填充的,另一些是描边的。

我们使用函数CGContextAddCurveToPoint将Bezier曲线连接到当前点,并传递控制点和端点作为参数,如图3-7所示。两个控制点的位置决定了曲线的形状。如果两个控制点都在两个端点上面,则曲线向上凸起。如果两个控制点都在两个端点下面,则曲线向下凹。如果第二个控制点比第一个控制点离得当前点近,则曲线自交叉,创建了一个回路。

我们也可以调用函数CGContextAddQuadCurveToPoint来创建Bezier,并传递端点及一个控制点,如图3-8所示。控制点决定了曲线弯曲的方向。由于只使用一个控制点,所以无法创建出如三次Bezier曲线一样多的曲线。例如我们无法创建出交叉的曲线

1     /*画贝塞尔曲线*/
2     //二次曲线
3     CGContextMoveToPoint(context, 120, 300);//设置Path的起点
4     CGContextAddQuadCurveToPoint(context,190, 310, 120, 390);//设置贝塞尔曲线的控制点坐标和终点坐标
5     CGContextStrokePath(context);
6     //三次曲线函数
7     CGContextMoveToPoint(context, 200, 300);//设置Path的起点
8     CGContextAddCurveToPoint(context,250, 280, 250, 400, 280, 300);//设置贝塞尔曲线的控制点坐标和控制点坐标终点坐标
9     CGContextStrokePath(context);  

闭合路径
我们可以调用函数CGContextClosePath来闭合曲线。该函数用一条直接来连接当前点与起始点,以使路径闭合。起始与终点重合的直线、弧和曲线并不自动闭合路径,我们必须调用CGContextClosePath来闭合路径。
Quartz的一些函数将路径的子路径看成是闭合的。这些函数显示地添加一条直线来闭合 子路径,如同调用了CGContextClosePath函数。
在闭合一条子路径后,如果程序再添加直线、弧或曲线到路径,Quartz将在闭合的子路径的起点开始创建一个子路径。

椭圆
椭圆是一种特殊的圆。椭圆是通过定义两个焦点,在平面内所有与这两个焦点的距离之和相等的点所构成的图形。图3-9显示了一些独立的路径。每个路径都包含一个随机生成的椭圆;一些进行了填充,另一边进行了描边。

我们可以调用CGContextAddEllipseInRect函数来添加一个椭圆到当前路径。我们提供一个矩形来定义一个椭圆。Quartz利用一系列的Bezier曲线来模拟椭圆。椭圆的中心就是矩形的中心。如果矩形的宽与高相等,则椭圆变成了圆,且圆的半径为矩形宽度的一半。如果矩形的宽与高不相等,则定义了椭圆的长轴与短轴。
添加到路径中的椭圆开始于一个move-to操作,结束于一个close-subpath操作,所有的移动方向都是顺时针。

矩形
我们可以调用CGContextAddRect来添加一个矩形到当前路径中,并提供一个CGRect结构体(包含矩形的原点及大小)作为参数。
添加到路径的矩形开始于一个move-to操作,结束于一个close-subpath操作,所有的移动方向都是顺时针。
我们也可能调用CGContextAddRects函数来添加一系列的矩形到当前路径,并传递一个CGRect结构体的数组。图3-10显示了一些独立的路径。每个路径包含一个随机生成的矩形;一些进行了填充,另一边进行了描边。

四、创建路径

当我们需要在一个图形上下文中构建一个路径时,我们需要调用CGContextBeginPath来标记Quartz。然后,我们调用函数CGContextMovePoint来设置每一个图形或子路径的起始点。在构建起始点后,我们可以添加直线、弧、曲线。记住如下规则:

  • 在开始绘制路径前,调用函数CGContextBeginPath;
  • 直线、弧、曲线开始于当前点。空路径没有当前点;我们必须调用CGContextMoveToPoint来设置第一个子路径的起始点,或者调用一个便利函数来隐式地完成该任务。
  • 如果要闭合当前子路径,调用函数CGContextClosePath。随后路径将开始一个新的子路径,即使我们不显示设置一个新的起始点。
  • 当绘制弧时,Quartz将在当前点与弧的起始点间绘制一条直线。
  • 添加椭圆和矩形的Quartz程序将在路径中添加新的闭合子路径。
  • 我们必须调用绘制函数来填充或者描边一条路径,因为创建路径时并不会绘制路径。

在绘制路径后,将清空图形上下文。我们也许想保留路径,特别是在绘制复杂场景时,我们需要反复使用。基于此,Quartz提供了两个数据类型来创建可复用路径—CGPathRef和CGMutablePathRef。我们可以调用函数CGPathCreateMutable来创建可变的CGPath对象,并可向该对象添加直线、弧、曲线和矩形。Quartz提供了一个类似于操作图形上下文的CGPath的函数集合。这些路径函数操作CGPath对象,而不是图形上下文。这些函数包括:

  • CGPathCreateMutable,取代CGContextBeginPath
  • CGPathMoveToPoint,取代CGContextMoveToPoint
  • CGPathAddLineToPoint,取代CGContexAddLineToPoint
  • CGPathAddCurveToPoint,取代CGContexAddCurveToPoint
  • CGPathAddEllipseInRect,取代CGContexAddEllipseInRect
  • CGPathAddArc,取代CGContexAddArc
  • CGPathAddRect,取代CGContexAddRect
  • CGPathCloseSubpath,取代CGContexClosePath

如果想要添加一个路径到图形上下文,可以调用CGContextAddPath。路径将保留在图形上下文中,直到Quartz绘制它。我们可以调用CGContextAddPath再次添加路径。

五、绘制路径

我们可以绘制填充或描边的路径。描边(Stroke)是绘制路径的边框。填充是绘制路径包含的区域。Quartz提供了函数来填充或描边路径。描边线的属性(宽度、颜色等),填充色及Quartz用于计算填充区域的方法都是图形状态的一部分。

影响描边的属性
我们可以使用表3-1中的属性来决定如何对路径进行描边操作。这边属性是图形上下文的一部分,这意味着我们设置的值将会影响到后续的描边操作,直到我们个性这些值。

linewidth是线的总宽度,单位是用户空间单元。
linejoin属性指定如何绘制线段间的联接点。Quartz支持表3-2中描述的联接样式。

Table 3-2 直线联接样式

linecap指定如何绘制直线的端点。Quartz支持表3-3所示的线帽类型。默认的是butt cap。

闭合路径将起始点看作是一个联接点;起始点同样也使用选定的直线连接方法进行渲染。如果通过添加一条连接到起始点的直线来闭合路径,则路径的两个端点都使用选定的线帽类型来绘制。
Linedash pattern(虚线模式)允许我们沿着描边绘制虚线。我们通过在CGContextSetLineDash结构体中指定虚线数组和虚线相位来控制虚线的大小及位置。

CGContextSetLineDash结构如下:

1 void CGContextSetLineDash (
2 CGContextRef ctx,
3 float phase,
4 const float lengths[],
5 size_t count,
6 );

其中lengths属性指定了虚线段的长度,该值是在绘制片断与未绘制片断之间交替。phase属性指定虚线模式的起始点。图3-11显示了虚线模式:

路径描边的函数
Quartz提供了表3-4中的函数来描边当前路径。其中一些是描边矩形及椭圆的便捷函数。
表3-4 描边路径函数

函数CGContextStrokeLineSegments等同于如下代码

1 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时,我们通过点数组来指定线段,并组织成点对的形式。每一对是由线段的起始点与终止点组成。例如,数组的第一个点指定了第一条直线的起始点,第二个点是第一条直线的终点,第三个点是第二条直线的起始点,依此类推。

六、填充路径
当我们填充当前路径时,Quartz将路径包含的每个子路径都看作是闭合的。然后,使用这些闭合路径并计算填充的像素。 Quartz有两种方式来计算填充区域。椭圆和矩形这样的路径其区域都很明显。但是如果路径是由几个重叠的部分组成或者路径包含多个子路径(如图3-12所示),我们则有两种规则来定义填充区域。
默认的规则是非零缠绕数规则(nonzero windingnumber rule)。为了确定一个点是否需要绘制,我们从该点开始绘制一条直线穿过绘图的边界。从0开始计数,每次路径片断从左到右穿过直线是,计数加1;而从右到左穿过直线时,计数减1。如果结果为0,则不绘制该点,否则绘制。路径片断绘制的方向会影响到结果。图3-13显示了使用非缠绕数规则对内圆和外圆进行填充的结果。当两个圆绘制方向相同时,两个圆都被填充。如果方向相反,则内圆不填充。
我们也可以使用偶数-奇数规则。为了确定一个点是否被绘制,我们从该点开始绘制一条直线穿过绘图的边界。计算穿过该直线的路径片断的数目。如果是奇数,则绘制该点,如果是偶数,则不绘制该点。路径片断绘制的方向不影响结果。如图3-12所示,无论两个圆的绘制方向是什么,填充结果都是一样的。

Quartz提供了表3-5中的函数来填充当前路径。其中一些是填充矩形及椭圆的便捷函数。
表3-5 填充路径的函数

好,说了这么多下面上代码

 1 - (void)drawLine1
 2 {
 3     //    1. 获取一个与视图相关联的上下文
 4     CGContextRef context = UIGraphicsGetCurrentContext();
 5
 6     //    2. 创建一个可变路径
 7     //    2.1 创建路径
 8     CGMutablePathRef path = CGPathCreateMutable();
 9     //    2.2 设置路径起点
10     CGPathMoveToPoint(path, NULL, 50, 50);
11     //    2.3 增加路径内容……
12     CGPathAddLineToPoint(path, NULL, 150, 150);
13     CGPathAddLineToPoint(path, NULL, 50, 150);
14     //    CGPathAddLineToPoint(path, NULL, 50, 50);
15     // 闭合路径,关闭路径,闭合路径是收尾相连的
16     CGPathCloseSubpath(path);
17
18     //    3. 将路径添加到上下文;
19     CGContextAddPath(context, path);
20
21     //    4. 设置上下文状态
22     //    4.1 设置边线颜色
23     // 颜色数值 = RGB数值 / 255
24     CGContextSetRGBStrokeColor(context, 255.0 / 255.0, 0.0, 0.0 , 1.0);
25     //    4.2 设置填充颜色
26     CGContextSetRGBFillColor(context, 0.0, 0.0, 128.0 / 255.0, 1.0);
27     //    4.3 设置线宽
28     CGContextSetLineWidth(context, 5);
29     //    4.4 设置线段连接样式
30     CGContextSetLineJoin(context, kCGLineJoinBevel);
31     //    4.5 设置线段首尾样式
32     CGContextSetLineCap(context, kCGLineCapRound);
33     //    4.6 设置虚线样式
34     // lengths 设置公有几条虚线,每条虚线的长度
35     // count 指的是lengths数组的长度
36     CGFloat lengthes[2] = {10.0, 10.0};
37     CGContextSetLineDash(context, 0, lengthes, 2);
38     //    5. 绘制路径
39     /**
40      kCGPathStroke  绘制边线
41      kCGPathFill    填充
42      */
43     CGContextDrawPath(context, kCGPathFillStroke);
44     //    6. 释放路径,不同对象对应着不同的release方法
45     CGPathRelease(path);
46 }

七、裁剪路径

当前裁剪区域是从路径中创建,作为一个遮罩,从而允许遮住我们不想绘制的部分。例如,我们有一个很大的图片,但只需要显示其中一小部分,则可以设置裁减区域来显示我们想显示的部分。

当我们绘制的时候,Quartz只渲染裁剪区域里面的东西。裁剪区域内的闭合路径是可见的;而在区域外的部分是不可见的。

当图形上下文初始创建时,裁减区域包含上下文所有的可绘制区域(例如,PDF上下文的media box)。我们可以通过设置当前路径来改变裁剪区域,然后使用裁减函数来取代绘制函数。裁剪函数与当前已有的裁剪区域求交集以获得路径的填充区域。因此,我们可以求交取得裁减区域,缩小图片的可视区域,但是不能扩展裁减区域。

裁减区域是图形状态的一部分。为了恢复先前的裁减区域,我们可以在裁减前保存图形状态,并在裁减绘制后恢复图形状态。

1 Listing 3-1 Setting up a circular clip area
2 CGContextBeginPath(context);
3 CGContextAddArc(context, w/2, h/2, ((w>h) ? h : w)/2, 0, 2*PI, 0);
4 CGContextClosePath(context);
5 CGContextClip(context);

表3-6 裁减图形上下文的函数

时间: 2024-10-09 12:53:19

Quartz-2D绘图之路径(Paths)详解的相关文章

Quartz-2D绘图之图形上下文详解

上一篇文章大概描述了下Quartz里面大体所包含的东西,但是对具体的细节实现以及如何调用相应API却没有讲.这篇文章就先讲讲图形上下文(Graphics Context)的具体操作. 所谓Graphics Context,其实就是表示了一个绘制目标,也就是你打算绘制的地方,它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息.Graphics Context定义了基本的绘制属性,如颜色.裁减区域.线条宽度和样式信息.字体信息.混合模式等.然而,我们怎样才能获得或者创建一个Graphics Co

1 、Quartz 2D绘图基础

Quartz 2D绘图 Quartz 2D绘图的核心API是CGContextRef,该API专门用于绘制各种图形. 关注微信公众号:ioscoding ,分享优质iOS编程技术.by:shuju 1.1  Quartz 2D绘图基础:CGContextRef 使用Quartz 2D绘图的关键步骤有两步:获取CGContextRef:调用CGContextRef的方法进行绘图. 不同场景下获取CGContextRef的方式各不相同,下面介绍iOS开发中最常见的场景下如何获取CGContextRe

Core Graphics框架 利用Quartz 2D绘图

首先,什么是Core Graphics和Quartz 2D? Core Graphics:是基于Quartz 2D绘图引擎的一个C语言的API绘图框架.它也是iOS开发中最基本的框架(Framework)之一.两点原因,第一是该框架是每一个iOS应用最初被建立时,就被系统默认添加的三个框架(Foundation.UIKit.Core Graphics)之一:另一点是,我们平时常见的各种UIKit框架提供的UI控件,实际上都是由Core Graphics进行绘制的. Quartz 2D:是一个绘图

Quartz 2D 绘图,图像变换

IOS的quartz 2d 绘图,绘图有时候需要进行图形变换. 切记:Quartz 2D的变换都是以CG 开头的,不要和Core Animation的搞混了,Core animatin的是CA开头的. IOS提供了一组可以进行图像变换的API接口,一般的图像变换分为,平移.缩放.旋转. 平移 void CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty); 缩放 void CGContextScaleCTM(CGContext

ios (Quartz 2D绘图)各种绘图方式及相机的使用

一: 具体使用的细节,本人也是参考http://blog.163.com/wkyuyang_001/blog/static/10802122820133190545227/ 下面介绍具体使用Quartz 2D绘图实现画图板功能 .m文件中,dog的实现如连接中所示一样的 <pre name="code" class="objc">#import "drawTestView.h" #import "Dog.h" @i

iOS开发——图形编程OC篇&amp;(三)Quartz 2D绘图

绘图 一.简单说明 图形上下文(Graphics Context):是一个CGContextRef类型的数据 图形上下文的作用:保存绘图信息.绘图状态 决定绘制的输出目标(绘制到什么地方去?)(输出目标可以是PDF文件.Bitmap或者显示器的窗口上) 相同的一套绘图序列,指定不同的Graphics Context,就可将相同的图像绘制到不同的目标上. Quartz2D提供了以下几种类型的Graphics Context: Bitmap Graphics Context PDF Graphics

Quartz.net开源作业调度框架使用详解(转)

前言 quartz.net作业调度框架是伟大组织OpenSymphony开发的quartz scheduler项目的.net延伸移植版本.支持 cron-like表达式,集群,数据库.功能性能强大更不用说. 下载项目文档官网:http://www.quartz-scheduler.net/ 项目中需引用:Common.Logging.dll , Common.Logging.Core.dll , Quartz.dll 下面给大家分解下我最近做的关于计划调度的一个小项目,来辅助理解quartz.n

Quartz.net开源作业调度框架使用详解

前言 quartz.net作业调度框架是伟大组织OpenSymphony开发的quartz scheduler项目的.net延伸移植版本.支持 cron-like表达式,集群,数据库.功能性能强大更不用说. 下载项目文档官网:http://www.quartz-scheduler.net/ 项目中需引用:Common.Logging.dll , Common.Logging.Core.dll , Quartz.dll 下面给大家分解下我最近做的关于计划调度的一个小项目,来辅助理解quartz.n

Java路径操作详解

1.基本概念的理解 绝对路径:绝对路径就是你的主页上的文件或目录在硬盘上真正的路径,(URL和物理路径)例如: C:\xyz\test.txt 代表了test.txt文件的绝对路径.http://www.sun.com/index.htm也代表了一个 URL绝对路径. 相对路径:相对与某个基准目录的路径.包含Web的相对路径(HTML中的相对目录),例如:在 Servlet中,"/"代表Web应用的根目录.和物理路径的相对表示.例如:". /" 代表当前目录, &q