Quartz 2D編程指南(2) - 圖形上下文

Quartz 2D編程指南(1) - 概覽

一個Graphics Context表示一個繪製目標。它包含繪製系統用於完成繪製指令的繪製參數和設備相關信息。Graphics Context定義了基本的繪製屬性,如顏色、裁減區域、線條寬度和樣式信息、字體信息、混合模式等。

我們可以通過幾種方式來獲取Graphics Context:Quartz提供的創建函數、Mac OS X框架或IOS的UIKit框架提供的函數。Quartz提供了多種Graphics Context的創建函數,包括bitmap和PDF,我們可以使用這些Graphics Context創建自定義的內容。

本章介紹瞭如何爲不同的繪製目標創建Graphics Context。在代碼中,我們用CGContextRef來表示一個Graphics Context。當獲得一個Graphics Context後,可以使用Quartz 2D函數在上下文(context)中進行繪製、完成操作(如平移)、修改圖形狀態參數(如線寬和填充顏色)等。

在iOS中的視圖Graphics Context進行繪製
在iOS應用程序中,如果要在屏幕上進行繪製,需要創建一個UIView對象,並實現它的drawRect:方法。視圖的drawRect:方法在視圖顯示在屏幕上及它的內容需要更新時被調用。在調用自定義的drawRect:後,視圖對象自動配置繪圖環境以便代碼能立即執行繪圖操作。作爲配置的一部分,視圖對象將爲當前的繪圖環境創建一個Graphics Context。我們可以通過調用UIGraphicsGetCurrentContext函數來獲取這個Graphics Context。

UIKit默認的座標系統與Quartz不同。在UIKit中,原點位於左上角,y軸正方向爲向下。UIView通過將修改Quartz的Graphics Context的CTM[原點平移到左下角,同時將y軸反轉(y值乘以-1)]以使其與UIView匹配。

在Mac OS X中創建一個窗口Graphics Context
在Mac OS X中繪製時,我們需要創建一個窗口Graphics Context。Quartz 2D API 沒有提供函數來獲取窗口Graphics Context。取而代之的是用Cocoa框架來獲取一個窗口上下文。

我們可以在Cocoa應用程序的drawRect:中獲取一個Quartz Graphics Context,如下代碼所示:

複製代碼
  1. CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];

currentContext方法在當前線程中返回NSGraphicsContext實例。graphicsPort方法返回一個低級別、平臺相關的Graphics Context(Quartz Graphics Context)。

在獲取到Graphics Context後,我們可以在Cocoa應用程序中調用任何Quartz 2D的繪製函數。我們同樣可以將Quartz 2D與Cocoa繪製操作混合使用。如圖2-1是一個在Cocoa視圖中用Quartz 2D繪製的實例。繪圖由兩個長方形組成(一個不透明的紅色長方形和半透明的藍色長方形)。

 
爲了實現圖2-1實例,需要先創建一個Cocoa應用程序。在Interface Builder中,拖動一個Custom View到窗口中,並子類化。然後實現子類視圖的,如代碼清單2-1所示。視圖的drawRect:包含了所有的Quartz繪製代碼。

引用
注:NSView的drawRect:方法在每次視圖需要繪製時自動調用。


複製代碼
  1. Listing 2-1  Drawing to a window graphics context
  2. @implementation MyQuartzView
  3. - (id)initWithFrame:(NSRect)frameRect
  4. {
  5.     self = [super initWithFrame:frameRect];
  6.     return self;
  7. }
  8.  
  9. - (void)drawRect:(NSRect)rec
  10. {
  11.     CGContextRef myContext = [[NSGraphicsContext  currentContext] graphicsPort]; //1
  12.  
  13.    // ********** Your drawing code here **********       //2
  14.     CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);     //3
  15.     CGContextFillRect (myContext, CGRectMake (0, 0, 200, 100 ));   //4
  16.     CGContextSetRGBFillColor (myContext, 0, 0, 1, .5);     //5
  17.     CGContextFillRect (myContext, CGRectMake (0, 0, 100, 200));   //6
  18. }
  19. @end

代碼說明:
1.爲視圖獲取一個Graphics Context
2.插入繪圖代碼的地方。以下四行是使用Quartz 2D函數的例子
3.設置完全不透明的紅色填充色。
4.填充一個長方形,其原點爲(0, 0), 大小爲(200, 100)
5.設置半透明的藍色填充色。
6.填充一個長方形,其原點爲(0, 0), 大小爲(100, 200)

創建一個PDF Graphics Context
當創建一個PDF Graphics Context並繪製時,Quartz將繪製操作記錄爲一系列的PDF繪製命令並寫入文件中。我們需要提供一個PDF輸出的位置及一個默認的media box(用於指定頁面邊界的長方形)。圖2-2顯示了在PDF Graphics Context中繪製及在preview打開PDF的結果。

 


 
Quartz 2D API提供了兩個函數來創建PDF Graphics Context:

  • CGPDFContextCreateWithURL:當你需要用Core Foundation URL指定pdf輸出的位置時使用該函數。代碼清單2-2顯示了該函數的使用方法(代碼2-2及後面代碼的詳細解釋略):

複製代碼
  1. Listing 2-2  Calling CGPDFContextCreateWithURL to create a PDF graphics context
  2. CGContextRef MyPDFContextCreate (const CGRect *inMediaBox, CFStringRef path)
  3. {
  4.     CGContextRef myOutContext = NULL;
  5.     CFURLRef url;
  6.     url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, false);
  7.  
  8.     if (url != NULL) {
  9.         myOutContext = CGPDFContextCreateWithURL (url,  inMediaBox,  NULL);
  10.         CFRelease(url);
  11.     }
  12.     return myOutContext;
  13. }

  • CGPDFContextCreate:當需要將pdf輸出發送給數據用戶時使用該方法。代碼清單2-3顯示了該函數的使用方法:

複製代碼
  1. Listing 2-3  Calling CGPDFContextCreate to create a PDF graphics context
  2. CGContextRef MyPDFContextCreate (const CGRect *inMediaBox, CFStringRef path)
  3. {
  4.     CGContextRef        myOutContext = NULL;
  5.     CFURLRef            url;
  6.     CGDataConsumerRef   dataConsumer;
  7.  
  8.     url = CFURLCreateWithFileSystemPath (NULL,  path, kCFURLPOSIXPathStyle, false);
  9.  
  10.     if (url != NULL)
  11.     {
  12.         dataConsumer = CGDataConsumerCreateWithURL (url);
  13.         if (dataConsumer != NULL)
  14.         {
  15.             myOutContext = CGPDFContextCreate (dataConsumer, inMediaBox, NULL);
  16.             CGDataConsumerRelease (dataConsumer);
  17.         }
  18.  
  19.         CFRelease(url);
  20.     }
  21.  
  22.     return myOutContext;
  23. }

代碼清單2-4顯示是如何調用MyPDFContextCreate程序及繪製操作。
複製代碼
  1. Listing 2-4  Drawing to a PDF graphics context
  2.     CGRect mediaBox;
  3.  
  4.     mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight);
  5.     myPDFContext = MyPDFContextCreate (&mediaBox, CFSTR("test.pdf"));
  6.  
  7.     CFStringRef myKeys[1];
  8.     CFTypeRef myValues[1];
  9.  
  10.     myKeys[0] = kCGPDFContextMediaBox;
  11.     myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));
  12.  
  13.     CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys,
  14.                                                         (const void **) myValues, 1,
  15.                                                         &kCFTypeDictionaryKeyCallBacks,
  16.                                                         & kCFTypeDictionaryValueCallBacks);
  17.  
  18.     CGPDFContextBeginPage(myPDFContext, &pageDictionary);
  19.         // ********** Your drawing code here **********
  20.         CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);
  21.         CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));
  22.         CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);
  23.         CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));
  24.     CGPDFContextEndPage(myPDFContext);
  25.  
  26.     CFRelease(pageDictionary);
  27.     CFRelease(myValues[0]);
  28.     CGContextRelease(myPDFContext);

我們可以將任何內容(圖片,文本,繪製路徑)繪製到pdf中,並能添加鏈接及加密。

創建位圖Graphics Context
一個位圖Graphics Context接受一個指向內存緩存(包含位圖存儲空間)的指針,當我們繪製一個位圖Graphics Context時,該緩存被更新。在釋放Graphics Context後,我們將得到一個我們指定像素格式的全新的位圖。

引用
注:位圖Graphics Context有時用於後臺繪製。CGLayer對象優化了後臺繪製,因爲Quartz在顯卡上緩存了層。

 

引用
iOS提示:iOS應用程序使用了UIGraphicsBeginImageContextWithOptions取代Quartz低層函數。如果使用Quartz創建一下後臺bitmap,bitmap Graphics Context使用的座標系統是Quartz默認的座標系統。而使用UIGraphicsBeginImageContextWithOptions創建圖形上下文,UIKit將會對座標系統使用與UIView對象的圖形上下文一樣的轉換。這允許應用程序使用相同的繪製代碼而不需要擔心座標系統問題。雖然我們的應用程序可以手動調整CTM達到相同的效果,但這種做沒有任何好處。


我們使用CGBitmapContextCreate來創建位圖Graphics Context,該函數有如下參數:

 

 

  • data:一個指向內存目標的指針,該內存用於存儲需要渲染的圖形數據。內存塊的大小至少需要(bytePerRow * height)字節。
  • width:指定位圖的寬度,單位是像素(pixel)。
  • height:指定位圖的高度, 單位是像素(pixel)。
  • bitsPerComponent:指定內存中一個像素的每個組件使用的位數。例如,一個32位的像素格式和一個rgb顏色空間,我們可以指定每個組件爲8位。
  • bytesPerRow:指定位圖每行的字節數。
  • colorspace:顏色空間用於位圖上下文。在創建位圖Graphics Context時,我們可以使用灰度(gray), RGB, CMYK, NULL顏色空間。
  • bitmapInfo:位圖的信息,這些信息用於指定位圖是否需要包含alpha組件,像素中alpha組件的相對位置(如果有的話),alpha組件是否是預乘的,及顏色組件是整型值還是浮點值。

代碼清單2-5顯示瞭如何創建位圖Graphics Context。當向位圖Graphics Context繪圖時,Quartz將繪圖記錄到內存中指定的塊中。

複製代碼
  1. Listing 2-5  Creating a bitmap graphics context
  2. CGContextRef MyCreateBitmapContext (int pixelsWide, int pixelsHigh)
  3. {
  4.     CGContextRef    context = NULL;
  5.     CGColorSpaceRef colorSpace;
  6.     void *          bitmapData;
  7.     int             bitmapByteCount;
  8.     int             bitmapBytesPerRow;
  9.  
  10.     bitmapBytesPerRow   = (pixelsWide * 4);
  11.     bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);
  12.     colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
  13.     bitmapData = calloc( bitmapByteCount );
  14.     if (bitmapData == NULL)
  15.     {
  16.         fprintf (stderr, "Memory not allocated!");
  17.         return NULL;
  18.     }
  19.     context = CGBitmapContextCreate (bitmapData, pixelsWide, pixelsHigh, 8, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
  20.  
  21.     if (context== NULL)
  22.     {
  23.         free (bitmapData);
  24.         fprintf (stderr, "Context not created!");
  25.         return NULL;
  26.     }
  27.     CGColorSpaceRelease( colorSpace );
  28.     return context;
  29. }

代碼清單2-6顯示了調用MyCreateBitmapContext 創建一個位圖Graphics Context,使用位圖Graphics Context來創建CGImage對象,然後將圖片繪製到窗口Graphics Context中。繪製結果如圖2-3所示:
複製代碼
  1. Listing 2-6  Drawing to a bitmap graphics context
  2.     CGRect myBoundingBox;
  3.     myBoundingBox = CGRectMake (0, 0, myWidth, myHeight);
  4.     myBitmapContext = MyCreateBitmapContext (400, 300);
  5.  
  6.     // ********** Your drawing code here ********** 
  7.     CGContextSetRGBFillColor (myBitmapContext, 1, 0, 0, 1);
  8.     CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 200, 100 ));
  9.     CGContextSetRGBFillColor (myBitmapContext, 0, 0, 1, .5);
  10.     CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 100, 200 ));
  11.     myImage = CGBitmapContextCreateImage (myBitmapContext);
  12.     CGContextDrawImage(myContext, myBoundingBox, myImage);
  13.     char *bitmapData = CGBitmapContextGetData(myBitmapContext); 
  14.     CGContextRelease (myBitmapContext);
  15.     if (bitmapData) free(bitmapData); 
  16.     CGImageRelease(myImage);

 

 

支持的像素格式
表2-1總結了位圖Graphics Context支持的像素格式,相關的顏色空間及像素格式支持的Mac OS X最早版本。像素格式用bpp(每像素的位數)和bpc(每個組件的位數)來表示。表格同時也包含與像素格式相關的位圖信息常量。

表2-1:位圖Graphics Context支持的像素格式

Null 8 bpp, 8 bpc, kCGImageAlphaOnly Mac OS X, iOS
Gray 8 bpp, 8 bpc,kCGImageAlphaNone Mac OS X, iOS
Gray 8 bpp, 8 bpc,kCGImageAlphaOnly Mac OS X, iOS
Gray 16 bpp, 16 bpc, kCGImageAlphaNone Mac OS X
Gray 32 bpp, 32 bpc, kCGImageAlphaNone|kCGBitmapFloatComponents Mac OS X
RGB 16 bpp, 5 bpc, kCGImageAlphaNoneSkipFirst Mac OS X, iOS
RGB 32 bpp, 8 bpc, kCGImageAlphaNoneSkipFirst Mac OS X, iOS
RGB 32 bpp, 8 bpc, kCGImageAlphaNoneSkipLast Mac OS X, iOS
RGB 32 bpp, 8 bpc, kCGImageAlphaPremultipliedFirst Mac OS X, iOS
RGB 32 bpp, 8 bpc, kCGImageAlphaPremultipliedLast Mac OS X, iOS
RGB 64 bpp, 16 bpc, kCGImageAlphaPremultipliedLast Mac OS X
RGB 64 bpp, 16 bpc, kCGImageAlphaNoneSkipLast Mac OS X
RGB 128 bpp, 32 bpc, kCGImageAlphaNoneSkipLast |kCGBitmapFloatComponents Mac OS X
RGB 128 bpp, 32 bpc, kCGImageAlphaPremultipliedLast |kCGBitmapFloatComponents Mac OS X
CMYK 32 bpp, 8 bpc, kCGImageAlphaNone Mac OS X
CMYK 64 bpp, 16 bpc, kCGImageAlphaNone Mac OS X
CMYK 128 bpp, 32 bpc, kCGImageAlphaNone |kCGBitmapFloatComponents Mac OS X

反鋸齒
位圖Graphics Context支持反鋸齒,這一操作是人爲的較正在位圖中繪製文本或形狀時產生的鋸齒邊緣。當位圖的分辯率明顯低於人眼的分辯率時就會產生鋸齒。爲了使位圖中的對象顯得平滑,Quartz使用不同的顏色來填充形狀周邊的像素。通過這種方式來混合顏色,使形狀看起來更平滑。如圖2-4顯示的效果。我們可以通過調用CGContextSetShouldAntialias來關閉位圖Graphics Context的反鋸齒效果。反鋸齒設置是圖形狀態的一部分。
可以調用函數CGContextSetAllowsAntialiasing來控制一個特定Graphics Context是否支持反鋸齒;false表示不支持。該設置不是圖形狀態的一部分。當上下文及圖形狀態設置爲true時,Quartz執行反鋸齒。

 

獲取打印的Graphics Context
Mac OS X中的Cocoa應用程序通過自定義的NSView子類來實現打印。一個視圖通過調用print:方法來進行打印。然後視圖以打印機爲目標創建一個Graphics Context,並調用drawRect:方法。應用程序使用與在屏幕進行繪製相同的繪製代碼。我們同樣可以自定義drawRect: 方法將圖形繪製到打印機。

原帖地址:http://www.cocoachina.com/bbs/read.php?tid=78029

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章