本文轉(zhuǎn)載自:http://southpeak.github.io/2014/12/05/quartz2d-6/
模式(Pattern)是繪制操作的一個(gè)序列,這些繪制操作可以重復(fù)地繪制到一個(gè)圖形上下文上。我們可以像使用顏色一樣使用這些模式。當(dāng)我們使用pattern來繪制時(shí),Quartz將Page分割成模式單元格的集合,其中每個(gè)單元格的大小不是模式圖片的大小,并使用我們提供的回調(diào)函數(shù)來繪制這些單元格。圖6-1演示了一個(gè)繪制到window圖形上下文的模式。
Figure 6-1 A pattern drawn to a window
模式單元格是模式的基礎(chǔ)組件。圖6-1中的模式的單元格如圖6-2所示。其中黑色邊框不是模式單元格的一部分;之所以畫出來是為了顯示模式單元格的邊界。
Figure 6-2 A pattern cell
該模式單元格的大小包含四個(gè)帶顏色的矩形以及這些矩形上部及右側(cè)的白色區(qū)域,如圖6-3所示。每個(gè)模式單元格的黑色邊框不是單元格的一部分;畫出來只是為是標(biāo)明單元格的邊界。當(dāng)我們創(chuàng)建一個(gè)模式單元格時(shí),我們需要定義單元格的邊界并在這個(gè)范圍內(nèi)進(jìn)行繪制。
Figure 6-3 Pattern cells with black rectangles drawn to show the bounds of each cell
我們可以指定水平和豎直方向上兩個(gè)單元格之間的間距。圖6-3所繪制的單元格是相互緊挨著的。而圖6-4在兩個(gè)方向上都指定了單元格之間的間距。我們可以為兩個(gè)方向指定不同的間距。我們亦可以指定間距為負(fù)數(shù),這樣單元格便會(huì)重疊。
Figure 6-4 Spacing between pattern cells
當(dāng)我們繪制一個(gè)模式單元格時(shí),Quartz使用模式空間(pattern space)作為坐標(biāo)系統(tǒng)。模式空間是一個(gè)抽象空間,它會(huì)使用我們創(chuàng)建模式時(shí)指定的變換矩陣(pattern matrix)來映射到默認(rèn)用戶空間。
注意:模式空間與用戶空間是分開的。未轉(zhuǎn)換的模式空間映射到基礎(chǔ)的用戶空間(未轉(zhuǎn)換的),而不管當(dāng)前轉(zhuǎn)換矩陣(CTM)。當(dāng)我們?cè)谀J娇臻g上應(yīng)用轉(zhuǎn)換時(shí),Quartz只將轉(zhuǎn)換應(yīng)用于模式空間。
如果我們不想要Quartz來轉(zhuǎn)換模式單元格,我們可以指定單位矩陣。然而,我們可以使用轉(zhuǎn)換矩陣來達(dá)到有趣的效果。圖6-5顯示了縮放6-2中的模式單元格的效果。圖6-6旋轉(zhuǎn)了這些單元格。圖6-7則平移了這些單元格。
Figure 6-5 A scaled pattern cell
Figure 6-6 A rotated pattern cell
Figure 6-7 A translated pattern cell
著色模式(Colored Patterns)和模板模式(Stencil Patterns)
著色模式有與其相關(guān)的固有顏色。如果修改了創(chuàng)建模式單元格的顏色,則模式也便失去了意義。圖6-8中顯示的蘇格蘭格子就是著色模式的一個(gè)例子。著色模式中的顏色是模式單元格創(chuàng)建流程的一部分,而不是繪制流程的一部分。
Figure 6-8 A colored pattern has inherent color
而其它模式只限定了形狀,因此可以認(rèn)為是模板模式(或者是非著色模式、甚至可以作為圖像蒙板)。圖6-9中展示的紅色和黑色星星就是使用相同的模式單元格。單元格由一個(gè)五角星組成。當(dāng)定義模式單元格時(shí),沒有與之相關(guān)的顏色。顏色值是在繪制過程中指定的,而不是創(chuàng)建過程的一部分。
Figure 6-9 A stencil pattern does not have inherent color
在Quartz 2D中,我們可以創(chuàng)建這兩種模式。
平鋪(Tiling)是將模式單元格繪制到頁面(Page)的某個(gè)部分的過程。當(dāng)Quartz將模式渲染到一個(gè)設(shè)備時(shí),Quartz可能需要調(diào)整模式以適應(yīng)設(shè)備空間。即,在用戶空間定義的模式單元格在渲染到設(shè)備時(shí)可能無法精確匹配,這是由用戶空間單元和設(shè)備像素之間的差異導(dǎo)致的。
Quartz有三個(gè)平鋪選項(xiàng),以在必要時(shí)調(diào)整模式:
沒有失真(no distortion): 以細(xì)微調(diào)整模式單元格之間的間距為代價(jià),但通常不超過一個(gè)設(shè)備像素。
最小的失真的恒定間距:設(shè)定單元格之間的間距,以細(xì)微調(diào)整單元大小為代價(jià),但通常不超過一個(gè)設(shè)備像素。
恒定間距:設(shè)定單元格之間間距,以調(diào)整單元格大小為代價(jià),以求盡快的平鋪
模式操作類似于顏色,我們?cè)O(shè)置一個(gè)填充或描邊(stroke)模式,然后調(diào)用繪制函數(shù)。Quartz使用我們?cè)O(shè)置的模式作為“涂料”。例如,如果我們要使用純色繪制一個(gè)填充的的矩形,我們首先調(diào)用函數(shù)(如CGContextSetFillColor)來設(shè)置填充顏色。然后調(diào)用函數(shù)CGContextFillRect以使用我們指定的顏色來填充矩形。為了繪制一個(gè)模式,顏色調(diào)用函數(shù)CGContextSetFillPattern來設(shè)置指定的模式。繪制顏色和繪制模式的不同之處在于我們必須先定義一個(gè)模式。我們?yōu)楹瘮?shù)CGContextSetFillPattern提供模式和顏色信息。我們將在下面的繪制著色模式和繪制模板模式章節(jié)看到如何創(chuàng)建、設(shè)置和繪制模式。
這里有個(gè)例子說明Quartz在幕后是如何繪制一個(gè)模式的。當(dāng)我們填充或描邊一個(gè)模式時(shí),Quartz會(huì)按照以下指令來繪制每一個(gè)模式單元格:
保存圖形狀態(tài)
將當(dāng)前轉(zhuǎn)換矩陣應(yīng)用到原始的模式單元格上
連接CTM與模式矩陣
裁剪模式單元格的邊界矩形
調(diào)用繪制回調(diào)函數(shù)來繪制單元格
恢復(fù)圖形狀態(tài)
Quartz會(huì)執(zhí)行所有平鋪操作,重復(fù)繪制模式單元格到繪制空間,直到渲染滿整個(gè)空間。我們可以填充和描邊一個(gè)模式。模式單元格可以是指定的任何大小。如果我們想要看到模式,我們需要確保模式單元格與繪制空間匹配。例如,如果我們的模式單元格是8*10個(gè)單位的,而我們用這個(gè)模式來描邊一個(gè)只有2個(gè)單位的直線,則這個(gè)模式單元格將會(huì)被裁剪。這種情況下,我們可能無法辨認(rèn)出我們的模式。
繪制著色模式需要執(zhí)行以下五步操作:
寫一個(gè)繪制著色模式單元格的回調(diào)函數(shù)
設(shè)置著色模式的顏色空間
設(shè)置著色模式的骨架(Anatomy)
指定著色模式作為填充或描邊模式
使用著色模式繪制
繪制模板模式也是類似這幾步。兩者之間的區(qū)別在于如何設(shè)置顏色信息。
寫一個(gè)繪制著色模式單元格的回調(diào)函數(shù)
一個(gè)模式單元格看起來是什么樣的完全取決于我們。在這個(gè)例子中,代碼清單6-1繪制了圖6-2所示的模式單元格。
Listing 6-1 A drawing callback that draws a colored pattern cell
#defineH_PATTERN_SIZE 16
#defineV_PATTERN_SIZE 18
voidMyDrawColoredPattern(void*info, CGContextRef myContext)
{
CGFloat subunit =5;
CGSize size = {subunit, subunit};
CGPoint point1 = {0,0}, point2 = {subunit, subunit}, point3 = {0,subunit}, point4 = {subunit,0};
CGRect myRect1 = {point1, size}, myRect2 = {point2, size}, myRect3 = {point3, size}, myRect4 = {point4, size};
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);
}
模式單元格繪制函數(shù)是類似于下面這種格式的一個(gè)回調(diào)函數(shù)
typedefvoid(*CGPatternDrawPatternCallback)(
void*info,
CGContextRef context
);
我們可以隨意命名我們的回調(diào)函數(shù)。代碼清單6-1中命名為MyDrawColoredPattern。這個(gè)回調(diào)函數(shù)帶有兩個(gè)參數(shù):
info: 一個(gè)指向模式相關(guān)數(shù)據(jù)的指針。這個(gè)參數(shù)是可選的,可以傳遞NULL。傳遞給回調(diào)的數(shù)據(jù)與后面創(chuàng)建模式的數(shù)據(jù)是一樣的。
context: 繪制模式單元格的圖形上下文
代碼清單6-1中繪制的模式單元格是隨意的。以下是一些關(guān)于繪制代碼的重要信息:
需要聲明模式大小。在繪制時(shí)我們需要記住模式大小。在這個(gè)例子中,大小是全局聲明的,繪制函數(shù)沒有具體提到大小,除了在注釋中。然后,我們將模式大小指定給Quartz 2D。
繪制函數(shù)后面是由CGPatternDrawPatternCallback回調(diào)函數(shù)類型定義定義的原型
代碼中執(zhí)行的繪制設(shè)置了顏色,讓其成為一個(gè)著色模式。
代碼清單6-1中的代碼使用顏色來繪制模式單元格。我們必須設(shè)置基本的模式顏色空間為NULL,以確保Quartz使用繪制路徑指定的顏色來繪制,如代碼清單6-2所示。
Listing 6-2 Creating a base pattern color space
CGColorSpaceRef patternSpace;
// 創(chuàng)建模式顏色空間,并傳遞NULL作為參數(shù)
patternSpace = CGColorSpaceCreatePattern (NULL);
// 在模式顏色空間中設(shè)置填充顏色
CGContextSetFillColorSpace (myContext, patternSpace);
// 釋放模式顏色空間
CGColorSpaceRelease (patternSpace);
一個(gè)模式的骨架基本信息保存在CGPattern對(duì)象中。我們調(diào)用CGPatternCreate函數(shù)來創(chuàng)建一個(gè)CGPattern對(duì)象,其原型如代碼清單6-3所示:
Listing 6-3 The CGPatternCreate function prototype
CGPatternRef CGPatternCreate (? void *info,
CGRect bounds,
CGAffineTransform matrix,
CGFloat xStep,
CGFloat yStep,
CGPatternTiling tiling,
bool isColored,
const CGPatternCallbacks *callbacks );
其中,
info:是一個(gè)指針,指向我們要傳遞給繪制回調(diào)函數(shù)的數(shù)據(jù)
bound:指定模式單元格的大小
matrix:指定模式矩陣,它將模式坐標(biāo)系統(tǒng)映射到圖形上下文的默認(rèn)坐標(biāo)系統(tǒng)。如果希望兩個(gè)坐標(biāo)系統(tǒng)是一樣的,則可以使用單位矩陣。
xStep, yStep:指定單元格之間的水平和豎直間距。
tiling:平鋪模式,可以是kCGPatternTilingNoDistortion、kCGPatternTilingConstantSpacingMinimalDistortion、kCGPatternTilingConstantSpacing
isColored:指定模式單元格是著色模式(true)還是模板模式(false)
callbacks:是一個(gè)指向CGPatternCallbacks結(jié)構(gòu)體的指針,則定義如下:
structCGPatternCallbacks
{
unsignedintversion;
CGPatternDrawPatternCallback drawPattern;
CGPatternReleaseInfoCallback releaseInfo;
};
我們可以設(shè)置version為0。drawPattern是指向繪制回調(diào)的指針。releaseInfo是指向一個(gè)回調(diào)函數(shù),該回調(diào)在釋放CGPattern對(duì)象時(shí)被調(diào)用,以釋放存儲(chǔ)在我們傳遞給繪制回調(diào)的info參數(shù)中的數(shù)據(jù)。如果在這個(gè)參數(shù)中沒有傳遞任何數(shù)據(jù),則設(shè)置該域?yàn)镹ULL。
我們可以調(diào)用CGContextSetFillPattern或者CGContextSetStrokePattern函數(shù)來使用模式進(jìn)行填充或描邊。Quartz可以將模式用于任何填充或描邊流程。
這兩個(gè)函數(shù)包含以下幾個(gè)參數(shù):
圖形上下文
先前創(chuàng)建的CGPattern對(duì)象
顏色組件的數(shù)組
雖然著色模式提供了自己的顏色,我們?nèi)匀恍枰獋鬟f一個(gè)單一的alpha值來告訴Quartz在繪制時(shí)著色模式的透明度。alpha值的范圍在0到1中。可以如以下代碼來設(shè)置著色模式的透明度:
CGFloat alpha =1;
CGContextSetFillPattern (myContext, myPattern, &alpha);
在完成前面的步驟之后,我們就可以調(diào)用Quartz 2D函數(shù)來繪制了。我們的模式被當(dāng)作“涂料”。例如,可以調(diào)用CGContextStrokePath, CGContextFillPath, CGContextFillRect或其它函數(shù)來繪制。
代碼清單6-4包含一個(gè)繪制著色模式的函數(shù)。這個(gè)函數(shù)包含了前面討論的所有步驟。
Listing 6-4 A function that paints a colored pattern
void MyColoredPatternPainting(CGContextRef myContext,
CGRect rect)
{
CGPatternRef? ? pattern;
CGColorSpaceRef patternSpace;
CGFloat? ? ? ? alpha =1,
width, height;
static const CGPatternCallbacks callbacks = {0,
&MyDrawPattern,
NULL};
CGContextSaveGState (myContext);
patternSpace = CGColorSpaceCreatePattern (NULL);
CGContextSetFillColorSpace (myContext, patternSpace);
CGColorSpaceRelease (patternSpace);
pattern = CGPatternCreate (NULL,
CGRectMake (0,0, H_PSIZE, V_PSIZE),
CGAffineTransformMake (1,0,0,1,0,0),
H_PATTERN_SIZE,
V_PATTERN_SIZE,
kCGPatternTilingConstantSpacing,
true,
&callbacks);
CGContextSetFillPattern (myContext, pattern, &alpha);
CGPatternRelease (pattern);
CGContextFillRect (myContext, rect);
CGContextRestoreGState (myContext);
}
與繪制著色模式類似,繪制模板模式也有5個(gè)步驟:
寫一個(gè)繪制模板模式單元格的回調(diào)函數(shù)
設(shè)置模板模式的顏色空間
設(shè)置模板模式的骨架(Anatomy)
指定模板模式作為填充或描邊模式
使用模板模式繪制
繪制模板模式與繪制著色模式的區(qū)別在于設(shè)置顏色信息。
寫一個(gè)繪制模板模式單元格的回調(diào)函數(shù)
繪制模板模式單元格的回調(diào)與前面描述的繪制顏色模式單元格類似。不同的是繪制模式單元格回調(diào)不需要指定顏色值。圖6-10中顯示的模式單元格即沒有從繪制回調(diào)中獲取顏色。
Figure 6-10 A stencil pattern cell
代碼清單6-5繪制了圖6-10中的模式單元格。可以看到代碼只是簡單地創(chuàng)建并填充了一個(gè)路徑,而沒有設(shè)置顏色。
Listing 6-5 A drawing callback that draws a stencil pattern cell
#definePSIZE 16// size of the pattern cell
static void MyDrawStencilStar(void*info, CGContextRef myContext)
{
intk;
doubler, 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);
}
模板模式要求我們?cè)O(shè)置一個(gè)模式顏色空間用于Quartz的繪制,如代碼清單6-6所示。
Listing 6-6 Code that creates a pattern color space for a stencil pattern
CGPatternRef pattern;
CGColorSpaceRef baseSpace;
CGColorSpaceRef patternSpace;
// 創(chuàng)建一個(gè)通用RGB顏色空間。
baseSpace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
// 創(chuàng)建一個(gè)模式顏色空間。該顏色空間指定如何表示模式的顏色。后面要設(shè)置模式的顏色時(shí),必須使用這個(gè)顏色空間來進(jìn)行設(shè)置
patternSpace = CGColorSpaceCreatePattern (baseSpace);
// 設(shè)置顏色空間來在填充模式時(shí)使用
CGContextSetFillColorSpace (myContext, patternSpace);
// 釋放模式顏色空間
CGColorSpaceRelease(patternSpace);
// 釋放基礎(chǔ)顏色空間
CGColorSpaceRelease(baseSpace);
這一步與上面設(shè)置著色模式是一樣的,不同的是isColored參數(shù)需要傳遞false。
我們可以調(diào)用CGContextSetFillPattern或者CGContextSetStrokePattern函數(shù)來使用模式進(jìn)行填充或描邊。Quartz可以將模式用于任何填充或描邊流程。
這兩個(gè)函數(shù)包含以下幾個(gè)參數(shù):
圖形上下文
先前創(chuàng)建的CGPattern對(duì)象
顏色組件的數(shù)組
由于模板模式在繪制回調(diào)中不提供顏色值,所以我們必須傳遞一個(gè)顏色給填充或描邊函數(shù)來告訴Quartz使用什么顏色。代碼清單6-7顯示了為模板模式設(shè)置顏色的例子。
Listing 6-7 Code that sets opacity for a colored pattern
static const CGFloat color[4] = {0,1,1,0.5};//cyan, 50% transparent
CGContextSetFillPattern (myContext, myPattern, color);
在完成前面的步驟之后,我們就可以調(diào)用Quartz 2D函數(shù)來繪制了。我們的模式被當(dāng)作“涂料”。例如,可以調(diào)用CGContextStrokePath, CGContextFillPath, CGContextFillRect或其它函數(shù)來繪制。
代碼清單6-8包含一個(gè)繪制模板模式的函數(shù)。這個(gè)函數(shù)包含了前面討論的所有步驟。
Listing 6-8 A function that paints a stencil pattern
#definePSIZE 16
void MyStencilPatternPainting(CGContextRef myContext,
constRect *windowRect)
{
CGPatternRef pattern;
CGColorSpaceRef baseSpace;
CGColorSpaceRef patternSpace;
staticconstCGFloat color[4] = {0,1,0,1};
staticconstCGPatternCallbacks callbacks = {0, &drawStar,NULL};
baseSpace = CGColorSpaceCreateDeviceRGB ();
patternSpace = CGColorSpaceCreatePattern (baseSpace);
CGContextSetFillColorSpace (myContext, patternSpace);
CGColorSpaceRelease (patternSpace);
CGColorSpaceRelease (baseSpace);
pattern = CGPatternCreate(NULL, CGRectMake(0,0, PSIZE, PSIZE),
CGAffineTransformIdentity, PSIZE, PSIZE,
kCGPatternTilingConstantSpacing,
false, &callbacks);
CGContextSetFillPattern (myContext, pattern, color);
CGPatternRelease (pattern);
CGContextFillRect (myContext,CGRectMake (0,0,PSIZE*20,PSIZE*20));
}