zoukankan      html  css  js  c++  java
  • iOS 绘画学习(3)


    Graphics Context Settings (图形上下文设置)

    一个图形上下文状态有很多个图形上下文设置构成,这个状态决定了在这个时刻绘画的行为和外观。下面列举了Core Graphics 函数,同时跟着对应的UIKit 提供的封装好的方便的方法:

    • 线的粗细和虚线样式

      CGContextSetLineWidth, CGContextSetLineDash (and UIBezierPath lineWidth, setLineDash:count:phase:)

    • 线末端样式和连接样式

      CGContextSetLineCap, CGContextSetLineJoin, CGContextSetMiterLimit (and UIBezierPath lineCapStyle, lineJoinStyle, miterLimit)

    • 线颜色和模式

      CGContextSetRGBStrokeColor, CGContextSetGrayStrokeColor, CGContextSet- StrokeColorWithColor, CGContextSetStrokePattern (and UIColor setStroke)

    • 填充颜色和模式

      CGContextSetRGBFillColor, CGContextSetGrayFillColor, CGContextSetFill- ColorWithColor, CGContextSetFillPattern (and UIColor setFill)

    • 阴影

      CGContextSetShadow, CGContextSetShadowWithColor

    • 整体透明度和组合

      CGContextSetAlpha, CGContextSetBlendMode

    • 反锯齿

      CGContextSetShouldAntialias

    • 其它额外的设置

      • 裁剪区域

        在裁剪区域外进行绘制,其实不会真的被绘制

      • 转换(或者“CTM”,当前转换矩阵)

        可以改变你指定的点在随后的绘制命令中,如何对应到画布的实际空间上。


    Paths and Shapes (路径和形状)

    下面是一些可能会用到的路径绘制命令:

    • 当前点的位置

      CGContextMoveToPoint

    • 描摹一条线

      CGContextAddLineToPoint, CGContextAddLines

    • 描摹一个矩形

      CGContextAddRect, CGContextAddRects

    • 描摹一个椭圆或者圆形

      CGContextAddEllipseInRect

    • 描摹一个圆弧

      CGContextAddArcToPoint, CGContextAddArc

    • 描摹有一个或者两个控制点的贝塞尔曲线

      CGContextAddQuadCurveToPoint, CGContextAddCurveToPoint

    • 关闭当前路径

      CGContextClosePath 这个会在路径的最后一个点和第一个点之间添加一条线,如果你是打算要填充这个路径,那么不需要调用这个方法,填充操作会自动帮我们完成。

    • 描边或者填充当前路径

      CGContextStrokePath, CGContextFillPath, CGContextEOFillPath, CGContext- DrawPath. 描边或者填充操作都会清除这个路径。如果你想既描边又填充路径,可以使用CGContextDrawPath。 如果你仅仅先通过 CGContextStrokePath 描边,你就不可能再填充这个路径了,因为这个路径已经被清除了。

      当然还有很多其它更方便的函数来创建一个路径,然后描边或者填充:CGContextStrokeLineSegments, CGContextStrokeRect, CGContextStrokeRectWithWidth, CGContextFillRect, CGContextFillRects, CGContextStrokeEllipseInRect, CGContextFillEllipseInRect.

    一个路径可以是混合而成的,意味着这个路径由多个独立的块组成。例如,一个路径可以包括两个分开的闭合的形状:一个矩形和一个圆形。 当你在构造一个路径的某个时刻(也就是在描摹出一个路径之后,同时没有描边,填充来清除路径,或者调用 CGContextBeginPath)调用 CGContextMoveToPoint,你提起画笔,然后移动到一个新的位置,接着准备开始一个独立的一块同样的路径。如果你担心之前绘制的路径会被清除,可以调用CGContextBeginPath 来指定你开始一段新的不同的路径,在苹果的很多例子中都会这么做,但是我在实际中通常发现不需要。

    为了说明上面路径绘制的命令,我会生成一个向上的箭头,如下图所示。这可能不是创建一个箭头的最好方式,同时我也会故意不使用一个方便的函数,但是过程是清晰的:

    // obtain the current graphics context
    CGContextRef con = UIGraphicsGetCurrentContext();
    // draw a black (by default) vertical line, the shaft of the arrow
    CGContextMoveToPoint(con, 100, 100);
    CGContextAddLineToPoint(con, 100, 19);
    CGContextSetLineWidth(con, 20);
    CGContextStrokePath(con);
    // draw a red triangle, the point of the arrow
    CGContextSetFillColorWithColor(con, [[UIColor redColor] CGColor]);
    CGContextMoveToPoint(con, 80, 25);
    CGContextAddLineToPoint(con, 100, 0);
    CGContextAddLineToPoint(con, 120, 25);
    
    CGContextFillPath(con);
    // snip a triangle out of the shaft by drawing in Clear blend mode
    CGContextMoveToPoint(con, 90, 101);
    CGContextAddLineToPoint(con, 100, 90);
    CGContextAddLineToPoint(con, 110, 101);
    CGContextSetBlendMode(con, kCGBlendModeClear);
    CGContextFillPath(con);
    

    如果你想复用或者分享一个路径,可以把这个路径包装成 CGPath,实际上就是CGPathRef。你也可以创建一个新的CGMutablePathRef ,像CGContext 中的路径构造方法一样,使用不同的CGPath 方法来构造路径,或者你可以使用CGContextCopyPath 来复制图形上下文中当前的路径。还有一些CGPath方法用来基于简单的几何图形来创建一个路径(CGPathCreateWithRect, CGPathCreateWithEllipseInRect)或者基于一个已存在的路径(CGPathCreateCopyByStrokingPath, CGPathCreateCopyByDashing- Path, CGPathCreateCopyByTransformingPath)。

    UIKit 中的UIBezierPath封装了CGPath,同样也提供了一些路径构造方法,例如 moveToPoint:, addLineToPoint:, bezierPathWithRect:, bezierPathWithOvalInRect:, addArcWithCenter:radius:startAngle:endAngle:clockwise:, addQuadCurveToPoint:controlPoint:, addCurveToPoint:controlPoint1:controlPoint2:, closePath。同样,UIBezierPath也提供了一个非常有用的方法:bezierPathWithRoundedRect:cornerRadius: —— 仅仅使用Core Graphics 方法来绘制圆角矩形是非常繁琐的。当你调用UIBezierPath 实例方法 fill 或者 stroke (或者 fillWithBlendMode:alpha: 或者 strokeWithBlend- Mode:alpha:)时,当前图形上下文状态会被保存,被封装好的CGPath 路径会变成当前上下文的绘制路径,然后描边或者填充该路径,最后当前上下文会被恢复原来状态。

    下面我们使用UIKit 提供的UIBezierPath 来重写上面的箭头:

    UIBezierPath* p = [UIBezierPath bezierPath];
    [p moveToPoint:CGPointMake(100,100)];
    [p addLineToPoint:CGPointMake(100, 19)];
    [p setLineWidth:20];
    [p stroke];
    [[UIColor redColor] set];
    [p removeAllPoints];
    [p moveToPoint:CGPointMake(80,25)];
    [p addLineToPoint:CGPointMake(100, 0)];
    [p addLineToPoint:CGPointMake(120, 25)];
    [p fill];
    [p removeAllPoints];
    [p moveToPoint:CGPointMake(90,101)];
    [p addLineToPoint:CGPointMake(100, 90)];
    [p addLineToPoint:CGPointMake(110, 101)];
    [p fillWithBlendMode:kCGBlendModeClear alpha:1.0];
    

    Clipping (裁剪)

    路径的另一种使用方式是遮罩某一块区域,防止在未来的绘制中被改变。这种就是裁剪。默认,一个图形上下文的裁剪区域就是整一个图形上下文,你可以在上下文中任意绘制。

    裁剪区域是整个上下文的一个功能,任何新的裁剪区域会被应用到现有的裁剪区域,相交而成。如果你应用了自己的裁剪区域,在后面想移除这个裁剪区域的办法就只能把你的处理包含在 CGContextSaveGState和CGContextRestoreGState方法之间。

    为了说明这一点,我将使用裁剪重写生成我们原来的箭头符号,而不是使用混合模式在箭头尾部“切出”三角缺口。这样会有些棘手,因为我们想要的不是裁剪三角形内部的区域,而是外面的区域。为了解析这个,我们使用包含超过一个闭合区域的混合路径 —— 一个三角形,以及整个绘制区域(我们可以使用CGContextGetClipBoundingBox 获取)

    当填充一个混合路径时,或者使用路径来表示一个裁剪区域时,系统遵循两个准则:

    • 非零环绕数规则(Winding rule)

      环绕(winding)就是一个路径环绕的方向,分顺时针(正方向)和逆时针(负方向)。在图形学中判断一个点是否在多边形内,若多边形不是自相交的,那么可以简单的判断这个点在多边形内部还是外部;若多边形是自相交的,那么就需要根据非零环绕数规则和奇-偶规则判断。这里推荐一篇文章:http://blog.csdn.net/freshforiphone/article/details/8273023。

    • 奇-偶规则(Odd-even Rule)

      从任意位置p作一条射线,若与该射线相交的多边形边的数目为奇数,则p是多边形内部点,否则是外部点。

    这里我们使用简单的奇-偶规则,所以我们使用CGContextEOClip 来设定裁剪区域,然后绘制箭头:

    // obtain the current graphics context
    CGContextRef con = UIGraphicsGetCurrentContext();
    // punch triangular hole in context clipping region
    CGContextMoveToPoint(con, 90, 100);
    CGContextAddLineToPoint(con, 100, 90);
    CGContextAddLineToPoint(con, 110, 100);
    CGContextClosePath(con);
    CGContextAddRect(con, CGContextGetClipBoundingBox(con));
    CGContextEOClip(con);
    // draw the vertical line
    CGContextMoveToPoint(con, 100, 100);
    CGContextAddLineToPoint(con, 100, 19);
    CGContextSetLineWidth(con, 20);
    CGContextStrokePath(con);
    // draw the red triangle, the point of the arrow
    CGContextSetFillColorWithColor(con, [[UIColor redColor] CGColor]);
    CGContextMoveToPoint(con, 80, 25);
    CGContextAddLineToPoint(con, 100, 0);
    CGContextAddLineToPoint(con, 120, 25);
    CGContextFillPath(con);
    

    UIBezierPath 的裁剪命令是 usesEvenOddFillRule 和 addClip。


    Gradients (渐变)

    渐变可以很简单,也可以很负责,这里只讲解简单的。一个简单的渐变是由一个起始点的颜色和一个结束点的颜色,再加上(可选的)一个中间点的颜色值决定,然后就会在上下文中在两个指定的点以线性或者反射形式绘制。

    你不能使用渐变来作为路径的填充颜色,但是你可以使用裁剪来规定一个路径形状的渐变。为了说明这一点,我会重画我们的箭头,使用一个线性渐变:

    // obtain the current graphics context
    CGContextRef con = UIGraphicsGetCurrentContext();
    CGContextSaveGState(con);
    // punch triangular hole in context clipping region
    CGContextMoveToPoint(con, 90, 100);
    CGContextAddLineToPoint(con, 100, 90);
    CGContextAddLineToPoint(con, 110, 100);
    CGContextClosePath(con);
    CGContextAddRect(con, CGContextGetClipBoundingBox(con));
    CGContextEOClip(con);
    // draw the vertical line, add its shape to the clipping region
    CGContextMoveToPoint(con, 100, 100);
    CGContextAddLineToPoint(con, 100, 19);
    CGContextSetLineWidth(con, 20);
    CGContextReplacePathWithStrokedPath(con);
    CGContextClip(con);
    // draw the gradient
    CGFloat locs[3] = { 0.0, 0.5, 1.0 };
    CGFloat colors[12] = {
        0.3,0.3,0.3,0.8, // starting color, transparent gray
        0.0,0.0,0.0,1.0, // intermediate color, black
        0.3,0.3,0.3,0.8 // ending color, transparent gray
    };
    CGColorSpaceRef sp = CGColorSpaceCreateDeviceGray();
    CGGradientRef grad =
        CGGradientCreateWithColorComponents (sp, colors, locs, 3);
    CGContextDrawLinearGradient (
        con, grad, CGPointMake(89,0), CGPointMake(111,0), 0);
    CGColorSpaceRelease(sp);
    CGGradientRelease(grad);
    CGContextRestoreGState(con); // done clipping
    // draw the red triangle, the point of the arrow
    CGContextSetFillColorWithColor(con, [[UIColor redColor] CGColor]);
    CGContextMoveToPoint(con, 80, 25);
    CGContextAddLineToPoint(con, 100, 0);
    CGContextAddLineToPoint(con, 120, 25);
    CGContextFillPath(con);
    

    调用CGContextReplacePathWithStrokedPath 这个方法来防止描边当前的路径。

  • 相关阅读:
    20145223《信息安全系统设计基础》第7周学习总结
    20145223《信息安全系统设计基础》第6周学习总结
    20145223《信息安全系统设计基础》第5周学习总结
    20145223《信息安全系统设计基础》第3周学习总结
    20145223《信息安全系统设计基础》第2周学习总结
    20145223《信息安全系统设计基础》第1周学习总结
    node小爬虫
    node知识
    css之单行缩略..以及多行缩略
    html两大布局
  • 原文地址:https://www.cnblogs.com/YungMing/p/4008215.html
Copyright © 2011-2022 走看看