美文网首页
Core Graphics 一: CGContext基本绘制

Core Graphics 一: CGContext基本绘制

作者: Trigger_o | 来源:发表于2020-09-23 12:43 被阅读0次

概览

硬件 -> OpenGL/core graphics -> core animation -> UI Kit/ App Kit


底层到上层

先看一下文档,打开xcode help的developer documentation


core graphics的api

先从最主要的部分进入
1.CGContext
Graphics Context:图形上下文,也就是画布,绘制完成后,将画布放到view中显示.
主要分为绘制,管理和配置等几个部分.
获取画布(CGContextRef对象)可以在uiview中重写- (void)drawRect:(CGRect)rect,也可以获取CALayer的画布,这里先从drawRect方法开始.
CGContext的绘制是绘制图形,基本上由路径的构建和填充策略组成

2.构建路径

  • 从画一条线开始CGContextAddLineToPoint
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(context, 100, 100); //画笔移动到(100,100)
CGContextAddLineToPoint(context, 200, 100); //向(200,100)画一条线
CGContextDrawPath(context, kCGPathStroke); 

kCGPathStroke是CGPathDrawingMode枚举,后面详细介绍

  • 画圆弧CGContextAddArc
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise);

xy是圆心坐标,radius是半径,startAngle是起始的角度,endAngle是结束的角度,clockwise是画圆弧的方向顺序,1为顺时针,0是逆时针.

CGContextAddArc(context, 100, 200, 50, M_PI/2, M_PI/2*3, 1);
CGContextDrawPath(context, kCGPathStroke);

如果是顺时针,则从圆心左边开始,为0,顺时针,往上90度为M_PI/2,以此类推,如果是逆时针,则从圆心右边开始,逆时针往上90度为M_PI/2,以此类推.

圆弧

CGContextAddArcToPoint函数

CGContextMoveToPoint(context, 190, 190);
CGContextAddArcToPoint(context, 200, 200, 300, 50, 100);
CGContextDrawPath(context, kCGPathStroke);

这个方法是从currentPoint(A)开始,再给定两个点B,C,然后再给一个半径,会画出一个和AB,BC两条线相切的圆弧以及A到圆弧的线段这样一个图形,
CGContextMoveToPoint和CGContextAddLineToPoint都会产生currentPoint.

CGContextAddArcToPoint
这个方法是用来解决图形上拼接圆弧的场景,只使用CGContextAddArc有时候很难把线段和圆弧连接上.

CGContextAddQuadCurveToPoint,二阶贝塞尔曲线,画出来的就是一个抛物线

CGContextMoveToPoint(context, 100, 290);
CGContextAddQuadCurveToPoint(context, 150, 80, 200, 390);
CGContextDrawPath(context, kCGPathStroke);

用两个点确定的一条曲线,和currentPoint组成两条线,与曲线相切


CGContextAddQuadCurveToPoint
二阶动画

CGContextAddCurveToPoint,三阶贝塞尔曲线

CGContextMoveToPoint(context, 300, 50);
CGContextAddCurveToPoint(context, 20, 300, 100, 300, 150, 350);
CGContextDrawPath(context, kCGPathStroke);

三个点可以确定一个曲线,当然这里没有算上currentPoint


CGContextAddCurveToPoint
三阶动画
  • 添加多条直线CGContextAddLines
CGPoint sPoints[4];//坐标点
sPoints[0] =CGPointMake(100, 220);//坐标1
sPoints[1] =CGPointMake(130, 220);//坐标2
sPoints[2] =CGPointMake(130, 160);//坐标3
sPoints[3] =CGPointMake(230, 160);//坐标4
CGContextAddLines(context, sPoints, 4);//添加线
CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径

参数需要一个CGPoint数组和一个点的数量,需要小于等于数组长度


CGContextAddLines
  • 绘制矩形CGContextAddRect
CGContextAddRect(context, CGRectMake(200, 100, 100, 150));
CGContextDrawPath(context, kCGPathStroke);

绘制一个矩形,与currentPoint无关.

绘制一组矩形CGContextAddRects.

CGRect sRects[4];
    sRects[0] = CGRectMake(100, 100, 30, 30);
    sRects[1] = CGRectMake(100, 130, 30, 30);
    sRects[2] = CGRectMake(100, 160, 30, 30);
    sRects[3] = CGRectMake(100, 190, 30, 30);
    CGContextAddRects(context, sRects, 4);
    CGContextDrawPath(context, kCGPathStroke);
CGContextAddRects
  • 绘制椭圆CGContextAddEllipseInRect
CGContextAddEllipseInRect(context, CGRectMake(100, 100, 300, 120));
    CGContextDrawPath(context, kCGPathStroke);

画一个内切于给定矩形的椭圆


CGContextAddEllipseInRect
  • 封闭路径 CGContextClosePath
    这个方法会把之前绘制的路径从开始点到结束点封闭起来
CGPoint sPoints[4];//坐标点
    sPoints[0] =CGPointMake(100, 220);//坐标1
    sPoints[1] =CGPointMake(130, 220);//坐标2
    sPoints[2] =CGPointMake(130, 160);//坐标3
    sPoints[3] =CGPointMake(230, 160);//坐标4
    CGContextAddLines(context, sPoints, 4);//添加线
    CGContextClosePath(context);//封闭路径
    CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径
CGContextClosePath
  • 重新开始路径CGContextBeginPath
    这个方法会重新开始一段路径的绘制,会重设各种属性,并且接下来需要从moveToPoint开始,并且CGContextClosePath也不会把之前的路径和CGContextBeginPath之后的路径连接起来,当需要绘制多个图形的时候可以使用.

3.填充路径

  • 清除上下文指定范围的内容CGContextClearRect
CGContextClearRect(context, CGRectMake(0, 0, 150, 300));

self.view.backgroundColor = UIColor.whiteColor;
    DrawView *view = [[DrawView alloc]initWithFrame:[UIScreen mainScreen].bounds];
    view.backgroundColor = [[UIColor alloc]initWithRed:0.0 green:1.0 blue:1.0 alpha:0.99];
    [self.view addSubview:view];

CGContextClearRect的效果取决于view是否不透明,这里透明度设置为.99,左上角被切除,露出下面的vc,如果透明度是1,则左上角会变成黑色.


清除左上角
黑色
  • 绘制 CGContextDrawPath
    前面一直在用
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode);

typedef CF_ENUM (int32_t, CGPathDrawingMode) {
  kCGPathFill,
  kCGPathEOFill,
  kCGPathStroke,
  kCGPathFillStroke,
  kCGPathEOFillStroke
};


CGContextAddEllipseInRect(context, CGRectMake(100, 100, 300, 120));
    CGContextAddEllipseInRect(context, CGRectMake(120, 120, 200, 80));
    CGContextAddEllipseInRect(context, CGRectMake(140, 140, 150, 50));
    CGContextDrawPath(context, kCGPathEOFill);

这个mode主要是两种策略,填充和描边
填充又有直接填充和奇偶规则两种
奇偶规则可以理解为,有一个向量横穿已经构建好的路径,遇到的第一个边为1,第二个边为2,那么1和2之间填充,再遇到3,2和3不填充,再遇到4,3和4填充


奇偶规则
  • 类似功能的api
    CGContextFillPath; CGContextEOFillPath; CGContextFillRect; CGContextFillRects; CGContextStrokePath;等一系列方法实现的效果和CGContextDrawPath+mode相同,不同的是这些方法在绘制之后会清除路径.

4.设置绘制的样式属性

  • CGContextSetLineWidth 设置线宽

  • CGContextSetLineCap 设置线末端样式
    typedef CF_ENUM(int32_t, CGLineCap) {
    kCGLineCapButt, //方形
    kCGLineCapRound, //圆弧
    kCGLineCapSquare //方形并且终点延伸线宽的一半距离
    };

  • CGContextSetLineJoin 连接点样式
    typedef CF_ENUM(int32_t, CGLineJoin) {
    kCGLineJoinMiter, //尖锐的
    kCGLineJoinRound, //方形的
    kCGLineJoinBevel //圆角
    };

  • CGContextSetLineDash 绘制虚线

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

CGFloat lengths[4] = {10.0,5.0,15.0,5.0};
CGContextSetLineDash(context, 0, lengths, 4);

phase表示第一段虚线从第几个点开始
lengths表示虚线的规则,比如{10,10}就是画10个点,跳过10个点
count是lengths的长度

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(context, 100, 100); //画笔移动到(100,100)
    CGContextAddLineToPoint(context, 200, 100); //向(200,100)画一条线
    CGContextAddLineToPoint(context, 150, 150);
    CGContextSetLineWidth(context, 4);
    CGContextSetLineJoin(context, kCGLineJoinRound);
    CGContextSetLineCap(context, kCGLineCapRound);
    CGFloat lengths[4] = {10.0,5.0,15.0,5.0};
    CGContextSetLineDash(context, 0, lengths, 4);
    CGContextDrawPath(context, kCGPathStroke);
各种样式
  • CGContextSetMiterLimit 用来更细致的处理kCGLineJoinMiter
  • CGContextSetAlpha 设置绘图透明度
  • CGContextSetFillColorWithColor 设置填充颜色,需要一个CGColor,另外还有CGContextSetRGBFillColor ,设置填充颜色,需要RGB值和alpha,同理CGContextSetRGBStrokeColor和CGContextSetStrokeColorWithColor
  • CGContextSetShadowWithColor 设置阴影,需要CGSize 和 CGColor

5.路径信息

  • CGContextGetPathCurrentPoint //获取current point
  • CGContextGetPathBoundingBox //获取包裹上下文路径的最小矩形
  • CGContextPathContainsPoint //判断一个点是否在绘制的路径内,需要提供一个mode,因为stoke和fill情况不一样

CGContextDrawPath调用之后,current point就没了,如果需要继续绘制,就需要设置新的current point

    CGContextMoveToPoint(context, 100, 200);
    CGContextAddLineToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 200, 100);
    CGContextClosePath(context);
    CGContextSetFillColorWithColor(context, UIColor.blackColor.CGColor);
    CGContextDrawPath(context, kCGPathFill);
    
    CGContextMoveToPoint(context, 200, 100);
    CGContextAddLineToPoint(context, 200, 200);
    CGContextAddLineToPoint(context, 100, 200);
    CGContextClosePath(context);
    CGContextSetFillColorWithColor(context, UIColor.whiteColor.CGColor);
    CGContextDrawPath(context, kCGPathFill);
两个三角形

6.绘制图片

  • CGContextDrawImage //需要一个CGRect和一个CGImageRef,这个方法绘制的图像会铺满贴合矩形边缘,产生形变

7.保存和重置上下文的设置
在绘制一个图形时,调用了一堆ContextSetxxx API之后,想要恢复context的设置,然后绘制下一个图形

  • CGContextSaveGState 保存上下文的状态
  • CGContextRestoreGState 恢复之前保存的上下文的状态
    save函数只能保存一个状态
    CGContextRef context = UIGraphicsGetCurrentContext();  //获取
    CGContextSaveGState(context);  //保存初始状态
    /* 进行各种设置 */
    /* CGContextDrawPath等绘图函数 */
    CGContextRestoreGState(context); //恢复初始
     /* 可以重新设置 */
     /* CGContextDrawPath等绘图函数 */

8.清除上下文

  • 这里说的其实是清除已经绘制的图形,清除其实和重绘是一个道理,都是调用setNeedsDisplay让uiview再走一遍drawRect方法,只不过需要清除的时候,drawRect里什么都不写,所以类似这么去清除.
- (void)drawRect:(CGRect)rect{
    if(!self.shouldClear){
        //绘图
    }
}

//清除
self.shouldClear = YES;
[self setNeedsDisplay];

9.内存管理
Core Graphics的内存管理是创建和加减引用计数,Create相关的函数,会创建,retain相关的函数增加引用,Release相关的方法会减计数,计数为0会被释放.

  • 创建函数,CGContextCreate,CGImageCreate,CGColorCreate等等
  • 减少引用计数 CGContextRelease等
  • 增加引用计数 CGContextRetain等
    也就是说本文使用的UIGraphicsGetCurrentContext并没有创建CGContextRef 对象,这个对象是layer创建并持有的,因此不需要去release.

相关文章

网友评论

      本文标题:Core Graphics 一: CGContext基本绘制

      本文链接:https://www.haomeiwen.com/subject/jrclyktx.html