平时生活和工作中的iOS其他一丢丢iOS之绘图动画

Quartz2D 编程指南(二)变换、图案、阴影

2016-05-13  本文已影响1096人  xuyafei86
  1. 概览
  2. 图形上下文
  3. 路径
  4. 颜色与颜色空间
  5. 变换
  6. 图案
  7. 阴影
  8. 渐变
  9. 透明层
  10. Quartz 2D 中的数据管理
  11. 位图与图像遮罩
  12. CoreGraphics 绘制 Layer

5.变换

简介

Quartz变换函数

使用变换函数修改CTM

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextTranslateCTM(context, 100, 200);//#1
CGContextRotateCTM(context, M_PI_4);

CGAffineTransform affineTransform = CGAffineTransformMakeScale(0.5, 0.8);//#2
CGContextConcatCTM(context, affineTransform);

CGContextDrawImage (context, CGRectMake(0, 0, 100, 100), [UIImage imageNamed:@"image"].CGImage);

创建仿射变换

CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(100, 200);
CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(M_PI_4);
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(0.5, 0.8);
CGContextConcatCTM(context, translateTransform);
CGContextConcatCTM(context, rotateTransform);
CGContextConcatCTM(context, scaleTransform);
CGAffineTransform translateTransform = CGAffineTransformTranslate(CGContextGetCTM(context), 100, 200);
CGAffineTransform rotateTransform = CGAffineTransformRotate(translateTransform, M_PI_4);
CGAffineTransform scaleTransform = CGAffineTransformScale(rotateTransform, 0.5, 0.8);
CGContextConcatCTM(context, scaleTransform);
CGAffineTransform invertTransform = CGAffineTransformInvert(scaleTransform);
CGContextConcatCTM(context, invertTransform);
CGPoint applyPoint = CGPointApplyAffineTransform(CGPointMake(50, 50), translateTransform);
CGSize applySize = CGSizeApplyAffineTransform(CGSizeMake(100, 100), translateTransform);
CGRect applyRect = CGRectApplyAffineTransform(CGRectMake(0, 0, 100, 100), translateTransform);

判断仿射变换

CGAffineTransformIsIdentity(invertTransform);
CGAffineTransformEqualToTransform(invertTransform, CGAffineTransformIdentity);

获取用户空间到设备空间的变换

CGAffineTransform userToDeviceTransform = CGContextGetUserSpaceToDeviceSpaceTransform(context);
CGPoint deviceSpacePoint = CGContextConvertPointToDeviceSpace(context, CGPointMake(100, 200));
CGPoint userSpacePoint = CGContextConvertPointToUserSpace(context, deviceSpacePoint);
CGSize deviceSpaceSize = CGContextConvertSizeToDeviceSpace(context, CGSizeMake(100, 100));
CGSize userSpaceSize = CGContextConvertSizeToUserSpace(context, deviceSpaceSize);
CGRect deviceSpaceRect = CGContextConvertRectToDeviceSpace(context, CGRectMake(100, 200, 100, 100));
CGRect userSpaceRect = CGContextConvertRectToUserSpace(context, deviceSpaceRect);

图案

简介

图案骨架(Anatomy)

着色图案和模板图案

平铺

  1. 无失真平铺: 以细微调整图案单元之间的间距为代价,但通常不超过一个设备像素。

  2. 最小失真恒定间距平铺:设定图案单元之间的间距,以细微调整单元大小为代价,但通常不超过一个设备像素。

  3. 恒定间距平铺:设定图案单元之间的间距,以调整图案单元大小为代价,以求尽快的平铺。

图案绘制原理

  1. 保存图形状态
  2. 将当前转换矩阵应用到原始的图案单元上
  3. 连接 CTM 与图案变换矩阵
  4. 裁剪图案单元的边界矩形
  5. 调用绘制回调函数来绘制图案单元
  6. 恢复图形状态

绘制着色图案

  1. 写一个绘制着色图案单元格的回调函数
  2. 设置着色图案的颜色空间
  3. 设置着色图案的骨架(Anatomy)
  4. 指定着色图案作为填充或描边模式
  5. 使用着色图案绘制

编写绘制着色图案单元格的回调函数

void MyDrawColoredPattern (void *info, CGContextRef myContext) {
    CGFloat subunit = 5;
    
    CGSize size = {subunit, subunit};
    CGPoint point1 = {0,0}, point2 = {subunit, subunit}, point3 = {0,subunit}, point4 = {subunit,0};
    CGRect myRect1 = {point1, size}, myRect2 = {point2, size}, myRect3 = {point3, size}, myRect4 = {point4, size};
    
    CGContextSetRGBFillColor(myContext, 0, 0, 1, 0.5);
    CGContextFillRect(myContext, myRect1);
    CGContextSetRGBFillColor(myContext, 1, 0, 0, 0.5);
    CGContextFillRect(myContext, myRect2);
    CGContextSetRGBFillColor(myContext, 0, 1, 0, 0.5);
    CGContextFillRect(myContext, myRect3);
    CGContextSetRGBFillColor(myContext, .5, 0, .5, 0.5);
    CGContextFillRect(myContext, myRect4);
}

设置着色图案的颜色空间

CGColorSpaceRef patternSpace;
patternSpace = CGColorSpaceCreatePattern(NULL); // 创建模式颜色空间,并传递NULL作为参数
CGContextSetFillColorSpace(myContext, patternSpace); // 在模式颜色空间中设置填充颜色
CGColorSpaceRelease(patternSpace);

设置着色图案的骨架(Anatomy)

CGPatternRef CGPatternCreate(void *info,
                             CGRect bounds,
                             CGAffineTransform matrix,
                             CGFloat xStep,
                             CGFloat yStep,
                             CGPatternTiling tiling,
                             bool isColored,
                             const CGPatternCallbacks *callbacks);
  1. info:是一个指针,指向我们要传递给绘制回调函数的数据
  2. bound:指定图案单元的大小
  3. matrix:指定图案的变换矩阵,它将图案空间坐标系统映射到图形上下文的默认坐标系统。如果希望两个坐标系统是一样的,则可以使用单位矩阵。
  4. xStep, yStep:指定单元之间的水平和竖直间距。
  5. tiling:平铺模式,可以是kCGPatternTilingNoDistortion、kCGPatternTilingConstantSpacingMinimalDistortion、kCGPatternTilingConstantSpacing
  6. isColored:指定图案单元是着色图案(true)还是模板图案(false)
  7. callbacks:是一个指向 CGPatternCallbacks 结构体的指针,定义如下。
struct CGPatternCallbacks {
    unsigned int version;
    CGPatternDrawPatternCallback __nullable drawPattern;
    CGPatternReleaseInfoCallback __nullable releaseInfo;
};
typedef struct CGPatternCallbacks CGPatternCallbacks;

7.1 version 为结构体的版本号,可以设置为 0。
7.2 drawPattern 指向绘制回调的指针。
7.3 releaseInfo 指向一个回调函数,该回调在释放 CGPattern 对象时被调用,以释放存储在我们传递给绘制回调的 info 参数中的数据。如果在这个参数中没有传递任何数据,则设置该域为 NULL。

指定着色图案作为填充或描边图案

使用着色图案绘制

完整示例

#define H_PATTERN_SIZE 10
#define V_PATTERN_SIZE 10
#define H_PSIZE H_PATTERN_SIZE + 5
#define V_PSIZE H_PATTERN_SIZE + 10

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    CGContextRef context = UIGraphicsGetCurrentContext();
    MyColoredPatternPainting(context, self.bounds);
}

void MyDrawColoredPattern(void *info, CGContextRef myContext) {
    CGFloat subunit = 5;
    
    CGSize size = {subunit, subunit};
    CGPoint point1 = {0,0}, point2 = {subunit, subunit}, point3 = {0,subunit}, point4 = {subunit,0};
    CGRect myRect1 = {point1, size}, myRect2 = {point2, size}, myRect3 = {point3, size}, myRect4 = {point4, size};
    
    CGContextSetRGBFillColor (myContext, 0, 0, 1, 0.5);
    CGContextFillRect (myContext, myRect1);
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 0.5);
    CGContextFillRect (myContext, myRect2);
    CGContextSetRGBFillColor (myContext, 0, 1, 0, 0.5);
    CGContextFillRect (myContext, myRect3);
    CGContextSetRGBFillColor (myContext, .5, 0, .5, 0.5);
    CGContextFillRect (myContext, myRect4);
}

void MyColoredPatternPainting(CGContextRef myContext, CGRect rect) {
    CGPatternRef pattern;
    CGColorSpaceRef patternSpace;
    CGFloat alpha = 1;
    static const CGPatternCallbacks callbacks = {0, &MyDrawColoredPattern, NULL};
    
    CGContextSaveGState(myContext);
    patternSpace = CGColorSpaceCreatePattern(NULL);
    CGContextSetFillColorSpace(myContext, patternSpace);
    CGColorSpaceRelease(patternSpace);
    
    pattern = CGPatternCreate(NULL,
                              CGRectMake (0, 0, H_PATTERN_SIZE, V_PATTERN_SIZE),
                              CGAffineTransformMake(1, 0, 0, 1, 0, 0),
                              H_PSIZE,
                              V_PSIZE,
                              kCGPatternTilingConstantSpacing,
                              true,
                              &callbacks);
    
    CGContextSetFillPattern(myContext, pattern, &alpha);
    CGPatternRelease(pattern);
    CGContextFillRect(myContext, rect);
    CGContextRestoreGState(myContext);
}
- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextAddEllipseInRect(context, self.bounds);
    
    CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
    CGContextSetFillColorSpace(context, patternSpace);
    CGColorSpaceRelease(patternSpace);
    
    static const CGPatternCallbacks callBack = {0, &MyDrawColoredPattern, NULL};
    CGPatternRef pattern = CGPatternCreate(NULL, CGRectMake(0, 0, 16, 16), CGAffineTransformIdentity, 12, 12, kCGPatternTilingNoDistortion, true, &callBack);
    CGFloat alpha = 1.0;
    CGContextSetFillPattern(context, pattern, &alpha);
    CGPatternRelease(pattern);
    
    CGContextDrawPath(context, kCGPathFill);
}

绘制模板图案

编写绘制模板图案单元的回调函数

#define PSIZE 16

void MyDrawStencilStar(CGContextRef myContext) {
    int k;
    double r, theta;
    
    r = 0.8 * PSIZE / 2;
    theta = 2 * M_PI * (2.0 / 5.0);
    
    CGContextTranslateCTM(myContext, PSIZE/2, PSIZE/2);
    
    CGContextMoveToPoint(myContext, 0, r);
    for (k = 1; k < 5; k++) {
        CGContextAddLineToPoint(myContext,
                                r * sin(k * theta),
                                r * cos(k * theta));
    }
    CGContextClosePath(myContext);
    CGContextFillPath(myContext);
}

设置模板图案的颜色空间

CGPatternRef pattern;
CGColorSpaceRef baseSpace;
CGColorSpaceRef patternSpace;
 
baseSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
patternSpace = CGColorSpaceCreatePattern (baseSpace); //创建一个模式颜色空间。该颜色空间指定如何表示模式的颜色。后面要设置模式的颜色时,必须使用这个颜色空间来进行设置
CGContextSetFillColorSpace (myContext, patternSpace); // 设置颜色空间来在填充模式时使用

CGColorSpaceRelease(patternSpace);
CGColorSpaceRelease(baseSpace);

设置模板图案的骨架(Anatomy)

指定模板图案作为填充或描边图案

static const CGFloat color[4] = {0, 1, 1, 0.5};transparent
CGContextSetFillPattern (context, pattern, color);

使用模板图案绘制

完整示例

#define PSIZE 16

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    CGContextRef context = UIGraphicsGetCurrentContext();
    MyStencilPatternPainting(context);
}

void MyDrawStencilStar(void *info, CGContextRef myContext) {
    int k;
    double r, theta;
    
    r = 0.8 * PSIZE / 2;
    theta = 2 * M_PI * (2.0 / 5.0);
    
    CGContextTranslateCTM(myContext, PSIZE/2, PSIZE/2);
    
    CGContextMoveToPoint(myContext, 0, r);
    for (k = 1; k < 5; k++) {
        CGContextAddLineToPoint(myContext,
                                r * sin(k * theta),
                                r * cos(k * theta));
    }
    CGContextClosePath(myContext);
    CGContextFillPath(myContext);
}

void MyStencilPatternPainting(CGContextRef myContext) {
    CGPatternRef pattern;
    CGColorSpaceRef baseSpace;
    CGColorSpaceRef patternSpace;
    static const CGFloat color[4] = { 0, 1, 0, 1 };
    static const CGPatternCallbacks callbacks = {0, &MyDrawStencilStar, NULL};
    
    baseSpace = CGColorSpaceCreateDeviceRGB();
    patternSpace = CGColorSpaceCreatePattern(baseSpace);
    CGContextSetFillColorSpace(myContext, patternSpace);
    CGColorSpaceRelease(patternSpace);
    CGColorSpaceRelease(baseSpace);
    
    pattern = CGPatternCreate(NULL, CGRectMake(0, 0, PSIZE, PSIZE),
                              CGAffineTransformIdentity, PSIZE, PSIZE,
                              kCGPatternTilingConstantSpacing,
                              false, &callbacks);
    
    CGContextSetFillPattern (myContext, pattern, color);
    CGPatternRelease(pattern);
    CGContextFillRect(myContext,CGRectMake (0,0,PSIZE*20,PSIZE*20));
}

阴影

简介

阴影绘制原理

CGContextSetShadow(context, CGSizeMake(5, 5), 5);
CGContextSetShadowWithColor(context, CGSizeMake(5, 5), 5, [UIColor redColor].CGColor);

基于图形上下文的阴影绘制约定

绘制阴影

  1. 保存图形状态
  2. 调用函数 CGContextSetShadow,传递相应的值
  3. 使用阴影绘制所有的对象
  4. 恢复图形状态
  1. 保存图形状态
  2. 创建一个 CGColorSpace 对象,确保 Quartz 能正确地解析阴影颜色
  3. 创建一个 CGColor 对象来指定阴影的颜色
  4. 调用 CGContextSetShadowWithColor,并传递相应的值
  5. 使用阴影绘制所有的对象
  6. 恢复图形状态
- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    CGContextRef context = UIGraphicsGetCurrentContext();
    MyDrawWithShadows(context, 300, 300);
}

void MyDrawWithShadows(CGContextRef myContext, float wd, float ht) {
    CGSize myShadowOffset = CGSizeMake(-15, 20);
    CGFloat myColorValues[] = {1, 0, 0, .6};
    CGColorRef myColor;
    CGColorSpaceRef myColorSpace;
    
    CGContextSaveGState(myContext);
    
    CGContextSetShadow(myContext, myShadowOffset, 5);
    CGContextSetRGBFillColor(myContext, 0, 1, 0, 1);
    CGContextFillRect(myContext, CGRectMake (wd/3 + 75, ht/2 , wd/4, ht/4));
    
    myColorSpace = CGColorSpaceCreateDeviceRGB();
    myColor = CGColorCreate(myColorSpace, myColorValues);
    CGContextSetShadowWithColor(myContext, myShadowOffset, 5, myColor);
    CGContextSetRGBFillColor(myContext, 0, 0, 1, 1);
    CGContextFillRect(myContext, CGRectMake(wd/3 - 75, ht/2 - 100, wd/4, ht/4));
    
    CGColorRelease(myColor);
    CGColorSpaceRelease(myColorSpace);
    
    CGContextRestoreGState(myContext);
}

博客:xuyafei.cn
简书:jianshu.com/users/2555924d8c6e
微博:weibo.com/xuyafei86
Github:github.com/xiaofei86

参考资料

上一篇 下一篇

猜你喜欢

热点阅读