Quartz 2D编程指南之六:模式(Pattern)

本文转载自:http://southpeak.github.io/2014/12/05/quartz2d-6/

模式(Pattern)是绘制操作的一个序列,这些绘制操作可以重复地绘制到一个图形上下文上。我们可以像使用颜色一样使用这些模式。当我们使用pattern来绘制时,Quartz将Page分割成模式单元格的集合,其中每个单元格的大小不是模式图片的大小,并使用我们提供的回调函数来绘制这些单元格。图6-1演示了一个绘制到window图形上下文的模式。

Figure 6-1 A pattern drawn to a window

模式的骨架(Anatomy)

模式单元格是模式的基础组件。图6-1中的模式的单元格如图6-2所示。其中黑色边框不是模式单元格的一部分;之所以画出来是为了显示模式单元格的边界。

Figure 6-2 A pattern cell

该模式单元格的大小包含四个带颜色的矩形以及这些矩形上部及右侧的白色区域,如图6-3所示。每个模式单元格的黑色边框不是单元格的一部分;画出来只是为是标明单元格的边界。当我们创建一个模式单元格时,我们需要定义单元格的边界并在这个范围内进行绘制。

Figure 6-3 Pattern cells with black rectangles drawn to show the bounds of each cell

我们可以指定水平和竖直方向上两个单元格之间的间距。图6-3所绘制的单元格是相互紧挨着的。而图6-4在两个方向上都指定了单元格之间的间距。我们可以为两个方向指定不同的间距。我们亦可以指定间距为负数,这样单元格便会重叠。

Figure 6-4 Spacing between pattern cells

当我们绘制一个模式单元格时,Quartz使用模式空间(pattern space)作为坐标系统。模式空间是一个抽象空间,它会使用我们创建模式时指定的变换矩阵(pattern matrix)来映射到默认用户空间。

注意:模式空间与用户空间是分开的。未转换的模式空间映射到基础的用户空间(未转换的),而不管当前转换矩阵(CTM)。当我们在模式空间上应用转换时,Quartz只将转换应用于模式空间。

如果我们不想要Quartz来转换模式单元格,我们可以指定单位矩阵。然而,我们可以使用转换矩阵来达到有趣的效果。图6-5显示了缩放6-2中的模式单元格的效果。图6-6旋转了这些单元格。图6-7则平移了这些单元格。

Figure 6-5 A scaled pattern cell

Figure 6-6 A rotated pattern cell

Figure 6-7 A translated pattern cell

着色模式(Colored Patterns)和模板模式(Stencil Patterns)

着色模式有与其相关的固有颜色。如果修改了创建模式单元格的颜色,则模式也便失去了意义。图6-8中显示的苏格兰格子就是着色模式的一个例子。着色模式中的颜色是模式单元格创建流程的一部分,而不是绘制流程的一部分。

Figure 6-8 A colored pattern has inherent color

而其它模式只限定了形状,因此可以认为是模板模式(或者是非着色模式、甚至可以作为图像蒙板)。图6-9中展示的红色和黑色星星就是使用相同的模式单元格。单元格由一个五角星组成。当定义模式单元格时,没有与之相关的颜色。颜色值是在绘制过程中指定的,而不是创建过程的一部分。

Figure 6-9 A stencil pattern does not have inherent color

在Quartz 2D中,我们可以创建这两种模式。

平铺(Tiling)

平铺(Tiling)是将模式单元格绘制到页面(Page)的某个部分的过程。当Quartz将模式渲染到一个设备时,Quartz可能需要调整模式以适应设备空间。即,在用户空间定义的模式单元格在渲染到设备时可能无法精确匹配,这是由用户空间单元和设备像素之间的差异导致的。

Quartz有三个平铺选项,以在必要时调整模式:

没有失真(no distortion): 以细微调整模式单元格之间的间距为代价,但通常不超过一个设备像素。

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

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

模式如何工作

模式操作类似于颜色,我们设置一个填充或描边(stroke)模式,然后调用绘制函数。Quartz使用我们设置的模式作为“涂料”。例如,如果我们要使用纯色绘制一个填充的的矩形,我们首先调用函数(如CGContextSetFillColor)来设置填充颜色。然后调用函数CGContextFillRect以使用我们指定的颜色来填充矩形。为了绘制一个模式,颜色调用函数CGContextSetFillPattern来设置指定的模式。绘制颜色和绘制模式的不同之处在于我们必须先定义一个模式。我们为函数CGContextSetFillPattern提供模式和颜色信息。我们将在下面的绘制着色模式和绘制模板模式章节看到如何创建、设置和绘制模式。

这里有个例子说明Quartz在幕后是如何绘制一个模式的。当我们填充或描边一个模式时,Quartz会按照以下指令来绘制每一个模式单元格:

保存图形状态

将当前转换矩阵应用到原始的模式单元格上

连接CTM与模式矩阵

裁剪模式单元格的边界矩形

调用绘制回调函数来绘制单元格

恢复图形状态

Quartz会执行所有平铺操作,重复绘制模式单元格到绘制空间,直到渲染满整个空间。我们可以填充和描边一个模式。模式单元格可以是指定的任何大小。如果我们想要看到模式,我们需要确保模式单元格与绘制空间匹配。例如,如果我们的模式单元格是8*10个单位的,而我们用这个模式来描边一个只有2个单位的直线,则这个模式单元格将会被裁剪。这种情况下,我们可能无法辨认出我们的模式。

绘制着色模式

绘制着色模式需要执行以下五步操作:

写一个绘制着色模式单元格的回调函数

设置着色模式的颜色空间

设置着色模式的骨架(Anatomy)

指定着色模式作为填充或描边模式

使用着色模式绘制

绘制模板模式也是类似这几步。两者之间的区别在于如何设置颜色信息。

写一个绘制着色模式单元格的回调函数

一个模式单元格看起来是什么样的完全取决于我们。在这个例子中,代码清单6-1绘制了图6-2所示的模式单元格。

Listing 6-1 A drawing callback that draws a colored pattern cell

#defineH_PATTERN_SIZE 16

#defineV_PATTERN_SIZE 18

voidMyDrawColoredPattern(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);

}

模式单元格绘制函数是类似于下面这种格式的一个回调函数

typedefvoid(*CGPatternDrawPatternCallback)(

void*info,

CGContextRef context

);

我们可以随意命名我们的回调函数。代码清单6-1中命名为MyDrawColoredPattern。这个回调函数带有两个参数:

info: 一个指向模式相关数据的指针。这个参数是可选的,可以传递NULL。传递给回调的数据与后面创建模式的数据是一样的。

context: 绘制模式单元格的图形上下文

代码清单6-1中绘制的模式单元格是随意的。以下是一些关于绘制代码的重要信息:

需要声明模式大小。在绘制时我们需要记住模式大小。在这个例子中,大小是全局声明的,绘制函数没有具体提到大小,除了在注释中。然后,我们将模式大小指定给Quartz 2D。

绘制函数后面是由CGPatternDrawPatternCallback回调函数类型定义定义的原型

代码中执行的绘制设置了颜色,让其成为一个着色模式。

设置着色模式的颜色空间

代码清单6-1中的代码使用颜色来绘制模式单元格。我们必须设置基本的模式颜色空间为NULL,以确保Quartz使用绘制路径指定的颜色来绘制,如代码清单6-2所示。

Listing 6-2 Creating a base pattern color space

CGColorSpaceRef patternSpace;

// 创建模式颜色空间,并传递NULL作为参数

patternSpace = CGColorSpaceCreatePattern (NULL);

// 在模式颜色空间中设置填充颜色

CGContextSetFillColorSpace (myContext, patternSpace);

// 释放模式颜色空间

CGColorSpaceRelease (patternSpace);

设置着色模式的骨架

一个模式的骨架基本信息保存在CGPattern对象中。我们调用CGPatternCreate函数来创建一个CGPattern对象,其原型如代码清单6-3所示:

Listing 6-3 The CGPatternCreate function prototype

CGPatternRef CGPatternCreate (  void *info,

CGRect bounds,

CGAffineTransform matrix,

CGFloat xStep,

CGFloat yStep,

CGPatternTiling tiling,

bool isColored,

const CGPatternCallbacks *callbacks );

其中,

info:是一个指针,指向我们要传递给绘制回调函数的数据

bound:指定模式单元格的大小

matrix:指定模式矩阵,它将模式坐标系统映射到图形上下文的默认坐标系统。如果希望两个坐标系统是一样的,则可以使用单位矩阵。

xStep, yStep:指定单元格之间的水平和竖直间距。

tiling:平铺模式,可以是kCGPatternTilingNoDistortion、kCGPatternTilingConstantSpacingMinimalDistortion、kCGPatternTilingConstantSpacing

isColored:指定模式单元格是着色模式(true)还是模板模式(false)

callbacks:是一个指向CGPatternCallbacks结构体的指针,则定义如下:

structCGPatternCallbacks

{

unsignedintversion;

CGPatternDrawPatternCallback drawPattern;

CGPatternReleaseInfoCallback releaseInfo;

};

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

指定着色模式作为填充或描边模式

我们可以调用CGContextSetFillPattern或者CGContextSetStrokePattern函数来使用模式进行填充或描边。Quartz可以将模式用于任何填充或描边流程。

这两个函数包含以下几个参数:

图形上下文

先前创建的CGPattern对象

颜色组件的数组

虽然着色模式提供了自己的颜色,我们仍然需要传递一个单一的alpha值来告诉Quartz在绘制时着色模式的透明度。alpha值的范围在0到1中。可以如以下代码来设置着色模式的透明度:

CGFloat alpha =1;

CGContextSetFillPattern (myContext, myPattern, &alpha);

使用颜色模式绘制

在完成前面的步骤之后,我们就可以调用Quartz 2D函数来绘制了。我们的模式被当作“涂料”。例如,可以调用CGContextStrokePath, CGContextFillPath, CGContextFillRect或其它函数来绘制。

完整示例

代码清单6-4包含一个绘制着色模式的函数。这个函数包含了前面讨论的所有步骤。

Listing 6-4 A function that paints a colored pattern

void MyColoredPatternPainting(CGContextRef myContext,

CGRect rect)

{

CGPatternRef    pattern;

CGColorSpaceRef patternSpace;

CGFloat        alpha =1,

width, height;

static const CGPatternCallbacks callbacks = {0,

&MyDrawPattern,

NULL};

CGContextSaveGState (myContext);

patternSpace = CGColorSpaceCreatePattern (NULL);

CGContextSetFillColorSpace (myContext, patternSpace);

CGColorSpaceRelease (patternSpace);

pattern = CGPatternCreate (NULL,

CGRectMake (0,0, H_PSIZE, V_PSIZE),

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);

}

绘制模板模式

与绘制着色模式类似,绘制模板模式也有5个步骤:

写一个绘制模板模式单元格的回调函数

设置模板模式的颜色空间

设置模板模式的骨架(Anatomy)

指定模板模式作为填充或描边模式

使用模板模式绘制

绘制模板模式与绘制着色模式的区别在于设置颜色信息。

写一个绘制模板模式单元格的回调函数

绘制模板模式单元格的回调与前面描述的绘制颜色模式单元格类似。不同的是绘制模式单元格回调不需要指定颜色值。图6-10中显示的模式单元格即没有从绘制回调中获取颜色。

Figure 6-10 A stencil pattern cell

代码清单6-5绘制了图6-10中的模式单元格。可以看到代码只是简单地创建并填充了一个路径,而没有设置颜色。

Listing 6-5 A drawing callback that draws a stencil pattern cell

#definePSIZE 16// size of the pattern cell

static void MyDrawStencilStar(void*info, CGContextRef myContext)

{

intk;

doubler, 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);

CGContextFillPath(myContext);

}

设置模板模式的颜色空间

模板模式要求我们设置一个模式颜色空间用于Quartz的绘制,如代码清单6-6所示。

Listing 6-6 Code that creates a pattern color space for a stencil pattern

CGPatternRef pattern;

CGColorSpaceRef baseSpace;

CGColorSpaceRef patternSpace;

// 创建一个通用RGB颜色空间。

baseSpace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);

// 创建一个模式颜色空间。该颜色空间指定如何表示模式的颜色。后面要设置模式的颜色时,必须使用这个颜色空间来进行设置

patternSpace = CGColorSpaceCreatePattern (baseSpace);

// 设置颜色空间来在填充模式时使用

CGContextSetFillColorSpace (myContext, patternSpace);

// 释放模式颜色空间

CGColorSpaceRelease(patternSpace);

// 释放基础颜色空间

CGColorSpaceRelease(baseSpace);

设置模板模式的骨架(Anatomy)

这一步与上面设置着色模式是一样的,不同的是isColored参数需要传递false。

指定模板模式作为填充或描边模式

我们可以调用CGContextSetFillPattern或者CGContextSetStrokePattern函数来使用模式进行填充或描边。Quartz可以将模式用于任何填充或描边流程。

这两个函数包含以下几个参数:

图形上下文

先前创建的CGPattern对象

颜色组件的数组

由于模板模式在绘制回调中不提供颜色值,所以我们必须传递一个颜色给填充或描边函数来告诉Quartz使用什么颜色。代码清单6-7显示了为模板模式设置颜色的例子。

Listing 6-7 Code that sets opacity for a colored pattern

static const CGFloat color[4] = {0,1,1,0.5};//cyan, 50% transparent

CGContextSetFillPattern (myContext, myPattern, color);

使用模板模式绘制

在完成前面的步骤之后,我们就可以调用Quartz 2D函数来绘制了。我们的模式被当作“涂料”。例如,可以调用CGContextStrokePath, CGContextFillPath, CGContextFillRect或其它函数来绘制。

完整示例

代码清单6-8包含一个绘制模板模式的函数。这个函数包含了前面讨论的所有步骤。

Listing 6-8 A function that paints a stencil pattern

#definePSIZE 16

void MyStencilPatternPainting(CGContextRef myContext,

constRect *windowRect)

{

CGPatternRef pattern;

CGColorSpaceRef baseSpace;

CGColorSpaceRef patternSpace;

staticconstCGFloat color[4] = {0,1,0,1};

staticconstCGPatternCallbacks callbacks = {0, &drawStar,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));

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容