两者关系
The Quartz 2D API defines a variety of opaque data types in addition to graphics contexts. Because the API is part of the Core Graphics framework, the data types and the routines that operate on them use the CG prefix.
从这段话理解,Quartz 2D
只是一套API,CoreGraphic
建立了一套图形绘制的体系。CoreGraphic
中那些提供图形绘制功能的API,开放出来集合成了Quartz 2D
。
图形绘制目标
核心为The Graphics Context,即CGContextRef。
A graphics context is an opaque data type (CGContextRef) that encapsulates the information Quartz uses to draw images to an output device
graphics context并不是绘制的输出目标,但是它包含了绘制和输出所需的所有数据,是一个封装。针对不同的输出,有不同的context封装,所以一定程度,可以把context理解为输出目标。或者说对于输出目标,开发者也只能接触到context这个级别了。
总之context包含了绘制所有的参数,这也正是context这类角色的作用,只需要向context里输入绘制要求就可以了,剩余的它来处理。
就这6种输出目标:
PDF跟打印机没什么可说的,bitmap就是生成一个位图,window就是窗口,iOS里就是view,这里的Layer不是跟view一起的CALayer,而是CGLayer。CGLayer作用就是单图形的复用,替代原本用图片做的事,要更高效些。
所以内容就清晰的分为了两大部分: 1. 针对绘制目标的特殊处理,bigmap CGLayer 2. 绘制指令,path color shadow等
绘制指令
1. 路径path
路径是什么?理解这个首先要理解Quartz 2D
的绘制模式:
- 画线条,图形有了
- 线条构成封闭的图形,填充颜色,有了色块
- 多层图形时上面的会覆盖下面的,产生混合效果,上下顺序影响最后结果。
所以图形根本是线条,这个线条就是路径。
绘制路径的手段有:
- 点+连线
CGContextMoveToPoint
CGContextAddLineToPoint
搞定所有只有直线的图形
- 圆弧
CGContextAddArcToPoint
这个就是指定圆心、半径、开始角度、结束角度,很好理解
CGContextAddArc
这个画圆弧的逻辑是:当前点+这个方法里的两个点,3个点构成一个夹角,然后画一个圆弧和这两条线都相切。这个方法在画圆角矩形的时候很好用,因为圆弧结束后还会把直线部分也一起画出来。
- 曲线
双控制点:CGContextAddCurveToPoint
单控制点:CGContextAddQuadCurveToPoint
然后是方便的添加矩形、椭圆。变化性最大的就是贝塞尔曲线了,可以模拟很多情况。
路线绘制好后还可以设置样式:
- 颜色,常用属性之1
- 宽度,常用属性之2
- 不常用的还有虚线、线边缘样式(lineCap)、两线连接处样式(lineJoin)
2. 特殊处理
- 混合模式
setBlendMode
graphics context的混合模式,并不是指view和其他view,或layer之间的混合,而是一次绘制过程中,对某个点的多次上色之前的混合。就像绘画时,在鼻子上画了黄色,然后加点白色表现高光。
- 路线剪切
currentCtx.addRect(CGRect(x: 40, y: 40, width: 100, height: 100))
currentCtx.clip()
之后的绘制只会在被切出来的那个区域,其他区域绘制无效。
3 transform
trasnform是对绘制的坐标进行了转换,前面都是说如何绘制出一个图,有了完整的图形后,可以用trasnform来对它进行整体的转换:移动、旋转、缩放以及这3个的叠加效果。
- 绘制图形之前应用的transform才有用,如drawImage,addRect
-
CGContextConcatCTM
来给context加入transform - 背后的原理:
- 公式:[图片上传失败...(image-cee73b-1534563448369)]
- 移动矩阵:[图片上传失败...(image-f769f8-1534563448369)]
- 缩放矩阵:[图片上传失败...(image-c4db7e-1534563448369)]
位图构建
位图是什么?
A bitmap image (or sampled image) is an array of pixels (or samples). Each pixel represents a single point in the image
一个位图就是一个像素数组,每个像素表示图片里的一个点。显示器上的图像都是一个个像素构成的,每个像素有自己的颜色,如果建一个表,把显示器上的每个像素的颜色都记录到表里,这个表数组就是一个位图bitmap。常用的JPEG PNG都是位图,当然这些图片都是压缩过得,内存和像素颜色不是一一对应的,但现实到屏幕之前一定会做解压。
有位图,就有对应的矢量图,它是由一系列计算机指令来描述和记录一幅图,显示之前再从这些指令从新把图绘制出来。
用一个圆来做例子,位图就是一张图,记录了每个位置的像素,跟其他的图没有不同。而矢量图可能就是一个命令:circle(radius:5,center:(20,20),color:...)
矢量图可能就要依赖于系统对指令的解析,而且绘制也需要消耗性能,但存储和传输更方便,而且一个很大的好处是可以应对不同的分辨率。
回到Quartz 2D
,bitmap的context的作用就是把绘制的截图输出到一张图片里,常见的应用就是屏幕截图了。
核心方法:
-
CGImageCreate
直接从纯数据构建,指定所有格式 -
CGBitmapContextCreateImage
从当前的位图上下文里构建图片,配合CGContextDrawImage
把图绘制进去,内部把图片解压 -
CGImageCreateWithImageInRect
从已有图截取一部分 -
CGImageCreateWithPNGDataProvider
和CGImageCreateWithJPEGDataProvider
从图片文件构建,配合CGDataProviderCopyData
,内部把图片解压
mask
mask在对个地方可以见到:这里的mask,CALayer里的mask,就是圆角的那个,哈希表里也有mask。总的来说就就是使用一种规则屏蔽掉某些数据,图像之间处理的mask,像这里,一般是根据alpha数据来的。alpha>0的地方图像显示,alpha==0的地方图像屏蔽。
-
CGImageCreateWithMask
使用一个mask来处理原图,跟文档里说的并不同,mask原图的alpha是1的地方才是显示出来的地方,公式应该是a*s+(1-a)*m
,a是mask的原图的alpha,s是要显示的图片,m是mask原图。 - mask是用
CGImageMaskCreate
构建的特殊图片,应该只包含灰度信息 - 不使用专门的mask方法,也可以使用图片之间构建的mask,但是需要颜色空间为灰度。
- 使用颜色剔除,
CGImageCreateWithMaskingColors
,传入一个数组,依次表示每个元素的最小值和最大值,在这个范围内的颜色都被剔除,不显示。
CGLayer
CAlayer是负责view的内容显示的,而CGLayer和它完全不同,它并不做显示,而是存储。构造一个样式,比如自己绘制一个小星星,然后要重用这个小星星,两个方案:1. 使用bitmap,即把绘制的小星星变成一张图,想怎么用都可以。 2.使用CGLayer保存绘制结果。
可以理解CGLayer为对一个图形的引用、保存,可以在需要的地方绘制出来,可以看成是一种特殊的图片,消耗更小、更高效复用的图片。
除了重复绘制,高性能离屏渲染(High-quality offscreen),缓冲数据(Buffering)时也可以使用。Buffering的场景不是很清楚,但High-quality offscreen可以用于复杂图形的副线程绘制,在副线程把复杂图形绘制出来,然后存储在CGLayer里,在显示的时候直接显示,略去绘制过程的性能影响。
核心方法:
- 构建
CGLayerCreateWithContext
- 获取它的context
CGLayerGetContext
- 有了context,就可以在这个context里使用Quartz 2D的API绘制任意的内容
- 使用
CGContextDrawLayerAtPoint
,把这个CGLayer绘制到另一个Graphic Context里,这个Graphic Context才是真正显示的那个。就像把一张贴纸贴到一张大图画上。