Skip to content

Latest commit

 

History

History
287 lines (216 loc) · 10.4 KB

CoreGraphics学习笔记.md

File metadata and controls

287 lines (216 loc) · 10.4 KB

CoreGraphics学习笔记

一、简介 1、CoreGraphics 坐标系 CoreGraphics 坐标系和我们平时用UIKit的坐标系是不样的,CoreGraphics 的左下角为(0,0)点,而UIKit的左上角为(0,0)点。 但通常我们无需关心此问题,因为我们都是如下方式使用 CoreGraphics :

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
}

其中的 UIGraphicsGetCurrentContext(); 方法是UIKit的方法,坐标系自然还是UIKit的坐标系。

2、几个名词区别 Quartz 、 Quartz 2D 和 QuartzCore 区别

Quartz 是 CoreGraphics 的核心,二者相互替代也没什么问题;Quartz 2D 是 CoreGraphics 的一部分; QuartzCore 实际就是 Core Animation,它扩展了 CoreGraphics 的部分功能,但二者没有互属关系。

二、API使用

1、绘制上下文

UIKIT_EXTERN CGContextRef __nullable UIGraphicsGetCurrentContext(void)

实际是 UIKit 的方法,用于获取当前绘制上下文,仅当使用 CoreGraphics 绘制时才需要手动调用,使用UIKit绘制(ex. UIBezierPath)时 framework会自动获取绘制上下文。

CG_EXTERN void CGContextSaveGState(CGContextRef __nullable c)

该方法保存当前的图形状态

CG_EXTERN void CGContextRestoreGState(CGContextRef __nullable c)

该方法弹出刚刚保存的 context 状态并恢复到上一个 context 状态,两个方法需要成对调用。

2、开始/结束绘制

UIKIT_EXTERN CGContextRef __nullable UIGraphicsGetCurrentContext(void)

获取当前绘制 context。

UIKIT_EXTERN void     UIGraphicsBeginImageContext(CGSize size)
UIKIT_EXTERN void     UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
UIKIT_EXTERN void     UIGraphicsEndImageContext(void)

前两个方法创建新的绘制上下文,并将其设为当前绘制上下文。最后一个方法结束当前的绘制工作,必须和前两个配对调用。

UIKIT_EXTERN BOOL UIGraphicsBeginPDFContextToFile(NSString *path, CGRect bounds, NSDictionary * __nullable documentInfo) 
UIKIT_EXTERN void UIGraphicsBeginPDFContextToData(NSMutableData *data, CGRect bounds, NSDictionary * __nullable documentInfo)
UIKIT_EXTERN void UIGraphicsEndPDFContext(void)

前两个方法创建新的PDF绘制 context 。最后一个方法结束当前绘制工作,必须和前两个配对调用。

CG_EXTERN void CGContextBeginPath(CGContextRef __nullable c)

在当前绘制 context 中创建新的 path,准备开始画线。

3、设置绘制条件

CG_EXTERN void CGContextSetLineWidth(CGContextRef __nullable c, CGFloat width)
CG_EXTERN void CGContextSetStrokeColorWithColor(CGContextRef __nullable c,
    CGColorRef __nullable color)

设置颜色、宽度等属性。

4、颜色

有RGB/CMYK等颜色空间,以RGB为例:

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

获取颜色有以下方法:

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
const CGFloat components[] = {0.90, 0.80, 0.80, 1.0};
CGColorRef color = CGColorCreate(colorSpace, components);

// 或

CGColorRef color = CGColorCreateGenericRGB(0.90, 0.80, 0.80, 1.0);

5、CTM(Current Transformation Matrix)

实现平移、缩放、旋转等基本变换效果的变换矩阵可通过 CoreGraphics 中直接修改 CTM 的相关方法实现:

CG_EXTERN void CGContextTranslateCTM(CGContextRef __nullable c,
    CGFloat tx, CGFloat ty)
CG_EXTERN void CGContextScaleCTM(CGContextRef __nullable c,
    CGFloat sx, CGFloat sy)
CG_EXTERN void CGContextRotateCTM(CGContextRef __nullable c, CGFloat angle)
CG_EXTERN void CGContextConcatCTM(CGContextRef __nullable c,
    CGAffineTransform transform)

同样的变换效果也可以通过 CGAffineTransform 相关方法配合 CGContextConcatCTM 实现:

CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, 100, 100);
transform = CGAffineTransformRotate(transform, M_PI_4);
transform = CGAffineTransformScale(transform, .5, .5);
CGContextConcatCTM(context, transform);

4、绘制直线/曲线

有以下两种类型的绘制方式:

  • 直接在 context 里作画
CG_EXTERN void CGContextMoveToPoint(CGContextRef __nullable c,
    CGFloat x, CGFloat y)

开始一个新的 subpath,并将起点设置在(x,y)

CG_EXTERN void CGContextAddLineToPoint(CGContextRef __nullable c,
    CGFloat x, CGFloat y)
CG_EXTERN void CGContextAddLines(CGContextRef __nullable c,
    const CGPoint * __nullable points, size_t count)

从当前点画一条到(x,y)点的直线。

CG_EXTERN void CGContextAddRect(CGContextRef __nullable c, CGRect rect)
CG_EXTERN void CGContextAddRects(CGContextRef __nullable c,
    const CGRect * __nullable rects, size_t count)

画矩形区域。

CG_EXTERN void CGContextAddEllipseInRect(CGContextRef __nullable c, CGRect rect)

画椭圆。

CG_EXTERN void CGContextAddCurveToPoint(CGContextRef __nullable c, CGFloat cp1x,
    CGFloat cp1y, CGFloat cp2x, CGFloat cp2y, CGFloat x, CGFloat y)
CG_EXTERN void CGContextAddQuadCurveToPoint(CGContextRef __nullable c,
    CGFloat cpx, CGFloat cpy, CGFloat x, CGFloat y)

画贝塞尔曲线和二次贝塞尔曲线。

CG_EXTERN void CGContextAddArc(CGContextRef __nullable c, CGFloat x, CGFloat y,
    CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
CG_EXTERN void CGContextAddArcToPoint(CGContextRef __nullable c,
    CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat radius)

画圆/弧。

CG_EXTERN void CGContextSetLineDash(CGContextRef __nullable c, CGFloat phase,
    const CGFloat * __nullable lengths, size_t count)

画虚线

CG_EXTERN void CGContextClosePath(CGContextRef __nullable c)

将最近的一个 subpath 的起点和终点连接到一起,形成封闭空间。和 CGContextBeginPath 方法没有配对关系。

  • 制作 path 后再将其绘制到 context

其基本方法在上面都能找到对应的,其 API 包含在 CGPath.h 文件中,这里不一一列出,只举个例子:

- (void)drawRect:(CGRect)rect{
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    
    // 创建并绘制 path
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path,NULL, self.frame.size.width, self.frame.origin.y);
    CGPathAddEllipseInRect(path, &CGAffineTransformIdentity, CGRectMake(0, 320, 320, 160));
    CGPathAddLineToPoint(path,NULL, self.frame.origin.x, self.frame.size.height);
    
    // 将 path 添加到绘制 context
    CGContextAddPath(currentContext, path);

    // 绘制 path
    CGContextDrawPath(currentContext, kCGPathStroke);
    CGPathRelease(path);
}

5、描绘/填充绘制区域

CG_EXTERN void CGContextStrokePath(CGContextRef __nullable c)

将 path 用指定的颜色、宽度等属性描绘出来。如果不调用该方法我们是看不到绘制出来的线的。

CG_EXTERN void CGContextFillPath(CGContextRef __nullable c)

使用 winding-number 规则将 path 所围区域用指定的颜色填充。如果path没有闭合,填充会默认闭合。

CG_EXTERN void CGContextEOFillPath(CGContextRef __nullable c)

功能同 CGContextFillPath 方法,只是填充规则变成 even-odd。

以上描绘/填充模式也可以使用下面方法通过设置 CGPathDrawingMode 的方式实现上述方法同样的功能:

CG_EXTERN void CGContextDrawPath(CGContextRef __nullable c,
    CGPathDrawingMode mode)

其中:

typedef CF_ENUM (int32_t, CGPathDrawingMode) {
  kCGPathFill,				// 等价于 CGContextFillPath 方法
  kCGPathEOFill,				// 等价于 CGContextEOFillPath 方法
  kCGPathStroke,				// 等价于 CGContextStrokePath 方法
  kCGPathFillStroke,		// 等价于 CGContextFillPath 和 CGContextStrokePath 方法的合成效果
  kCGPathEOFillStroke		// 等价于 kCGPathEOFill 和 CGContextStrokePath 方法的合成效果
};

剩余其它描绘/填充方法都类似。

6、裁剪

CG_EXTERN void CGContextClip(CGContextRef __nullable c)  // the winding-number fill rule

该方法使用一个 path(是否闭合都可以)去裁剪另一个 path,或者说该方法以取交集的方式来得到两个 path 的共同区域。使用方式是首先画一个 path(不能对该 path 调用 fill/stroke 方法);然后调用 CGContextClip 方法;其后再画另一个 path;最后调用 fill/stroke 方法将裁剪得到的 path 画出来。比如:

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
    CGContextSetLineWidth(context, 1);
   
    CGContextBeginPath(context);
    
    CGContextMoveToPoint(context, 5, 5);
    CGContextAddLineToPoint(context, 5, 30);
    CGContextAddLineToPoint(context, 30, 30);
    CGContextAddLineToPoint(context, 30, 5);
//    CGContextClosePath(context);
    
    CGContextClip(context);
//    CGContextClipToRect(context, CGRectMake(5, 5, 25, 25));
    
    CGContextMoveToPoint(context, 10, 10);
    CGContextAddLineToPoint(context, 10, 100);
    CGContextAddLineToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 100, 60);
    CGContextClosePath(context);
    
    CGContextStrokePath(context);

与该方法功能类似的还有以下方法:

CG_EXTERN void CGContextClipToRect(CGContextRef __nullable c, CGRect rect)
CG_EXTERN void CGContextClipToRects(CGContextRef __nullable c, const CGRect *  rects, size_t count)
CG_EXTERN void CGContextEOClip(CGContextRef __nullable c)   // even-odd fill rule
CG_EXTERN void CGContextClipToMask(CGContextRef __nullable c, CGRect rect, CGImageRef __nullable mask)

7、用户空间 & 设备空间

Quartz 2D绘图模型有两种空间,用户空间(user space)和设备空间(device space)。用户空间表示当前需绘制的文档页(document page),设备空间表示原始分辨率的设备。Quartz 2D使用一个变换矩阵CTM(current transformation matrix)将用户空间映射到设备空间。

设备空间与用户空间的概念,可理解为两张纸,设备空间为一张纸,固定着不动,代表着屏幕;用户空间也是一张纸,实际绘图在用户空间这张纸上画,但最终需要贴到设备空间那张纸上,怎么贴就是CTM描述的问题,我可能将用户空间的纸平移一些距离再贴,也可能放大缩小一些再贴,也可能旋转一定的角度再贴。用户空间的纸对应与绘画过程中的每一page,不同的page可能用不同的用户空间,即每次绘制时的CTM可能都不一样。