CGPatternRef 学习

看了好多天CoreAnimation文档,CALayer可以设置background是CGColor。而CGColor可以通过CGPatternRef获取,这样就可以让背景颜色多姿多彩了,但是当时只是看了下这块,没有详细探讨。今天想把这块详细探讨下。为以后再看相关资料就一幕了然。

api 初始化

CG_EXTERN CGPatternRef __nullable CGPatternCreate(void * __nullable info,
    CGRect bounds, CGAffineTransform matrix, CGFloat xStep, CGFloat yStep,
    CGPatternTiling tiling, bool isColored,
    const CGPatternCallbacks * cg_nullable callbacks)

/*填充模式
     info://传递给callback的参数
     bounds:需要铺瓷砖大小
     matrix:形变
     xStep:瓷砖横向间距
     yStep:瓷砖纵向间距
     tiling:贴砖的方法(瓷砖摆放的方式)
     isClored:绘制的瓷砖是否已经指定了颜色(对于无颜色瓷砖此处指定位false)
     callbacks:回调函数
     */

模型图


基本使用

#define H_PATTERN_SIZE 16
#define V_PATTERN_SIZE 18

void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    CGFloat subunit = 5;
    CGRect  myRect1 = {{0,0}, {subunit, subunit}},
    myRect2 = {{subunit, subunit}, {subunit, subunit}},
    myRect3 = {{0,subunit}, {subunit, subunit}},
    myRect4 = {{subunit,0}, {subunit, subunit}};
    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)drawRect:(CGRect)rect {
 CGContextRef myContext =    UIGraphicsGetCurrentContext();
    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_PATTERN_SIZE,
                               V_PATTERN_SIZE,
                               kCGPatternTilingConstantSpacing,
                               true,
                               &callbacks);
    CGContextSetFillPattern (myContext, pattern, &alpha);
    CGPatternRelease (pattern);
    CGContextFillRect (myContext, rect);
    CGContextRestoreGState (myContext);
}

概述

patterns是重复绘制到图形上下文的一系列绘图操作。你可以把他当做颜色的一种吧。当我们是使用patterns绘制时,Quartz将页面划分为一组pattern cell,每个cell具有图案图像的大小,并使用您提供的回调绘制每个cell。

cell 就是上图的红色的方块

模式剖析

cell是pattern的基本组件。上图的cell是下图所示。(黑色矩形不是图案的一部分,他是用来显示一个cell的bound的

这个cell的大小包含四个彩色矩形就面积和矩形上方和右侧的空间。若果将cell排列开来就是下图的样子。


我们可以指定quaztz在水平或者垂直方向上将每个cell起点和下一个图案cell的起点相距多远。下图就是图例


cell之间有空隙

这里需要注意,要是我们设置间距太短,会导致pattern 的cell重叠

当我们绘制cell的时候,quzrtz使用pattern的空间作为坐标系,pattern的space是一个抽象空间。他通过创建我们指定的transformation matrix 映射到默认的用户空间。

pattern空间与用户空间是分开的。不管当transformation matrix的状态如何,untransformed pattern space映射到基本用户空间(未转换的untransform)。当将transformation应用于pattern 的spcace时,quzartz仅将transformation应用于pattern 的space。这里就是说要是我们给pattern设置了transformation,只会影响pattern的转换,不会对其他的颜色空间产生影响。

pattern的坐标系模式和graphics context(图形上下文)一样的。默认情况下,Quartz使用坐标系,其中正x值表示向右的位移,正y值表示向上的位移。然而,UIKit创建的图形上下文使用不同的约定,其中正y值指示向下位移。通常,两个坐标系之间转换只是通过将transform来改变就可以了,但在这种情况下,Quartz还修改pattern space 来与之匹配。

如果不想用quartz来pattern cell,可以指定标准矩阵。但是,可以通过提供转换矩阵来实现有趣的效果。
下面可以实现的效果

  CGAffineTransform transform =  CGAffineTransformIdentity;
    transform = CGAffineTransformScale(transform, 1.0, 5.0);
    pattern = CGPatternCreate (NULL,
                               CGRectMake (0, 0, H_PATTERN_SIZE, V_PATTERN_SIZE),
                               transform,
                               H_PATTERN_SIZE,
                               V_PATTERN_SIZE,
                               kCGPatternTilingConstantSpacing,
                               false,
                               &callbacks);
  CGAffineTransform transform =  CGAffineTransformIdentity;
    transform = CGAffineTransformScale(transform, 1.0, 5.0);
    transform = CGAffineTransformRotate(transform, M_PI/4);
    pattern = CGPatternCreate (NULL,
                               CGRectMake (0, 0, H_PATTERN_SIZE, V_PATTERN_SIZE),
                               transform,
                               H_PATTERN_SIZE,
                               V_PATTERN_SIZE,
                               kCGPatternTilingConstantSpacing,
                               false,
                               &callbacks);

Colored Patterns 和 Stencil (Uncolored) Patterns

Colored Patterns 自身带有颜色。colored pattern通过改变cell的着色达到pattern颜色的改变,这样就失去了该模式的意义了。一个Scottish tartan 格子是colored patterns的一个例子。colored Pattern中的颜色只是pattern cell创建的一部分,而不是图案绘制过程中的一部分。


Stencil (Uncolored) Patterns 仅根据形状来定义,因此可以认为是模板图案、没有着色的图案或者是layer的 mask。下图展示的是相同cell的重复排列。cell本身仅是一个形状,一个填充的五角星。单pattern cell被定义时,没有任何颜色相关。颜色被指定为parttern绘制过程的一部分,而不是patterncell 创建的一部分。


我们可以通过Quartz 2D 创建上述的两种模式pattern。

Tiling (拼接)

Tiling 是将pattern cell绘制到页面的过程中的一部分。当quartz 渲染一个pattern到设备时,quartz可能需要调整pattern来适配设备空间。也就是说,当呈现给设备的时候,作为被定义在用user space的pattern cell可能不匹配设备像素,因为user space和设备的像素之间可能有差异。

因此,quartz 提供三种选项,在必要的时候来调整pattern。

  • pattern 通过调整pattern cell之间的间距为代价来适配pattern。这种无失真。kCGPatternTilingNoDistortion
  • cell之间以稍微扭曲模式来适配pattern。这种模式恒定间距的最小失真。kCGPatternTilingConstantSpacingMinimalDistortion
  • cell之间的间距以牺牲pattern cell的扭曲成都来获取快速平铺。这被称为恒定间距。kCGPatternTilingConstantSpacing

如何使用pattern

pattern类似简单的颜色,我们需要设置一个fill或者stroke pattern,然后调用绘制函数。Quartz用设置的pattern进行绘制。例如,绘制纯色的fill 矩形,首先调用一个函数,比如CGContextSetFill,来色值fill颜色,然后调用函数CGContextFillRect 将我们指定的pattern颜色进行绘制。若用pattern 绘制,必须调用函数CGContextSetFillPattern函数来设置pattern。然后调用CGContextFillRect函数用我们指定的pattern进行填充fill 矩形。用color和pattern绘制的不同点在于我们必须定义pattern。我们需要提供pattern或者color信息给函数CGContextSetFillPattern。下面我们会看到在Colored Patterns 和 Painting Stencil Patterns模式下如何创建设置和绘制patterns

这里是quartz在幕后如何使用我们提供的pattern绘制的例子。当我们用pattern fill或者stroke时,quartz在概念下将执行以下任务来绘制pattern cell:

  • 1.保存graphics 的状态 。
  • 2.将当前的矩阵转换为pattern cell的原点
  • 3.将CTM与pattern matrix级联
  • 4.剪切cell的边界矩形
  • 5.调用绘制函数绘制cell
  • 6.绘制graphics 的状态

Quartz 为我们处理平铺的事情,重复的将cell渲染到绘图空间,直到整个空间被绘制。我们可以用pattern 进行fill或者stroke。pattern cell可以指定任意大小。如果我们想看到pattern,我们必须确保pattern适合绘制空间。例如:如果我们的pattern cell是个8个单位乘以10个单位的大小,但是如果我们用带有两个单位的宽度的pattern来stroke 一条线,那么pattern 的cell 将被裁剪,因为它是10个单位宽。(线的宽度没有pattern的宽度宽)在这种模式下,就可能导致无法识别该模式。

绘制 Colored Patterns

五步完成绘制Colored Patterns

  • 1.写一个回调函数
  • 2.设置colorPattern颜色空间
  • 3.设置Colored Patterns 的pattern
  • 4.指定Colored Patterns fill或者stoke pattern
  • 5.用 Colored Patterns进行绘制

最开始的例子就是官方demo啦。

#define H_PATTERN_SIZE 16
#define V_PATTERN_SIZE 18

void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    CGFloat subunit = 5;
    CGRect  myRect1 = {{0,0}, {subunit, subunit}},
    myRect2 = {{subunit, subunit}, {subunit, subunit}},
    myRect3 = {{0,subunit}, {subunit, subunit}},
    myRect4 = {{subunit,0}, {subunit, subunit}};
    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)drawRect:(CGRect)rect {
 CGContextRef myContext =    UIGraphicsGetCurrentContext();
    CGPatternRef    pattern;// 1
    CGColorSpaceRef patternSpace;// 2
    CGFloat         alpha = 1,// 3
    width, height;// 4
    static const    CGPatternCallbacks callbacks = {0, // 5
        &MyDrawColoredPattern,
        NULL};
    
    CGContextSaveGState (myContext);
    patternSpace = CGColorSpaceCreatePattern (NULL);// 6
    CGContextSetFillColorSpace (myContext, patternSpace);// 7
    CGColorSpaceRelease (patternSpace);// 8
    
    pattern = CGPatternCreate (NULL, // 9
                               CGRectMake (0, 0, 300, 300),// 10
                               CGAffineTransformMake (1, 0, 0, 1, 0, 0),// 11
                               H_PATTERN_SIZE, // 12
                               V_PATTERN_SIZE, // 13
                               kCGPatternTilingConstantSpacing,// 14
                               true, // 15
                               &callbacks);// 16
    
    CGContextSetFillPattern (myContext, pattern, &alpha);// 17
    CGPatternRelease (pattern);// 18
    CGContextFillRect (myContext, rect);// 19
    CGContextRestoreGState (myContext);
}

具体讲解

  • 1.声明CGPatternRef对象。
  • 2.声明一个CGColorSpaceRef 对象
  • 3.声明alpha通道,并且将其设置为1,并且指定模式的不透明度为1.
  • 4.定义宽高。
  • 5.声明生成CGPatternRef对象所需要的callback结构体
  • 6.创建CGColorSpaceRef对象,基色空间设置为null。在绘制colored pattern的时候,改图案会调用自身提供的颜色。
  • 7.将pattern 的space添加到fill颜色空间中
  • 8.释放pattern 的space
  • 9.创建pattern (相当于获取一个color)
  • 10.指定pattern cell的需要绘制的大小
  • 11.通过转换矩阵将pattern的space转换到context使用的颜色空间
  • 12.cell的width
  • 13.cell的高度
  • 14.告诉quartz 如何渲染pattern
  • 15.指定pattern是colored pattern
  • 16.指定回调函数
  • 17.给context 设定fill pattern
  • 18.释放pattern对象
  • 19.填充对象

绘制 Stencil Patterns

步骤如下:

  • 1.写一个回调函数
  • 2.设置stencil Pattern颜色空间
  • 3.设置stencil Patterns 的pattern
  • 4.指定stencil Patterns fill或者stoke pattern
  • 5.用 stencil Patterns进行绘制

步骤和colored 一样的这里不做讲解了。我们应该看具体效果更直观了


参数配置效果

参数可以分一下几个角度来看

  • CGAffineTransform matrix
  • CGPatternTiling tiling
  • bool isColored
  • CGRect bounds

CGAffineTransform matrix

这里我们将矩阵设置成标准矩阵,缩放矩阵,旋转,平移矩阵看效果
采用colored pattern 和kCGPatternTilingNoDistortion 渲染样式。

标准矩阵
#define SizeItem 16
#define H_PATTERN_SIZE SizeItem*4
#define V_PATTERN_SIZE SizeItem*4

void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
    CGContextAddArc(myContext, SizeItem, SizeItem, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);
    
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);
    CGContextAddArc(myContext, SizeItem*3, SizeItem*3, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);

    CGContextSetRGBStrokeColor(myContext, 1, 0, 1, 1);
    CGContextStrokeRect(myContext, CGRectMake(0, 0, H_PATTERN_SIZE, V_PATTERN_SIZE));
    
}

- (void)drawRect:(CGRect)rect {
 CGContextRef myContext =    UIGraphicsGetCurrentContext();
    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, 160, 160),
                              CGAffineTransformIdentity,
                               H_PATTERN_SIZE,
                               V_PATTERN_SIZE,
                               kCGPatternTilingNoDistortion,
                               true,
                               &callbacks);
    CGContextSetFillPattern (myContext, pattern, &alpha);
    CGPatternRelease (pattern);
    CGContextFillRect (myContext, rect);
    CGContextRestoreGState (myContext);
}
标准矩阵
旋转
#define SizeItem 16
#define H_PATTERN_SIZE SizeItem*4
#define V_PATTERN_SIZE SizeItem*4

void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
    CGContextAddArc(myContext, SizeItem, SizeItem, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);
    
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);
    CGContextAddArc(myContext, SizeItem*3, SizeItem*3, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);

    CGContextSetRGBStrokeColor(myContext, 1, 0, 1, 1);
    CGContextStrokeRect(myContext, CGRectMake(0, 0, H_PATTERN_SIZE, V_PATTERN_SIZE));
    
}

- (void)drawRect:(CGRect)rect {
 CGContextRef myContext =    UIGraphicsGetCurrentContext();
    CGPatternRef    pattern;
    CGColorSpaceRef patternSpace;
    CGFloat         alpha = 1;
    
    static const    CGPatternCallbacks callbacks = {0,
        &MyDrawColoredPattern,
        NULL};
    
    CGContextSaveGState (myContext);
    patternSpace = CGColorSpaceCreatePattern (NULL);
    CGContextSetFillColorSpace (myContext, patternSpace);
    CGColorSpaceRelease (patternSpace);
    
    CGAffineTransform transform =CGAffineTransformRotate(CGAffineTransformIdentity, M_PI/4);
    pattern = CGPatternCreate (NULL,
                               CGRectMake (0, 0, 160, 160),
                              transform,
                               H_PATTERN_SIZE,
                               V_PATTERN_SIZE,
                               kCGPatternTilingNoDistortion,
                               true,
                               &callbacks);
    CGContextSetFillPattern (myContext, pattern, &alpha);
    CGPatternRelease (pattern);
    CGContextFillRect (myContext, rect);
    CGContextRestoreGState (myContext);
}
旋转
缩放
#define SizeItem 16
#define H_PATTERN_SIZE SizeItem*4
#define V_PATTERN_SIZE SizeItem*4

void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
    CGContextAddArc(myContext, SizeItem, SizeItem, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);
    
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);
    CGContextAddArc(myContext, SizeItem*3, SizeItem*3, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);

    CGContextSetRGBStrokeColor(myContext, 1, 0, 1, 1);
    CGContextStrokeRect(myContext, CGRectMake(0, 0, H_PATTERN_SIZE, V_PATTERN_SIZE));
    
}

- (void)drawRect:(CGRect)rect {
 CGContextRef myContext =    UIGraphicsGetCurrentContext();
    CGPatternRef    pattern;
    CGColorSpaceRef patternSpace;
    CGFloat         alpha = 1;
    
    static const    CGPatternCallbacks callbacks = {0,
        &MyDrawColoredPattern,
        NULL};
    
    CGContextSaveGState (myContext);
    patternSpace = CGColorSpaceCreatePattern (NULL);
    CGContextSetFillColorSpace (myContext, patternSpace);
    CGColorSpaceRelease (patternSpace);
    
    CGAffineTransform transform= CGAffineTransformScale(CGAffineTransformIdentity, 0.5, 0.5);
    pattern = CGPatternCreate (NULL,
                               CGRectMake (0, 0, 160, 160),
                              transform,
                               H_PATTERN_SIZE,
                               V_PATTERN_SIZE,
                               kCGPatternTilingNoDistortion,
                               true,
                               &callbacks);
    CGContextSetFillPattern (myContext, pattern, &alpha);
    CGPatternRelease (pattern);
    CGContextFillRect (myContext, rect);
    CGContextRestoreGState (myContext);
}
缩放

平移

//
//  PatternLearningView.m
//  CGPatternLearning
//
//  Created by 温杰 on 2018/10/8.
//  Copyright © 2018年 温杰. All rights reserved.
//

#import "PatternLearningView.h"

@implementation PatternLearningView
#define SizeItem 16
#define H_PATTERN_SIZE SizeItem*4
#define V_PATTERN_SIZE SizeItem*4

void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
    CGContextAddArc(myContext, SizeItem, SizeItem, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);
    
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);
    CGContextAddArc(myContext, SizeItem*3, SizeItem*3, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);

    CGContextSetRGBStrokeColor(myContext, 1, 0, 1, 1);
    CGContextStrokeRect(myContext, CGRectMake(0, 0, H_PATTERN_SIZE, V_PATTERN_SIZE));
    
}

- (void)drawRect:(CGRect)rect {
 CGContextRef myContext =    UIGraphicsGetCurrentContext();
    CGPatternRef    pattern;
    CGColorSpaceRef patternSpace;
    CGFloat         alpha = 1;
    
    static const    CGPatternCallbacks callbacks = {0,
        &MyDrawColoredPattern,
        NULL};
    
    CGContextSaveGState (myContext);
    patternSpace = CGColorSpaceCreatePattern (NULL);
    CGContextSetFillColorSpace (myContext, patternSpace);
    CGColorSpaceRelease (patternSpace);
    
    CGAffineTransform transform= CGAffineTransformTranslate(CGAffineTransformIdentity,SizeItem , SizeItem);
    pattern = CGPatternCreate (NULL,
                               CGRectMake (0, 0, 160, 160),
                              transform,
                               H_PATTERN_SIZE,
                               V_PATTERN_SIZE,
                               kCGPatternTilingNoDistortion,
                               true,
                               &callbacks);
    CGContextSetFillPattern (myContext, pattern, &alpha);
    CGPatternRelease (pattern);
    CGContextFillRect (myContext, rect);
    CGContextRestoreGState (myContext);
}
平移

CGPatternTiling tiling

CGPatternTiling 有三种模式。具体看看有啥影响
不过我试了三种模式,没发现有啥区别,可能与具体渲染有关系吧。有知道的大神可以提醒下。

bool isColored

这里我们举例官方demo

Colored pattern
#define PSIZE 16
void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    int k;
    double r, theta;
    
    r = 0.8 * PSIZE / 2;
    theta = 2 * M_PI * (2.0 / 5.0); // 144 degrees
    
    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);
    CGContextStrokePath(myContext);
}



- (void)drawRect:(CGRect)rect {
 CGContextRef myContext =    UIGraphicsGetCurrentContext();
    CGPatternRef pattern;
    CGColorSpaceRef baseSpace;
    CGColorSpaceRef patternSpace;
    static const CGFloat color[4] = { 1, 0, 0, 1 };// 1
    static const CGPatternCallbacks callbacks = {0, &MyDrawColoredPattern, NULL};// 2
    
    baseSpace = CGColorSpaceCreateDeviceRGB ();// 3
    patternSpace = CGColorSpaceCreatePattern (baseSpace);// 4
    CGContextSetFillColorSpace (myContext, patternSpace);// 5
    CGColorSpaceRelease (patternSpace);
    CGColorSpaceRelease (baseSpace);
    pattern = CGPatternCreate(NULL, CGRectMake(0, 0, PSIZE, PSIZE),// 6
                              CGAffineTransformIdentity, PSIZE, PSIZE,
                              kCGPatternTilingConstantSpacing,
                              true, &callbacks);
    CGContextSetFillPattern (myContext, pattern, color);// 7
    CGPatternRelease (pattern);// 8
    CGContextFillRect (myContext,CGRectMake (0,0,PSIZE*20,PSIZE*20));
}

看上图我们的五角星是黑色的默认颜色,没有更改。

其实colored pattern调用CGColorSpaceCreatePattern 需要传入null,可以不传参数。

Stencil pattern
#define PSIZE 16
void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    int k;
    double r, theta;
    
    r = 0.8 * PSIZE / 2;
    theta = 2 * M_PI * (2.0 / 5.0); // 144 degrees
    
    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);
    CGContextStrokePath(myContext);
}



- (void)drawRect:(CGRect)rect {
 CGContextRef myContext =    UIGraphicsGetCurrentContext();
    CGPatternRef pattern;
    CGColorSpaceRef baseSpace;
    CGColorSpaceRef patternSpace;
    static const CGFloat color[4] = { 1, 0, 0, 1 };// 1
    static const CGPatternCallbacks callbacks = {0, &MyDrawColoredPattern, NULL};// 2
    
    baseSpace = CGColorSpaceCreateDeviceRGB ();// 3
    patternSpace = CGColorSpaceCreatePattern (baseSpace);// 4
    CGContextSetFillColorSpace (myContext, patternSpace);// 5
    CGColorSpaceRelease (patternSpace);
    CGColorSpaceRelease (baseSpace);
    pattern = CGPatternCreate(NULL, CGRectMake(0, 0, PSIZE, PSIZE),// 6
                              CGAffineTransformIdentity, PSIZE, PSIZE,
                              kCGPatternTilingConstantSpacing,
                              false, &callbacks);
    CGContextSetFillPattern (myContext, pattern, color);// 7
    CGPatternRelease (pattern);// 8
    CGContextFillRect (myContext,CGRectMake (0,0,PSIZE*20,PSIZE*20));
}

这里我们只是简单的将colored pattern 代码中的true 改成了false ,我们发现五角星变成了红色。

因此,这里我们就名了colored 和 Stencil pattern的真正区别了。

colored pattern 不会使用当前空间的任何颜色,需要我们指定颜色
Stencil pattern 使用当前颜色空间中设置好的颜色。我们不需要在指定颜色。只是不需要指定颜色,当然也可以设置颜色了。

下图只是简单的在callback回调函数中设置线颜色为蓝色,最终就渲染成蓝色的了

#define PSIZE 16
void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    int k;
    double r, theta;
    
    r = 0.8 * PSIZE / 2;
    theta = 2 * M_PI * (2.0 / 5.0); // 144 degrees
    
    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);
    CGContextSetRGBStrokeColor(myContext, 0, 0, 1, 1.0);
    CGContextStrokePath(myContext);
}

CGrect bounds

具体看例子

#define SizeItem 16
#define H_PATTERN_SIZE SizeItem*4
#define V_PATTERN_SIZE SizeItem*4

void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
    CGContextAddArc(myContext, SizeItem, SizeItem, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);
    
    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);
    CGContextAddArc(myContext, SizeItem*3, SizeItem*3, SizeItem, 0, M_PI*2, YES);
    CGContextEOFillPath(myContext);

    CGContextSetRGBStrokeColor(myContext, 1, 0, 1, 1);
    CGContextStrokeRect(myContext, CGRectMake(0, 0, H_PATTERN_SIZE, V_PATTERN_SIZE));
    
}

- (void)drawRect:(CGRect)rect {
 CGContextRef myContext =    UIGraphicsGetCurrentContext();
    CGPatternRef    pattern;
    CGColorSpaceRef patternSpace;
    CGFloat         alpha = 1;
    
    static const    CGPatternCallbacks callbacks = {0,
        &MyDrawColoredPattern,
        NULL};
    
    CGContextSaveGState (myContext);
    patternSpace = CGColorSpaceCreatePattern (NULL);
    CGContextSetFillColorSpace (myContext, patternSpace);
    CGColorSpaceRelease (patternSpace);

    CGAffineTransform transform= CGAffineTransformIdentity;
    pattern = CGPatternCreate (NULL,
                               CGRectMake (20, 20, H_PATTERN_SIZE, V_PATTERN_SIZE),
                              transform,
                               H_PATTERN_SIZE,
                               V_PATTERN_SIZE,
                               kCGPatternTilingNoDistortion,
                               true,
                               &callbacks);
//    CGContextTranslateCTM (myContext, 20, 20);
    CGContextSetFillPattern (myContext, pattern, &alpha);
    CGPatternRelease (pattern);
    CGContextFillRect (myContext, rect);
    CGContextRestoreGState (myContext);
}

bound的origin 起作用。相当于tiling之间的间隔。origin起作用必须是bound的size必须大于cell的大小才行。

给UIView 的backgroundColor增加CGPatternRef图片

给layer 的backgroundColor 增加CGPatternRef 图片

为什么先给layer 增加呢?保证我们给layer可以增加上图片。再试UIView的backgroundColor

测试代码

void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
    UIImage * image = [UIImage imageNamed:@"guaguale.jpg"];
    CGContextDrawImage(myContext,[UIScreen mainScreen].bounds , image.CGImage);
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        CALayer *layer = [CALayer layer];
        [self.layer addSublayer:layer];
        layer.anchorPoint = CGPointZero;
        layer.bounds = self.bounds;
        layer.backgroundColor=[self getBgColor];;
    }
    return self;
}



-(CGColorRef)getBgColor{
    
    CGPatternRef    pattern;
    CGColorSpaceRef patternSpace;
    CGFloat         alpha = 1;
    static const    CGPatternCallbacks callbacks = {0,
        &MyDrawColoredPattern,
        NULL};
    CGColorSpaceRef patcolor=CGColorSpaceCreateDeviceRGB();
    patternSpace = CGColorSpaceCreatePattern (NULL);
    pattern = CGPatternCreate (NULL,
                               self.bounds,
                               CGAffineTransformMake (1, 0, 0, 1, 0, 0),
                               self.bounds.size.width,
                               self.bounds.size.height,
                               kCGPatternTilingConstantSpacing,
                               true,
                               &callbacks);
    CGColorRef color = CGColorCreateWithPattern(patternSpace, pattern, &alpha);
    CGPatternRelease(pattern);
    CGColorSpaceRelease(patternSpace);
    return color;
}

测试结果


给layer增加背景颜色是图片是没有问题的。

给UIView的layer设置backgroundColor是pattern

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UIView * view = [[PatternLearningView alloc]initWithFrame:self.view.bounds];
//  view.backgroundColor = [UIColor colorWithCGColor:[self getBgColor]];
    [self.view addSubview:view];
    view.layer.backgroundColor = [self getBgColor];

}


-(CGColorRef)getBgColor{
    
    CGPatternRef    pattern;
    CGColorSpaceRef patternSpace;
    CGFloat         alpha = 1;
    static const    CGPatternCallbacks callbacks = {0,
        &MyDrawColoredPattern1,
        NULL};
    patternSpace = CGColorSpaceCreatePattern (NULL);
    pattern = CGPatternCreate (NULL,
                               self.view.bounds,
                               CGAffineTransformMake (1, 0, 0, 1, 0, 0),
                               self.view.bounds.size.width,
                               self.view.bounds.size.height,
                               kCGPatternTilingConstantSpacing,
                               true,
                               &callbacks);
    CGColorRef color = CGColorCreateWithPattern(patternSpace, pattern, &alpha);
    CGPatternRelease(pattern);
    CGColorSpaceRelease(patternSpace);
    return color;
    
}

void MyDrawColoredPattern1 (void *info, CGContextRef myContext)
{
    UIImage * image = [UIImage imageNamed:@"guaguale.jpg"];
    CGContextDrawImage(myContext,[UIScreen mainScreen].bounds , image.CGImage);
}

结果如图


给UIView的backgroundColor设置pattern color

这里肯定也是没问题的,UIview的backgroundColor 和layer的backgroundColor 之间的关系只是映射而已。


- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UIView * view = [[PatternLearningView alloc]initWithFrame:self.view.bounds];
  view.backgroundColor = [UIColor colorWithCGColor:[self getBgColor]];
    [self.view addSubview:view];

}


-(CGColorRef)getBgColor{
    
    CGPatternRef    pattern;
    CGColorSpaceRef patternSpace;
    CGFloat         alpha = 1;
    static const    CGPatternCallbacks callbacks = {0,
        &MyDrawColoredPattern1,
        NULL};
    patternSpace = CGColorSpaceCreatePattern (NULL);
    pattern = CGPatternCreate (NULL,
                               self.view.bounds,
                               CGAffineTransformMake (1, 0, 0, 1, 0, 0),
                               self.view.bounds.size.width,
                               self.view.bounds.size.height,
                               kCGPatternTilingConstantSpacing,
                               true,
                               &callbacks);
    CGColorRef color = CGColorCreateWithPattern(patternSpace, pattern, &alpha);
    CGPatternRelease(pattern);
    CGColorSpaceRelease(patternSpace);
    return color;
    
}

void MyDrawColoredPattern1 (void *info, CGContextRef myContext)
{
    UIImage * image = [UIImage imageNamed:@"guaguale.jpg"];
    CGContextDrawImage(myContext,[UIScreen mainScreen].bounds , image.CGImage);
}

测试结果


但是绘制的图片是反着的。这里我们需要设置下pattern 的transform。这里就不做了。图形变换经常搞,这点知识才搞起来就没劲啦。

app 文档

你可能感兴趣的:(CGPatternRef 学习)