美文网首页
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