本文轉(zhuǎn)載自:http://southpeak.github.io/2015/01/08/quartz2d-12/
CGLayer對(duì)象(CGLayerRef數(shù)據(jù)類型)允許程序使用層來(lái)進(jìn)行繪制。
層適合于以下幾種情況:
高質(zhì)量離屏渲染,以繪制我們想重用的圖形。例如,我們可能要建立一個(gè)場(chǎng)景并重用相同的背景。將背景場(chǎng)景繪制于一個(gè)層上,然后在需要的時(shí)候再繪制層。一個(gè)額外的好處是我們不需要知道顏色空間或其它設(shè)備依賴的信息來(lái)繪制層。
重復(fù)繪制。例如,我們可能想創(chuàng)建一個(gè)由相同元素反復(fù)繪制而組成的模式。將元素繪制到一個(gè)層中,然后重復(fù)繪制這個(gè)層,如圖12-1所示。任何我們重復(fù)繪制的Quartz對(duì)象,包括CGPath, CGShading和CGPDFPage對(duì)象,都可以通過(guò)將其繪制到CGLayer來(lái)優(yōu)化性能。注意一個(gè)層不僅僅是用于離屏繪制;我們也可以將其用于那些不是面向屏幕的圖形上下文,如PDF圖形上下文。
緩存。雖然我們可以將層用于此目的,但通常不需要這樣做,因?yàn)镼uartz Compositor已經(jīng)做了此事。如果我們必須繪制一個(gè)緩存,則使用層來(lái)代替位圖圖形上下文。
Figure 12-1 Repeatedly painting the same butterfly image
CGLayer對(duì)象和透明層是與CGPath對(duì)象以及CGContext函數(shù)創(chuàng)建的路徑并行的。對(duì)于一個(gè)CGLayer或者CGPath對(duì)象,我們可以將其繪制到一個(gè)抽象目標(biāo),之后可以將其完整地繪制到另一個(gè)目標(biāo),如顯示器或才PDF中。當(dāng)我們?cè)谕该鲗由侠L制或者使用繪制路徑的CGContext函數(shù)時(shí),可以直接繪制到圖形上下文表示的目標(biāo)上,而不需要負(fù)責(zé)組裝繪制的中間抽象目標(biāo)。
一個(gè)層由CGLayerRef數(shù)據(jù)類型表示,是為優(yōu)化性能而設(shè)計(jì)的。在可能的時(shí)候,Quartz使用合適的機(jī)制將一個(gè)CGLayer對(duì)象緩存到與之相關(guān)的Quartz圖形上下文中。例如,與顯卡相關(guān)的圖形上下文可能將層緩存到顯卡中,這樣繪制在層中的內(nèi)容時(shí),就比渲染從一個(gè)位圖圖形上下文中構(gòu)造的類似圖像要快得多?;谶@個(gè)原因,層比位圖圖形上下文更適用于離屏繪制。
所有的Quartz繪制函數(shù)都是繪制到圖形上下文中。圖形上下文提供了一個(gè)抽象的渲染目標(biāo),而將我們從目標(biāo)的細(xì)節(jié)中解放出來(lái)。我們使用用戶空間,Quartz執(zhí)行必要的轉(zhuǎn)換來(lái)將繪圖正確地渲染到目標(biāo)。當(dāng)我們使用CGLayer對(duì)象來(lái)繪制時(shí),我們也是繪制到圖形上下文中。圖12-1演示了層繪制的必要步驟。
Figure 12-2 Layer drawing
所有在圖形上下文中層的繪制都是以使用函數(shù)CGLayerCreateWithContext創(chuàng)建一個(gè)CGLayer對(duì)象開始的。用于創(chuàng)建CGLayer對(duì)象的圖形上下文通常是一個(gè)window圖形上下文。Quartz創(chuàng)建一個(gè)層,使得它具有圖形上下文的所有特性:包括分辨率,顏色空間和圖形狀態(tài)設(shè)置。如果我們不想使用圖形上下文的大小,則可以提供一個(gè)大小給層。在圖12-2中,左側(cè)顯示了用于創(chuàng)建層的圖形上下文??蛴覀?cè)的灰色部分,即標(biāo)記為CGLayer對(duì)象的部分表示新創(chuàng)建的層。
在我們可以繪制層之前,我們必須通過(guò)調(diào)用CGLayerGetContext函數(shù)來(lái)獲取與層相關(guān)的圖形上下文。這個(gè)圖形上下文與用于創(chuàng)建層的圖形上下文是差不多的。只要用于創(chuàng)建層的圖形上下文是一個(gè)window圖形上下文,則CGLayer圖形上下文會(huì)盡可能地被緩存到GPU中。圖12-2中位于框右側(cè)的白色部分表示新創(chuàng)建的層圖形上下文。
在層圖形上下文中繪制與在其它圖形上下文中繪制一樣,將層圖形上下文作為參數(shù)傳給繪制函數(shù)。圖12-2顯示了一片繪制到層圖形上下文的葉子。
當(dāng)我們準(zhǔn)備使用層的內(nèi)容時(shí),我們可以調(diào)用函數(shù)CGContextDrawLayerInRect或者CGContextDrawLayerAtPoint將層繪制到一個(gè)圖形上下文。通常情況下,我們會(huì)將層繪制到創(chuàng)建層對(duì)象的圖形上下文中,但這不是必須的。我們可以將層繪制到任意的圖形上下文,記?。簩訋в袆?chuàng)建層對(duì)象的圖形上下文的所有特性,這可能會(huì)產(chǎn)生一些限制(如性能或分辨率)。例如,與屏幕關(guān)聯(lián)的層可能會(huì)被緩存到顯卡中。如果目標(biāo)上下文是一個(gè)打印機(jī)或PDF上下文,則可能需要將層對(duì)象從顯卡中取出并放到內(nèi)存中,從而導(dǎo)致性能很差。
圖12-2顯示了層的內(nèi)容–葉子–被重復(fù)地繪制到創(chuàng)建層對(duì)象的圖形上下文中。我們可以在釋放CGLayer對(duì)象之前,任意地重復(fù)使用層中的繪圖。
我們需要按照如下幾個(gè)步驟來(lái)使用層對(duì)象進(jìn)行繪制:
創(chuàng)建一個(gè)使用已存在的圖形上下文初始化的層對(duì)象
為層獲取圖形上下文
繪制到CGLayer圖形上下文
將層繪制到目標(biāo)圖形上下文
我們將在下面詳細(xì)描述這幾個(gè)步驟。
創(chuàng)建一個(gè)使用已存在的圖形上下文初始化的層對(duì)象
函數(shù)CGLayerCreateWithContext返回一個(gè)使用已存在的圖形上下文初始化的層對(duì)象。這個(gè)層對(duì)象繼承了該圖形上下文的所有特性,包括顏色空間、大小、分辨率和像素格式。后期當(dāng)我們繪制層對(duì)象到一個(gè)目標(biāo)時(shí),Quartz會(huì)自動(dòng)對(duì)層與目標(biāo)上下文進(jìn)行顏色匹配。
函數(shù)CGLayerCreateWithContext帶有三個(gè)參數(shù):
用于創(chuàng)建層的圖形上下文。通常我們傳遞一個(gè)window圖形上下文以便后面可以離屏繪制層。
層相對(duì)于圖形上下文的大小。層的大小可以和圖形上下文一樣,或者更小。如果想要獲得層的大小,我們可以調(diào)用函數(shù)CGLayerGetSize。
一個(gè)輔助字典。這個(gè)參數(shù)現(xiàn)在已經(jīng)不用了,所以傳遞NULL即可。
Quartz總是在一個(gè)圖形上下文中進(jìn)行繪制。現(xiàn)在我們有了一個(gè)層對(duì)象,我們必須創(chuàng)建一個(gè)與層相關(guān)的圖形上下文。所有繪制到層圖形上下文的內(nèi)容都是層的一部分。
函數(shù)CGLayerGetContext獲取一個(gè)層對(duì)象作為參數(shù),并返回與之相關(guān)的圖形上下文。
在獲取到與層相關(guān)的圖形上下文之后,我們可以在層圖形上下文中繪制任何東西。我們可以打開一個(gè)PDF文件或一個(gè)圖像文件,并將文件內(nèi)容繪制到層中。我們可以使用Quartz 2D的任何函數(shù)來(lái)繪制矩形、直線或其它繪制單元。圖12-3顯示了在層中繪制一個(gè)矩形和直線。
Figure 12-3 A layer that contains two rectangles and a series of lines
例如,為了在CGLayer圖形上下文中繪制一個(gè)填充矩形,我們調(diào)用函數(shù)CGContextFillRect,并提供從CGLayerGetContext函數(shù)中獲取到的圖形上下文作為參數(shù)。假設(shè)這個(gè)圖形上下文命名為myLayerContext,則函數(shù)調(diào)用如下:
CGContextFillRect (myLayerContext, myRect)
當(dāng)我們已經(jīng)準(zhǔn)備好將層繪制到目標(biāo)圖形上下文時(shí),我們可以使用以下任一一個(gè)函數(shù):
CGContextDrawLayerInRect:將層繪制到圖形上下文中指定的矩形內(nèi)。
CGContextDrawLayerAtPoint:將層繪制到圖形上下文中指定的點(diǎn)。
通常情況下,我們提供的目標(biāo)圖形上下文是一個(gè)window圖形上下文,這也是我們用于創(chuàng)建層對(duì)象所使用的圖形上下文。圖12-4顯示了重復(fù)繪制圖12-3所繪制的層。為了達(dá)到模式效果,我們可以使用上面兩個(gè)方法中的任意一個(gè),只是每次改變偏移量而已。例如,我們每次繪制層時(shí),可以調(diào)用函數(shù)CGContextTranslateCTM來(lái)改變坐標(biāo)系統(tǒng)的原點(diǎn)。
Figure 12-4 Drawing a layer repeatedly
注意:我們不必要將層繪制到初始層所使用的圖形上下文中。然而,如果我們將層繪制到其它圖形上下文中,原始圖形上下文的所有限制都會(huì)反映到我們的繪圖中。
例子:使用多個(gè)CGLayer對(duì)象來(lái)繪制旗子
這一節(jié)演示了如何使用CGLayer對(duì)象來(lái)在屏幕上繪制圖12-5中的旗子。首先我們會(huì)看到如何將旗子分解成簡(jiǎn)單的繪制單元,然后會(huì)看到要完成這些任務(wù)的代碼。
Figure 12-5 The result of using layers to draw the United States flag
從上面可以看出,旗子主要分三部分:
紅色條紋和白色條紋的模式。我們可以將這個(gè)模式分解為一個(gè)單一的紅色條紋,因?yàn)閷?duì)于屏幕繪制來(lái)說(shuō),我們可以假設(shè)其背景顏色為白色。我們創(chuàng)建一個(gè)紅色矩形,然后以變化的偏移量來(lái)重復(fù)繪制這個(gè)矩形,以創(chuàng)建美國(guó)國(guó)旗上的七條紅色條紋。我們將紅色矩形繪制到一個(gè)層,然后將其繪制到屏幕上七次。
一個(gè)藍(lán)色矩形。我們只需要一個(gè)藍(lán)色矩形,所以沒(méi)有必要使用層。當(dāng)繪制藍(lán)色矩形時(shí),直接將其繪制到屏幕上。
50個(gè)白色星星的模式。與紅色條紋一下,可以使用層來(lái)繪制星星。我們創(chuàng)建星星邊框的一個(gè)路徑,然后使用白條來(lái)填充。將一個(gè)星星繪制到層,然后重復(fù)50次繪制這個(gè)層,每次繪制時(shí)適當(dāng)調(diào)整偏移量。
代碼清單12-2完成了對(duì)圖12-5的繪制。myDrawFlag例程在一個(gè)Cocoa程序中調(diào)用。這個(gè)程序傳遞一個(gè)window圖形上下文和一個(gè)與圖形上下文相關(guān)的視圖的大小。
Listing 12-1 Code that uses layers to draw a flag
void myDrawFlag(CGContextRef context, CGRect* contextRect)
{
inti, j,
num_six_star_rows =5,
num_five_star_rows =4;
CGFloat? ? ? start_x =5.0,
start_y =108.0,
red_stripe_spacing =34.0,
h_spacing =26.0,
v_spacing =22.0;
CGContextRef myLayerContext1,
myLayerContext2;
CGLayerRef? stripeLayer,
starLayer;
CGRect? ? ? myBoundingBox,
stripeRect,
starField;
// ***** Setting up the primitives *****
CGPoint point1 = {5,5}, point2 = {10,15}, point3 = {10,15}, point4 = {15,5};
CGPoint point5 = {15,5}, point6 = {2.5,11}, point7 = {2.5,11}, point8 = {16.5,11};
CGPoint point9 = {16.5,11}, point10 = {5,5};
constCGPoint myStarPoints[] = {point1, point2,
point3, point4,
point5, point6,
point7, point8,
point9, point10};
stripeRect? = CGRectMake (0,0,400,17);// stripe
starField? =? CGRectMake (0,102,160,119);// star field
myBoundingBox = CGRectMake (0,0, contextRect->size.width,
contextRect->size.height);
// ***** Creating layers and drawing to them *****
stripeLayer = CGLayerCreateWithContext (context,
stripeRect.size,NULL);
myLayerContext1 = CGLayerGetContext (stripeLayer);
CGContextSetRGBFillColor (myLayerContext1,1,0,0,1);
CGContextFillRect (myLayerContext1, stripeRect);
starLayer = CGLayerCreateWithContext (context,
starField.size,NULL);
myLayerContext2 = CGLayerGetContext (starLayer);
CGContextSetRGBFillColor (myLayerContext2,1.0,1.0,1.0,1);
CGContextAddLines (myLayerContext2, myStarPoints,10);
CGContextFillPath (myLayerContext2);
// ***** Drawing to the window graphics context *****
CGContextSaveGState(context);
for(i=0; i<7;? i++)
{
CGContextDrawLayerAtPoint (context, CGPointZero, stripeLayer);
CGContextTranslateCTM (context,0.0, red_stripe_spacing);
}
CGContextRestoreGState(context);
CGContextSetRGBFillColor (context,0,0,0.329,1.0);
CGContextFillRect (context, starField);
CGContextSaveGState (context);
CGContextTranslateCTM (context, start_x, start_y);
for(j=0; j< num_six_star_rows;? j++)
{
for(i=0; i<6;? i++)
{
CGContextDrawLayerAtPoint (context,CGPointZero,
starLayer);
CGContextTranslateCTM (context, h_spacing,0);
}
CGContextTranslateCTM (context, (-i*h_spacing), v_spacing);
}
CGContextRestoreGState(context);
CGContextSaveGState(context);
CGContextTranslateCTM (context, start_x + h_spacing/2,
start_y + v_spacing/2);
for(j=0; j< num_five_star_rows;? j++)
{
for(i=0; i<5;? i++)
{
CGContextDrawLayerAtPoint (context, CGPointZero,
starLayer);
CGContextTranslateCTM (context, h_spacing,0);
}
CGContextTranslateCTM (context, (-i*h_spacing), v_spacing);
}
CGContextRestoreGState(context);
CGLayerRelease(stripeLayer);
CGLayerRelease(starLayer);
}
在此不再翻譯對(duì)代碼的注釋,請(qǐng)各位看官查看文檔原文Core Graphics Layer Drawing。