模式是重復繪制到圖形上下文的繪制操作序列。 您可以使用與使用顏色相同的方式使用模式。 當使用模式繪制時,Quartz將頁面劃分為一組模式單元格,每個單元格都是模式圖像的大小,并使用您提供的回調繪制每個單元格。 圖6-1顯示了繪制到窗口圖形上下文的模式。
模式的解剖學
圖案單元是圖案的基本組成部分。 圖6-1所示模式的模式單元如圖6-2所示。 黑色矩形不是圖案的一部分; 它被繪制以顯示模式單元格結束的位置。
該特定模式單元的大小包括四個彩色矩形的區域和矩形的上方和右側的空間,如圖6-3所示。 圖中圍繞每個圖案單元的黑色矩形不是單元的一部分; 它被繪制以指示單元的邊界。 當創建模式單元格時,您定義單元格的邊界并在邊界內繪制。
您可以指定Quartz在水平和垂直方向上從下一個圖元單元開始繪制每個圖案單元的開始距離。 繪制圖6-3中的圖案單元,使得一個圖案單元的開始恰好是遠離下一圖案單元的圖案寬度,導致每個圖案單元鄰接下一圖案單元。 圖6-4中的模式單元具有在水平和垂直兩個方向上添加的空間。 您可以為每個方向指定不同的間距值。 如果使間距小于圖案單元格的寬度或高度,則圖案單元格重疊。
當你繪制一個模式單元格,Quartz使用模式空間作為坐標系。 模式空間是一種抽象空間,它通過您創建模式(模式矩陣)時指定的變換矩陣映射到默認用戶空間。
注:模式空間與用戶空間分離。 未變換的模式空間映射到基本(未變換的)用戶空間,而不考慮當前變換矩陣的狀態。 當將變換應用于模式空間時,Quartz僅將變換應用于模式空間。
模式坐標系的默認約定是基礎圖形上下文的默認約定。 默認情況下,Quartz使用坐標系統,其中正x值表示向右的位移,正y值表示向上位移。 然而,UIKit創建的圖形上下文使用不同的約定,其中正y值表示向下位移。 雖然這個約定通常通過將變換連接到坐標系統上來應用于圖形上下文,但在這種情況下,Quartz也會修改模式空間的默認約定以匹配。
如果你不想讓Quartz轉換模式單元格,你可以指定單位矩陣。 但是,您可以通過提供轉換矩陣來實現有趣的效果。 圖6-5顯示了縮放圖6-2所示模式單元的效果。 圖6-6演示了旋轉模式單元。 平移圖案單元有點更微妙。 圖6-7顯示了圖案的起源,圖案單元在水平和垂直兩個方向上平移,使得圖案不再像圖6-1中那樣靠在窗口上。
色的樣式和模板(未上色)
彩色圖案具有與其相關的固有顏色。 更改用于創建圖案單元的顏色,并且該圖案失去其含義。 蘇格蘭格子(如圖6-8所示的樣品)是彩色圖案的一個例子。 彩色圖案中的顏色被指定為圖案單元格創建過程的一部分,而不是圖案繪制過程的一部分。
其它圖案僅僅基于它們的形狀來定義,并且因此可以被認為是模板圖案,未著色圖案或者甚至作為圖像掩模。 圖6-9中所示的紅色和黑色星形是同一圖案單元的每個再現。 單元格本身由一個形狀 - 填充的星。 當定義圖案單元時,沒有顏色與其相關聯。 顏色被指定為圖案繪制過程的一部分,而不是模式單元格創建的一部分。
您可以在Quartz 2D中創建任何一種圖案顏色或模板。
平鋪
平鋪是將圖案單元呈現到頁面的一部分的過程。 當Quartz向設備渲染模式時,Quartz可能需要調整模式以適應設備空間。 也就是說,由于用戶空間單位和設備像素之間的差異,當在用戶空間中定義的圖案單元在被呈現到設備時可能不完美地適合。
Quartz有三個平鋪選項,可以在必要時調整模式。 Quartz可以保存:
- 該圖案,以調整圖案單元之間的間隔為代價,但是不超過一個器件像素。 這被稱為無失真。
- 單元之間的間隔,以犧牲圖案單元稍微變形為代價,但是不超過一個器件像素。 這被稱為具有最小失真的恒定間隔。
- 在單元之間的間隔(對于最小失真選項),以使得模式單元失真達到獲得快速平鋪所需的程度為代價。 這稱為恒定間距。
模式如何工作
模式的操作類似于顏色,因為你設置一個填充或筆觸模式,然后調用繪畫功能。 Quartz使用你設置為“paint”的模式。例如,如果你想用一個純色繪制一個填充的矩形,你首先調用一個函數,如CGContextSetFillColor來設置填充顏色。然后調用函數CGContextFillRect,用指定的顏色繪制填充矩形。要使用模式繪制,您首先調用函數CGContextSetFillPattern來設置模式。然后你調用CGContextFillRect,實際上用你指定的模式繪制填充矩形。用顏色和圖案繪畫之間的區別是,你必須定義模式。您向函數CGContextSetFillPattern提供模式和顏色信息。你將看到如何在繪畫色彩模式和繪畫模具模式中創建,設置和繪制模式。
這里有一個例子,說明Quartz如何在幕后使用你提供的模式繪制。當你用一個模式填充或描線時,Quartz概念上執行以下任務來繪制每個模式單元格:
- 保存圖形狀態。
- 將當前轉換矩陣轉換為模式單元格的原點。
- 將CTM與模式矩陣連接。
- 剪切到模式單元格的邊界矩形。
- 調用繪圖回調來繪制模式單元格。
- 恢復圖形狀態。
Quartz照顧你的所有平鋪,重復渲染圖案單元格到繪圖空間,直到整個空間被繪。 您可以用圖案填充或描邊。 模式單元格可以是您指定的任何大小。 如果要查看模式,應確保模式單元格適合繪圖空間。 例如,如果您的圖案單元格為8個單位,以10個單位為單位,并且您使用該圖案描畫寬度為2個單位的線,則由于圖案單元格的寬度為10個單位,因此會被裁切。 在這種情況下,您可能無法識別該模式。
繪色模式
以下部分描述了繪制彩色圖案所需執行的五個步驟:
- 編寫一個繪制彩色圖案單元的回調函數
- 設置彩色圖案顏色空間
- 設置彩色圖案的解剖
- 將彩色圖案指定為填充或描邊圖案
- 彩色的圖案繪制
這些是用于繪制模具模式的相同步驟。 兩者之間的區別是如何設置顏色信息。 你可以看到所有的步驟如何在一個完整的彩色圖案繪畫函數中。
編寫一個繪制彩色圖案單元的回調函數
什么樣的模式單元格看起來完全取決于你。 對于這個例子,清單6-1中的代碼繪制了如圖6-2所示的模式單元格。 回想一下,圖案單元周圍的黑線不是單元的一部分; 它被繪制以示出模式單元的邊界大于由代碼繪制的矩形。 您稍后將模式大小指定為Quartz。
您的模式單元格繪制函數是一個遵循此形式的回調:
typedef void (*CGPatternDrawPatternCallback) (
void *info,
CGContextRef context
);
你可以命名你的回調名稱為任何你喜歡的。 清單6-1中的名稱為MyDrawColoredPattern。 回調有兩個參數:
- info,一個指向與模式相關聯的私有數據的通用指針。 此參數是可選的; 你可以傳遞NULL。 傳遞到回調的數據是您稍后在創建模式時提供的相同數據。
- context,用于繪制圖案單元的圖形上下文。
清單6-1中的代碼繪制的模式單元是任意的。 你的代碼繪制任何適合你創建的模式。 有關代碼的這些詳細信息很重要:
- 聲明模式大小。 在編寫繪圖代碼時,需要考慮模式大小。 這里,大小被聲明為全局。 繪圖函數不特別指向大小,除非在注釋中。 稍后,您可以將模式大小指定為Quartz 2D。 請參閱設置彩色圖案的解剖。
- 繪制函數遵循由CGPatternDrawPatternCallback回調類型定義定義的原型。
- 在代碼中執行的繪圖設置顏色,這使得這是一個有色圖案。
// 繪制彩色圖案單元格的繪圖回調
#define H_PATTERN_SIZE 16
#define V_PATTERN_SIZE 18
void MyDrawColoredPattern (void *info, CGContextRef myContext)
{
CGFloat subunit = 5; // the pattern cell itself is 16 by 18
CGRect myRect1 = {{0,0}, {subunit, subunit}},
myRect2 = {{subunit, subunit}, {subunit, subunit}},
myRect3 = {{0,subunit}, {subunit, subunit}},
myRect4 = {{subunit,0}, {subunit, subunit}};
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);
}
設置彩色圖案顏色空間
清單6-1中的代碼使用顏色來繪制模式單元格。 您必須通過將基本模式顏色空間設置為NULL,確保Quartz使用您在繪制例程中使用的顏色繪制顏色,如清單6-2所示。 每個編號的代碼行的詳細說明在列表之后。
// 創建基本圖案顏色空間
CGColorSpaceRef patternSpace;
patternSpace = CGColorSpaceCreatePattern (NULL);// 1
CGContextSetFillColorSpace (myContext, patternSpace);// 2
CGColorSpaceRelease (patternSpace);// 3
這里是代碼做了什么:
- 通過調用函數CGColorSpaceCreatePattern,傳遞NULL作為基本顏色空間來創建適合于顏色模式的模式顏色空間。
- 將填充顏色空間設置為圖案顏色空間。 如果你正在描邊你的模式,調用CGContextSetStrokeColorSpace。
- 釋放模式顏色空間。
設置彩色圖案的解剖
有關模式解剖的信息保存在CGPattern對象中。 您可以通過調用函數CGPatternCreate創建一個CGPattern對象,其原型如清單6-3所示。
// CGPatternCreate函數原型
CGPatternRef CGPatternCreate ( void *info,
CGRect bounds,
CGAffineTransform matrix,
CGFloat xStep,
CGFloat yStep,
CGPatternTiling tiling,
bool isColored,
const CGPatternCallbacks *callbacks );
info參數是指向想要傳遞給繪圖回調的數據的指針。 這是在編寫繪制彩色圖案單元的回調函數中討論的相同指針。
您可以在bounds參數中指定模式單元格的大小。 矩陣參數是指定模式矩陣的位置,它將模式坐標系映射到圖形上下文的默認坐標系。 如果要使用與圖形上下文相同的坐標系繪制圖案,請使用單位矩陣。 xStep和yStep參數指定模式坐標系中單元格之間的水平和垂直間距。 請參閱模式解剖以查看有關邊界,模式矩陣和間距的信息。
平鋪參數可以是三個值之一:
- kCGPatternTilingNoDistortion
- kCGPatternTilingConstantSpacingMinimalDistortion
- kCGPatternTilingConstantSpacing
請參閱平鋪以查看關于平鋪的信息。
isColored參數指定模式單元格是彩色模式(true)還是模板模式(false)。 如果在此傳遞true,則繪制模式回調指定模式顏色,并且必須將模式顏色空間設置為彩色模式顏色空間(請參閱設置彩色模式顏色空間)。
傳遞給函數CGPatternCreate的最后一個參數是指向CGPatternCallbacks數據結構的指針。 此結構有三個字段:
struct CGPatternCallbacks
{
unsigned int version;
CGPatternDrawPatternCallback drawPattern;
CGPatternReleaseInfoCallback releaseInfo;
};
將version字段設置為0. drawPattern字段是指向繪圖回調的指針。 releaseInfo字段是指向在釋放CGPattern對象時調用的回調的指針,以釋放傳遞給繪圖回調的info參數的存儲空間。 如果您未傳遞此參數中的任何數據,則將此字段設置為NULL。
將彩色圖案指定為填充或描邊圖案
您可以通過調用相應的函數CGContextSetFillPattern或CGContextSetStrokePattern使用您的模式來填充或描邊。 Quartz使用你的模式進行任何后續的填充或描邊。
這些函數各有三個參數:
- 圖形上下文
- 您先前創建的CGPattern對象
- 顏色分量數組
雖然彩色圖案提供了自己的顏色,但是你必須傳遞一個單一的alpha值,以告知Quartz繪制時模式的整體不透明度。 α可以從1(完全不透明)到0(完全透明)。 這些代碼行顯示了如何為用于填充的彩色圖案設置不透明度的示例。
CGFloat alpha = 1;
CGContextSetFillPattern (myContext, myPattern, &alpha);
彩色的圖案繪制
完成上述步驟后,您可以調用任何繪制的Quartz 2D函數。 您的模式用作“繪畫”。例如,您可以調用CGContextStrokePath,CGContextFillPath,CGContextFillRect或任何其他繪制的函數。
完整的彩色圖案繪畫函數
清單6-4中的代碼包含一個繪制彩色模式的函數。 該功能包括前面討論的所有步驟。 每個編號的代碼行的詳細說明在列表之后。
// 繪制彩色圖案的函數
void MyColoredPatternPainting (CGContextRef myContext,
CGRect rect)
{
CGPatternRef pattern;// 1
CGColorSpaceRef patternSpace;// 2
CGFloat alpha = 1,// 3
width, height;// 4
static const CGPatternCallbacks callbacks = {0, // 5
&MyDrawPattern,
NULL};
CGContextSaveGState (myContext);
patternSpace = CGColorSpaceCreatePattern (NULL);// 6
CGContextSetFillColorSpace (myContext, patternSpace);// 7
CGColorSpaceRelease (patternSpace);// 8
pattern = CGPatternCreate (NULL, // 9
CGRectMake (0, 0, H_PSIZE, V_PSIZE),// 10
CGAffineTransformMake (1, 0, 0, 1, 0, 0),// 11
H_PATTERN_SIZE, // 12
V_PATTERN_SIZE, // 13
kCGPatternTilingConstantSpacing,// 14
true, // 15
&callbacks);// 16
CGContextSetFillPattern (myContext, pattern, &alpha);// 17
CGPatternRelease (pattern);// 18
CGContextFillRect (myContext, rect);// 19
CGContextRestoreGState (myContext);
}
這里是代碼做了什么:
- 聲明稍后創建的CGPattern對象的存儲。
- 聲明稍后創建的模式顏色空間的存儲。
- 聲明alpha變量,并將其設置為1,它指定模式的不透明度為完全不透明。
- 聲明變量以保存窗口的高度和寬度。在該示例中,圖案被繪制在窗口的區域上。
- 聲明和填充回調結構,傳遞0作為版本和指向繪圖回調函數的指針。此示例不提供發布信息回調,因此該字段設置為NULL。
- 創建模式顏色空間對象,將模式的基本顏色空間設置為NULL。當繪制彩色圖案時,該圖案在圖形回調中提供自己的顏色,這就是為什么將顏色空間設置為NULL的原因。
- 將填充顏色空間設置為剛剛創建的圖案顏色空間對象。
- 釋放模式顏色空間對象。
- 傳遞NULL,因為模式不需要傳遞給繪圖回調的任何附加信息。
- 傳遞指定模式單元格邊界的CGRect對象。
- 傳遞CGAffineTransform矩陣,該矩陣指定如何將模式空間轉換為使用該模式的上下文的默認用戶空間。這個例子傳遞單位矩陣。
- 通過水平圖案大小作為每個單元格的開始之間的水平位移。在這個例子中,一個單元格被涂在相鄰的單元格之下。
- 將垂直圖案尺寸作為每個單元開始之間的垂直位移。
- 通過常量kCGPatternTilingConstantSpacing來指定Quartz應該如何渲染模式。有關詳細信息,請參閱平鋪。
- 對isColored參數傳遞true,以指定模式是彩色模式。
- 傳遞指向包含版本信息的回調結構的指針,以及指向繪制回調函數的指針。
- 設置填充模式,傳遞上下文,剛剛創建的CGPattern對象和指向一個alpha值的指針,該值指定Quartz應用于模式的不透明度。
- 釋放CGPattern對象。
- 填充一個矩形,該矩形是傳遞到MyColoredPatternPainting例程的窗口大小。 Quartz使用剛剛設置的模式填充矩形。
繪畫模具樣式
以下部分描述了繪制模板模式所需執行的五個步驟:
- 編寫一個繪制模板模式的回調函數
- 設置模板圖案顏色空間
- 設置模板模式的解剖
- 將模板模式指定為填充或描邊模式
- 用鋼板蠟紙圖案繪圖
這些實際上是用來繪制彩色圖案的步驟。 兩者之間的區別是如何設置顏色信息。 你可以看到所有的步驟如何在一個完整的模板圖案繪制功能。
編寫一個繪制模板模式的回調函數
您為繪制模板模式而寫入的回調與針對彩色模式單元格描述的回調相同。 請參閱編寫繪制彩色圖案單元的回調函數。 唯一的區別是你的繪制回調沒有指定任何顏色。 圖6-10所示的模式單元格不會從繪圖回調中獲得其顏色。 顏色設置在圖案顏色空間中繪圖顏色之外。
看看清單6-5中的代碼,繪制如圖6-10所示的模式單元格。 請注意,代碼只是創建一個路徑并填充路徑。 代碼不設置顏色。
// 繪制模板模式單元格的繪圖回調
#define PSIZE 16 // size of the pattern cell
static void MyDrawStencilStar (void *info, CGContextRef myContext)
{
int k;
double r, 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);
}
設置模板圖案顏色空間
模板模式需要為Quartz繪制一個模式顏色空間,如代碼清單6-6所示。 每個編號的代碼行的詳細說明在列表之后。
// 為模板模式創建模式顏色空間的代碼
CGPatternRef pattern;
CGColorSpaceRef baseSpace;
CGColorSpaceRef patternSpace;
baseSpace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);// 1
patternSpace = CGColorSpaceCreatePattern (baseSpace);// 2
CGContextSetFillColorSpace (myContext, patternSpace);// 3
CGColorSpaceRelease(patternSpace);// 4
CGColorSpaceRelease(baseSpace);// 5
這里是代碼做了什么:
- 此函數創建一個通用的RGB空間。 通用顏色空間會使顏色與系統匹配。 有關詳細信息,請參閱創建常規顏色空間。
- 創建圖案顏色空間。 您提供的顏色空間指定顏色如何表示模式。 稍后,為模式設置顏色時,必須使用模式顏色空間設置它們。 對于此示例,您將需要使用RGB值指定顏色。
- 設置填充圖案時要使用的顏色空間。 您可以通過調用函數CGContextSetStrokeColorSpace設置筆觸顏色空間。
- 釋放模式顏色空間對象。
- 釋放基本顏色空間對象。
設置模板模式的解剖
您可以通過調用函數CGPatternCreate來指定有關模式的解剖結構的信息。 唯一的區別是你為isColored參數傳遞false。 有關您提供給CGPatternCreate函數的參數的更多信息,請參閱設置彩色模式的解剖。
將模板模式指定為填充或描邊模式
你可以通過調用相應的函數CGContextSetFillPattern或CGContextSetStrokePattern來使用你的模式來填充或描邊。 Quartz使用你的模式進行任何后續的填充或描邊。
這些函數各有三個參數:
- 圖形上下文
- 您先前創建的CGPattern對象
- 顏色分量數組
模板模式在繪圖回調中不提供顏色,因此您必須向填充或描邊函數傳遞一個顏色,以告知Quartz要使用的顏色。 清單6-7顯示了如何為模板模式設置顏色的示例。 Quartz在您之前設置的顏色空間中解析顏色數組中的值。 因為此示例使用設備RGB,所以顏色數組包含紅色,綠色和藍色組件的值。 第四個值指定顏色的不透明度。
// 為彩色圖案設置不透明度的代碼
static const CGFloat color[4] = { 0, 1, 1, 0.5 }; //cyan, 50% transparent
CGContextSetFillPattern (myContext, myPattern, color);
用鋼板蠟紙圖案繪圖
完成上述步驟后,您可以調用任何繪制的Quartz 2D函數。 您的模式用作“繪畫”。例如,您可以調用CGContextStrokePath,CGContextFillPath,CGContextFillRect或任何其他繪制的函數。
完整的模板圖案繪制功能
代碼清單6-8中的代碼包含一個繪制模板模式的函數。 該功能包括前面討論的所有步驟。 每個編號的代碼行的詳細說明在列表之后。
// 繪制模具模式的函數
#define PSIZE 16
void MyStencilPatternPainting (CGContextRef myContext,
const Rect *windowRect)
{
CGPatternRef pattern;
CGColorSpaceRef baseSpace;
CGColorSpaceRef patternSpace;
static const CGFloat color[4] = { 0, 1, 0, 1 };// 1
static const CGPatternCallbacks callbacks = {0, &drawStar, NULL};// 2
baseSpace = CGColorSpaceCreateDeviceRGB ();// 3
patternSpace = CGColorSpaceCreatePattern (baseSpace);// 4
CGContextSetFillColorSpace (myContext, patternSpace);// 5
CGColorSpaceRelease (patternSpace);
CGColorSpaceRelease (baseSpace);
pattern = CGPatternCreate(NULL, CGRectMake(0, 0, PSIZE, PSIZE),// 6
CGAffineTransformIdentity, PSIZE, PSIZE,
kCGPatternTilingConstantSpacing,
false, &callbacks);
CGContextSetFillPattern (myContext, pattern, color);// 7
CGPatternRelease (pattern);// 8
CGContextFillRect (myContext,CGRectMake (0,0,PSIZE*20,PSIZE*20));// 9
}
這里是代碼做了什么:
- 聲明一個數組來保存顏色值,并將該值(將在RGB顏色空間中)設置為不透明的綠色。
- 聲明和填充回調結構,傳遞0作為版本和指向繪圖回調函數的指針。此示例不提供發布信息回調,因此該字段設置為NULL。
- 創建RGB設備顏色空間。如果圖案繪制到顯示器,則需要提供此類型的顏色空間。
- 從RGB設備顏色空間創建模式顏色空間對象。
- 將填充顏色空間設置為剛剛創建的圖案顏色空間對象。
- 創建模式對象。注意,第二個到最后一個參數 - isColored參數 - 是false。模板模式不提供顏色,因此必須為此參數傳遞false。所有其他參數與為彩色圖案示例傳遞的參數類似。看到一個完整的彩色圖案繪畫功能。
- 設置填充模式,傳遞先前聲明的顏色數組。
- 釋放CGPattern對象。
- 填充一個矩形。 Quartz使用剛剛設置的模式填充矩形。