Quartz 2D編程指南之二:圖形上下文(Graphics Contexts)

本文轉(zhuǎn)載自:http://southpeak.github.io/2014/11/11/quartz2d-2/

一個Graphics Context表示一個繪制目標(biāo)狸吞。它包含繪制系統(tǒng)用于完成繪制指令的繪制參數(shù)和設(shè)備相關(guān)信息。Graphics Context定義了基本的繪制屬性,如顏色畔派、裁減區(qū)域、線條寬度和樣式信息骂际、字體信息帽氓、混合模式等。

我們可以通過幾種方式來獲取Graphics Context:Quartz提供的創(chuàng)建函數(shù)绣夺、Mac OS X框架或IOS的UIKit框架提供的函數(shù)吏奸。Quartz提供了多種Graphics Context的創(chuàng)建函數(shù),包括bitmap和PDF陶耍,我們可以使用這些Graphics Context創(chuàng)建自定義的內(nèi)容奋蔚。

本章介紹了如何為不同的繪制目標(biāo)創(chuàng)建Graphics Context。在代碼中,我們用CGContextRef來表示一個Graphics Context泊碑。當(dāng)獲得一個Graphics Context后坤按,可以使用Quartz 2D函數(shù)在上下文(context)中進行繪制、完成操作(如平移)馒过、修改圖形狀態(tài)參數(shù)(如線寬和填充顏色)等臭脓。

在iOS中的視圖Graphics Context進行繪制

在iOS應(yīng)用程序中,如果要在屏幕上進行繪制腹忽,需要創(chuàng)建一個UIView對象来累,并實現(xiàn)它的drawRect:方法。視圖的drawRect:方法在視圖顯示在屏幕上及它的內(nèi)容需要更新時被調(diào)用窘奏。在調(diào)用自定義的drawRect:后嘹锁,視圖對象自動配置繪圖環(huán)境以便代碼能立即執(zhí)行繪圖操作。作為配置的一部分着裹,視圖對象將為當(dāng)前的繪圖環(huán)境創(chuàng)建一個Graphics Context领猾。我們可以通過調(diào)用UIGraphicsGetCurrentContext函數(shù)來獲取這個Graphics Context。

UIKit默認(rèn)的坐標(biāo)系統(tǒng)與Quartz不同骇扇。在UIKit中摔竿,原點位于左上角,y軸正方向為向下少孝。UIView通過將修改Quartz的Graphics Context的CTM[原點平移到左下角继低,同時將y軸反轉(zhuǎn)(y值乘以-1)]以使其與UIView匹配。

在Mac OS X中創(chuàng)建一個窗口Graphics Context

在Mac OS X中繪制時韭山,我們需要創(chuàng)建一個窗口Graphics Context郁季。Quartz 2D API 沒有提供函數(shù)來獲取窗口Graphics Context。取而代之的是用Cocoa框架來獲取一個窗口上下文钱磅。

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

CGContextRefmyContext = [[NSGraphicsContextcurrentContext] graphicsPort];

currentContext方法在當(dāng)前線程中返回NSGraphicsContext實例。graphicsPort方法返回一個低級別盖淡、平臺相關(guān)的Graphics Context(Quartz Graphics Context)年柠。

在獲取到Graphics Context后,我們可以在Cocoa應(yīng)用程序中調(diào)用任何Quartz 2D的繪制函數(shù)褪迟。我們同樣可以將Quartz 2D與Cocoa繪制操作混合使用冗恨。如圖2-1是一個在Cocoa視圖中用Quartz 2D繪制的實例。繪圖由兩個長方形組成(一個不透明的紅色長方形和半透明的藍色長方形)味赃。

Figure 2-1 A view in the Cocoa framework that contains Quartz drawing

為了實現(xiàn)圖2-1實例掀抹,需要先創(chuàng)建一個Cocoa應(yīng)用程序。在Interface Builder中心俗,拖動一個Custom View到窗口中傲武,并子類化蓉驹。然后實現(xiàn)子類視圖的,如代碼清單2-1所示揪利。視圖的drawRect:包含了所有的Quartz繪制代碼态兴。

注:NSView的drawRect:方法在每次視圖需要繪制時自動調(diào)用。

Listing 2-1 Drawing to a window graphics context

@implementationMyQuartzView

- (id)initWithFrame:(NSRect)frameRect

{

self= [superinitWithFrame:frameRect];

returnself;

}

- (void)drawRect:(NSRect)rec

{

CGContextRefmyContext = [[NSGraphicsContextcurrentContext] graphicsPort];//1

// ********** 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

代碼說明:

為視圖獲取一個Graphics Context

插入繪圖代碼的地方疟位。以下四行是使用Quartz 2D函數(shù)的例子

設(shè)置完全不透明的紅色填充色瞻润。

填充一個長方形,其原點為(0, 0), 大小為(200, 100)

設(shè)置半透明的藍色填充色甜刻。

填充一個長方形绍撞,其原點為(0, 0), 大小為(100, 200)

創(chuàng)建一個PDF Graphics Context

當(dāng)創(chuàng)建一個PDF Graphics Context并繪制時,Quartz將繪制操作記錄為一系列的PDF繪制命令并寫入文件中罢吃。我們需要提供一個PDF輸出的位置及一個默認(rèn)的media box(用于指定頁面邊界的長方形)楚午。圖2-2顯示了在PDF Graphics Context中繪制及在preview打開PDF的結(jié)果昭齐。

Figure 2-2 A PDF created by using CGPDFContextCreateWithURL

Quartz 2D API提供了兩個函數(shù)來創(chuàng)建PDF Graphics Context:

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

Listing 2-2 Calling CGPDFContextCreateWithURL to create a PDF graphics context

CGContextRefMyPDFContextCreate (constCGRect*inMediaBox,CFStringRefpath)

{

CGContextRefmyOutContext =NULL;

CFURLRefurl;

url =CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle,false);

if(url !=NULL) {

myOutContext =CGPDFContextCreateWithURL(url,? inMediaBox,NULL);

CFRelease(url);

}

returnmyOutContext;

}

CGPDFContextCreate:當(dāng)需要將pdf輸出發(fā)送給數(shù)據(jù)用戶時使用該方法。代碼清單2-3顯示了該函數(shù)的使用方法:

Listing 2-3 Calling CGPDFContextCreate to create a PDF graphics context

CGContextRefMyPDFContextCreate (constCGRect*inMediaBox,CFStringRefpath)

{

CGContextRefmyOutContext =NULL;

CFURLRefurl;

CGDataConsumerRefdataConsumer;

url =CFURLCreateWithFileSystemPath(NULL,? path, kCFURLPOSIXPathStyle,false);

if(url !=NULL)

{

dataConsumer =CGDataConsumerCreateWithURL(url);

if(dataConsumer !=NULL)

{

myOutContext =CGPDFContextCreate(dataConsumer, inMediaBox,NULL);

CGDataConsumerRelease(dataConsumer);

}

CFRelease(url);

}

returnmyOutContext;

}

代碼清單2-4顯示是如何調(diào)用MyPDFContextCreate程序及繪制操作阱驾。

Listing 2-4 Drawing to a PDF graphics context

CGRectmediaBox;

mediaBox =CGRectMake(0,0, myPageWidth, myPageHeight);

myPDFContext = MyPDFContextCreate (&mediaBox,CFSTR("test.pdf"));

CFStringRefmyKeys[1];

CFTypeRefmyValues[1];

myKeys[0] = kCGPDFContextMediaBox;

myValues[0] = (CFTypeRef)CFDataCreate(NULL,(constUInt8*)&mediaBox,sizeof(CGRect));

CFDictionaryRefpageDictionary =CFDictionaryCreate(NULL, (constvoid**) myKeys,

(constvoid**) myValues,1,

&kCFTypeDictionaryKeyCallBacks,

& kCFTypeDictionaryValueCallBacks);

CGPDFContextBeginPage(myPDFContext, &pageDictionary);

// ********** Your drawing code here **********

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);

CFRelease(pageDictionary);

CFRelease(myValues[0]);

CGContextRelease(myPDFContext);

我們可以將任何內(nèi)容(圖片就谜,文本,繪制路徑)繪制到pdf中里覆,并能添加鏈接及加密丧荐。

創(chuàng)建位圖Graphics Context

一個位圖Graphics Context接受一個指向內(nèi)存緩存(包含位圖存儲空間)的指針,當(dāng)我們繪制一個位圖Graphics Context時喧枷,該緩存被更新虹统。在釋放Graphics Context后,我們將得到一個我們指定像素格式的全新的位圖隧甚。

注:位圖Graphics Context有時用于后臺繪制车荔。CGLayer對象優(yōu)化了后臺繪制,因為Quartz在顯卡上緩存了層戚扳。

iOS提示:iOS應(yīng)用程序使用了UIGraphicsBeginImageContextWithOptions取代Quartz低層函數(shù)忧便。如果使用Quartz創(chuàng)建一下后臺bitmap,bitmap Graphics Context使用的坐標(biāo)系統(tǒng)是Quartz默認(rèn)的坐標(biāo)系統(tǒng)帽借。而使用UIGraphicsBeginImageContextWithOptions創(chuàng)建圖形上下文珠增,UIKit將會對坐標(biāo)系統(tǒng)使用與UIView對象的圖形上下文一樣的轉(zhuǎn)換。這允許應(yīng)用程序使用相同的繪制代碼而不需要擔(dān)心坐標(biāo)系統(tǒng)問題砍艾。雖然我們的應(yīng)用程序可以手動調(diào)整CTM達到相同的效果蒂教,但這種做沒有任何好處。

我們使用CGBitmapContextCreate來創(chuàng)建位圖Graphics Context脆荷,該函數(shù)有如下參數(shù):

data:一個指向內(nèi)存目標(biāo)的指針凝垛,該內(nèi)存用于存儲需要渲染的圖形數(shù)據(jù)。內(nèi)存塊的大小至少需要(bytePerRow * height)字節(jié)。

width:指定位圖的寬度苔严,單位是像素(pixel)定枷。

height:指定位圖的高度, 單位是像素(pixel)届氢。

bitsPerComponent:指定內(nèi)存中一個像素的每個組件使用的位數(shù)欠窒。例如,一個32位的像素格式和一個rgb顏色空間退子,我們可以指定每個組件為8位岖妄。

bytesPerRow:指定位圖每行的字節(jié)數(shù)。

colorspace:顏色空間用于位圖上下文寂祥。在創(chuàng)建位圖Graphics Context時荐虐,我們可以使用灰度(gray), RGB, CMYK, NULL顏色空間。

bitmapInfo:位圖的信息丸凭,這些信息用于指定位圖是否需要包含alpha組件福扬,像素中alpha組件的相對位置(如果有的話),alpha組件是否是預(yù)乘的惜犀,及顏色組件是整型值還是浮點值铛碑。

代碼清單2-5顯示了如何創(chuàng)建位圖Graphics Context。當(dāng)向位圖Graphics Context繪圖時虽界,Quartz將繪圖記錄到內(nèi)存中指定的塊中汽烦。

Listing 2-5 Creating a bitmap graphics context

CGContextRefMyCreateBitmapContext (intpixelsWide,intpixelsHigh)

{

CGContextRefcontext =NULL;

CGColorSpaceRefcolorSpace;

void*? ? ? ? ? bitmapData;

intbitmapByteCount;

intbitmapBytesPerRow;

bitmapBytesPerRow? = (pixelsWide *4);

bitmapByteCount? ? = (bitmapBytesPerRow * pixelsHigh);

colorSpace =CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);

bitmapData = calloc( bitmapByteCount );

if(bitmapData ==NULL)

{

fprintf (stderr,"Memory not allocated!");

returnNULL;

}

context =CGBitmapContextCreate(bitmapData, pixelsWide, pixelsHigh,8, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);

if(context==NULL)

{

free (bitmapData);

fprintf (stderr,"Context not created!");

returnNULL;

}

CGColorSpaceRelease( colorSpace );

returncontext;

}

代碼清單2-6顯示了調(diào)用MyCreateBitmapContext 創(chuàng)建一個位圖Graphics Context,使用位圖Graphics Context來創(chuàng)建CGImage對象莉御,然后將圖片繪制到窗口Graphics Context中撇吞。繪制結(jié)果如圖2-3所示:

Listing 2-6 Drawing to a bitmap graphics context

CGRectmyBoundingBox;

myBoundingBox =CGRectMake(0,0, myWidth, myHeight);

myBitmapContext = MyCreateBitmapContext (400,300);

// ********** Your drawing code here **********

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);

CGContextDrawImage(myContext, myBoundingBox, myImage);

char*bitmapData =CGBitmapContextGetData(myBitmapContext);

CGContextRelease(myBitmapContext);

if(bitmapData) free(bitmapData);

CGImageRelease(myImage);

Figure 2-3 An image created from a bitmap graphics context and drawn to a window graphics context

支持的像素格式

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

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

反鋸齒

位圖Graphics Context支持反鋸齒晴圾,這一操作是人為的較正在位圖中繪制文本或形狀時產(chǎn)生的鋸齒邊緣颂砸。當(dāng)位圖的分辯率明顯低于人眼的分辯率時就會產(chǎn)生鋸齒。為了使位圖中的對象顯得平滑死姚,Quartz使用不同的顏色來填充形狀周邊的像素人乓。通過這種方式來混合顏色,使形狀看起來更平滑都毒。如圖2-4顯示的效果色罚。我們可以通過調(diào)用CGContextSetShouldAntialias來關(guān)閉位圖Graphics Context的反鋸齒效果。反鋸齒設(shè)置是圖形狀態(tài)的一部分账劲。

可以調(diào)用函數(shù)CGContextSetAllowsAntialiasing來控制一個特定Graphics Context是否支持反鋸齒戳护;false表示不支持金抡。該設(shè)置不是圖形狀態(tài)的一部分。當(dāng)上下文及圖形狀態(tài)設(shè)置為true時腌且,Quartz執(zhí)行反鋸齒梗肝。

Figure 2-4 A comparison of aliased and anti-aliasing drawing

獲取打印的Graphics Context

Mac OS X中的Cocoa應(yīng)用程序通過自定義的NSView子類來實現(xiàn)打印。一個視圖通過調(diào)用print:方法來進行打印铺董。然后視圖以打印機為目標(biāo)創(chuàng)建一個Graphics Context巫击,并調(diào)用drawRect:方法。應(yīng)用程序使用與在屏幕進行繪制相同的繪制代碼精续。我們同樣可以自定義drawRect: 方法將圖形繪制到打印機坝锰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市重付,隨后出現(xiàn)的幾起案子顷级,更是在濱河造成了極大的恐慌,老刑警劉巖确垫,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弓颈,死亡現(xiàn)場離奇詭異,居然都是意外死亡森爽,警方通過查閱死者的電腦和手機恨豁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爬迟,“玉大人,你說我怎么就攤上這事菊匿「杜唬” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵跌捆,是天一觀的道長徽职。 經(jīng)常有香客問我,道長佩厚,這世上最難降的妖魔是什么姆钉? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮抄瓦,結(jié)果婚禮上潮瓶,老公的妹妹穿的比我還像新娘。我一直安慰自己钙姊,他們只是感情好毯辅,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著煞额,像睡著了一般思恐。 火紅的嫁衣襯著肌膚如雪沾谜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天胀莹,我揣著相機與錄音基跑,去河邊找鬼。 笑死描焰,一個胖子當(dāng)著我的面吹牛涩僻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播栈顷,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼逆日,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了萄凤?” 一聲冷哼從身側(cè)響起室抽,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎靡努,沒想到半個月后坪圾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡惑朦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年兽泄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漾月。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡病梢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梁肿,到底是詐尸還是另有隱情蜓陌,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布吩蔑,位于F島的核電站钮热,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏烛芬。R本人自食惡果不足惜隧期,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赘娄。 院中可真熱鬧仆潮,春花似錦、人聲如沸擅憔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暑诸。三九已至蚌讼,卻和暖如春辟灰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背篡石。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工芥喇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凰萨。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓继控,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胖眷。 傳聞我的和親對象是個殘疾皇子武通,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容