Quartz2D 编程指南(一)概览、图形上下文、路径、颜色与颜色空间
- 概览
- 图形上下文
- 路径
- 颜色与颜色空间
- 变换
- 图案
- 阴影
- 渐变
- 透明层
- Quartz 2D 中的数据管理
- 位图与图像遮罩
- CoreGraphics 绘制 Layer
0.说明
本篇博客主要是对官方文档的总结与补充。翻译部分参考了南峰子的博客。你可以在参考资料中查看。
1.概览
简介
- Quartz2D 是二维图形绘制引擎,支持 iOS 和 OS X。
Page
- Quartz2D 在图像中使用了绘画者模型。在绘画者模型中,每个连续的绘制操作都是将一个绘制层放置于一个画布,我们通常称这个画布为 Page。
Graphics Context
- Graphics Context 是一个数据类型(CGContextRef),用于封装 Quartz 绘制图像到输出设备的信息。设备可以是PDF文件、bitmap或者显示器的窗口。 CGContextRef 对应绘画者模式中的 Page。
- 当用 Quartz 绘图时,所有设备相关的特性都包含在我们所使用的Graphics Context 中。我们可以简单地给 Quartz 绘图序列指定不同的 Graphics Context,就可将相同的图像绘制到不同的设备上。
- Quartz提供了 5 种类型的 Graphics Context。Bitmap Graphics Context、PDF Graphics Context、Window Graphics Context、Layer Context、Post Graphics Context。
数据类型
- Quartz 2D 使用如下数据类型来创建对象,通过操作这些对象来获取特定的图形。
- CGPathRef:用于向量图,可创建路径,并进行填充或描画(stroke)
- CGImageRef:用于表示bitmap图像和基于采样数据的bitmap图像遮罩。
- CGLayerRef:用于表示可用于重复绘制(如背景)和幕后 (offscreen)绘制的绘画层
- CGPatternRef:用于重绘图
- CGShadingRef、CGGradientRef:用于绘制渐变
- CGFunctionRef:用于定义回调函数,该函数包含一个随机的浮点值参数。当为阴影创建渐变时使用该类型
- CGColorRef, CGColorSpaceRef:用于告诉Quartz如何解释颜色
- CGImageSourceRef,CGImageDestinationRef:用于在Quartz中移入移出数据
- CGFontRef:用于绘制文本
- *CGPDFDictionaryRef, CGPDFObjectRef, CGPDFPageRef, CGPDFStream, CGPDFStringRef, and CGPDFArrayRef:用于访问PDF的元数据
- *CGPDFScannerRef, CGPDFContentStreamRef:用于解析PDF元数据
- *CGPSConverterRef:用于将PostScript转化成PDF。在iOS中不能使用。
图形状态
-
Quartz 通过修改当前图形状态(current graphics state)来修改绘制操作的结果。图形状态包含用于绘制程序的参数。绘制程序根据这些绘图状态来决定如何渲染结果。
-
可使用函数CGContextSaveGState来保存图形状态,CGContextRestoreGState来还原图形状态。
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextRestoreGState(context);
- 并不是当前绘制环境的所有属性都是图形状态的元素。如,图形状态不包含当前路径(current path)。
坐标系统
-
Quartz 的坐标系与 UIKit 坐标系 Y 轴相反。
-
Quartz 通过使用当前转换矩阵(current transformation matrix, CTM)将一个独立的坐标系统(user space)映射到输出设备的坐标系统(device space),以此来解决设备依赖问题。
-
使用 UIGraphicsBeginImageContextWithOptions 返回的绘图上下文与UIKit 的坐标系统相同。
-
在 iOS 中,如果使用 UIImage 对象来包裹创建的 CGImage 对象,可以不需要修改 CTM。UIImage 将自动进行补偿以适用 UIKit 的坐标系统。
-
在 iOS 3.2 后,当 UIKit 为你的应用程序创建一个绘图上下文时,也对上下文进行了额外的修改以匹配 UIKit 的约定。
内存管理
-
如果创建或拷贝一个对象,你将拥有它,因此你必须释放它。通常,如果使用含有”Create”或“Copy”单词的函数获取一个对象,当使用完后必须释放,否则将导致内存泄露。
-
如果使用不含有”Create”或“Copy”单词的函数获取一个对象,你将不会拥有对象的引用,不需要释放它。
-
如果你不拥有一个对象而打算保持它,则必须 retain 它并且在不需要时 release 掉。可以使用 Quartz2D 的函数来指定 retain 和 release 一个对象。例如,如果创建了一个 CGColorspace 对象,则使用函数 CGColorSpaceRetain 和 CGColorSpaceRelease 来 retain 和 release 对象。同样,可以使用 Core Foundation 的 CFRetain 和 CFRelease,但是注意不能传递 NULL 值给这些函数。
2.图形上下文
简介
我们可以通过两种方式来获取 Graphics Context:Quartz提供的创建函数、OS X 框架或 iOS 的 UIKit 框架提供的函数。
创建 Window Graphics Context
-
在 iOS 应用程序中,如果要在屏幕上进行绘制,需要创建一个 UIView 对象,并实现它的 drawRect: 方法。
-
drawRect: 方法中视图对象将为当前的绘图环境创建一个 Graphics Context。我们可以通过调用 UIGraphicsGetCurrentContext 函数来获取这个 Graphics Context。
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
CGContextFillRect(context, CGRectMake (0, 0, 200, 100));
CGContextSetRGBFillColor(context, 0, 0, 1, .5);
CGContextFillRect(context, CGRectMake (0, 0, 100, 200));
*创建 PDF Graphics Context
Quartz2D API 提供了两个函数来创建 PDF Graphics Context。
-
CGPDFContextCreateWithURL。当你需要用 Core Foundation URL 指定 PDF 输出的位置时使用该函数。
-
CGPDFContextCreate 当需要将pdf输出发送给数据用户时使用该方法。
创建 Bitmap Graphics Context
- iOS 中使用 UIGraphicsBeginImageContextWithOptions 取代 CGBitmapContextCreate 来创建 Bitmap Graphics Context 以便获得相同的坐标系。
UIGraphicsBeginImageContext([UIScreen mainScreen].bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
CGContextFillRect(context, CGRectMake (0, 0, 200, 100));
CGContextSetRGBFillColor(context, 0, 0, 1, .5);
CGContextFillRect(context, CGRectMake (0, 0, 100, 200));
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();//CGBitmapContextCreateImage(context)
UIGraphicsEndImageContext();
像素格式
-
像素格式是在使用 CGBitmapContextCreate 创建 Bitmap Graphics Context 时需要指定的参数。
-
像素格式用 bpp(每像素的位数)和 bpc(每个组件的位数)来表示。
-
iOS 共支持 8 种像素格式。
- Null 8 bpp, 8 bpc, kCGImageAlphaOnly
- Gray 8 bpp, 8 bpc, kCGImageAlphaNone
- Gray 8 bpp, 8 bpc, kCGImageAlphaOnly
- RGB 16 bpp, 5 bpc, kCGImageAlphaNoneSkipFirst
- RGB 32 bpp, 8 bpc, kCGImageAlphaNoneSkipFirst
- RGB 32 bpp, 8 bpc, kCGImageAlphaNoneSkipLast
- RGB 32 bpp, 8 bpc, kCGImageAlphaPremultipliedFirst
- RGB 32 bpp, 8 bpc, kCGImageAlphaPremultipliedLast
抗锯齿
-
我们可以通过调用 CGContextSetShouldAntialias 来关闭位图Graphics Context的反锯齿效果。反锯齿设置是图形状态的一部分。
-
可以调用函数CGContextSetAllowsAntialiasing来控制一个特定Graphics Context是否支持反锯齿;false表示不支持。该设置不是图形状态的一部分。当上下文及图形状态设置为true时,Quartz执行反锯齿。
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShouldAntialias(context, YES);
CGContextSetAllowsAntialiasing(context, YES);
3.路径
简介
-
路径定义了一个或多个形状或者子路径。一个子路径可由直线,曲线,或者同时由两者构成。它可以是开放的,也可以是闭合的;可以是实线,可以是曲线;可以填充,也可以描边等等。
-
路径创建及路径绘制是两个独立的工作。首先我们创建路径。当我们需要渲染路径时,我们需要使用Quartz来绘制它。
点
- 调用 CGContextMoveToPoint(context, 0, 0) 为新的子路径指定起始点。
直线
-
调用 CGContextAddLineToPoint(context, 200, 200) 从起始点到指定点添加直线。
-
调用 CGContextAddLines(context, points, 3) 函数添加一系列相关的直线到子路径中。
弧
-
调用 CGContextAddArc(context, 200, 200, 100, 0, M_PI_2, 0) 以指定圆心、半径、起始/终止角度、顺/逆时针画弧。
-
调用 CGContextAddArcToPoint(context, 200, 400, 400, 400, 100) 以当前点与指定的两个点连接的两条直线为切线画弧。
曲线
-
调用 CGContextAddQuadCurveToPoint(context, 0, 100, 200, 100) 以一个控制点画曲线。
-
调用 CGContextAddCurveToPoint(context, 100, 400, 200, 300, 100, 200) 以两个控制点画曲线。
闭合路径
- 调用 CGContextClosePath(context) 闭合路径。
椭圆
- 调用 CGContextAddEllipseInRect(context, CGRectMake(0, 0, 100, 200)) 画椭圆。
矩形
- 调用 CGContextAddRect(context, CGRectMake(200, 400, 100, 200)) 画矩形。
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, 200, 200);
CGPoint point2 = CGPointMake(200, 200);
CGPoint point3 = CGPointMake(200, 400);
CGPoint point4 = CGPointMake(0, 200);
CGPoint points[3] = {point2, point3, point4};
CGContextAddLines(context, points, 3); // reset start point
CGContextAddArc(context, 200, 200, 100, 0, M_PI_2, 0);
CGContextAddArcToPoint(context, 200, 400, 400, 400, 100);
CGContextAddCurveToPoint(context, 100, 400, 200, 300, 100, 200);
CGContextAddQuadCurveToPoint(context, 0, 100, 200, 100);
CGContextClosePath(context);
CGContextAddEllipseInRect(context, CGRectMake(0, 0, 100, 200));
CGContextAddRect(context, CGRectMake(200, 400, 100, 200));
CGContextDrawPath(context, kCGPathStroke);
创建路径
-
在开始绘制路径前,调用 CGContextBeginPath 或 UI。
-
直线、弧、曲线开始于当前点。空路径没有当前点;我们必须调用CGContextMoveToPoint来设置第一个子路径的起始点,或者调用一个便利函数来隐式地完成该任务。
-
如果要闭合当前子路径,调用函数 CGContextClosePath。随后路径将开始一个新的子路径,即使我们不显示设置一个新的起始点。
-
当绘制弧时,Quartz 将在当前点与弧的起始点间绘制一条直线。
-
添加椭圆和矩形的 Quartz 程序将在路径中添加新的闭合子路径。
-
我们必须调用绘制函数来填充或者描边一条路径,因为创建路径时并不会绘制路径。
-
Quartz 提供了两个数据类型来创建可复用路径 CGPathRef 和 CGMutablePathRef。
-
Quartz 提供了一个类似于操作图形上下文的 CGPath 的函数集合。这些路径函数操作 CGPath 对象,而不是图形上下文。
- CGPathCreateMutable 取代 CGContextBeginPath
- CGPathMoveToPoint 取代 CGContextMoveToPoint
- CGPathAddLineToPoint 取代 CGContexAddLineToPoint
- CGPathAddCurveToPoint 取代 CGContexAddCurveToPoint
- CGPathAddEllipseInRect 取代 CGContexAddEllipseInRect
- CGPathAddArc 取代 CGContexAddArc
- CGPathAddRect 取代 CGContexAddRect
- CGPathCloseSubpath 取代 CGContexClosePath
- 如果想要添加一个路径到图形上下文,可以调用 CGContextAddPath。
填充规则
-
填充规则有两种:非零缠绕数规则(nonzero winding number rule)、偶数-奇数规则(even-odd rule)。
-
默认的填充规则为非零缠绕数规则。方法或枚举带有“EO”的为偶数-奇数规则。
-
非零缠绕数的填充规则与绘制的方向有关、偶数-奇数规则则与方向无关。如图。
绘制路径
-
调用 CGContextDrawPath(context, kCGPathFill) 填充路径。
-
调用 CGContextDrawPath(context, kCGPathEOFill) 使用奇偶规则填充路径。
-
调用 CGContextDrawPath(context, kCGPathStroke) 描边路径。
-
调用 CGContextDrawPath(context, kCGPathFillStroke) 填充并描边路径。
-
调用 CGContextDrawPath(context, kCGPathEOFillStroke) 使用奇偶规则填充并描边路径。
描边路径
-
调用快捷方法 CGContextStrokePath(context) 来描边路径。
-
调用如下函数来快捷的创建形状路径并描边。
CGContextStrokeRect(context, CGRectMake(200, 400, 100, 200));
CGContextStrokeRectWithWidth(context, CGRectMake(200, 400, 100, 200), 2);
CGContextStrokeEllipseInRect(context, CGRectMake(200, 400, 100, 200));
- 调用 CGContextStrokeLineSegments(context, points, 4) 快捷的创建多条不连续的线段并描边。
CGPoint point2 = CGPointMake(200, 200);
CGPoint point3 = CGPointMake(200, 400);
CGPoint point4 = CGPointMake(100, 300);
CGPoint point5 = CGPointMake(300, 300);
CGPoint points[4] = {point2, point3, point4, point5};
CGContextStrokeLineSegments(context, points, 4);
填充路径
-
调用快捷方法 CGContextFillPath(context) 或 CGContextEOFillPath(context) 填充路径。
-
CGContextStrokePath(context) 和 CGContextFillPath(context) 不能同时使用。
-
调用如下函数来快捷的创建形状路径并填充。
CGContextFillRect(context, CGRectMake(100, 100, 100, 200));
CGRect rects[2] = {CGRectMake(100, 100, 100, 200), CGRectMake(200, 300, 100, 200)};
CGContextFillRects(context, rects, 2);
CGContextFillEllipseInRect(context, CGRectMake(100, 100, 100, 200));
混合模式
-
调用 CGContextSetBlendMode(context, kCGBlendModeNormal) 设置回合模式。
-
常用的混合模式:
- kCGBlendModeNormal 正常
- kCGBlendModeMultiply 正片叠底
- kCGBlendModeScreen 滤色(屏幕)
- kCGBlendModeOverlay 叠加
- kCGBlendModeDarken 变暗
- kCGBlendModeLighten 变亮
- kCGBlendModeColorDodge 颜色减淡
- kCGBlendModeColorBurn 颜色加深
- kCGBlendModeSoftLight 柔光
- kCGBlendModeHardLight 强光
- kCGBlendModeDifference 差值
- kCGBlendModeExclusion 排除
- kCGBlendModeHue 色相
- kCGBlendModeSaturation 饱和度
- kCGBlendModeColor 颜色
- kCGBlendModeLuminosity 明度
- 你可以在《Quartz 2D Programming Guide》官方文档中的 Paths 章节和 Bitmap Images and Image Masks 章节的最后部分查看混合模式的具体效果。这里的混合模式与常用的图形软件(如 Photoshop)的混合模式效果相同,这里只展示正片叠底和滤色两种效果。
-
原图
-
正片叠底
-
滤色
颜色与颜色空间
简介
-
Quartz 中的颜色是用一组数值来表示。而颜色空间用于解析这些颜色信息,常用颜色空间有 RGB 、CMYK等。
-
Quartz 支持通用颜色空间、设备独立颜色空间、设备依赖颜色空间、索引颜色空间和模式(Pattern)颜色空间。
-
iOS不支持设备独立颜色空间和通用颜色空间。iOS应用程序必须使用设备颜色空间。
透明度
-
使用 CGContextSetAlpha(context, 0.2) 设置透明度。
-
使用 CGContextClearRect 清除上下文的 alpha 通道。
创建设备依赖颜色空间
-
CGColorSpaceCreateDeviceGray() 创建设备依赖灰度颜色空间。
-
CGColorSpaceCreateDeviceRGB() 创建设备依赖RGB颜色空间。
-
CGColorSpaceCreateDeviceCMYK() 创建设备依赖CMYK颜色空间。
-
调用 CGContextSetFillColorSpace(context, colorSpace) 或 CGContextSetStrokeColorSpace(context, colorSpace) 设置颜色空间。
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextSetFillColorSpace(context, colorSpace);
CGContextSetStrokeColorSpace(context, colorSpace);
- 调用如下函数来便捷的设置设备依赖RGB颜色空间并设置颜色值。
//Device RGB.
CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
- 调用如下函数来便捷的设置设备依赖CMYK颜色空间并设置颜色值。
//Device CMYK.
CGContextSetCMYKStrokeColor(context, 1, 0, 0, 0, 1);
CGContextSetCMYKFillColor(context, 1, 0, 0, 0, 1);
- 调用如下函数来便捷的设置设备依赖灰度颜色空间并设置颜色值。
//Device Gray.
CGContextSetGrayStrokeColor(context, 0.5, 1);
CGContextSetGrayFillColor(context, 0.5, 1);
- 调用如下函数来便捷的使用 CGColor 设置颜色值并使用 CGColor 指定的颜色空间。
//Any color space; you supply a CGColor object that specifies the color space. Use these functions for colors you need repeatedly.
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGFloat colors[4] = {1.0, 0.0, 0.0, 1.0};
CGColorRef color = CGColorCreate(colorSpace, colors);
CGContextSetStrokeColorWithColor(context, color);
CGContextSetFillColorWithColor(context, color);
- 调用如下函数来便捷的设置颜色值并使用正在使用的颜色空间。
//The current color space. Not recommended. Instead, set color using a CGColor object and the functions CGContextSetStrokeColorWithColor and CGContextSetFillColorWithColor.
CGContextSetStrokeColor(context, colors);
CGContextSetFillColor(context, colors);
设置和创建颜色
- 通过如下函数设置和创建颜色。
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor));
设置再现意图(Rending Intent)
每个设备都有固定的可复制的颜色范围(gamut),这是设备的物理性质决定的。当图像从一个颜色空间向另一个颜色空间转换时,有些源设备颜色空间中呈现的颜色,不能在目标设备颜色空间中复制出来,这些不能复制的颜色叫色域外(out-of-gamut)颜色。比如 RGB 颜色空间比 CMYK 的颜色空间要大,有些在显示器上能显示的颜色不能在打印机上同样打印出来。因为我们不能在目标设备颜色空间中复制出色域外颜色,我们必须用一些其他颜色来替代他们。颜色空间转换时颜色替换调整的规则就是再现意图。更详细的说明可以查看 这里 和 这里
-
再现意图用于指定如何将源颜色空间的颜色映射到图形上下文的目标颜色空间的颜色范围内。
-
如果不显式的指定再现意图,Quartz 使用“相对色度再现意图”应用于所有绘制(不包含位图图像)。
-
对于位图图像,Quartz默认使用“感知再现意图”。
-
调用 CGContextSetRenderingIntent(context, kCGRenderingIntentDefault) 来设置再现意图。
-
再现意图共有以下 5 种。
typedef CF_ENUM (int32_t, CGColorRenderingIntent) {
kCGRenderingIntentDefault,
kCGRenderingIntentAbsoluteColorimetric,
kCGRenderingIntentRelativeColorimetric,
kCGRenderingIntentPerceptual,
kCGRenderingIntentSaturation
};
- kCGRenderingIntentDefault:默认再现意图。
- kCGRenderingIntentAbsoluteColorimetric:绝对色度再现意图。将输出设备颜色域外的颜色映射为输出设备域内与之最接近的颜色。这可以产生一个裁减效果,因为色域外的两个不同的颜色值可能被映射为色域内的同一个颜色值。当图形使用的颜色值同时包含在源色域及目标色域内时,这种方法是最好的。常用于logo或者使用专色(spot color)时。
- kCGRenderingIntentRelativeColorimetric:相对色度再现意图。转换所有的颜色(包括色域内的),以补偿图形上下文的白点与输出设备白点之间的色差。
- kCGRenderingIntentPerceptual:感知再现意图。通过压缩图形上下文的色域来适应输出设备的色域,并保持源颜色空间的颜色之间的相对性。感知渲染意图适用于相片及其它复杂的高细度图片。
- kCGRenderingIntentSaturation:饱和度再现意图。把颜色转换到输出设备色域内时,保持颜色的相对饱和度。结果是包含亮度、饱和度颜色的图片。饱和度意图适用于生成低细度的图片,如描述性图表。
原文:http://xuyafei.cn/post/cocoatouch/quartz2d-bian-cheng-zhi-nan-gai-lan-tu-xing-shang-xia-wen-lu-jing-yan-se-yu-yan-se-kong-jian