Core Graphics Layer Drawing
CGLayer对象(CGLayerRef数据类型)允许应用程序使用图层进行绘制。
图层适用于以下:
- 您计划重复使用的绘图的高质量屏幕外显示。例如,您可能正在构建场景并计划重复使用相同的背景。将背景场景绘制到图层,然后在需要时绘制图层。一个额外的好处是,您不需要知道颜色空间或设备相关的信息绘制到图层。
- 重复绘图。例如,您可能想要创建一个由重复绘制的相同项目组成的模式。将项目绘制到图层,然后重复绘制图层,如图12-1所示。您反复绘制的任何Quartz对象(包括CGPath,CGShading和CGPDFPage对象)都可以通过提高性能(如果将其绘制到CGLayer)获益。注意,图层不仅仅用于屏幕上的绘制;您可以将其用于不面向屏幕的图形上下文,例如PDF图形上下文。
- 缓冲。虽然你可以使用图层为此目的,你不应该需要,因为石英合成器使你的部分缓冲不必要。如果必须绘制到缓冲区,请使用图层而不是位图图形上下文。
CGLayer对象和透明层与CGContext函数创建的CGPath对象和路径并行。 在CGLayer或CGPath对象的情况下,您绘制到抽象目标,然后可以绘制完整的绘画到另一个目的地,如显示或PDF。 当您绘制到透明层或使用绘制路径的CGContext函数时,您直接绘制由图形上下文表示的目标。 没有中间抽象的目的地组装这幅画。
图层绘制如何工作
由CGLayerRef数据类型表示的图层是为实现最佳性能而设计的。如果可能,Quartz使用适合于与其相关联的Quartz图形上下文类型的机制缓存CGLayer对象。例如,与视频卡相关联的图形上下文可以在视频卡上缓存该层,这使得绘制层中的内容比渲染从位图图形上下文构造的类似图像快得多。由于这个原因,一个图层通常是屏幕外绘制比位图图形上下文更好的选择。
所有Quartz绘图函数绘制到一个图形上下文。图形上下文提供了目标的抽象,使您无需了解目标的详细信息,例如其分辨率。您在用户空间中工作,Quartz执行必要的转换以正确地将图形渲染到目标。当您使用CGLayer对象进行绘制时,还会绘制到图形上下文。图12-1说明了图层绘制的必要步骤。
12-2 图层绘图所有图层绘图都从一个图形上下文开始,从中使用函数CGLayerCreateWithContext创建一个CGLayer对象。用于创建CGLayer对象的图形上下文通常是窗口图形上下文。 Quartz创建一个图层,使其具有图形上下文的所有特征 - 其分辨率,颜色空间和图形状态设置。如果不想使用图形上下文的大小,可以为图层提供大小。在图12-2中,左侧显示了用于创建图层的图形上下文。右侧框中的灰色部分,标记为CGLayer对象,表示新创建的图层。
在您可以绘制到图层之前,必须通过调用函数CGLayerGetContext来获取与图层相关联的图形上下文。此图形上下文与用于创建图层的图形上下文具有相同的风格。只要用于创建图层的图形上下文是一个窗口图形上下文,那么CGLayer图形上下文将被缓存到GPU,如果可能的话。图12-2右侧的框的白色部分表示新创建的层图形上下文。
您可以绘制到图层的图形上下文,就像绘制任何图形上下文,将图层的图形上下文传递到绘图功能。图12-2显示了绘制到图层上下文的叶形。
当你准备好使用图层的内容,你可以调用函数CGContextDrawLayerInRect或CGContextDrawLayerAtPoint,将图层绘制到图形上下文中。通常,你会绘制到用于创建图层对象的相同的图形上下文,但不是必需的。您可以将图层绘制到任何图形上下文,请记住,图层绘图具有用于创建图层上下文的特征,这可能会施加某些约束(例如性能或分辨率)。例如,与屏幕相关联的层可以被高速缓存在视频硬件中。如果目的地上下文是打印或PDF上下文,则可能需要从图形硬件取回到存储器,导致差的性能。
图12-2显示了图层的内容 - 重复绘图到用于创建图层对象的图形上下文。在释放CGLayer对象之前,您可以重复使用图层中的绘图次数。
小提示:当您想要绘制图形的各个部分以实现像阴影一组对象这样的效果时,请使用透明度图层。 (请参阅透明层。)当您要绘制屏幕或需要重复绘制相同的事物时,使用CGLayer对象。
用图层绘制
您需要执行以下部分中描述的任务以使用CGLayer对象进行绘制:
- 创建使用现有图形上下文初始化的CGLayer对象
- 为图层获取图形上下文
- 绘制到CGLayer图形上下文
- 将图层绘制到目标图形上下文
有关详细代码示例,请参阅示例:使用多个CGLayer对象绘制标志。
创建使用现有图形上下文初始化的CGLayer对象
函数CGLayerCreateWithContext返回使用现有图形上下文初始化的图层。 该图层继承了图形上下文的所有特性,包括颜色空间,大小,分辨率和像素格式。 后来,当你将图层绘制到目的地时,Quartz自动将图层匹配到目标上下文。
函数CGLayerCreateWithContext有三个参数:
- 从中创建图层的图形上下文。 通常,您传递一个窗口图形上下文,以便以后可以在屏幕上绘制图层。
- 图层相对于图形上下文的大小。 该图层可以与图形上下文的大小相同或更小。 如果以后需要检索图层大小,可以调用函数CGLayerGetSize。
- 辅助字典。 此参数当前未使用,因此传递NULL。
为图层获取图形上下文
Quartz总是绘制一个图形上下文。 既然你有一个图层,你必须创建一个与图层相关的图形上下文。 你绘制到图层上下文中的任何内容都是图层的一部分。
函数CGLayerGetContext采用一个图层作为参数,并返回与图层相关的图形上下文。
绘制到CGLayer图形上下文
获取与图层相关联的图形上下文后,可以对图层图形上下文执行任何绘图。 您可以打开PDF文件或图像文件,并将文件内容绘制到图层。 您可以使用任何Quartz 2D函数来绘制矩形,线条和其他绘图图元。 图12-3显示了将矩形和线绘制到图层的示例。
12-3 包含两个矩形和一系列线的图层例如,要将填充矩形绘制到CGLayer图形上下文,您可以调用函数CGContextFillRect,提供从函数CGLayerGetContext获取的图形上下文。 如果图形上下文命名为myLayerContext,则函数调用如下所示:
CGContextFillRect(myLayerContext,myRect)
将图层绘制到目标图形上下文
当您准备好将图层绘制到其目标图形上下文时,可以使用以下任一函数:
- CGContextDrawLayerInRect,它在指定的矩形中将图层绘制到图形上下文。
- CGContextDrawLayerAtPoint,它在指定的点将图层绘制到图形上下文。
通常,您提供的目标图形上下文是一个窗口图形上下文,它与您用于创建图层的图形上下文相同。 图12-4显示了重复绘制图12-3所示图层的结果。 要实现图案效果,您可以重复调用任一图层绘制函数 - CGContextDrawLayerAtPoint或CGContextDrawLayerInRect - 每次更改偏移量。 例如,您可以调用函数CGContextTranslateCTM来更改每次绘制图层时坐标空间的原点。
12-4 重复绘制图层提示:您不需要将图层绘制到用于初始化图层的相同图形上下文。 但是,如果将图层绘制到另一个图形上下文,原始图形上下文的任何限制都会强加在您的图形上。
示例:使用多个CGLayer对象来绘制国旗
本节说明如何使用两个CGLayer对象来绘制屏幕上图12-5所示的标志。 首先,你将看到如何将标志减少到简单的绘图基元,然后你将看到完成绘图所需的代码。
12-5 用图层绘制美国国旗的结果从在屏幕上绘制的角度来看,该标志具有三个部分:
- 红色和白色条纹的图案。您可以将图案缩小为单个红色条纹,因为在屏幕上绘制时,可以假设为白色背景。您创建一个单一的红色矩形,然后重复绘制矩形在各种偏移量,以创建美国国旗必要的七条红色条纹。层是重复绘图的理想选择。您将红色矩形绘制到图层,然后在屏幕上绘制七次。
- 一个蓝色矩形。你需要一个蓝色的矩形,所以使用一个层是没有什么好处。当到了绘制蓝色矩形的时候,直接在屏幕上绘制。
- 50个白色星的图案。像红色条纹,一层是理想的绘制的星星。您创建 一个概述星形形状的路径,然后用白色填充路径。绘制一个星星到一个图层,然后绘制图层50次,每次调整偏移量以获得适当的间距。
图12-2中的代码生成如图12-5所示的输出。每个编号的代码行的详细说明显示在列表之后。列表是相当长的,所以你可能想打印的解释,以便你可以看看你看代码。 myDrawFlag例程从Cocoa应用程序中调用。应用程序传递窗口图形上下文和指定与窗口图形上下文相关联的视图大小的矩形。
注:在调用此或任何使用CGLayer对象的例程之前,必须检查以确保系统正在运行Mac OS X v10.4或更高版本,并且具有支持使用CGLayer对象的图形卡。
// 使用图层绘制国旗的代码
void myDrawFlag (CGContextRef context, CGRect* contextRect)
{
int i, j,
num_six_star_rows = 5,
num_five_star_rows = 4;
CGFloat start_x = 5.0,// 1
start_y = 108.0,// 2
red_stripe_spacing = 34.0,// 3
h_spacing = 26.0,// 4
v_spacing = 22.0;// 5
CGContextRef myLayerContext1,
myLayerContext2;
CGLayerRef stripeLayer,
starLayer;
CGRect myBoundingBox,// 6
stripeRect,
starField;
// ***** Setting up the primitives *****
const CGPoint myStarPoints[] = {{ 5, 5}, {10, 15},// 7
{10, 15}, {15, 5},
{15, 5}, {2.5, 11},
{2.5, 11}, {16.5, 11},
{16.5, 11},{5, 5}};
stripeRect = CGRectMake (0, 0, 400, 17); // stripe// 8
starField = CGRectMake (0, 102, 160, 119); // star field// 9
myBoundingBox = CGRectMake (0, 0, contextRect->size.width, // 10
contextRect->size.height);
// ***** Creating layers and drawing to them *****
stripeLayer = CGLayerCreateWithContext (context, // 11
stripeRect.size, NULL);
myLayerContext1 = CGLayerGetContext (stripeLayer);// 12
CGContextSetRGBFillColor (myLayerContext1, 1, 0 , 0, 1);// 13
CGContextFillRect (myLayerContext1, stripeRect);// 14
starLayer = CGLayerCreateWithContext (context,
starField.size, NULL);// 15
myLayerContext2 = CGLayerGetContext (starLayer);// 16
CGContextSetRGBFillColor (myLayerContext2, 1.0, 1.0, 1.0, 1);// 17
CGContextAddLines (myLayerContext2, myStarPoints, 10);// 18
CGContextFillPath (myLayerContext2); // 19
// ***** Drawing to the window graphics context *****
CGContextSaveGState(context); // 20
for (i=0; i< 7; i++) // 21
{
CGContextDrawLayerAtPoint (context, CGPointZero, stripeLayer);// 22
CGContextTranslateCTM (context, 0.0, red_stripe_spacing);// 23
}
CGContextRestoreGState(context);// 24
CGContextSetRGBFillColor (context, 0, 0, 0.329, 1.0);// 25
CGContextFillRect (context, starField);// 26
CGContextSaveGState (context); // 27
CGContextTranslateCTM (context, start_x, start_y); // 28
for (j=0; j< num_six_star_rows; j++) // 29
{
for (i=0; i< 6; i++)
{
CGContextDrawLayerAtPoint (context,CGPointZero,
starLayer);// 30
CGContextTranslateCTM (context, h_spacing, 0);// 31
}
CGContextTranslateCTM (context, (-i*h_spacing), v_spacing); // 32
}
CGContextRestoreGState(context);
CGContextSaveGState(context);
CGContextTranslateCTM (context, start_x + h_spacing/2, // 33
start_y + v_spacing/2);
for (j=0; j< num_five_star_rows; j++) // 34
{
for (i=0; i< 5; i++)
{
CGContextDrawLayerAtPoint (context, CGPointZero,
starLayer);// 35
CGContextTranslateCTM (context, h_spacing, 0);// 36
}
CGContextTranslateCTM (context, (-i*h_spacing), v_spacing);// 37
}
CGContextRestoreGState(context);
CGLayerRelease(stripeLayer);// 38
CGLayerRelease(starLayer); // 39
}
这里是代码做了什么:
- 声明第一个星的水平位置的变量。
- 声明第一个星的垂直位置的变量。
- 声明标志上红色条纹之间的间距的变量。
- 声明一个变量的标志上的星星之间的水平间距。
- 声明一个变量的标志上的星星之间的垂直间距。
- 声明指定在哪里绘制标志(边界框),条纹图层和星形字段的矩形。
- 声明一个点数组,指定描绘一个星的线。
- 创建一个是单个条带形状的矩形。
- 创建一个为星形字段形状的矩形。
- 创建一个与传递给myDrawFlag例程的窗口图形上下文大小相同的边界框。
- 创建一个使用传递给myDrawFlag例程的窗口图形上下文初始化的图层。
- 获取与该图层相关联的图形上下文。您将使用此图层进行条带绘图。
- 将与条纹图层关联的图形上下文的填充颜色设置为不透明红色。
- 填充代表一个红色条纹的矩形。
- 创建另一个层,该层使用传递给myDrawFlag例程的窗口图形上下文初始化。
- 获取与该图层相关联的图形上下文。您将使用此图层作为星形绘图。
- 将与星形图层关联的图形上下文的填充颜色设置为不透明白色。
- 将由myStarPoints数组定义的10行添加到与星形图层相关联的上下文中。
- 填充路径,其中包含刚刚添加的10行。
- 保存Windows图形上下文的图形状态。你需要这样做,因为你会反复绘制相同的条纹,但在不同的位置。
- 设置一个迭代7次的循环,每次对标志上的每个红色条带。
- 绘制条纹图层(由一条红色条纹组成)。
- 翻转当前变换矩阵,使原点位于必须绘制下一个红色条纹的位置。
- 将图形状态恢复为绘制条纹之前的状态。
- 将填充颜色设置为星形字段的适当蓝色阴影。请注意,此颜色的透明度为1.0。虽然这个例子中的所有颜色都是不透明的,但它们不需要是。通过使用部分透明的颜色,您可以使用分层绘图创建漂亮的效果。回想一下,alpha值为0.0指定透明颜色。
- 用蓝色填充星形字段矩形。将此矩形直接绘制到窗口图形上下文。如果你只画一次,不要使用图层。
- 保存窗口图形上下文的图形状态,因为您将转换CTM以正确定位星星。
- 翻转CTM,使原点位于星形字段中,为第一个(下)行中的第一个星(左侧)定位。
- 这和下一个for循环设置代码重复绘制星形图层,所以标志上的五个奇数行包含六颗星。
- 将星形图层绘制到窗口图形上下文。回想一下,星形图层包含一个白色星形。
- 定位CTM,使原点向右移动,准备绘制下一个星。
- 定位CTM,使原点向上移动,准备绘制下一行星。
- 翻转CTM,使原点位于星形字段中,为从底部起的第二行中的第一个星(左侧)定位。注意,偶数行相对于奇数行偏移。
- 这和下一个for循环设置代码重复绘制星形图层,所以标志上的四个偶数行都包含五颗星。
- 将星形图层绘制到窗口图形上下文。
- 定位CTM,使原点向右移动,准备绘制下一个星。
- 将CTM定位,使原点向下,向左移动,以准备绘制下一行星。
- 释放条带层。
- 释放星形图层