Quartz 2D 概念、图形上下文、路径

Quartz 2D是二维图形绘制引擎,可以实现N多图形图像的操作功能,如基本路径的绘制、透明度、描影、绘制阴影、透明层、颜色管理、反锯齿、PDF文档生成和PDF元数据访问。

画布Page

在图像操作过程中使用了绘画者模型,绘制过程是将绘制层铺到画布上,这个画布称为Page。类似于Photoshop中,将每个图层叠加放在画布上,形成最后的图像。

图形上下文Graphics Context

图形上下文(Graphics Context)是一个数据类型CGContextRef,它存放了Quartz 2D绘制的图形输出信息,可以看做是图形到设备输出的介质工具。Quartz 2D绘制的图形可以放到多种设备上,比如:PDF文件、显示器窗口、bitmap(位图)、view的Layer层等等,这就需要不同的Graphics Context来完成输出到不同设备上的工作。这好比人要上高速需要汽车,去海上需要船,去天空需要飞机,这里人相当于图形,高速路、海、天空相当于设备,而汽车、船、飞机就是Graphics Context。

Graphics Context的几种类型:

1. Bitmap Graphics Context

2. PDF Graphics Context

3. Window Graphics Context

4. Layer Context

5. Post Graphics Context

Quartz 2D的数据类型

Quartz 2D API属于Code Graphics框架,所以Quartz 2D的数据类型是以CG开头的。有以下数据类型:

1. CGPathRef:用于向量图,可创建路径,并进行填充或描画(stroke)

2. CGImageRef:用于表示bitmap图像和基于采样数据的bitmap图像遮罩。

3. CGLayerRef:用于表示可用于重复绘制(如背景)和幕后(offscreen)绘制的绘画层

4. CGPatternRef:用于重绘图

5. CGShadingRef、CGGradientRef:用于绘制渐变

6. CGFunctionRef:用于定义回调函数,该函数包含一个随机的浮点值参数。当为阴影创建渐变时使用该类型

7. CGColorRef, CGColorSpaceRef:用于告诉Quartz如何解释颜色

8. CGImageSourceRef,CGImageDestinationRef:用于在Quartz中移入移出数据

9. CGFontRef:用于绘制文本

10. CGPDFDictionaryRef, CGPDFObjectRef, CGPDFPageRef, CGPDFStream, CGPDFStringRef, and CGPDFArrayRef:用于访问PDF的元数据

11. CGPDFScannerRef, CGPDFContentStreamRef:用于解析PDF元数据

12. CGPSConverterRef:用于将PostScript转化成PDF。在iOS中不能使用。

图形状态

Quartz通过修改图形状态来修改绘制结果,图形状态直接决定了图形的最终渲染结果。图形状态包含用于绘制程序的参数,绘制参数改变了,图形自然就变了。比如修改了填充色值,图形颜色就变了。

iOS中使用Graphics Context绘制图形

在iOS中要想用Quartz 2D在屏幕上绘图,需要自定义一个UIView,在UIView的- (void)drawRect:(CGRect)rect方法中实现绘图操作,这个方法会在UIView显示在屏幕上和需要被刷新的时候调用。创建上下文的方法如下:

- (void)drawRect:(CGRect)rect {

CGContextRef context = UIGraphicsGetCurrentContext();

}

在上下文中绘制图形:

- (void)drawRect:(CGRect)rect {

//获取图形上下文

CGContextRef context = UIGraphicsGetCurrentContext();

//设置红色透明度0.5的填充色

CGContextSetRGBFillColor(context, 1, 0, 0, 0.5);

//填充一个矩形frame为(10, 20, 100, 40)

CGContextFillRect(context, CGRectMake(10, 20, 100, 40));

//设置蓝色透明度0.5的填充色

CGContextSetRGBFillColor(context, 0, 0, 1, 0.5);

//填充一个矩形frame为(10, 20, 40,  100)

CGContextFillRect(context, CGRectMake(10, 20, 40, 100));

}


注意:设置填充色和填充矩形的方法顺序不能颠倒,不然填充色填充不到想填充的矩形中。得不到填充色填充的会是黑色。

路径

路径可以构建出多种图形,可以是点、直线、弧线、不规则线、规则或不规则形状,可以对闭合路径进行填充行程面。使用路径绘制出想要的图形,有两步:创建路径和绘制路径。创建出点、线等路径,使用函数CGContextDrawPath绘制路径。

点是依靠x、y值固定的位置,可作为路径的起始点位置。比如要画一个线段,必须要有一个起点和一个终点,两点确定一条线段。使用函数CGContextMoveToPoint来确定起始点,传入图形上下文和x,y坐标点。

//确定一个坐标为(10,10)的点

CGContextMoveToPoint(context, 10, 10);

直线

有起始点,再指定一个终点位置就能确定一条直线了,使用函数CGContextAddLineToPoint来指定终点位置。

- (void)drawRect:(CGRect)rect {

//获取图形上下文

CGContextRef context = UIGraphicsGetCurrentContext();

//设置起点

CGContextMoveToPoint(context, 20, 20);

//起点延长至终点位置

CGContextAddLineToPoint(context, 50, 50);

//绘制路径

CGContextDrawPath(context, kCGPathStroke);

}

可以使用CGContextAddLineToPoint函数指定多个位置点,线段会一个接一个画下去,形成折线图形。也可以使用CGContextAddLines函数一次性指定多个位置点,完成折线图形,此时无需指定起始点,第一个点默认为起始点(即使用CGContextMoveToPoint指定起始点也无效)。

//一个一个点继续加

CGContextMoveToPoint(context, 20, 20);

CGContextAddLineToPoint(context, 50, 50);

CGContextAddLineToPoint(context, 30, 80);

//一次性指定多个点

CGPoint pos[3] = {CGPointMake(80, 20), CGPointMake(20, 50), CGPointMake(100, 200)};

CGContextAddLines(context, pos, 3);

由于CGContextAddLineToPoint函数必须得有CGContextMoveToPoint函数固定起始点,CGContextAddLines函数默认第一个点为起始点,所以CGContextAddLines后面可以跟着CGContextAddLineToPoint继续加点画线,能连成一组折线,而CGContextMoveToPoint+CGContextAddLineToPoint画线后面不能跟CGContextAddLines,会画成两组无关联的折线。

圆弧

画圆弧提供了两个函数,一个是CGContextAddArc,依次指定图形上下文、圆心坐标、半径、起始弧度、终止弧度、画线顺时针(1)或逆时针(0)。

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextAddArc(context, 100, 200, 30, 0, M_PI, 0);

CGContextDrawPath(context, kCGPathStroke);

第二个函数是CGContextAddArcToPoint,三点+半径确定一个圆弧。CGContextMoveToPoint函数确定第一个点,CGContextAddArcToPoint添加第二个点、第三个点和还有半径。原理:以第二点为中心,分别向第一点、第三点延长两条射线,射线夹角小于180°的一侧,以指定半径画圆弧,圆弧与射线相切。

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextMoveToPoint(context, 100, 100);

CGContextAddArcToPoint(context, 100, 150, 150, 150, 50);

CGContextDrawPath(context, kCGPathStroke);

曲线

画Bezier曲线也提供了两个函数,函数CGContextAddCurveToPoint用于画三次Bezier曲线,由一个起点、一个终点和两个控制点构成一条三次Bezier曲线。当两个控制点在起点和终点连线的同侧时,曲线会只有一个拱向,在不同侧时有两个拱向。

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextMoveToPoint(context, 100, 100);

CGContextAddCurveToPoint(context, 150, 50, 200, 200, 300, 100);

CGContextDrawPath(context, kCGPathStroke);

画二次Bezier曲线,使用函数CGContextAddQuadCurveToPoint。以当前点为起始点,指定一个控制点一个终点,确定一条二次Bezier曲线。控制点决定了曲线拱的方向,该函数只能创建一个拱向的Bezier曲线,而且曲线不可能交叉。

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextMoveToPoint(context, 100, 100);

CGContextAddQuadCurveToPoint(context, 200, 200, 300, 100);

CGContextDrawPath(context, kCGPathStroke);

闭合路径

Quartz 2D提供函数CGContextClosePath来闭合路径。就是将终点和起点用直线连接起来,使整个路径闭合。对于直线、弧、曲线等不能自动闭合的路径,如果想完成路径闭合必须调用该函数。

调用了CGContextClosePath完成闭合路径后,再调用Add...添加路径的函数,添加直线、弧线、曲线等路径,会从闭合路径的起始点开始。如果没有完成闭合路径函数的调用,则从终点继续开始新添加的路径。  

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextMoveToPoint(context, 100, 100);

CGContextAddCurveToPoint(context, 150, 50, 150, 200, 300, 100);

CGContextClosePath(context);

CGContextAddLineToPoint(context, 200, 500);

CGContextDrawPath(context, kCGPathStroke);

矩形

使用函数CGContextAddRect来画矩形,函数中的rect中x,y是矩形左上角,宽高是矩形的宽高。

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextAddRect(context, CGRectMake(20, 20, 100, 40));

CGContextDrawPath(context, kCGPathStroke);

椭圆

使用函数CGContextAddEllipseInRect来画椭圆,是以矩形来确定椭圆。坐标参数意义和矩形一样,以确定的矩形内切画出的椭圆。矩形的圆心即是椭圆的圆心,矩形的宽高即是椭圆的长轴短轴,当矩形的宽高相等时,椭圆就是一个圆了。

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextAddEllipseInRect(context, CGRectMake(0, 0, 30, 50));

CGContextDrawPath(context, kCGPathStroke);

创建路径

*在开始绘制路径前,调用 CGContextBeginPath 或 UI。

*直线、弧、曲线开始于当前点。空路径没有当前点;我们必须调用CGContextMoveToPoint来设置第一个子路径的起始点,或者调用一个便利函数来隐式地完成该任务。

*如果要闭合当前子路径,调用函数 CGContextClosePath。随后路径将开始一个新的子路径,即使我们不显示设置一个新的起始点。

*当绘制弧时,Quartz 将在当前点与弧的起始点间绘制一条直线。

*添加椭圆和矩形的 Quartz 程序将在路径中添加新的闭合子路径。

*我们必须调用绘制函数来填充或者描边一条路径,因为创建路径时并不会绘制路径。

*Quartz 提供了两个数据类型来创建可复用路径 CGPathRef 和 CGMutablePathRef。

*Quartz 提供了一个类似于操作图形上下文的 CGPath 的函数集合。这些路径函数操作 CGPath 对象,而不是图形上下文。

1.    CGPathCreateMutable 取代 CGContextBeginPath

2.    CGPathMoveToPoint 取代 CGContextMoveToPoint

3.    CGPathAddLineToPoint 取代 CGContexAddLineToPoint

4.    CGPathAddCurveToPoint 取代 CGContexAddCurveToPoint

5.    CGPathAddEllipseInRect 取代 CGContexAddEllipseInRect

6.    CGPathAddArc 取代 CGContexAddArc

7.    CGPathAddRect 取代 CGContexAddRect

8.    CGPathCloseSubpath 取代 CGContexClosePath

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

绘制路径

通过图形上下文加上一系列创建路径的函数得到最终的路径,还需要绘制路径操作,才能使得路径呈现。如上文中一直用到的绘制路径的函数操作CGContextDrawPath(context, kCGPathStroke);。

绘制路径分为描边和填充,描边是绘制路径的边框,填充是绘制路径所包含的区域。

影响描边的属性


填充路径

填充规则有两种:非零缠绕数规则(nonzero winding number rule)、偶数-奇数规则(even-odd rule)。

默认的填充规则为非零缠绕数规则。方法或枚举带有“EO”的为偶数-奇数规则。

非零缠绕数的填充规则与绘制的方向有关、偶数-奇数规则则与方向无关。如图。

CGContextDrawPath函数的使用:

*CGContextDrawPath(context, kCGPathFill) 填充路径。

*CGContextDrawPath(context, kCGPathEOFill) 使用奇偶规则填充路径。

*CGContextDrawPath(context, kCGPathStroke) 描边路径。

*CGContextDrawPath(context, kCGPathFillStroke) 填充并描边路径。

*CGContextDrawPath(context, kCGPathEOFillStroke) 使用奇偶规则填充并描边路径。

混合模式

混合模式是Quartz将绘图绘制到背景上的方式,其实就是前景图和背景图怎么混合叠加。叠加公式如下:

result = (alpha * foreground) + (1 - alpha) *background

Quartz默认使用普通混合模式,也就是kCGBlendModeNormal。下面举例几个混合模式的样式,案例样式的代码见下一节裁剪路径:

裁剪路径

裁剪是一个遮罩,会遮住不允许绘制的地方。裁剪区域是一个闭合路径,Quartz只会渲染裁剪区域里面的东西,外面的东西不渲染。通过函数CGContextClip裁剪,具体的裁剪代码和执行结果如下,注意代码位置:


参考文章:南峰子翻译的Quartz2D编程指南

原文:http://mp.weixin.qq.com/s/Sbt2goTNRDjgGQpmuus_gQ

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容