Patterns
模式是重复绘制到图形上下文的一系列绘图操作。你可以像使用颜色一样使用图案。当您使用模式进行绘制时,Quartz将页面划分为一组模式单元格,每个单元格的大小与模式图像相同,并且使用您提供的回调来绘制每个单元格。图6-1显示了绘制到窗口图形上下文的模式。
The Anatomy of a Pattern
模式单元是模式的基本组件。图6-1所示模式的模式单元如图6-2所示。黑色矩形不是图案的一部分;它的绘制目的是显示模式单元格的结束位置。这个特定模式单元格的大小包括四个彩色矩形的面积和矩形上方和右侧的空间,如图6-3所示。图中每个图案单元格周围的黑色矩形不是单元格的一部分;它表示单元格的边界。创建模式单元格时,定义单元格的界限并在界限内绘制。
您可以指定在水平和垂直方向上,Quartz从每个模式单元的开始绘制到下一个单元的距离。绘制图6-3中的模式单元,使一个模式单元的开始与下一个模式单元的开始完全是一个模式宽度,从而导致每个模式单元邻接下一个模式单元。图6-4中的模式单元在水平方向和垂直方向上都增加了空间。可以为每个方向指定不同的间距值。如果间距小于模式单元格的宽度或高度,模式单元格就会重叠。
绘制模式单元时,Quartz使用模式空间作为坐标系统。模式空间是一个抽象空间,它通过创建模式矩阵时指定的转换矩阵映射到默认用户空间。
注意:模式空间与用户空间是分开的。未转换的模式空间映射到基本(未转换)用户空间,而不考虑当前转换矩阵的状态。当您对模式空间应用转换时,Quartz只对模式空间应用转换。
模式坐标系统的默认约定是基础图形上下文的约定。默认情况下,Quartz使用一个坐标系,其中正值x表示向右的位移,正值y表示向上的位移。然而,UIKit创建的图形上下文使用不同的约定,其中正值y表示向下位移。虽然这种约定通常通过将转换连接到坐标系统来应用于图形上下文,但是在本例中,Quartz还修改了模式空间的默认约定来匹配。
如果不希望Quartz转换模式单元格,可以指定单位矩阵。然而,您可以通过提供一个转换矩阵来实现有趣的效果。图6-5显示了缩放模式单元的效果,如图6-2所示。图6-6显示了模式单元格的旋转。转换模式单元格要微妙一些。图6-7显示了模式的起源,模式单元在水平方向和垂直方向都进行了翻译,因此模式不再像图6-1那样与窗口相连。
Colored Patterns and Stencil (Uncolored) Patterns
彩色图案有与之相关的固有颜色。改变用于创建模式单元格的颜色,模式就失去了意义。苏格兰格子呢(如图6-8所示的样品)是彩色图案的一个例子。彩色模式中的颜色被指定为模式单元创建过程的一部分,而不是模式绘图过程的一部分.
其他的图案完全是根据它们的形状来定义的,因此,可以把它们看作是蜡纸图案、没有颜色的图案,甚至是图像蒙版。图6-9所示的红色和黑色恒星是相同模式单元的每个表示。细胞本身由一个形状组成——一个充满恒星。在定义模式单元格时,没有与之关联的颜色。颜色被指定为图案绘制过程的一部分,而不是图案单元创建的一部分。
您可以在Quartz 2D中创建图案颜色或模板。
Tiling
平铺是将模式单元格渲染到页面的一部分的过程。当Quartz向设备呈现模式时,Quartz可能需要调整模式以适应设备空间。也就是说,在用户空间中定义的模式单元在呈现给设备时可能不太适合,因为用户空间单元和设备像素之间存在差异。
石英有三个瓷砖选择,它可以用来调整模式,在必要时。石英可以保存:
1、这种模式,以稍微调整模式单元格之间的间距为代价,但不超过一个设备像素。这被称为无失真。
2、单元格之间的间距,以稍微扭曲模式单元格为代价,但不超过一个设备像素。这被称为恒定间距与最小失真。
3、单元格之间的间隔(对于最小失真选项)以尽可能多地扭曲模式单元格来获得快速平铺为代价。这被称为常数间距。
How Patterns Work
模式的操作与颜色类似,您可以设置一个填充或笔画模式,然后调用一个绘画函数。Quartz使用您设置的模式作为“油漆”。例如,如果你想用纯色绘制一个填充矩形,你首先调用一个函数,比如CGContextSetFillColor来设置填充颜色。然后调用函数CGContextFillRect,用指定的颜色绘制填充矩形。要使用模式进行绘制,首先调用函数CGContextSetFillPattern来设置模式。然后调用CGContextFillRect,实际使用指定的模式绘制填充矩形。用颜色画和用图案画的区别在于你必须定义图案。您可以向CGContextSetFillPattern函数提供模式和颜色信息。您将看到如何在绘画中创建、设置和绘制模式、彩色模式和绘画模板模式。
下面是Quartz如何在幕后使用您提供的模式进行绘制的示例。当您使用模式填充或描边时,Quartz概念性地执行以下任务来绘制每个模式单元格:
1、保存图形状态。
2、将当前变换矩阵转换为模式单元格的原点。
3、将CTM与模式矩阵连接起来。
4、剪辑到模式单元格的边框。
5、调用绘图回调来绘制模式单元格。
6、恢复图像的状态。
Quartz会为您处理所有的平铺,不断地将图案单元渲染到绘图空间,直到整个空间都被绘制出来。你可以用图案填充或描边。模式单元格可以是您指定的任何大小。如果您想要查看模式,您应该确保模式单元格适合绘图空间。例如,如果您的模式单元格是8个单位乘10个单位,并且您使用模式来描出宽度为2个单位的线,那么模式单元格将被裁剪,因为它是10个单位宽。在这种情况下,您可能无法识别模式。
Painting Colored Patterns
画彩色图案需要执行的五个步骤如下所示:
1、编写一个绘制彩色模式单元格的回调函数
2、设置彩色图案的颜色空间
3、建立彩色图案的解剖结构
4、将彩色模式指定为填充模式或笔画模式
5、用彩色图案画画
这些步骤与您绘制模具图案的步骤相同。两者的区别在于如何设置颜色信息。您可以看到所有的步骤如何配合在一个完整的彩色图案绘画功能。
Write a Callback Function That Draws a Colored Pattern Cell
模式单元格的外观完全取决于您。对于这个示例,清单6-1中的代码绘制了图6-2所示的模式单元格。回想一下,模式单元格周围的黑线不是单元格的一部分;绘制它是为了显示模式单元格的边界大于代码绘制的矩形。稍后将模式大小指定为Quartz。
您的模式单元格绘制函数是一个回调函数,它遵循以下形式:
typedef void (*CGPatternDrawPatternCallback) (
void *info,
CGContextRef context
);
你可以任意命名你的回调。清单6-1中的一个名为MyDrawColoredPattern。回调需要两个参数:
info,一个指向与模式关联的私有数据的通用指针。这个参数是可选的;你可以传递NULL。传递给回调的数据与您稍后创建模式时提供的数据相同。
上下文,用于绘制模式单元格的图形上下文。
清单6-1中的代码绘制的模式单元格是任意的。您的代码绘制适合您创建的模式的任何内容。这些代码的细节非常重要:
模式大小被声明。在编写绘图代码时,需要记住模式大小。在这里,大小被声明为全局的。绘图函数不专门引用大小,除非在注释中引用。稍后,您将模式大小指定为Quartz 2D。请看对彩色图案的解剖。
绘图函数遵循由CGPatternDrawPatternCallback类型定义定义的原型。
代码中执行的绘图设置了颜色,这使其成为彩色模式。
Listing 6-1 A drawing callback that draws a colored pattern cell
#define H_PATTERN_SIZE 16
#define V_PATTERN_SIZE 18
void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
CGFloat subunit = 5; // the pattern cell itself is 16 by 18
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);
}
Set Up the Colored Pattern Color Space
清单6-1中的代码使用颜色来绘制模式单元格。通过将基本模式颜色空间设置为NULL,您必须确保使用绘图例程中使用的颜色绘制Quartz,如清单6-2所示。下面是对每一行代码的详细说明。
Listing 6-2 Creating a base pattern color space
CGColorSpaceRef patternSpace;
patternSpace = CGColorSpaceCreatePattern (NULL);// 1
CGContextSetFillColorSpace (myContext, patternSpace);// 2
CGColorSpaceRelease (patternSpace);
下面是代码的作用:
1、通过调用函数CGColorSpaceCreatePattern,传递NULL作为基本颜色空间,创建适合于彩色图案的图案颜色空间。
2、将填充颜色空间设置为模式颜色空间。如果您正在绘制模式,请调用CGContextSetStrokeColorSpace。
3、释放图案颜色空间。
Set Up the Anatomy of the Colored Pattern
有关模式解剖的信息保存在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参数是一个指向要传递给绘图回调的数据的指针。这是在Write回调函数中讨论的相同指针,该函数绘制彩色模式单元格。
在bounds参数中指定模式单元格的大小。矩阵参数是指定模式矩阵的地方,它将模式坐标系统映射到图形上下文的默认坐标系统。如果你想用和图形上下文相同的坐标系来绘制图案,就使用单位矩阵。xStep和yStep参数指定模式坐标系中单元格之间的水平和垂直间距。请参阅对模式的剖析,以查看关于界限、模式矩阵和间距的信息。
平铺参数可以是以下三个值之一:
kCGPatternTilingNoDistortion
kCGPatternTilingConstantSpacingMinimalDistortion
kCGPatternTilingConstantSpacing
参见Tiling查看关于Tiling的信息。
isColored参数指定模式单元格是彩色模式(true)还是模板模式(false)。如果在这里传递true,则绘图模式回调将指定模式颜色,并且必须将模式颜色空间设置为彩色模式颜色空间(请参阅设置彩色模式颜色空间)。
传递给函数CGPatternCreate的最后一个参数是指向一个CGPatternCallbacks数据结构的指针。这个结构有三个领域:
struct CGPatternCallbacks
{
unsigned int version;
CGPatternDrawPatternCallback drawPattern;
CGPatternReleaseInfoCallback releaseInfo;
};
将version字段设置为0。drawPattern字段是一个指向绘图回调的指针。releaseInfo字段是在释放CGPattern对象时调用的回调的指针,用于释放传递给绘图回调的info参数的存储。如果没有在此参数中传递任何数据,则将该字段设置为NULL。
Specify the Colored Pattern as a Fill or Stroke Pattern
您可以通过调用适当的函数—cgcontextsetfillpattern或CGContextSetStrokePattern来使用您的模式来填充或描边。Quartz会使用您的模式进行后续的填充或抚摸。
这些函数都有三个参数:
1、图形上下文
2、您之前创建的CGPattern对象
3、颜色组件的数组
尽管彩色模式提供了它们自己的颜色,但是您必须传递一个alpha值,以便在绘制模式时通知Quartz该模式的整体不透明度。Alpha值可以从1(完全不透明)到0(完全透明)不等。这些代码行显示了如何为填充颜色模式设置不透明度的示例。
CGFloat alpha = 1;
CGContextSetFillPattern (myContext, myPattern, &alpha);
Draw With the Colored Pattern
完成前面的步骤后,可以调用绘制的任何Quartz 2D函数。你的图案被用作“颜料”。例如,您可以调用CGContextStrokePath、CGContextFillPath、CGContextFillRect或任何其他绘制函数。
A Complete Colored Pattern Painting Function
清单6-4中的代码包含一个绘制彩色模式的函数。该函数包含前面讨论的所有步骤。下面是对每一行代码的详细说明。
Listing 6-4 A function that paints a colored pattern
void MyColoredPatternPainting (CGContextRef myContext,
CGRect rect)
{
CGPatternRef pattern;// 1
CGColorSpaceRef patternSpace;// 2
CGFloat alpha = 1,// 3
width, height;// 4
static const CGPatternCallbacks callbacks = {0, // 5
&MyDrawPattern,
NULL};
CGContextSaveGState (myContext);
patternSpace = CGColorSpaceCreatePattern (NULL);// 6
CGContextSetFillColorSpace (myContext, patternSpace);// 7
CGColorSpaceRelease (patternSpace);// 8
pattern = CGPatternCreate (NULL, // 9
CGRectMake (0, 0, H_PSIZE, V_PSIZE),// 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、为稍后创建的CGPattern对象声明存储。
2、为稍后创建的模式颜色空间声明存储。
3、为alpha声明一个变量,并将其设置为1,这将指定模式的不透明度为完全不透明。
4、声明变量以保存窗口的高度和宽度。在这个例子中,图案被绘制在窗口的区域上。
5、声明并填充回调结构,传递0作为版本和一个指向绘图回调函数的指针。这个示例没有提供release info回调,因此该字段被设置为NULL。
6、创建一个模式颜色空间对象,将模式的基本颜色空间设置为NULL。当您绘制有颜色的图案时,图案在绘图回调中提供自己的颜色,这就是为什么您将颜色空间设置为NULL。
7、将填充颜色空间设置为刚才创建的模式颜色空间对象。
释放图案颜色空间对象。
8、传递NULL,因为模式不需要传递给绘图回调的任何额外信息。
9、传递指定模式单元格边界的CGRect对象。
10、传递CGAffineTransform矩阵,该矩阵指定如何将模式空间转换为使用模式的上下文的默认用户空间。这个例子传递单位矩阵。
11、将水平模式大小作为每个单元开始之间的水平位移传递。在本例中,一个单元格与下一个单元格相邻。
12、通过垂直模式大小作为每个单元开始之间的垂直位移。
13、传递常量kcgpatterntilingconstant间隔以指定Quartz如何呈现模式。有关更多信息,请参见平铺。
14、为isColored参数传递true,以指定模式是有色模式。
传递一个指针到包含版本信息的回调结构,以及一个指向绘图回调函数的指针。
15、设置填充模式,传递上下文、刚刚创建的CGPattern对象和一个指向alpha值的指针,该值指定要将Quartz应用到该模式的不透明度。
16、 释放CGPattern对象。
17、填充一个矩形,该矩形是传递给MyColoredPatternPainting例程的窗口大小。Quartz使用刚才设置的模式填充矩形。
Painting Stencil Patterns
您需要执行的五个步骤来绘制一个钢网图案在以下部分描述:
1、编写一个回调函数来绘制模板单元格
2、设置模板颜色空间
3、建立钢网图案的解剖结构
4、将模板模式指定为填充模式或笔画模式
5、用蜡纸图案画画
这些步骤和你画彩色图案的步骤是一样的。两者的区别在于如何设置颜色信息。您可以看到所有的步骤如何配合在一个完整的钢网图案绘画功能。
Write a Callback Function That Draws a Stencil Pattern Cell
您为绘制模板模式而编写的回调与为彩色模式单元格所描述的格式相同。请参阅编写一个回调函数来绘制彩色模式单元格。唯一的区别是绘图回调没有指定任何颜色。图6-10所示的模式单元格没有从绘图回调中获得颜色。颜色设置在图案颜色空间的绘图颜色之外。
看看清单6-5中的代码,它绘制了图6-10所示的模式单元格。注意,代码只是创建一个路径并填充路径。代码没有设置颜色。
Listing 6-5 A drawing callback that draws a stencil pattern cell
#define PSIZE 16 // size of the pattern cell
static void MyDrawStencilStar (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);
CGContextFillPath(myContext);
}
Set Up the Stencil Pattern Color Space
模板模式要求您为Quartz设置一个用于绘制的模式颜色空间,如清单6-6所示。下面是对每一行代码的详细说明。
Listing 6-6 Code that creates a pattern color space for a stencil pattern
CGPatternRef pattern;
CGColorSpaceRef baseSpace;
CGColorSpaceRef patternSpace;
baseSpace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);// 1
patternSpace = CGColorSpaceCreatePattern (baseSpace);// 2
CGContextSetFillColorSpace (myContext, patternSpace);// 3
CGColorSpaceRelease(patternSpace);// 4
CGColorSpaceRelease(baseSpace);
下面是代码的作用:
1、这个函数创建一个通用的RGB空间。泛型颜色空间将颜色匹配留给系统。有关更多信息,请参见创建通用颜色空间。
2、创建一个图案颜色空间。您提供的颜色空间指定如何为模式表示颜色。稍后,当您为模式设置颜色时,您必须使用模式颜色空间来设置它们。对于本例,您需要使用RGB值指定颜色。
3、设置填充图案时要使用的颜色空间。您可以通过调用函数
CGContextSetStrokeColorSpace来设置笔画的颜色空间。
4、释放图案颜色空间对象。
5、释放基本颜色空间对象。
Set Up the Anatomy of the Stencil Pattern
您可以通过调用CGPatternCreate函数来指定模式解析的信息,如你可以用有色模式的方法来指定模式的解剖学知识。唯一的区别是,对于isColored参数传递false。有关为CGPatternCreate函数提供的参数的更多信息,请参阅建立彩色模式的解剖关系。
Specify the Stencil Pattern as a Fill or Stroke Pattern
您可以通过调用适当的函数CGContextSetFillPattern或CGContextSetStrokePattern来使用您的模式来填充或描边。Quartz会使用您的模式进行后续的填充或抚摸。
这些函数都有三个参数:
图形上下文
您之前创建的CGPattern对象
颜色组件的数组
在绘图回调中,模板模式不提供颜色,因此必须向填充或笔画函数传递颜色,以告知Quartz使用什么颜色。清单6-7显示了如何为模板模式设置颜色的示例。颜色数组中的值由前面设置的颜色空间中的Quartz解释。因为本例使用设备RGB,所以颜色数组包含红色、绿色和蓝色组件的值。第四个值指定颜色的不透明度。
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);
Drawing with the Stencil Pattern
完成前面的步骤后,可以调用绘制的任何Quartz 2D函数。你的图案被用作“颜料”。例如,您可以调用CGContextStrokePath、CGContextFillPath、CGContextFillRect或任何其他绘制函数。
A Complete Stencil Pattern Painting Function
清单6-8中的代码包含一个绘制模板模式的函数。该函数包含前面讨论的所有步骤。下面是对每一行代码的详细说明。
Listing 6-8 A function that paints a stencil pattern
#define PSIZE 16
void MyStencilPatternPainting (CGContextRef myContext,
const Rect *windowRect)
{
CGPatternRef pattern;
CGColorSpaceRef baseSpace;
CGColorSpaceRef patternSpace;
static const CGFloat color[4] = { 0, 1, 0, 1 };// 1
static const CGPatternCallbacks callbacks = {0, &drawStar, 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));// 9
}
下面是代码的作用:
1、声明一个数组以保存颜色值,并将该值(将位于RGB颜色空间中)设置为不透明绿色。
2、声明并填充回调结构,传递0作为版本和一个指向绘图回调函数的指针。这个示例没有提供release info回调,因此该字段被设置为NULL。
3、创建一个RGB设备颜色空间。如果将模式绘制到显示器上,则需要提供这种类型的颜色空间。
4、从RGB设备颜色空间创建一个模式颜色空间对象。
5、将填充颜色空间设置为刚才创建的模式颜色空间对象。
6、创建一个模式对象。注意,最后一个参数是isColored参数是false。蜡纸图案不提供颜色,因此您必须为这个参数传递false。所有其他参数都类似于为彩色模式示例传递的参数。查看完整的彩色图案绘画功能。
7、设置填充模式,传递之前声明的颜色数组。
8、释放CGPattern对象。
9、填补了一个矩形。Quartz使用刚才设置的模式填充矩形。