本文轉載自:http://southpeak.github.io/2015/01/08/quartz2d-12/
CGLayer對象(CGLayerRef數據類型)允許程序使用層來進行繪制。
層適合于以下幾種情況:
高質量離屏渲染,以繪制我們想重用的圖形。例如,我們可能要建立一個場景并重用相同的背景。將背景場景繪制于一個層上,然后在需要的時候再繪制層。一個額外的好處是我們不需要知道顏色空間或其它設備依賴的信息來繪制層。
重復繪制。例如,我們可能想創建一個由相同元素反復繪制而組成的模式。將元素繪制到一個層中,然后重復繪制這個層,如圖12-1所示。任何我們重復繪制的Quartz對象,包括CGPath, CGShading和CGPDFPage對象,都可以通過將其繪制到CGLayer來優化性能。注意一個層不僅僅是用于離屏繪制;我們也可以將其用于那些不是面向屏幕的圖形上下文,如PDF圖形上下文。
緩存。雖然我們可以將層用于此目的,但通常不需要這樣做,因為Quartz Compositor已經做了此事。如果我們必須繪制一個緩存,則使用層來代替位圖圖形上下文。
Figure 12-1 Repeatedly painting the same butterfly image
CGLayer對象和透明層是與CGPath對象以及CGContext函數創建的路徑并行的。對于一個CGLayer或者CGPath對象,我們可以將其繪制到一個抽象目標,之后可以將其完整地繪制到另一個目標,如顯示器或才PDF中。當我們在透明層上繪制或者使用繪制路徑的CGContext函數時,可以直接繪制到圖形上下文表示的目標上,而不需要負責組裝繪制的中間抽象目標。
一個層由CGLayerRef數據類型表示,是為優化性能而設計的。在可能的時候,Quartz使用合適的機制將一個CGLayer對象緩存到與之相關的Quartz圖形上下文中。例如,與顯卡相關的圖形上下文可能將層緩存到顯卡中,這樣繪制在層中的內容時,就比渲染從一個位圖圖形上下文中構造的類似圖像要快得多。基于這個原因,層比位圖圖形上下文更適用于離屏繪制。
所有的Quartz繪制函數都是繪制到圖形上下文中。圖形上下文提供了一個抽象的渲染目標,而將我們從目標的細節中解放出來。我們使用用戶空間,Quartz執行必要的轉換來將繪圖正確地渲染到目標。當我們使用CGLayer對象來繪制時,我們也是繪制到圖形上下文中。圖12-1演示了層繪制的必要步驟。
Figure 12-2 Layer drawing
所有在圖形上下文中層的繪制都是以使用函數CGLayerCreateWithContext創建一個CGLayer對象開始的。用于創建CGLayer對象的圖形上下文通常是一個window圖形上下文。Quartz創建一個層,使得它具有圖形上下文的所有特性:包括分辨率,顏色空間和圖形狀態設置。如果我們不想使用圖形上下文的大小,則可以提供一個大小給層。在圖12-2中,左側顯示了用于創建層的圖形上下文。框右側的灰色部分,即標記為CGLayer對象的部分表示新創建的層。
在我們可以繪制層之前,我們必須通過調用CGLayerGetContext函數來獲取與層相關的圖形上下文。這個圖形上下文與用于創建層的圖形上下文是差不多的。只要用于創建層的圖形上下文是一個window圖形上下文,則CGLayer圖形上下文會盡可能地被緩存到GPU中。圖12-2中位于框右側的白色部分表示新創建的層圖形上下文。
在層圖形上下文中繪制與在其它圖形上下文中繪制一樣,將層圖形上下文作為參數傳給繪制函數。圖12-2顯示了一片繪制到層圖形上下文的葉子。
當我們準備使用層的內容時,我們可以調用函數CGContextDrawLayerInRect或者CGContextDrawLayerAtPoint將層繪制到一個圖形上下文。通常情況下,我們會將層繪制到創建層對象的圖形上下文中,但這不是必須的。我們可以將層繪制到任意的圖形上下文,記住:層帶有創建層對象的圖形上下文的所有特性,這可能會產生一些限制(如性能或分辨率)。例如,與屏幕關聯的層可能會被緩存到顯卡中。如果目標上下文是一個打印機或PDF上下文,則可能需要將層對象從顯卡中取出并放到內存中,從而導致性能很差。
圖12-2顯示了層的內容–葉子–被重復地繪制到創建層對象的圖形上下文中。我們可以在釋放CGLayer對象之前,任意地重復使用層中的繪圖。
我們需要按照如下幾個步驟來使用層對象進行繪制:
創建一個使用已存在的圖形上下文初始化的層對象
為層獲取圖形上下文
繪制到CGLayer圖形上下文
將層繪制到目標圖形上下文
我們將在下面詳細描述這幾個步驟。
函數CGLayerCreateWithContext返回一個使用已存在的圖形上下文初始化的層對象。這個層對象繼承了該圖形上下文的所有特性,包括顏色空間、大小、分辨率和像素格式。后期當我們繪制層對象到一個目標時,Quartz會自動對層與目標上下文進行顏色匹配。
函數CGLayerCreateWithContext帶有三個參數:
用于創建層的圖形上下文。通常我們傳遞一個window圖形上下文以便后面可以離屏繪制層。
層相對于圖形上下文的大小。層的大小可以和圖形上下文一樣,或者更小。如果想要獲得層的大小,我們可以調用函數CGLayerGetSize。
一個輔助字典。這個參數現在已經不用了,所以傳遞NULL即可。
Quartz總是在一個圖形上下文中進行繪制。現在我們有了一個層對象,我們必須創建一個與層相關的圖形上下文。所有繪制到層圖形上下文的內容都是層的一部分。
函數CGLayerGetContext獲取一個層對象作為參數,并返回與之相關的圖形上下文。
在獲取到與層相關的圖形上下文之后,我們可以在層圖形上下文中繪制任何東西。我們可以打開一個PDF文件或一個圖像文件,并將文件內容繪制到層中。我們可以使用Quartz 2D的任何函數來繪制矩形、直線或其它繪制單元。圖12-3顯示了在層中繪制一個矩形和直線。
Figure 12-3 A layer that contains two rectangles and a series of lines
例如,為了在CGLayer圖形上下文中繪制一個填充矩形,我們調用函數CGContextFillRect,并提供從CGLayerGetContext函數中獲取到的圖形上下文作為參數。假設這個圖形上下文命名為myLayerContext,則函數調用如下:
CGContextFillRect (myLayerContext, myRect)
當我們已經準備好將層繪制到目標圖形上下文時,我們可以使用以下任一一個函數:
CGContextDrawLayerInRect:將層繪制到圖形上下文中指定的矩形內。
CGContextDrawLayerAtPoint:將層繪制到圖形上下文中指定的點。
通常情況下,我們提供的目標圖形上下文是一個window圖形上下文,這也是我們用于創建層對象所使用的圖形上下文。圖12-4顯示了重復繪制圖12-3所繪制的層。為了達到模式效果,我們可以使用上面兩個方法中的任意一個,只是每次改變偏移量而已。例如,我們每次繪制層時,可以調用函數CGContextTranslateCTM來改變坐標系統的原點。
Figure 12-4 Drawing a layer repeatedly
注意:我們不必要將層繪制到初始層所使用的圖形上下文中。然而,如果我們將層繪制到其它圖形上下文中,原始圖形上下文的所有限制都會反映到我們的繪圖中。
這一節演示了如何使用CGLayer對象來在屏幕上繪制圖12-5中的旗子。首先我們會看到如何將旗子分解成簡單的繪制單元,然后會看到要完成這些任務的代碼。
Figure 12-5 The result of using layers to draw the United States flag
從上面可以看出,旗子主要分三部分:
紅色條紋和白色條紋的模式。我們可以將這個模式分解為一個單一的紅色條紋,因為對于屏幕繪制來說,我們可以假設其背景顏色為白色。我們創建一個紅色矩形,然后以變化的偏移量來重復繪制這個矩形,以創建美國國旗上的七條紅色條紋。我們將紅色矩形繪制到一個層,然后將其繪制到屏幕上七次。
一個藍色矩形。我們只需要一個藍色矩形,所以沒有必要使用層。當繪制藍色矩形時,直接將其繪制到屏幕上。
50個白色星星的模式。與紅色條紋一下,可以使用層來繪制星星。我們創建星星邊框的一個路徑,然后使用白條來填充。將一個星星繪制到層,然后重復50次繪制這個層,每次繪制時適當調整偏移量。
代碼清單12-2完成了對圖12-5的繪制。myDrawFlag例程在一個Cocoa程序中調用。這個程序傳遞一個window圖形上下文和一個與圖形上下文相關的視圖的大小。
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);
}
在此不再翻譯對代碼的注釋,請各位看官查看文檔原文Core Graphics Layer Drawing。