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


    颜色和图案

    一个颜色就是一个CGColor(实际上是CGColorRef)。CGColor非常容易使用,而且也可以通过UIColor的 colorWithCGColor: 方法和 CGColor的相关方法来相互转换。

    一个图案其实就是一个CGPattern(实际上是CGPatternRef)。你可以创建一个图案并描边或者填充它。这个过程非常复杂,这里我把箭头变为一个红,蓝相接的三角形来说明,为了绘制这个图案,把下面这一行去掉:

    CGContextSetFillColorWithColor(con, [[UIColor redColor] CGColor]);
    

    在这一行的位置,写上如下代码:

    CGColorSpaceRef sp2 = CGColorSpaceCreatePattern(nil);
    CGContextSetFillColorSpace (con, sp2);
    CGColorSpaceRelease (sp2);
    CGPatternCallbacks callback = {
        0, drawStripes, nil
    };
    CGAffineTransform tr = CGAffineTransformIdentity;
    CGPatternRef patt = CGPatternCreate(nil,  CGRectMake(0,0,4,4), tr, 4, 4, kCGPatternTilingConstantSpacingMinimalDistortion, true, &callback);
    CGFloat alph = 1.0;
    CGContextSetFillPattern(con, patt, &alph);
    CGPatternRelease(patt);
    

    上面的代码有点啰嗦,但几乎可以算是一个完整的样板。为了理解这段代码,我们从后面读起。我们看到 CGContextSetFillPattern,设置一个填充图案,而不是一个填充颜色来填充一个路径(这里就是一个三角箭头)。第三个参数是一个指向CGFloat的指针,所以我们需要在之前设置这个CGFloat。第二个参数是一个CGPatternRef,所以我们需要在之前创建一个CGPatternRef(然后在之后释放它)。

    现在让我们来看看这个CGPatternCreate调用。一个图案是绘制在一个三角形的“单元”里;我们必须确定这个单元的大小(第二个参数),还有每个单元原点之间的距离(第四和第五个参数)。这样,这个单元就是4x4的,每个单元在垂直和水平方向上都紧靠一起。我们还需要提供一个转换来应用到单元上(第三个参数)。我们还提供一个平铺规则(第六个参数)。我们必须确定这是一个颜色图案,还是一个模版图案,如果是颜色图案,第七个参数就是true。我们还要提供一个指向回调函数的指针,这个函数就是实际上在单元里绘制图案(第八个参数)。

    对于第八个参数,为了让事情现在变得不那么复杂,我们实际上只是提供了一个指向CGPatternCallbacks 结构体的指针,这个结构体包含一个数字0,和两个函数的指针,一个会被调用来在它的单元里绘制图案,另一个会在这个图案释放的时候被调用。这里我们没有指定第二个参数,因为是用来内存管理的,这个简单的例子里不需要用到。

    往前一点,我们还需要给一个图案的色彩空间设置上下文的填充色彩空间。如果你忽略了这个,当你调用CGContextSetFillPattern.时会得到一个错误。所以我们创建一个色彩空间,设置它为上下文的填充色彩空间,然后释放它。

    但是我们还没有完成,因为我还没有向你展示实际绘制图案单元的方法!

    void drawStripes (void *info, CGContextRef con) {
        // assume 4 x 4 cell
        CGContextSetFillColorWithColor(con, [[UIColor redColor] CGColor]);
        CGContextFillRect(con, CGRectMake(0,0,4,4));
        CGContextSetFillColorWithColor(con, [[UIColor blueColor] CGColor]);
        CGContextFillRect(con, CGRectMake(0,0,4,2));
    }
    

    如你所见,实际的图案代码非常简单,唯一需要注意的地方就是绘制的大小必须和CGPatternCreate创建的图案时指定的单元大小一样,否则绘制出来的图案不是我们想要的。这里我们的单元大小是4x4,所以我们用红色填充它,然后用蓝色填充下半部。当这些单元在水平和垂直方向上紧挨着平铺后,就是我们看到的图案。

    最后,需要注意,我们应该用 CGContextSaveGState 和 CGContextRestoreGState.把我们的代码包起来。

    你可能注意到上图中的条纹并没有刚好适应三角形的箭头里,底部的一些条纹像是半个蓝色条纹。这是因为一个图案并不是绘制在你指定填充或者描边的形状里,而是整个图形上下文。我们可以通过CGContextSetPatternPhase 来在绘制之前移动这个图案的位置。

    对于一个这么简单的图案,我们也可以很简单地通过UIColor的 colorWithPatternImage:方法来获得一个UIImage:

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(4,4), NO, 0);
    drawStripes(nil, UIGraphicsGetCurrentContext());
    UIImage* stripes = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIColor* stripesPattern = [UIColor colorWithPatternImage:stripes];
    [stripesPattern setFill];
    UIBezierPath* p = [UIBezierPath bezierPath];
    [p moveToPoint:CGPointMake(80,25)];
    [p addLineToPoint:CGPointMake(100,0)];
    [p addLineToPoint:CGPointMake(120,25)];
    [p fill];
    

    图形上下文转换

    正如UIView可以有转换,图形上下文也可以有转换。但是,给一个图形上下文提供一个转换并不会影响已经绘制在上面的图像,它仅仅影响之后的绘制。一个图形上下文的转换,称为这个图形上下文的CTM,“current transformation matrix 当前的转换矩阵”。

    充分利用图形上下文的CTM,可以减少你的很多计算。你可以调用CGContextConcatCTM 用任意的CGAffineTransform 乘以当前的转换;还有很多方便的函数对当前的转换进行平移,拉伸和旋转。

    当你获得一个上下文时,一个基本的转换已经设置好了,使得系统能够把上下文的绘制坐标和屏幕的坐标对应起来。所有的转换都是应用到当前的转换,所有这个基本的转换仍然有效。通过用CGContextSaveGState 和 CGContextRestoreGState.包围起你的代码,你可以在最后恢复原始的基本转换。

    例如,我们的向上箭头,左上方的三角形位置是写死{80,0}。这样不好理解,为了能够更好地重用我们的代码,我们可以理解箭头原来的绘制坐标是{0,0},只是在x轴方向上平移了80.那么现在把箭头绘制在{80,0}的位置,可以使用转换:

    CGContextTranslateCTM(con, 80, 0);
    // now draw the arrow at (0,0)
    

    下面重复绘制不同旋转角度下的箭头。由于这个箭头需要绘制多次,我会把这个箭头包装成一个UIImage,这样可以大大减少重复工作,同时也让绘制更快

    - (UIImage*) arrowImage {
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(40,100), NO, 0.0);
        // obtain the current graphics context
        CGContextRef con = UIGraphicsGetCurrentContext();
        // draw the arrow into the image context
        // draw it at (0,0)! adjust all x-values by subtracting 80
        // ... actual code omitted ...
        UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return im;
    }
    

    我们生成了这个箭头的图像,把它保存在一个实例变量中,用self.arrow来访问:

    - (void)drawRect:(CGRect)rect {
        CGContextRef con = UIGraphicsGetCurrentContext();
        [self.arrow drawAtPoint:CGPointMake(0,0)];
        for (int i=0; i<3; i++) {
            CGContextTranslateCTM(con, 20, 100);
            CGContextRotateCTM(con, 30 * M_PI/180.0);
            CGContextTranslateCTM(con, -20, -100);
            [self.arrow drawAtPoint:CGPointMake(0,0)];
        }
    }
    

    转换也可以作为我们之前提到的使用CGContextDrawImage时产生的“反转”问题的一个解决方法。我们反转图形上下文,而不是绘画视图。你可以向下移动上下文的顶部,然后通过拉伸转换,反转y坐标方向:

    CGContextTranslateCTM(con, 0, theHeight);
    CGContextScaleCTM(con, 1.0, -1.0);
    

    至于你想顶部移动多少(theHeight)取决于你打算怎么绘制图像。

  • 相关阅读:
    linux查看CPU和内存信息
    linux yum命令详解
    查看文件中关键字前后几行的内容
    vue.js+web storm安装及第一个vue.js
    android GPS: code should explicitly check to see if permission is available
    ASP.NET MVC Identity 使用自己的SQL Server数据库
    阿里云服务器,tomcat启动,一直卡在At least one JAR was scanned for TLDs yet contained no TLDs就不动了
    ASP.NET MVC4 MVC 当前上下文中不存在名称“Scripts”
    python 将windows字体中的汉字生成图片的方法
    Java android DES+Base64加密解密
  • 原文地址:https://www.cnblogs.com/YungMing/p/4008217.html
Copyright © 2011-2022 走看看