模式是重复绘制到图形上下文的绘制操作序列。 您可以使用与使用颜色相同的方式使用模式。 当使用模式绘制时,Quartz将页面划分为一组模式单元格,每个单元格都是模式图像的大小,并使用您提供的回调绘制每个单元格。 图6-1显示了绘制到窗口图形上下文的模式。
模式的解剖学
图案单元是图案的基本组成部分。 图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中那样靠在窗口上。
色的样式和模板(未上色)
彩色图案具有与其相关的固有颜色。 更改用于创建图案单元的颜色,并且该图案失去其含义。 苏格兰格子(如图6-8所示的样品)是彩色图案的一个例子。 彩色图案中的颜色被指定为图案单元格创建过程的一部分,而不是图案绘制过程的一部分。
其它图案仅仅基于它们的形状来定义,并且因此可以被认为是模板图案,未着色图案或者甚至作为图像掩模。 图6-9中所示的红色和黑色星形是同一图案单元的每个再现。 单元格本身由一个形状 - 填充的星。 当定义图案单元时,没有颜色与其相关联。 颜色被指定为图案绘制过程的一部分,而不是模式单元格创建的一部分。
您可以在Quartz 2D中创建任何一种图案颜色或模板。
平铺
平铺是将图案单元呈现到页面的一部分的过程。 当Quartz向设备渲染模式时,Quartz可能需要调整模式以适应设备空间。 也就是说,由于用户空间单位和设备像素之间的差异,当在用户空间中定义的图案单元在被呈现到设备时可能不完美地适合。
Quartz有三个平铺选项,可以在必要时调整模式。 Quartz可以保存:
- 该图案,以调整图案单元之间的间隔为代价,但是不超过一个器件像素。 这被称为无失真。
- 单元之间的间隔,以牺牲图案单元稍微变形为代价,但是不超过一个器件像素。 这被称为具有最小失真的恒定间隔。
- 在单元之间的间隔(对于最小失真选项),以使得模式单元失真达到获得快速平铺所需的程度为代价。 这称为恒定间距。
模式如何工作
模式的操作类似于颜色,因为你设置一个填充或笔触模式,然后调用绘画功能。 Quartz使用你设置为“paint”的模式。例如,如果你想用一个纯色绘制一个填充的矩形,你首先调用一个函数,如CGContextSetFillColor来设置填充颜色。然后调用函数CGContextFillRect,用指定的颜色绘制填充矩形。要使用模式绘制,您首先调用函数CGContextSetFillPattern来设置模式。然后你调用CGContextFillRect,实际上用你指定的模式绘制填充矩形。用颜色和图案绘画之间的区别是,你必须定义模式。您向函数CGContextSetFillPattern提供模式和颜色信息。你将看到如何在绘画色彩模式和绘画模具模式中创建,设置和绘制模式。
这里有一个例子,说明Quartz如何在幕后使用你提供的模式绘制。当你用一个模式填充或描线时,Quartz概念上执行以下任务来绘制每个模式单元格:
- 保存图形状态。
- 将当前转换矩阵转换为模式单元格的原点。
- 将CTM与模式矩阵连接。
- 剪切到模式单元格的边界矩形。
- 调用绘图回调来绘制模式单元格。
- 恢复图形状态。
Quartz照顾你的所有平铺,重复渲染图案单元格到绘图空间,直到整个空间被绘。 您可以用图案填充或描边。 模式单元格可以是您指定的任何大小。 如果要查看模式,应确保模式单元格适合绘图空间。 例如,如果您的图案单元格为8个单位,以10个单位为单位,并且您使用该图案描画宽度为2个单位的线,则由于图案单元格的宽度为10个单位,因此会被裁切。 在这种情况下,您可能无法识别该模式。
绘色模式
以下部分描述了绘制彩色图案所需执行的五个步骤:
- 编写一个绘制彩色图案单元的回调函数
- 设置彩色图案颜色空间
- 设置彩色图案的解剖
- 将彩色图案指定为填充或描边图案
- 彩色的图案绘制
这些是用于绘制模具模式的相同步骤。 两者之间的区别是如何设置颜色信息。 你可以看到所有的步骤如何在一个完整的彩色图案绘画函数中。
编写一个绘制彩色图案单元的回调函数
什么样的模式单元格看起来完全取决于你。 对于这个例子,清单6-1中的代码绘制了如图6-2所示的模式单元格。 回想一下,图案单元周围的黑线不是单元的一部分; 它被绘制以示出模式单元的边界大于由代码绘制的矩形。 您稍后将模式大小指定为Quartz。
您的模式单元格绘制函数是一个遵循此形式的回调:
typedef void (*CGPatternDrawPatternCallback) (
void *info,
CGContextRef context
);
你可以命名你的回调名称为任何你喜欢的。 清单6-1中的名称为MyDrawColoredPattern。 回调有两个参数:
- info,一个指向与模式相关联的私有数据的通用指针。 此参数是可选的; 你可以传递NULL。 传递到回调的数据是您稍后在创建模式时提供的相同数据。
- context,用于绘制图案单元的图形上下文。
清单6-1中的代码绘制的模式单元是任意的。 你的代码绘制任何适合你创建的模式。 有关代码的这些详细信息很重要:
- 声明模式大小。 在编写绘图代码时,需要考虑模式大小。 这里,大小被声明为全局。 绘图函数不特别指向大小,除非在注释中。 稍后,您可以将模式大小指定为Quartz 2D。 请参阅设置彩色图案的解剖。
- 绘制函数遵循由CGPatternDrawPatternCallback回调类型定义定义的原型。
- 在代码中执行的绘图设置颜色,这使得这是一个有色图案。
// 绘制彩色图案单元格的绘图回调
#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);
}
设置彩色图案颜色空间
清单6-1中的代码使用颜色来绘制模式单元格。 您必须通过将基本模式颜色空间设置为NULL,确保Quartz使用您在绘制例程中使用的颜色绘制颜色,如清单6-2所示。 每个编号的代码行的详细说明在列表之后。
// 创建基本图案颜色空间
CGColorSpaceRef patternSpace;
patternSpace = CGColorSpaceCreatePattern (NULL);// 1
CGContextSetFillColorSpace (myContext, patternSpace);// 2
CGColorSpaceRelease (patternSpace);// 3
这里是代码做了什么:
- 通过调用函数CGColorSpaceCreatePattern,传递NULL作为基本颜色空间来创建适合于颜色模式的模式颜色空间。
- 将填充颜色空间设置为图案颜色空间。 如果你正在描边你的模式,调用CGContextSetStrokeColorSpace。
- 释放模式颜色空间。
设置彩色图案的解剖
有关模式解剖的信息保存在CGPattern对象中。 您可以通过调用函数CGPatternCreate创建一个CGPattern对象,其原型如清单6-3所示。
// CGPatternCreate函数原型
CGPatternRef CGPatternCreate ( void *info,
CGRect bounds,
CGAffineTransform matrix,
CGFloat xStep,
CGFloat yStep,
CGPatternTiling tiling,
bool isColored,
const CGPatternCallbacks *callbacks );
info参数是指向想要传递给绘图回调的数据的指针。 这是在编写绘制彩色图案单元的回调函数中讨论的相同指针。
您可以在bounds参数中指定模式单元格的大小。 矩阵参数是指定模式矩阵的位置,它将模式坐标系映射到图形上下文的默认坐标系。 如果要使用与图形上下文相同的坐标系绘制图案,请使用单位矩阵。 xStep和yStep参数指定模式坐标系中单元格之间的水平和垂直间距。 请参阅模式解剖以查看有关边界,模式矩阵和间距的信息。
平铺参数可以是三个值之一:
- kCGPatternTilingNoDistortion
- kCGPatternTilingConstantSpacingMinimalDistortion
- kCGPatternTilingConstantSpacing
请参阅平铺以查看关于平铺的信息。
isColored参数指定模式单元格是彩色模式(true)还是模板模式(false)。 如果在此传递true,则绘制模式回调指定模式颜色,并且必须将模式颜色空间设置为彩色模式颜色空间(请参阅设置彩色模式颜色空间)。
传递给函数CGPatternCreate的最后一个参数是指向CGPatternCallbacks数据结构的指针。 此结构有三个字段:
struct CGPatternCallbacks
{
unsigned int version;
CGPatternDrawPatternCallback drawPattern;
CGPatternReleaseInfoCallback releaseInfo;
};
将version字段设置为0. drawPattern字段是指向绘图回调的指针。 releaseInfo字段是指向在释放CGPattern对象时调用的回调的指针,以释放传递给绘图回调的info参数的存储空间。 如果您未传递此参数中的任何数据,则将此字段设置为NULL。
将彩色图案指定为填充或描边图案
您可以通过调用相应的函数CGContextSetFillPattern或CGContextSetStrokePattern使用您的模式来填充或描边。 Quartz使用你的模式进行任何后续的填充或描边。
这些函数各有三个参数:
- 图形上下文
- 您先前创建的CGPattern对象
- 颜色分量数组
虽然彩色图案提供了自己的颜色,但是你必须传递一个单一的alpha值,以告知Quartz绘制时模式的整体不透明度。 α可以从1(完全不透明)到0(完全透明)。 这些代码行显示了如何为用于填充的彩色图案设置不透明度的示例。
CGFloat alpha = 1;
CGContextSetFillPattern (myContext, myPattern, &alpha);
彩色的图案绘制
完成上述步骤后,您可以调用任何绘制的Quartz 2D函数。 您的模式用作“绘画”。例如,您可以调用CGContextStrokePath,CGContextFillPath,CGContextFillRect或任何其他绘制的函数。
完整的彩色图案绘画函数
清单6-4中的代码包含一个绘制彩色模式的函数。 该功能包括前面讨论的所有步骤。 每个编号的代码行的详细说明在列表之后。
// 绘制彩色图案的函数
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);
}
这里是代码做了什么:
- 声明稍后创建的CGPattern对象的存储。
- 声明稍后创建的模式颜色空间的存储。
- 声明alpha变量,并将其设置为1,它指定模式的不透明度为完全不透明。
- 声明变量以保存窗口的高度和宽度。在该示例中,图案被绘制在窗口的区域上。
- 声明和填充回调结构,传递0作为版本和指向绘图回调函数的指针。此示例不提供发布信息回调,因此该字段设置为NULL。
- 创建模式颜色空间对象,将模式的基本颜色空间设置为NULL。当绘制彩色图案时,该图案在图形回调中提供自己的颜色,这就是为什么将颜色空间设置为NULL的原因。
- 将填充颜色空间设置为刚刚创建的图案颜色空间对象。
- 释放模式颜色空间对象。
- 传递NULL,因为模式不需要传递给绘图回调的任何附加信息。
- 传递指定模式单元格边界的CGRect对象。
- 传递CGAffineTransform矩阵,该矩阵指定如何将模式空间转换为使用该模式的上下文的默认用户空间。这个例子传递单位矩阵。
- 通过水平图案大小作为每个单元格的开始之间的水平位移。在这个例子中,一个单元格被涂在相邻的单元格之下。
- 将垂直图案尺寸作为每个单元开始之间的垂直位移。
- 通过常量kCGPatternTilingConstantSpacing来指定Quartz应该如何渲染模式。有关详细信息,请参阅平铺。
- 对isColored参数传递true,以指定模式是彩色模式。
- 传递指向包含版本信息的回调结构的指针,以及指向绘制回调函数的指针。
- 设置填充模式,传递上下文,刚刚创建的CGPattern对象和指向一个alpha值的指针,该值指定Quartz应用于模式的不透明度。
- 释放CGPattern对象。
- 填充一个矩形,该矩形是传递到MyColoredPatternPainting例程的窗口大小。 Quartz使用刚刚设置的模式填充矩形。
绘画模具样式
以下部分描述了绘制模板模式所需执行的五个步骤:
- 编写一个绘制模板模式的回调函数
- 设置模板图案颜色空间
- 设置模板模式的解剖
- 将模板模式指定为填充或描边模式
- 用钢板蜡纸图案绘图
这些实际上是用来绘制彩色图案的步骤。 两者之间的区别是如何设置颜色信息。 你可以看到所有的步骤如何在一个完整的模板图案绘制功能。
编写一个绘制模板模式的回调函数
您为绘制模板模式而写入的回调与针对彩色模式单元格描述的回调相同。 请参阅编写绘制彩色图案单元的回调函数。 唯一的区别是你的绘制回调没有指定任何颜色。 图6-10所示的模式单元格不会从绘图回调中获得其颜色。 颜色设置在图案颜色空间中绘图颜色之外。
看看清单6-5中的代码,绘制如图6-10所示的模式单元格。 请注意,代码只是创建一个路径并填充路径。 代码不设置颜色。
// 绘制模板模式单元格的绘图回调
#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);
}
设置模板图案颜色空间
模板模式需要为Quartz绘制一个模式颜色空间,如代码清单6-6所示。 每个编号的代码行的详细说明在列表之后。
// 为模板模式创建模式颜色空间的代码
CGPatternRef pattern;
CGColorSpaceRef baseSpace;
CGColorSpaceRef patternSpace;
baseSpace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);// 1
patternSpace = CGColorSpaceCreatePattern (baseSpace);// 2
CGContextSetFillColorSpace (myContext, patternSpace);// 3
CGColorSpaceRelease(patternSpace);// 4
CGColorSpaceRelease(baseSpace);// 5
这里是代码做了什么:
- 此函数创建一个通用的RGB空间。 通用颜色空间会使颜色与系统匹配。 有关详细信息,请参阅创建常规颜色空间。
- 创建图案颜色空间。 您提供的颜色空间指定颜色如何表示模式。 稍后,为模式设置颜色时,必须使用模式颜色空间设置它们。 对于此示例,您将需要使用RGB值指定颜色。
- 设置填充图案时要使用的颜色空间。 您可以通过调用函数CGContextSetStrokeColorSpace设置笔触颜色空间。
- 释放模式颜色空间对象。
- 释放基本颜色空间对象。
设置模板模式的解剖
您可以通过调用函数CGPatternCreate来指定有关模式的解剖结构的信息。 唯一的区别是你为isColored参数传递false。 有关您提供给CGPatternCreate函数的参数的更多信息,请参阅设置彩色模式的解剖。
将模板模式指定为填充或描边模式
你可以通过调用相应的函数CGContextSetFillPattern或CGContextSetStrokePattern来使用你的模式来填充或描边。 Quartz使用你的模式进行任何后续的填充或描边。
这些函数各有三个参数:
- 图形上下文
- 您先前创建的CGPattern对象
- 颜色分量数组
模板模式在绘图回调中不提供颜色,因此您必须向填充或描边函数传递一个颜色,以告知Quartz要使用的颜色。 清单6-7显示了如何为模板模式设置颜色的示例。 Quartz在您之前设置的颜色空间中解析颜色数组中的值。 因为此示例使用设备RGB,所以颜色数组包含红色,绿色和蓝色组件的值。 第四个值指定颜色的不透明度。
// 为彩色图案设置不透明度的代码
static const CGFloat color[4] = { 0, 1, 1, 0.5 }; //cyan, 50% transparent
CGContextSetFillPattern (myContext, myPattern, color);
用钢板蜡纸图案绘图
完成上述步骤后,您可以调用任何绘制的Quartz 2D函数。 您的模式用作“绘画”。例如,您可以调用CGContextStrokePath,CGContextFillPath,CGContextFillRect或任何其他绘制的函数。
完整的模板图案绘制功能
代码清单6-8中的代码包含一个绘制模板模式的函数。 该功能包括前面讨论的所有步骤。 每个编号的代码行的详细说明在列表之后。
// 绘制模具模式的函数
#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
}
这里是代码做了什么:
- 声明一个数组来保存颜色值,并将该值(将在RGB颜色空间中)设置为不透明的绿色。
- 声明和填充回调结构,传递0作为版本和指向绘图回调函数的指针。此示例不提供发布信息回调,因此该字段设置为NULL。
- 创建RGB设备颜色空间。如果图案绘制到显示器,则需要提供此类型的颜色空间。
- 从RGB设备颜色空间创建模式颜色空间对象。
- 将填充颜色空间设置为刚刚创建的图案颜色空间对象。
- 创建模式对象。注意,第二个到最后一个参数 - isColored参数 - 是false。模板模式不提供颜色,因此必须为此参数传递false。所有其他参数与为彩色图案示例传递的参数类似。看到一个完整的彩色图案绘画功能。
- 设置填充模式,传递先前声明的颜色数组。
- 释放CGPattern对象。
- 填充一个矩形。 Quartz使用刚刚设置的模式填充矩形。