国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Quartz-2D繪圖之圖形上下文詳解

2019-11-14 19:46:46
字體:
來源:轉載
供稿:網友

  上一篇文章大概描述了下Quartz里面大體所包含的東西,但是對具體的細節實現以及如何調用相應API卻沒有講。這篇文章就先講講圖形上下文(Graphics Context)的具體操作。

  所謂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中的UIView的Graphics Context進行繪制

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

例如在UIView中畫個圓

 1 - (void)drawRect:(CGRect)rect { 2      3     //    1. 獲取一個與視圖相關聯的上下文 4     CGContextRef context = UIGraphicsGetCurrentContext(); 5      6     // 設置顏色 7     [[UIColor greenColor]setFill]; 8      9     // 2. 增加圓形的路徑10     CGContextAddEllipseInRect(context, CGRectMake(50, 50, 200, 200));11     // 3. 畫圓12     CGContextFillPath(context);13 14 }

PS:UIKit默認的坐標系統與Quartz不同。在UIKit中,原點位于左上角,y軸正方向為向下。UIView通過將修改Quartz的Graphics Context的CTM[原點平移到左下角,同時將y軸反轉(y值乘以-1)]以使其與UIView匹配。這些都是系統自動幫我們完成。

二、創建一個PDF Graphics Context

 Quartz 2D API提供了兩個函數來創建PDF Graphics Context:當創建一個PDF Graphics Context并繪制時,Quartz將繪制操作記錄為一系列的PDF繪制命令并寫入文件中。我們需要提供一個PDF輸出的位置及一個默認的media box(用于指定頁面邊界的長方形)。

1.CGPDFContextCreateWithURL:當你需要用Core Foundation URL指定pdf輸出的位置時使用該函數。

 1 CGContextRef MyPDFContextCreate (const CGRect *inMediaBox, CFStringRef path) 2 { 3     CGContextRef myOutContext = NULL; 4     CFURLRef url; 5     url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, false); 6     if (url != NULL) { 7         myOutContext = CGPDFContextCreateWithURL (url,  inMediaBox,  NULL); 8         CFRelease(url); 9     }10     return myOutContext;11 }

2.CGPDFContextCreate:當需要將pdf輸出發送給數據用戶時使用該方法。

 1 CGContextRef MyPDFContextCreate (const CGRect *inMediaBox, CFStringRef path) 2 { 3     CGContextRef        myOutContext = NULL; 4     CFURLRef            url; 5     CGDataConsumerRef   dataConsumer; 6     url = CFURLCreateWithFileSystemPath (NULL,  path, kCFURLPOSIXPathStyle, false); 7     if (url != NULL) 8     { 9         dataConsumer = CGDataConsumerCreateWithURL (url);10         if (dataConsumer != NULL)11         {12             myOutContext = CGPDFContextCreate (dataConsumer, inMediaBox, NULL);13             CGDataConsumerRelease (dataConsumer);14         }15         CFRelease(url);16     }17     return myOutContext;18 } 

下面是如何調用MyPDFContextCreate程序及繪制操作。

 1     CGRect mediaBox; 2     mediaBox = CGRectMake (0, 0, myPageWidth, myPageHeight); 3     myPDFContext = MyPDFContextCreate (&mediaBox, CFSTR("test.pdf")); 4     CFStringRef myKeys[1]; 5     CFTypeRef myValues[1]; 6     myKeys[0] = kCGPDFContextMediaBox; 7     myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect)); 8     CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys, 9                                                         (const void **) myValues, 1,10                                                         &kCFTypeDictionaryKeyCallBacks,11                                                         & kCFTypeDictionaryValueCallBacks);12     CGPDFContextBeginPage(myPDFContext, &pageDictionary);13         // ********** 繪制代碼 **********14         CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);15         CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));16         CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);17         CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));18     CGPDFContextEndPage(myPDFContext);19     CFRelease(pageDictionary);20     CFRelease(myValues[0]);21     CGContextRelease(myPDFContext);

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

添加鏈接
我們可以在PDF上下文中添加鏈接和錨點。Quartz提供了三個函數,每個函數都以PDF圖形上下文作為參數,還有鏈接的信息:
· CGPDFContextSetURLForRect可以讓我們指定在點擊當前PDF頁中的矩形時打開一個URL。
· CGPDFContextSetDestinationForRect指定在點擊當前PDF頁中的矩形區域時設置目標以進行跳轉。我們需要提供一個目標名。
· CGPDFContextAddDestinationAtPoint指定在點擊當前PDF頁中的一個點時設置目標以進行跳轉。我們需要提供一個目標名。
保護PDF內容
為了保護PDF內容,我們可以在輔助字典中指定一些安全選項并傳遞給CGPDFContextCreate。我們可以通過包含如下關鍵字來設置所有者密碼、用戶密碼、PDF是否可以被打印或拷貝:
· kCGPDFContextOwnerPassWord定義PDF文檔的所有者密碼。如果指定該值,則文檔使用所有者密碼來加密;否則文檔不加密。該關鍵字的值必須是ASCII編碼的CFString對象。只有前32位是用于密碼的。該值沒有默認值。如果該值不能表示成ASCII,則無法創建文檔并返回NULL。Quartz使用40-bit加密。
· kCGPDFContextUserPassword: 定義PDF文檔的用戶密碼。如果文檔加密了,則該值是文檔的用戶密碼。如果沒有指定,則用戶密碼為空。該關鍵字的值必須是ASCII編碼的CFString對象。只有前32位是用于密碼的。如果該值不能表示成ASCII,則無法創建文檔并返回NULL。
· kCGPDFContextAllowsPRinting:指定當使用用戶密碼鎖定時文檔是否可以打印。該值必須是CFBoolean對象。默認值是kCGBooleanTrue。
· kCGPDFContextAllowsCopying: 指定當使用用戶密碼鎖定時文檔是否可以拷貝。該值必須是CFBoolean對象。默認值是kCGBooleanTrue。
代碼清單14-4(下一章)顯示了確認PDF文檔是否被鎖定,及用密碼打開文檔。

當然在iOS中,我們完全可以pass掉這些晦澀難懂的底層代碼,因為系統給我們提供了更高一級的函數來生成PDF文件,下面是代碼

 1 - (void)createPDFFile 2 { 3     // 1. 上下文 4     // 1) 路徑 5     // 2) 大小,指定為空,那么使用612 * 792大小作為pdf文件的頁面大小 6     // 3) dict 7     UIGraphicsBeginPDFContextToFile(@"/Users/apple/Desktop/demo.pdf", CGRectZero, nil); 8     9     // 2. 寫入內容10     // 在pdf里面是有頁面的,一個頁面一個頁面的寫入的11     // 建立PDF頁面12     // 一個頁面最多能夠寫入兩張圖片,因此寫入6張圖片需要三個頁面13     for (NSInteger i = 0; i < 6; i++) {14         if (i % 2 == 0) {15             UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);16         }17        18         // 生成UIImage19         UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"NatGeo%02d.png", i + 1]];20         // 寫入21         [image drawAtPoint:CGPointMake(0, 400 * (i % 2))];22     }23 24     // 3. 關閉上下文25     UIGraphicsEndPDFContext();26 }

三、創建位圖Graphics Context

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

我們使用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組件是否是預乘的,及顏色組件是整型值還是浮點值。

下列代碼顯示了如何創建位圖Graphics Context。當向位圖Graphics Context繪圖時,Quartz將繪圖記錄到內存中指定的塊中。

 

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

 

下面是調用MyCreateBitmapContext 創建一個位圖Graphics Context,使用位圖Graphics Context來創建CGImage對象,然后將圖片繪制到窗口Graphics Context中。

 1 CGRect myBoundingBox; 2     myBoundingBox = CGRectMake (0, 0, myWidth, myHeight); 3     myBitmapContext = MyCreateBitmapContext (400, 300); 4     // ********** Your drawing code here **********  5     CGContextSetRGBFillColor (myBitmapContext, 1, 0, 0, 1); 6     CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 200, 100 )); 7     CGContextSetRGBFillColor (myBitmapContext, 0, 0, 1, .5); 8     CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 100, 200 )); 9     myImage = CGBitmapContextCreateImage (myBitmapContext);10     CGContextDrawImage(myContext, myBoundingBox, myImage);11     char *bitmapData = CGBitmapContextGetData(myBitmapContext); 12     CGContextRelease (myBitmapContext);13     if (bitmapData) free(bitmapData); 14     CGImageRelease(myImage);

上面的都是Quartz的底層代碼,然后在iOS的實際開發中,iOS應用程序使用了UIGraphicsBeginImageContextWithOptions取代Quartz低層函數。所以說如果只搞iOS開發,上面晦澀的代碼完全可以pass掉。如果使用Quartz創建一下后臺bitmap,bitmap Graphics Context使用的坐標系統是Quartz默認的坐標系統。而使用UIGraphicsBeginImageContextWithOptions創建圖形上下文,UIKit將會對坐標系統使用與UIView對象的圖形上下文一樣的轉換。這允許應用程序使用相同的繪制代碼而不需要擔心坐標系統問題。雖然我們的應用程序可以手動調整CTM達到相同的效果,但這種做沒有任何好處。

下面是iOS中利用UIGraphicsBeginImageContextWithOptions來剪裁成圓型位圖并返回的代碼

+ (instancetype)circleImageWithName:(NSString *)name borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor{    // 1.加載原圖    UIImage *oldImage = [UIImage imageNamed:name];        // 2.開啟上下文    CGFloat imageW = oldImage.size.width + 2 * borderWidth;    CGFloat imageH = oldImage.size.height + 2 * borderWidth;    CGSize imageSize = CGSizeMake(imageW, imageH);    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);        // 3.取得當前的上下文    CGContextRef ctx = UIGraphicsGetCurrentContext();        // 4.畫邊框(大圓)    [borderColor set];    CGFloat bigRadius = imageW * 0.5; // 大圓半徑    CGFloat centerX = bigRadius; // 圓心    CGFloat centerY = bigRadius;    CGContextAddArc(ctx, centerX, centerY, bigRadius, 0, M_PI * 2, 0);    CGContextFillPath(ctx); // 畫圓        // 5.小圓    CGFloat smallRadius = bigRadius - borderWidth;    CGContextAddArc(ctx, centerX, centerY, smallRadius, 0, M_PI * 2, 0);    // 裁剪(后面畫的東西才會受裁剪的影響)    CGContextClip(ctx);        // 6.畫圖    [oldImage drawInRect:CGRectMake(borderWidth, borderWidth, oldImage.size.width, oldImage.size.height)];        // 7.取圖    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();        // 8.結束上下文    UIGraphicsEndImageContext();        return newImage;}

支持的像素格式

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

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

 

Null8 bpp, 8 bpc, kCGImageAlphaOnlyMac OS X, iOS
Gray8 bpp, 8 bpc,kCGImageAlphaNoneMac OS X, iOS
Gray8 bpp, 8 bpc,kCGImageAlphaOnlyMac OS X, iOS
Gray16 bpp, 16 bpc, kCGImageAlphaNoneMac OS X
Gray32 bpp, 32 bpc, kCGImageAlphaNone|kCGBitmapFloatComponentsMac OS X
RGB16 bpp, 5 bpc, kCGImageAlphaNoneSkipFirstMac OS X, iOS
RGB32 bpp, 8 bpc, kCGImageAlphaNoneSkipFirstMac OS X, iOS
RGB32 bpp, 8 bpc, kCGImageAlphaNoneSkipLastMac OS X, iOS
RGB32 bpp, 8 bpc, kCGImageAlphaPremultipliedFirstMac OS X, iOS
RGB32 bpp, 8 bpc, kCGImageAlphaPremultipliedLastMac OS X, iOS
RGB64 bpp, 16 bpc, kCGImageAlphaPremultipliedLastMac OS X
RGB64 bpp, 16 bpc, kCGImageAlphaNoneSkipLastMac OS X
RGB128 bpp, 32 bpc, kCGImageAlphaNoneSkipLast |kCGBitmapFloatComponentsMac OS X
RGB128 bpp, 32 bpc, kCGImageAlphaPremultipliedLast |kCGBitmapFloatComponentsMac OS X
CMYK32 bpp, 8 bpc, kCGImageAlphaNoneMac OS X
CMYK64 bpp, 16 bpc, kCGImageAlphaNoneMac OS X
CMYK128 bpp, 32 bpc, kCGImageAlphaNone |kCGBitmapFloatComponentsMac 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: 方法將圖形繪制到打印機。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 剑川县| 邢台县| 北京市| 张家港市| 寻甸| 柳河县| 固安县| 中山市| 荣成市| 汝阳县| 浦东新区| 浦江县| 疏勒县| 南宫市| 临城县| 镇沅| 沐川县| 东乌珠穆沁旗| 伊宁县| 双鸭山市| 陈巴尔虎旗| 兰西县| 罗江县| 娄底市| 汝城县| 台州市| 当阳市| 彝良县| 万载县| 四平市| 黑水县| 龙岩市| 阜城县| 濉溪县| 兴宁市| 万年县| 岑溪市| 乌什县| 佳木斯市| 大连市| 东光县|