Quartz 2D編程指南之十二:Core Graphics層繪制

本文轉載自: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獲取一個層對象作為參數,并返回與之相關的圖形上下文。

繪制到CGLayer圖形上下文

在獲取到與層相關的圖形上下文之后,我們可以在層圖形上下文中繪制任何東西。我們可以打開一個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對象來繪制旗子

這一節演示了如何使用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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容