圖形上下文表示繪圖目的地。它包含繪圖系統執行任何后續繪圖命令所需的繪圖參數和所有特定于設備的信息。圖形上下文定義基本繪圖屬性,例如繪制時使用的顏色,剪切區域,線寬和樣式信息,字體信息,合成選項以及其他幾個。
您可以通過使用Quartz上下文創建函數或使用Mac OS X框架或iOS中的UIKit框架提供的更高級別的函數來獲取圖形上下文。 Quartz提供了各種Quartz圖形上下文的功能,包括位圖和PDF,您可以使用它們來創建自定義內容。
本章介紹如何為各種繪圖目的地創建圖形上下文。圖形上下文在代碼中由數據類型CGContextRef表示,它是一種不透明的數據類型。獲得圖形上下文后,可以使用Quartz 2D函數繪制上下文,對上下文執行操作(如旋轉),以及更改圖形狀態參數(如線寬和填充顏色)。
繪制到iOS中的視圖圖形上下文
要在iOS應用程序中繪制到屏幕,您需要設置一個UIView對象并實現其drawRect:方法來執行繪制。當視圖在屏幕上可見并且其內容需要更新時,將調用視圖的drawRect:方法。在調用自定義drawRect:方法之前,視圖對象會自動配置其繪圖環境,以便您的代碼可以立即開始繪制。作為此配置的一部分,UIView對象為當前繪圖環境創建圖形上下文(CGContextRef不透明類型)。您通過調用UIKit函數UIGraphicsGetCurrentContext在drawRect:方法中獲取此圖形上下文。
UIKit中使用的默認坐標系統與Quartz使用的坐標系統不同。在UIKit中,原點在左上角,正-y值指向下。 UIView對象修改Quartz圖形上下文的CTM以匹配UIKit約定,將原點轉換為視圖的左上角,并通過將其乘以-1來反轉y軸。有關修改坐標系統以及您自己的繪圖代碼的含義的更多信息,請參閱Quartz 2D坐標系統。
UIView對象在iOS的視圖編程指南中有詳細描述。
在Mac OS X中創建窗口圖形上下文
在Mac OS X中繪制時,您需要創建一個適合您所使用的框架的窗口圖形上下文。 Quartz 2D API本身不提供獲取窗口圖形上下文的函數。 相反,您使用Cocoa框架來獲取在Cocoa中創建的窗口的上下文。
您可以從Cocoa應用程序的drawRect:例程中使用以下代碼行獲取Quartz圖形上下文:
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
方法currentContext返回當前線程的NSGraphicsContext實例。 方法graphicsPort返回由接收器表示的低級,平臺特定的圖形上下文,這是一個Quartz圖形上下文。 (不要被方法名稱混淆;它們是歷史的。)有關更多信息,請參閱NSGraphicsContext類參考。
獲得圖形上下文后,可以在Cocoa應用程序中調用任何Quartz 2D繪圖函數。 你也可以混合Quartz 2D調用和Cocoa繪圖調用。 您可以通過查看圖2-1,看到Quartz 2D繪圖到Cocoa視圖的示例。 該圖由兩個重疊的矩形組成,不透明的紅色和部分透明的藍色。 您將了解有關顏色和顏色空間透明度的更多信息。 控制多少可以“看透”顏色的能力是Quartz 2D的標志性功能之一。
要創建圖2-1中的圖形,首先創建一個Cocoa應用程序Xcode項目。 在Interface Builder中,將自定義視圖拖動到窗口并將其子類化。 然后為子類視圖編寫一個實現,類似于清單2-1所示。 對于此示例,子類視圖命名為MyQuartzView。 視圖的drawRect:方法包含所有Quartz繪圖代碼。 每個編號的代碼行的詳細說明顯示在列表之后。
注:NSView類的drawRect:方法在每次需要繪制視圖時自動調用。 要了解更多關于覆蓋drawRect:方法,請參閱NSView類參考。
// 繪制到窗口圖形上下文
@implementation MyQuartzView
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
return self;
}
- (void)drawRect:(NSRect)rect
{
CGContextRef myContext = [[NSGraphicsContext // 1
currentContext] graphicsPort];
// ********** Your drawing code here ********** // 2
CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);// 3
CGContextFillRect (myContext, CGRectMake (0, 0, 200, 100 ));// 4
CGContextSetRGBFillColor (myContext, 0, 0, 1, .5);// 5
CGContextFillRect (myContext, CGRectMake (0, 0, 100, 200));// 6
}
@end
這里是代碼所做的事情:
- 獲取視圖的圖形上下文。
- 這是您插入繪圖代碼的位置。 以下四行代碼是使用Quartz 2D函數的示例。
- 設置完全不透明的紅色填充顏色。 有關顏色和alpha(設置不透明度)的信息,請參閱顏色和顏色空間。
- 填充原點為(0,0)且寬度為200,高度為100的矩形。有關繪制矩形的信息,請參閱路徑。
- 設置部分透明的藍色填充顏色。
- 填充原點為(0,0)且寬度為100,高度為200的矩形。
創建PDF圖形上下文
當您創建PDF圖形上下文并繪制到該上下文時,Quartz將您的繪圖記錄為寫入文件的一系列PDF繪圖命令。 您提供PDF輸出的位置和默認媒體框 - 指定頁面邊界的矩形。 圖2-2顯示了繪制到PDF圖形上下文,然后在預覽中打開生成的PDF的結果。
Quartz 2D API提供了兩個創建PDF圖形上下文的函數:
- CGPDFContextCreateWithURL,當您想要將PDF輸出的位置指定為Core Foundation URL時使用它。 清單2-2顯示了如何使用此函數創建PDF圖形上下文。
- CGPDFContextCreate,您希望將PDF輸出發送到數據使用者時使用。 (有關更多信息,請參閱Quartz 2D中的數據管理。)清單2-3顯示了如何使用此函數創建PDF圖形上下文。
每個編號行之后的每個編號行的詳細說明。
iOS注釋:iOS中的PDF圖形上下文使用Quartz提供的默認坐標系,而不應用變換來匹配UIKit坐標系。 如果應用程序計劃在PDF圖形上下文和UIView對象提供的圖形上下文之間共享圖形代碼,應用程序應修改PDF圖形上下文的CTM以修改坐標系。 參見 Quartz 2D坐標系。
// 調用CGPDFContextCreateWithURL以創建PDF圖形上下文
CGContextRef MyPDFContextCreate (const CGRect *inMediaBox,
CFStringRef path)
{
CGContextRef myOutContext = NULL;
CFURLRef url;
url = CFURLCreateWithFileSystemPath (NULL, // 1
path,
kCFURLPOSIXPathStyle,
false);
if (url != NULL) {
myOutContext = CGPDFContextCreateWithURL (url,// 2
inMediaBox,
NULL);
CFRelease(url);// 3
}
return myOutContext;// 4
}
這里是代碼做了什么:
- 調用Core Foundation函數以從提供給MyPDFContextCreate函數的CFString對象創建CFURL對象。 您傳遞NULL作為第一個參數以使用默認分配器。 您還需要指定路徑樣式,對于此示例,它是一個POSIX樣式的路徑名。
- 調用Quartz 2D函數使用剛創建的PDF位置(作為CFURL對象)和指定PDF邊界的矩形創建PDF圖形上下文。 矩形(CGRect)傳遞到MyPDFContextCreate函數,并且是PDF的默認頁面媒體邊界框。
- 釋放CFURL對象。
- 返回PDF圖形上下文。 調用者必須在不再需要時釋放圖形上下文。
// 調用CGPDFContextCreate創建PDF圖形上下文
CGContextRef MyPDFContextCreate (const CGRect *inMediaBox,
CFStringRef path)
{
CGContextRef myOutContext = NULL;
CFURLRef url;
CGDataConsumerRef dataConsumer;
url = CFURLCreateWithFileSystemPath (NULL, // 1
path,
kCFURLPOSIXPathStyle,
false);
if (url != NULL)
{
dataConsumer = CGDataConsumerCreateWithURL (url);// 2
if (dataConsumer != NULL)
{
myOutContext = CGPDFContextCreate (dataConsumer, // 3
inMediaBox,
NULL);
CGDataConsumerRelease (dataConsumer);// 4
}
CFRelease(url);// 5
}
return myOutContext;// 6
}
這里是代碼做了什么:
- 調用Core Foundation函數以從提供給MyPDFContextCreate函數的CFString對象創建CFURL對象。您傳遞NULL作為第一個參數以使用默認分配器。您還需要指定路徑樣式,對于此示例,它是一個POSIX樣式的路徑名。
- 使用CFURL對象創建Quartz數據使用者對象。如果不想使用CFURL對象(例如,要將PDF數據放置在CFURL對象無法指定的位置),可以從一組回調函數中創建數據使用者您在應用程序中實現。有關更多信息,請參閱Quartz 2D中的數據管理。
- 調用Quartz 2D函數創建一個PDF圖形上下文作為傳遞給MyPDFContextCreate函數的數據使用者和矩形(類型CGRect)的參數。此矩形是PDF的默認頁面媒體邊界框。
- 釋放數據使用者。
- 釋放CFURL對象。
- 返回PDF圖形上下文。調用者必須在不再需要時釋放圖形上下文。
清單2-4顯示了如何調用MyPDFContextCreate例程并繪制它。 每個編號的代碼行的詳細說明顯示在列表之后。
// 繪制到PDF圖形上下文
CGRect mediaBox;// 1
mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight);// 2
myPDFContext = MyPDFContextCreate (&mediaBox, CFSTR("test.pdf"));// 3
CFStringRef myKeys[1];// 4
CFTypeRef myValues[1];
myKeys[0] = kCGPDFContextMediaBox;
myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));
CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys,
(const void **) myValues, 1,
&kCFTypeDictionaryKeyCallBacks,
& kCFTypeDictionaryValueCallBacks);
CGPDFContextBeginPage(myPDFContext, &pageDictionary);// 5
// ********** Your drawing code here **********// 6
CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);
CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));
CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);
CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));
CGPDFContextEndPage(myPDFContext);// 7
CFRelease(pageDictionary);// 8
CFRelease(myValues[0]);
CGContextRelease(myPDFContext);
這里是代碼做了什么:
- 聲明用于定義PDF媒體框的矩形的變量。
- 將媒體框的原點設置為(0,0),將寬度和高度設置為應用程序提供的變量。
- 調用函數MyPDFContextCreate(參見清單2-3)以獲取PDF圖形上下文,提供媒體框和路徑名。宏CFSTR將字符串轉換為CFStringRef數據類型。
- 使用頁面選項設置字典。在此示例中,僅指定了媒體框。您不必傳遞與用于設置PDF圖形上下文相同的矩形。您在此處添加的媒體框將取代您傳遞的用于設置PDF圖形上下文的矩形。
- 表示頁面的開始。此功能用于面向頁面的圖形,這是PDF圖紙。
調用Quartz 2D繪圖函數。您將此代碼和以下四行代碼替換為適合6. 您的應用程序的繪圖代碼。 - 表示PDF頁面的結尾。
- 當不再需要字典和PDF圖形上下文時,發布字典和PDF圖形上下文
您可以將任何內容寫入適合您的應用程序的PDF - 圖像,文本,路徑繪圖 - 您可以添加鏈接和加密。 有關詳細信息,請參閱PDF文檔創建,查看和轉換。
創建位圖圖形上下文
位圖圖形上下文接受指向包含用于位圖的存儲空間的存儲器緩沖器的指針。 當您繪制到位圖圖形上下文中時,緩沖區將更新。 釋放圖形上下文后,您可以使用指定的像素格式完全更新位圖。
注:位圖圖形上下文有時用于繪制屏幕。 在決定為此目的使用位圖圖形上下文之前,請參閱Core Graphics Layer Drawing。 CGLayer對象(CGLayerRef)針對離屏繪制進行了優化,因為只要有可能,Quartz緩存視頻卡上的圖層。
iOS 注:iOS應用程序應該使用函數UIGraphicsBeginImageContextWithOptions而不是使用這里描述的低級Quartz函數。 如果應用程序使用Quartz創建屏幕外位圖,則位圖圖形上下文使用的坐標系是默認的Quartz坐標系。 相反,如果您的應用程序通過調用函數UIGraphicsBeginImageContextWithOptions創建一個圖像上下文,UIKit將應用相同的變換到上下文的坐標系統,就像對UIView對象的圖形上下文一樣。 這允許您的應用程序使用相同的繪圖代碼,無需擔心不同的坐標系。 雖然您的應用程序可以手動調整坐標變換矩陣以獲得正確的結果,但在實踐中,這樣做沒有性能優勢。
您使用函數CGBitmapContextCreate創建位圖圖形上下文。 此函數采用以下參數:
- ** data**。 提供一個指針指向您想要繪制圖形的內存中的目標。 此內存塊的大小應至少為(bytesPerRow * height)字節。
- ** width**。 指定位圖的寬度(以像素為單位)。
- ** height**。 指定位圖的高度(以像素為單位)。
- ** bitsPerComponent**。 指定要用于存儲器中像素的每個分量的位數。 例如,對于32位像素格式和RGB顏色空間,您可以指定每個組件8位的值。 請參閱支持的像素格式。
- ** bytesPerRow**。 指定位圖每行使用的內存字節數。
小記:當創建位圖圖形上下文時,如果確保數據和bytesPerRow為16字節對齊,您將獲得最佳性能。
- ** colorspace**。用于位圖上下文的顏色空間。創建位圖圖形上下文時,可以提供灰色,RGB,CMYK或NULL顏色空間。有關顏色空間和顏色管理原則的詳細信息,請參閱顏色管理概述。有關在Quartz中創建和使用顏色空間的信息,請參閱顏色和顏色空間。有關支持的顏色空間的信息,請參閱位圖圖像和圖像掩碼章節中的顏色空間和位圖布局。
- bitmapInfo。位圖布局信息,表示為CGBitmapInfo常量,指定位圖是否應包含alpha分量,alpha分量(如果有一個)在像素中的相對位置,alpha分量是否預乘,以及顏色分量是整數或浮點值。有關這些常量,使用每個常量以及位圖圖形上下文和圖像的Quartz支持的像素格式的詳細信息,請參閱位圖圖像和圖像掩碼章節中的顏色空間和位圖布局。
清單2-5顯示了如何創建位圖圖形上下文。 當你繪制到產生的位圖圖形上下文中時,Quartz將你的繪圖記錄為指定的內存塊中的位圖數據。 每個編號的代碼行的詳細說明在列表之后。
// 創建位圖圖形上下文
CGContextRef MyCreateBitmapContext (int pixelsWide,
int pixelsHigh)
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
bitmapBytesPerRow = (pixelsWide * 4);// 1
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);// 2
bitmapData = calloc( bitmapByteCount, sizeof(uint8_t) );// 3
if (bitmapData == NULL)
{
fprintf (stderr, "Memory not allocated!");
return NULL;
}
context = CGBitmapContextCreate (bitmapData,// 4
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
if (context== NULL)
{
free (bitmapData);// 5
fprintf (stderr, "Context not created!");
return NULL;
}
CGColorSpaceRelease( colorSpace );// 6
return context;// 7
}
這里是代碼做了什么:
- 聲明一個變量來表示每行的字節數。在該示例中位圖中的每個像素由4個字節表示;紅色,綠色,藍色和alpha各8位。
- 創建一個通用的RGB顏色空間。您還可以創建CMYK顏色空間。有關詳細信息和關于通用顏色空間與器件相關顏色空間的討論,請參閱顏色和顏色空間。
- 調用calloc函數來創建和清除存儲位圖數據的內存塊。此示例創建一個32位RGBA位圖(即,每個像素有32位的數組,每個像素包含紅色,綠色,藍色和alpha信息各8位)。位圖中的每個像素占用4個字節的內存。在Mac OS X 10.6和iOS 4中,可以省略此步驟 - 如果將NULL傳遞為位圖數據,Quartz會自動為位圖分配空間。
- 創建位圖圖形上下文,提供位圖數據,位圖的寬度和高度,每個組件的位數,每行的字節數,顏色空間以及指定位圖是否應包含Alpha通道及其像素中的相對位置。常數kCGImageAlphaPremultipliedLast表示alpha分量存儲在每個像素的最后一個字節,顏色分量已經乘以該alpha值。有關預乘alpha的更多信息,請參閱Alpha值。
- 如果由于某種原因未創建上下文,則釋放為位圖數據分配的內存。
- 釋放顏色空間。
- 返回位圖圖形上下文。調用者必須在不再需要時釋放圖形上下文
清單2-6顯示了調用MyCreateBitmapContext創建位圖圖形上下文,使用位圖圖形上下文創建一個CGImage對象,然后將結果圖像繪制到窗口圖形上下文的代碼。 圖2-3顯示了繪制到窗口的圖像。 每個編號的代碼行的詳細說明在列表之后。
// 繪制到位圖圖形上下文
CGRect myBoundingBox;// 1
myBoundingBox = CGRectMake (0, 0, myWidth, myHeight);// 2
myBitmapContext = MyCreateBitmapContext (400, 300);// 3
// ********** Your drawing code here ********** // 4
CGContextSetRGBFillColor (myBitmapContext, 1, 0, 0, 1);
CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 200, 100 ));
CGContextSetRGBFillColor (myBitmapContext, 0, 0, 1, .5);
CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 100, 200 ));
myImage = CGBitmapContextCreateImage (myBitmapContext);// 5
CGContextDrawImage(myContext, myBoundingBox, myImage);// 6
char *bitmapData = CGBitmapContextGetData(myBitmapContext); // 7
CGContextRelease (myBitmapContext);// 8
if (bitmapData) free(bitmapData); // 9
CGImageRelease(myImage);
這里是代碼做了什么:
- 聲明一個變量來存儲邊界框的原點和尺寸,Quartz將繪制從位圖圖形上下文創建的圖像。
- 將邊界框的原點設置為(0,0),將寬度和高度設置為先前聲明的變量,但其聲明未在此代碼中顯示。
- 調用應用程序提供的函數MyCreateBitmapContext(參見清單2-5)創建一個寬400像素,高300像素的位圖上下文。您可以使用適 合于應用程序的任何尺寸創建位圖圖形上下文。
- 調用Quartz 2D函數來繪制位圖圖形上下文。您將使用適合您的應用程序的繪圖代碼替換此代碼和接下來的四行代碼。
- 從位圖圖形上下文創建一個Quartz 2D圖像(CGImageRef)。
- 將圖像繪制到由邊界框指定的窗口圖形上下文中的位置。邊界框指定用戶空間中用于繪制圖像的位置和尺寸。
此示例不顯示窗口圖形上下文的創建。有關如何創建窗口圖形上下文的信息,請參閱在Mac OS X中創建窗口圖形上下文。 - 獲取與位圖圖形上下文相關聯的位圖數據。
- 在不再需要時釋放位圖圖形上下文。
- 釋放位圖數據(如果存在)。
- 在不再需要時釋放圖像。
支持的像素格式
表2-1總結了位圖圖形上下文,相關顏色空間(cs)和格式首次可用的Mac OS X版本支持的像素格式。 像素格式被指定為每像素比特(bpp)和每分量比特(bpc)。 該表還包括與該像素格式相關聯的位圖信息常數。 有關每個位圖信息格式常量表示的詳細信息,請參閱CGImage參考
抗鋸齒
位圖圖形上下文支持反鋸齒,這是人工糾正在繪制文本或形狀時有時在位圖圖像中看到的鋸齒狀(或鋸齒狀)邊緣的過程。當位圖的分辨率顯著低于眼睛的分辨率時,會出現這些鋸齒狀邊緣。為了使對象在位圖中顯示平滑,Quartz為包圍形狀輪廓的像素使用不同的顏色。通過以這種方式混合顏色,形狀看起來平滑。您可以在圖2-4中看到使用抗鋸齒的效果。您可以通過調用函數CGContextSetShouldAntialias關閉特定位圖圖形上下文的反鋸齒。抗鋸齒設置是圖形狀態的一部分。
您可以通過使用CGContextSetAllowsAntialiasing函數來控制是否允許特定圖形上下文的反鋸齒。傳遞true到此函數允許抗鋸齒; false不允許它。此設置不是圖形狀態的一部分。當上下文和圖形狀態設置設置為true時,Quartz執行反鋸齒。
獲取打印的圖形上下文
Mac OS X中的Cocoa應用程序通過自定義NSView子類實現打印。 通過調用它的print:方法告訴視圖打印。 然后,視圖將創建一個圖形上下文,以打印機為目標并調用其drawRect:方法。 您的應用程序使用相同的繪圖代碼繪制到用于繪制到屏幕的打印機。 它還可以自定義drawRect:調用到發送到屏幕的打印機的圖像不同。
有關在Cocoa中打印的詳細討論,請參閱Mac的“打印編程指南”。