概述
Core Graphics Framework
是一套基于C的API框架,使用了Quartz作为绘图引擎。它提供了低级别、轻量级、高保真度的2D渲染。该框架可以用于基于路径的绘图、变换、颜色管理、脱屏渲染,模板、渐变、遮蔽、图像数据管理、图像的创建、遮罩以及PDF文档的创建、显示和分析。Quartz 2D
是Core Graphics Framework
的一部分。能实现以下的功能
- 画图
- 在程序中提供图形编辑
- 生成或者展示位图
- 和PDF文档工作
Painter Model
一个画图模型就是将CALayer
上的内容渲染到画布上,
Graphics context
一个GraphicsContext
对象是一个数据类型,封装了Quartz
图像到设备的信息,总共有5种类型的context:
- 窗口
- 位图
- 打印
- 反锯齿
这些类型主要是包括:绘制参数,设备相关信息。
图形API
OpenGL ES是跨平台的图形API,属于OpenGL的一个简化版本。QuartZ 2D是苹果公司开发的一套API.OpenGL ES是应用程序编程接口,该接口描述了方法、结构、函数应具有的行为以及应该如何被使用的语义。也就是说它只定义了一套规范,具体的实现由设备制造商根据规范去做.
Core Graphics API所有的操作都在一个上下文中进行
获取当前context的方法
调用UIGraphicsBeginImageContextWithOptions
函数就可获得用来处理图片的图形上下文。利用该上下文,你就可以在其上进行绘图,并生成图片。调用UIGraphicsGetImageFromCurrentImageContext
函数可从当前上下文中获取一个UIImage对象。记住在你所有的绘图操作后别忘了调用UIGraphicsEndImageContext
函数关闭图形上下文。UIView
和CALayer
中的渲染方法(比如drawRect
和dispaly
)中,默认就是当前的context
上实现的,当需要在其他的方法上去对layer
上进行的渲染的时候,需要获取当前的山下文,通过func UIGraphicsGetCurrentContext() -> CGContext?
获取当前的上下文。
绘图形式
用Core Graphics之前需要指定一个用于绘图的图形上下文(CGContextRef),这个图形上下文会在每个绘图函数中都会被用到。用UIKit使用的时候自定义UIView
,实现drawRect
方法时,系统会自动的生成一个CGContextRef
。总共有6种的绘图方式。
-
在UIView的子类方法drawRect:
func drawRect(_ aRect: NSRect) { var p = UIBezierPath(UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)) UIColor.blueColor().setFill() p.fill() }
-
使用Core Graphics,在
drawRect
中实现func drawRect(_ aRect: NSRect) {
let con = UIGraphicsGetCurrentContext() CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)) CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor) CGContextFillPath(con);
}
CALayer的drawLayer上实现 。 view调用setNeedsDisplay,或者view.layer调用display。和1相似
和2相似,
CALayer
的drawLayer
上实现 通过提供的上下文-
UIGraphicsBeginImageContextWithOptions
实现UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0); let p = UIBezierPath(bezierPathWithOvalInRect:CGRectMake(0,0,100,100)) UIColor.blueColor().setFill() p.fill() let im = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext()
-
使用
UIGraphicsBeginImageContextWithOptions
基于Core Graphics实现UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0); let con = UIGraphicsGetCurrentContext(); CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); CGContextSetFillColorWithColor(con, UIColor.blueColor().CGColor); CGContextFillPath(con); let im = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
Quartz2D的数据类型
下面列出了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
, CGPDFArrayRef
:用于访问PDF的元数据
CGPDFScannerRef
, CGPDFContentStreamRef
:用于解析PDF元数据
CGPSConverterRef
:用于将PostScript转化成PDF。在iOS中不能使用。
CGPath路径的组成
- 线:实线,虚线--->通过
CGPathAddLines(_:_:_:_:)
等方法 - 点:通过
moveToPoint(_:)
等方法。 - 弧:
CGContextAddArc(_:_:_:_:_:_:_:)
和CGContextAddArcToPoint(_:_:_:_:_:_:)
等方法添加弧线 - 曲线:可以通过贝塞尔曲线获取
CGContextAddArcToPoint(_:_:_:_:_:_:)
,或者自己定义一个贝塞尔曲线 - 最后可以选择闭合曲线
CGContextClosePath(_:)
路径的创建
具体步骤是:
- 通过
UIGraphicsGetCurrentContext
获取上下文,如果是在UIView和CALayer的渲染方法中,可以不用获取 - 在开始绘制路径前,调用函数
CGContextBeginPath
,用CGContextBeginPath
来标记Quartz
,因为context一个时间只能有一个path被使用,如果不使用的话,会丢弃之前的path - 进行线和点的绘制,
CGContextMoveToPoint CGContextAddLineToPoint
- 通过
CGContextAddPath(_:_:)
将路线加到上下文上
绘制路径
路径的绘制包括填充和描边,在绘制前可以先设置绘制属性,包括线宽,颜色等。
描边
描边的属性包括:线宽,连接点,是否为虚线。以下是一些常用的描边函数
填充
- 填充区域:填充当前路径时,Quartz将路径包含的每个子路径都看作是闭合的。然后,使用这些闭合路径并计算填充的像素。但是如果路径是由几个重叠的部分组成或者路径包含多个子路径,则需一定的规则来定义填充区域(具体参考官方文档)
- 填充模式:Quartz默认使用普通混合模式(normal blend mode),也就是多种颜色重叠的时候,绘制模式。
路径剪裁
绘制时,Quartz
只渲染裁剪区内的路径,裁剪区域内的闭合路径是可见的;而在区域外的部分是不可见的。
颜色与颜色空间
颜色空间是指一个设备所能表示的颜色,通过以下方法
func deviceRGBColorSpace() -> NSColorSpace
-
func deviceCMYKColorSpace() -> NSColorSpace
颜色空间有许多种,常用有RGB,CMY,HSB,CMYK,BGR等.alpha表示存在的对象与新对象如何混合。
CGContextSetAlpha
可以指定全局alpha。
变化
在绘图空间上,分为用户空间和设备空间。context
是作用在用户空间的,然后通过CTM(current transformation matrix)映射到设备空间。CTM有三种操作包括平移CGContextTranslateCTM(_:_:_:)
,旋转
CGContextRotateCTM(_:_:)
,缩放CGContextScaleCTM(_:_:_:)
。从用户空间到设备空间的映射空间可以通过
CGContextGetUserSpaceToDeviceSpaceTransform(_:)
获取到。
模式
模式(Pattern)是绘制操作的一个序列,这些绘制操作可以重复地绘制到一个图形上下文上。当用模式的时候,`Quartz将Page分割成模式单元格的集合,其中每个单元格的大小不是模式图片的大小,并使用我们提供的回调函数来绘制这些单元格。模式单元格之间可以设置间距,并且在模式单元格上被绘制
阴影(Shadow)
阴影包括阴影偏移(iOS和MacOS的坐标不一样),通过CGContextSetShadow(_:_:_:)
和CGContextSetShadowWithColor(_:_:_:_:)
设置阴影。
具体的步骤如下:
- 保存图形状态
CGContextSaveGState(_:)
- 调用
CGContextSetShadow(_:_:_:)
- 使用阴影绘制对象
- 恢复图形状态
渐变
渐变对象主要是两个CGShadingRef
和CGGradientRef
,CGGradientRef
对象是对CGShadingRef
的封装,区别是不需要提供渐变计算函数,只需要提供颜色和位置素组就好
CGGradientRef
的创建和使用
使用过程:
- 创建
CGGradientRef
对象,提供一个颜色空间,以及两个以上的位置和颜色数组 - 调用
CGContextDrawLinearGradient(_:_:_:_:_:)
或者CGContextDrawRadialGradient(_:_:_:_:_:_:_:)
- 释放内存
使用CGGradient对象
一个CGGradient对象是一个渐变的抽象定义—它简单地指定了颜色值和位置,但没有指定几何形状。我们可以在轴向和径向几何形状中使用这个对象。作为一个抽象定义,CGGradient对象可能比CGShading对象更容易重用。没有将几何形状存储在CGGradient对象中,这样允许我们使用相同的颜色方案来绘制不同的几何图形,而不需要为多个图形创建多个CGGradient对象。
因为Quartz为我们计算渐变,使用一个CGGradient对象来创建和绘制一个渐变则更直接,只需要以下几步:
创建一个CGGradient对象,提供一个颜色空间,一个饱含两个或更多颜色组件的数组,一个包含两个或多个位置的数组,和两个数组中元素的个数。
调用CGContextDrawLinearGradient或CGContextDrawRadialGradient函数并提供一个上下文、一个CGGradient对象、绘制选项和开始结束几何图形来绘制渐变。
当不再需要时释放CGGradient对象。
let locations = 2;
let locations[2] = { 0.0, 1.0 };
let components[8] = { 1.0, 0.5, 0.4, 1.0, 0.8, 0.8, 0.3, 1.0 }
let myColorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
let myGradient = CGGradientCreateWithColorComponents (myColorspace, components,locations, num_locations);
// myStartPoint, myEndPoint起始点和终点
CGContextDrawLinearGradient (myContext, myGradient, myStartPoint, myEndPoint, 0)
CGShadingRef
的创建和使用
CGShadingRef
分为创建和使用
CGShadingRef
的创建
- 获取颜色空间(
CGColorSpace
) - 设置起始点和终点
- 起始半径和终止半径
-
CGFunctionCreate
这个是关键:需要保证绘制到特低点的颜色值 - 一个bool值,指定是否用纯色来绘制起始点和终点的扩展区(可选值)
CGShadingRef
的使用
- 设置
CGFunction
对象计算颜色值 : 也就是计算每一个位置的颜色 - 创建
CGShadingRef
对象 - 裁剪上下文
- 使用
CGShading
对象:CGFunctionEvaluate
的重写 - 释放对象
透明
Quartz为每一个上下文维护一个透明层栈,绘制透明层的步骤如下
- 调用
CGContextBeginTransparencyLayer(_:_:)
方法表明开始绘制透明 - 在透明层中绘制需要的组合对象
- 调用
CGContextEndTransparencyLayer(_:)
后,透明才会被绘制上去
CGImageRef类型
CGImageRef
是CGGraphics 的一种重要的数据类型,主要处理的是位图与图像的遮罩。创建一个位图(CGImageRef)时,Quartz使用以下信息:
- 位图数据源:可以是一个Quartz数据提供者或者是一个Quartz图像源。
- 可选的解码数组。(Decode Array)
- 插值设置:这是一个布尔值,指定Quartz在重置图像大小时是否使用插值算法。
- 渲染意图:指定如何映射位于图形上下文中的目标颜色空间中的颜色。该值在图像遮罩中不需要。
- 图像尺寸
- 像素格式,包括每个分量中的位数,每个像素的位数和每行中的字节数。
如何创建
创建一个位图对象主要有以下几种方法:
- 依赖于图像的数据源
- 常用
CGImageCreate
来创建一个我吐 - 从PNG,JPEG创建一个
CGImage
对象,主要通过:CGImageSourceCreateWithURL(_:_:)
,CGImageSourceCreateImageAtIndex(_:_:_:)
- 从位图上下文获取
CGImage
对象。调用CGBitmapContextCreateImage(_:)
- 从一个已存在图像中创建子图像
CGImageCreateWithImageInRect(_:_:)
遮罩
一个位图图像遮罩定义了如何转换颜色,而不是使用哪些颜色。函数CGImageCreateWithMask
通过将图像遮罩使用到一个图像上的方式来创建一个图像。
总结
首先CoreGraphics是Quartz的一部分,这部分大多数是C语言结构的,Swift和OC的使用上没有太大区别。而在获取到了上下文后,关键的是如何去确定Path,贝塞尔曲线等等,这部分也可以通过专门的工具,在这推荐一个好的软件PaintCode,能直接导出Swift代码和OC代码。具体可以参考用Sketch和PaintCode快速得到绘制代码。let's Swift