--繪圖與濾鏡全面解析html
在iOS中能夠很容易的開發出絢麗的界面效果,一方面得益於成功系統的設計,另外一方面得益於它強大的開發框架。今天咱們將圍繞iOS中兩大圖形、圖像繪圖框架進行介紹:Quartz 2D繪製2D圖形和Core Image中強大的濾鏡功能。html5
在iOS中經常使用的繪圖框架就是Quartz 2D,Quartz 2D是Core Graphics框架的一部分,是一個強大的二維圖像繪製引擎。Quartz 2D在UIKit中也有很好的封裝和集成,咱們平常開發時所用到的UIKit中的組件都是由Core Graphics進行繪製的。不只如此,當咱們引入UIKit框架時系統會自動引入Core Graphics框架,而且爲了方便開發者使用在UIKit內部還對一些經常使用的繪圖API進行了封裝。程序員
在iOS中繪圖通常分爲如下幾個步驟:算法
1.獲取繪圖上下文編程
2.建立並設置路徑canvas
3.將路徑添加到上下文數組
4.設置上下文狀態app
5.繪製路徑框架
6.釋放路徑ide
圖形上下文CGContextRef表明圖形輸出設備(也就是繪製的位置),包含了繪製圖形的一些設備信息,Quartz 2D中的全部對象最終都必須繪製到圖形上下文。這樣一來,咱們在繪製圖形時就沒必要關心具體的設備信息,統一了代碼編寫方式(在Quartz 2D中的繪圖上下文能夠是位圖Bitmap、PDF、窗口Window、層Layer、打印對象Printer)。
在UIKit中默認已經爲咱們準備好了一個圖形上下文對象,在UI控件的drawRect:方法(這個方法在loadView、viewDidLoad方法後執行)中咱們能夠經過UIKit封裝函數UIGraphicsGetCurrentContext()方法得到這個圖形上下文(注意在其餘UI控件方法中沒法取得這個對象),而後咱們只要按照繪圖步驟一步步執行便可。下面自定義一個KCView繼承自UIView,重寫drawRect:方法繪製兩條直線說明上面繪圖的步驟:
KCView.m
// // KCView.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // /** 基本繪圖 */ #import "KCView.h" @implementation KCView #pragma mark 繪圖 //繪圖只能在此方法中調用,不然沒法獲得當前圖形上下文 -(void)drawRect:(CGRect)rect{ //1.取得圖形上下文對象 CGContextRef context = UIGraphicsGetCurrentContext(); //2.建立路徑對象 CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, nil, 20, 50);//移動到指定位置(設置路徑起點) CGPathAddLineToPoint(path, nil, 20, 100);//繪製直線(從起始位置開始) CGPathAddLineToPoint(path, nil, 300, 100);//繪製另一條直線(從上一直線終點開始繪製) //3.添加路徑到圖形上下文 CGContextAddPath(context, path); //4.設置圖形上下文狀態屬性 CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);//設置筆觸顏色 CGContextSetRGBFillColor(context, 0, 1.0, 0, 1);//設置填充色 CGContextSetLineWidth(context, 2.0);//設置線條寬度 CGContextSetLineCap(context, kCGLineCapRound);//設置頂點樣式,(20,50)和(300,100)是頂點 CGContextSetLineJoin(context, kCGLineJoinRound);//設置鏈接點樣式,(20,100)是鏈接點 /*設置線段樣式 phase:虛線開始的位置 lengths:虛線長度間隔(例以下面的定義說明第一條線段長度8,而後間隔3從新繪製8點的長度線段,固然這個數組能夠定義更多元素) count:虛線數組元素個數 */ CGFloat lengths[2] = { 18, 9 }; CGContextSetLineDash(context, 0, lengths, 2); /*設置陰影 context:圖形上下文 offset:偏移量 blur:模糊度 color:陰影顏色 */ CGColorRef color = [UIColor grayColor].CGColor;//顏色轉化,因爲Quartz 2D跨平臺,因此其中不能使用UIKit中的對象,可是UIkit提供了轉化方法 CGContextSetShadowWithColor(context, CGSizeMake(2, 2), 0.8, color); //5.繪製圖像到指定圖形上下文 /*CGPathDrawingMode是填充方式,枚舉類型 kCGPathFill:只有填充(非零纏繞數填充),不繪製邊框 kCGPathEOFill:奇偶規則填充(多條路徑交叉時,奇數交叉填充,偶交叉不填充) kCGPathStroke:只有邊框 kCGPathFillStroke:既有邊框又有填充 kCGPathEOFillStroke:奇偶填充並繪製邊框 */ CGContextDrawPath(context, kCGPathFillStroke);//最後一個參數是填充類型 //6.釋放對象 CGPathRelease(path); } @end
在視圖控制器建立KCView並添加到根視圖中:
- (void)viewDidLoad { [super viewDidLoad]; KCView *view=[[KCView alloc]initWithFrame:[UIScreen mainScreen].bounds]; view.backgroundColor=[UIColor whiteColor]; [self.view addSubview:view]; }
上面的繪圖方式未免顯得有些麻煩,其實Core Graphics 內部對建立對象添加到上下文這兩步操做進行了封裝,能夠一步完成。另外前面也說過UIKit內部其實封裝了一些以「UI」開頭的方法幫助你們進行圖形繪製。就拿前面的例子來講咱們改進一些繪製方法:
-(void)drawLine2{ //1.得到圖形上下文 CGContextRef context=UIGraphicsGetCurrentContext(); //2.繪製路徑(至關於前面建立路徑並添加路徑到圖形上下文兩步操做) CGContextMoveToPoint(context, 20, 50); CGContextAddLineToPoint(context, 20, 100); CGContextAddLineToPoint(context, 300, 100); //封閉路徑:a.建立一條起點和終點的線,不推薦 //CGPathAddLineToPoint(path, nil, 20, 50); //封閉路徑:b.直接調用路徑封閉方法 CGContextClosePath(context); //3.設置圖形上下文屬性 [[UIColor redColor]setStroke];//設置紅色邊框 [[UIColor greenColor]setFill];//設置綠色填充 //[[UIColor blueColor]set];//同時設置填充和邊框色 //4.繪製路徑 CGContextDrawPath(context, kCGPathFillStroke); }
上面的操做相比前面的方法應該說已經簡化了很多,除了路徑以外其餘矩形、橢圓等都有對應的建立方法。另外上面咱們也演示了封閉路徑的方法,你們能夠運行看一下效果。
相信你們瞭解了上面的繪製步驟其餘圖形繪製並不麻煩,下面以一個例子簡單演示一下其餘圖形的繪製,包括文字和圖像的繪製。
在下面的方法中還能夠看到UIKit對繪圖方法的封裝,使用起來更加簡單。
#pragma mark 繪製矩形 -(void)drawRectWithContext:(CGContextRef)context{ //添加矩形對象 CGRect rect=CGRectMake(20, 50, 280.0, 50.0); CGContextAddRect(context,rect); //設置屬性 [[UIColor blueColor]set]; //繪製 CGContextDrawPath(context, kCGPathFillStroke); } #pragma mark 繪製矩形(利用UIKit的封裝方法) -(void)drawRectByUIKitWithContext:(CGContextRef)context{ CGRect rect= CGRectMake(20, 150, 280.0, 50.0); CGRect rect2=CGRectMake(20, 250, 280.0, 50.0); //設置屬性 [[UIColor yellowColor]set]; //繪製矩形,至關於建立對象、添加對象到上下文、繪製三個步驟 UIRectFill(rect);//繪製矩形(只有填充) [[UIColor redColor]setStroke]; UIRectFrame(rect2);//繪製矩形(只有邊框) } @end
#pragma mark 繪製橢圓 -(void)drawEllipse:(CGContextRef)context{ //添加對象,繪製橢圓(圓形)的過程也是先建立一個矩形 CGRect rect=CGRectMake(50, 50, 220.0, 200.0); CGContextAddEllipseInRect(context, rect); //設置屬性 [[UIColor purpleColor]set]; //繪製 CGContextDrawPath(context, kCGPathFillStroke); } @end
運行效果:
-(void)drawArc:(CGContextRef)context{ /*添加弧形對象 x:中心點x座標 y:中心點y座標 radius:半徑 startAngle:起始弧度 endAngle:終止弧度 closewise:是否逆時針繪製,0則順時針繪製 */ CGContextAddArc(context, 160, 160, 100.0, 0.0, M_PI_2, 1); //設置屬性 [[UIColor yellowColor]set]; //繪製 CGContextDrawPath(context, kCGPathFillStroke); }
要繪製規則圖形在iOS中至關簡單,可是不規則圖形怎麼繪製呢?此時就要利用路徑。前面咱們繪製了直線,它和曲線繪製都屬於路徑繪製。和直線繪製相比曲線繪製就要複雜一些,可是路徑做爲高級動畫的基礎又是咱們必須掌握的,所以這裏咱們就一塊兒來熟悉一下曲線繪製。在Quartz 2D中曲線繪製分爲兩種:二次貝塞爾曲線和三次貝塞爾曲線。二次曲線只有一個控制點,而三次曲線有兩個控制點,以下圖所示:
固然,在iOS中兩種曲線分別對應兩種方法:
CGContextAddQuadCurveToPoint(CGContextRef c, CGFloat cpx, CGFloat cpy, CGFloat x, CGFloat y);
CGContextAddCurveToPoint(context, CGFloat cp1x, CGFloat cp1y, CGFloat cp2x, CGFloat cp2y, CGFloat x, CGFloat y);
下面就演示一下這兩種曲線的繪製方法
#pragma mark 繪製貝塞爾曲線 -(void)drawCurve:(CGContextRef)context{ //繪製曲線 CGContextMoveToPoint(context, 20, 100);//移動到起始位置 /*繪製二次貝塞爾曲線 c:圖形上下文 cpx:控制點x座標 cpy:控制點y座標 x:結束點x座標 y:結束點y座標 */ CGContextAddQuadCurveToPoint(context, 160, 0, 300, 100); CGContextMoveToPoint(context, 20, 500); /*繪製三次貝塞爾曲線 c:圖形上下文 cp1x:第一個控制點x座標 cp1y:第一個控制點y座標 cp2x:第二個控制點x座標 cp2y:第二個控制點y座標 x:結束點x座標 y:結束點y座標 */ CGContextAddCurveToPoint(context, 80, 300, 240, 500, 300, 300); //設置圖形上下文屬性 [[UIColor yellowColor]setFill]; [[UIColor redColor]setStroke]; //繪製路徑 CGContextDrawPath(context, kCGPathFillStroke); }
備註:貝塞爾曲線是由法國數學家「貝塞爾」發現的,他發現:任何一條曲線都可以由和它相切的直線的兩個端點來描述,這種曲線表示方式後來被普遍應用到計算機中,稱爲「貝塞爾曲線」。
除了繪製圖形還能夠繪製文本內容。
-(void)drawText:(CGContextRef)context{ //繪製到指定的區域內容 NSString *str=@"Star Walk is the most beautiful stargazing app you’ve ever seen on a mobile device. It will become your go-to interactive astro guide to the night sky, following your every movement in real-time and allowing you to explore over 200, 000 celestial bodies with extensive information about stars and constellations that you find."; CGRect rect= CGRectMake(20, 50, 280, 300); UIFont *font=[UIFont systemFontOfSize:18];//設置字體 UIColor *color=[UIColor redColor];//字體顏色 NSMutableParagraphStyle *style=[[NSMutableParagraphStyle alloc]init];//段落樣式 NSTextAlignment align=NSTextAlignmentLeft;//對齊方式 style.alignment=align; [str drawInRect:rect withAttributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:color,NSParagraphStyleAttributeName:style}]; }
運行效果:
Quartz 2D還能夠將圖像繪製到圖形上下文。
-(void)drawImage:(CGContextRef)context{ UIImage *image=[UIImage imageNamed:@"image2.jpg"]; //從某一點開始繪製 [image drawAtPoint:CGPointMake(10, 50)]; //繪製到指定的矩形中,注意若是大小不合適會會進行拉伸 // [image drawInRect:CGRectMake(10, 50, 300, 450)]; //平鋪繪製 // [image drawAsPatternInRect:CGRectMake(0, 0, 320, 568)]; }
運行效果:
從前面的示例中咱們能夠看到如何設置填充顏色,事實上不少時候純色的填充並不能知足咱們的需求,例若有時候咱們要繪製一些圖形可能須要設置一個漂亮的背景,這個時候咱們可能就會選擇漸變填充方式。Quartz 2D的漸變方式分爲兩種:
a.線性漸變線:漸變色以直線方式從開始位置逐漸向結束位置漸變
b.徑向漸變:以中心點爲圓心從起始漸變色向四周輻射,直到終止漸變色
要作漸變則必須先設置從開始位置到結束位置的漸變顏色,作過photoshop的朋友相信對於漸變色設置並不陌生,只要在指定位置指定不一樣的顏色,剩下的事情交給系統處理便可,以下圖在起始位置、3/10位置、結束位置指定了三種顏色就造成由三種顏色組成的漸變色:
另外,在iOS中繪製漸變還須要注意一點就是指定顏色空間,所謂顏色空間就是不一樣顏色在不一樣的維度上取值最終組成一種顏色的過程。就拿RGB來講,若是將紅色、綠色、藍色當作是x、y、z軸座標系,那麼在三個座標上分別取0~255範圍內的不一樣值則能夠組成各種顏色。固然,不一樣顏色空間的「座標系」也是不一樣的(也就是說顏色表示的方式是不一樣的),經常使用的顏色空間除了RGB還有CMYK(印刷業經常使用這種顏色模式)、Gray。
在使用Quartz 2D繪圖時咱們的顏色除了使用常規的方法(如何前面CGContextSetRGBFillColor(CGContextRef context, CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha)方法)設置RGB和透明度外,有時還會遇到顏色參數是一個數組狀況。如使用顏色空間填充時用到的CGContextSetFillColor(CGContextRef context, const CGFloat *components)方法,這個時候components數組中具體是如何存儲顏色就要根據顏色空間而定,若是顏色空間使用RGB則數組中的元素四個爲一組,分別是red(紅)、green(綠)、blue(藍)、alpha(透明度);若是使用CMYK顏色空間,那麼數組中的元素五個爲一組,分別是cyan(青)、magenta(洋紅)、yellow(黃)、black(黑)、alpha(透明度)。
下面的代碼分別演示了兩種漸變方式,具體漸變繪製函數參數代碼中已經註釋的很清楚了:
// // KCView3.m // Quartz2D // // Created by Kenshin Cui on 14-3-16. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCView3.h" @implementation KCView3 -(void)drawRect:(CGRect)rect{ CGContextRef context=UIGraphicsGetCurrentContext(); // [self drawLinearGradient:context]; [self drawRadialGradient:context]; } #pragma mark 線性漸變 -(void)drawLinearGradient:(CGContextRef)context{ //使用rgb顏色空間 CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB(); /*指定漸變色 space:顏色空間 components:顏色數組,注意因爲指定了RGB顏色空間,那麼四個數組元素表示一個顏色(red、green、blue、alpha), 若是有三個顏色則這個數組有4*3個元素 locations:顏色所在位置(範圍0~1),這個數組的個數不小於components中存放顏色的個數 count:漸變個數,等於locations的個數 */ CGFloat compoents[12]={ 248.0/255.0,86.0/255.0,86.0/255.0,1, 249.0/255.0,127.0/255.0,127.0/255.0,1, 1.0,1.0,1.0,1.0 }; CGFloat locations[3]={0,0.3,1.0}; CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3); /*繪製線性漸變 context:圖形上下文 gradient:漸變色 startPoint:起始位置 endPoint:終止位置 options:繪製方式,kCGGradientDrawsBeforeStartLocation 開始位置以前就進行繪製,到結束位置以後再也不繪製, kCGGradientDrawsAfterEndLocation開始位置以前不進行繪製,到結束點以後繼續填充 */ CGContextDrawLinearGradient(context, gradient, CGPointZero, CGPointMake(320, 300), kCGGradientDrawsAfterEndLocation); //釋放顏色空間 CGColorSpaceRelease(colorSpace); } #pragma mark 徑向漸變 -(void)drawRadialGradient:(CGContextRef)context{ //使用rgb顏色空間 CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB(); /*指定漸變色 space:顏色空間 components:顏色數組,注意因爲指定了RGB顏色空間,那麼四個數組元素表示一個顏色(red、green、blue、alpha), 若是有三個顏色則這個數組有4*3個元素 locations:顏色所在位置(範圍0~1),這個數組的個數不小於components中存放顏色的個數 count:漸變個數,等於locations的個數 */ CGFloat compoents[12]={ 248.0/255.0,86.0/255.0,86.0/255.0,1, 249.0/255.0,127.0/255.0,127.0/255.0,1, 1.0,1.0,1.0,1.0 }; CGFloat locations[3]={0,0.3,1.0}; CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3); /*繪製徑向漸變 context:圖形上下文 gradient:漸變色 startCenter:起始點位置 startRadius:起始半徑(一般爲0,不然在此半徑範圍內容無任何填充) endCenter:終點位置(一般和起始點相同,不然會有偏移) endRadius:終點半徑(也就是漸變的擴散長度) options:繪製方式,kCGGradientDrawsBeforeStartLocation 開始位置以前就進行繪製,可是到結束位置以後再也不繪製, kCGGradientDrawsAfterEndLocation開始位置以前不進行繪製,但到結束點以後繼續填充 */ CGContextDrawRadialGradient(context, gradient, CGPointMake(160, 284),0, CGPointMake(165, 289), 150, kCGGradientDrawsAfterEndLocation); //釋放顏色空間 CGColorSpaceRelease(colorSpace); } @end
運行效果:
上面咱們只是繪製漸變到圖形上下文,實際開發中有時候咱們還須要填充對應的漸變色,例如如今繪製了一個矩形,如何填充成漸變色呢?在此能夠利用漸變裁切來完成(固然利用層CALayer更加方便但這不在今天的話題討論範圍內),特別說明一下區域裁切並不只僅適用於漸變填充,對於其餘圖形繪製仍然適用,而且注意裁切只能限於矩形裁切。
-(void)drawRectWithLinearGradientFill:(CGContextRef)context{ CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB(); //裁切處一塊矩形用於顯示,注意必須先裁切再調用漸變 //CGContextClipToRect(context, CGRectMake(20, 50, 280, 300)); //裁切還可使用UIKit中對應的方法 UIRectClip(CGRectMake(20, 50, 280, 300)); CGFloat compoents[12]={ 248.0/255.0,86.0/255.0,86.0/255.0,1, 249.0/255.0,127.0/255.0,127.0/255.0,1, 1.0,1.0,1.0,1.0 }; CGFloat locations[3]={0,0.3,1.0}; CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3); CGContextDrawLinearGradient(context, gradient, CGPointMake(20, 50), CGPointMake(300, 300), kCGGradientDrawsAfterEndLocation); //釋放顏色空間 CGColorSpaceRelease(colorSpace); }
運行效果:
經常使用的圖形上下文狀態設置上面基本都用到了,咱們再也不一一解釋,這裏着重說一下疊加模式和填充模式,初學者對於這兩個狀態設置每每容易產生疑惑。
使用Quartz 2D繪圖時後面繪製的圖像會覆蓋前面的,默認狀況下若是前面的被覆蓋後將看不到後面的內容,可是有時候這個結果並非咱們想要的,所以在Quartz 2D中提供了填充模式供開發者配置調整。因爲填充模式類別特別多,所以下面以一個例子來講明:
-(void)drawRectByUIKitWithContext2:(CGContextRef)context{ CGRect rect= CGRectMake(0, 130.0, 320.0, 50.0); CGRect rect1= CGRectMake(0, 390.0, 320.0, 50.0); CGRect rect2=CGRectMake(20, 50.0, 10.0, 250.0); CGRect rect3=CGRectMake(40.0, 50.0, 10.0, 250.0); CGRect rect4=CGRectMake(60.0, 50.0, 10.0, 250.0); CGRect rect5=CGRectMake(80.0, 50.0, 10.0, 250.0); CGRect rect6=CGRectMake(100.0, 50.0, 10.0, 250.0); CGRect rect7=CGRectMake(120.0, 50.0, 10.0, 250.0); CGRect rect8=CGRectMake(140.0, 50.0, 10.0, 250.0); CGRect rect9=CGRectMake(160.0, 50.0, 10.0, 250.0); CGRect rect10=CGRectMake(180.0, 50.0, 10.0, 250.0); CGRect rect11=CGRectMake(200.0, 50.0, 10.0, 250.0); CGRect rect12=CGRectMake(220.0, 50.0, 10.0, 250.0); CGRect rect13=CGRectMake(240.0, 50.0, 10.0, 250.0); CGRect rect14=CGRectMake(260.0, 50.0, 10.0, 250.0); CGRect rect15=CGRectMake(280.0, 50.0, 10.0, 250.0); CGRect rect16=CGRectMake(30.0, 310.0, 10.0, 250.0); CGRect rect17=CGRectMake(50.0, 310.0, 10.0, 250.0); CGRect rect18=CGRectMake(70.0, 310.0, 10.0, 250.0); CGRect rect19=CGRectMake(90.0, 310.0, 10.0, 250.0); CGRect rect20=CGRectMake(110.0, 310.0, 10.0, 250.0); CGRect rect21=CGRectMake(130.0, 310.0, 10.0, 250.0); CGRect rect22=CGRectMake(150.0, 310.0, 10.0, 250.0); CGRect rect23=CGRectMake(170.0, 310.0, 10.0, 250.0); CGRect rect24=CGRectMake(190.0, 310.0, 10.0, 250.0); CGRect rect25=CGRectMake(210.0, 310.0, 10.0, 250.0); CGRect rect26=CGRectMake(230.0, 310.0, 10.0, 250.0); CGRect rect27=CGRectMake(250.0, 310.0, 10.0, 250.0); CGRect rect28=CGRectMake(270.0, 310.0, 10.0, 250.0); CGRect rect29=CGRectMake(290.0, 310.0, 10.0, 250.0); [[UIColor yellowColor]set]; UIRectFill(rect); [[UIColor greenColor]setFill]; UIRectFill(rect1); [[UIColor redColor]setFill]; UIRectFillUsingBlendMode(rect2, kCGBlendModeClear); UIRectFillUsingBlendMode(rect3, kCGBlendModeColor); UIRectFillUsingBlendMode(rect4, kCGBlendModeColorBurn); UIRectFillUsingBlendMode(rect5, kCGBlendModeColorDodge); UIRectFillUsingBlendMode(rect6, kCGBlendModeCopy); UIRectFillUsingBlendMode(rect7, kCGBlendModeDarken); UIRectFillUsingBlendMode(rect8, kCGBlendModeDestinationAtop); UIRectFillUsingBlendMode(rect9, kCGBlendModeDestinationIn); UIRectFillUsingBlendMode(rect10, kCGBlendModeDestinationOut); UIRectFillUsingBlendMode(rect11, kCGBlendModeDestinationOver); UIRectFillUsingBlendMode(rect12, kCGBlendModeDifference); UIRectFillUsingBlendMode(rect13, kCGBlendModeExclusion); UIRectFillUsingBlendMode(rect14, kCGBlendModeHardLight); UIRectFillUsingBlendMode(rect15, kCGBlendModeHue); UIRectFillUsingBlendMode(rect16, kCGBlendModeLighten); UIRectFillUsingBlendMode(rect17, kCGBlendModeLuminosity); UIRectFillUsingBlendMode(rect18, kCGBlendModeMultiply); UIRectFillUsingBlendMode(rect19, kCGBlendModeNormal); UIRectFillUsingBlendMode(rect20, kCGBlendModeOverlay); UIRectFillUsingBlendMode(rect21, kCGBlendModePlusDarker); UIRectFillUsingBlendMode(rect22, kCGBlendModePlusLighter); UIRectFillUsingBlendMode(rect23, kCGBlendModeSaturation); UIRectFillUsingBlendMode(rect24, kCGBlendModeScreen); UIRectFillUsingBlendMode(rect25, kCGBlendModeSoftLight); UIRectFillUsingBlendMode(rect26, kCGBlendModeSourceAtop); UIRectFillUsingBlendMode(rect27, kCGBlendModeSourceIn); UIRectFillUsingBlendMode(rect28, kCGBlendModeSourceOut); UIRectFillUsingBlendMode(rect29, kCGBlendModeXOR); }
運行效果:
相信你們對比代碼和顯示效果並不難發現每種疊加的效果。例子中只是使用UIKit的封裝方法進行疊加模式設置,更通常的方法固然是使用CGContextSetBlendMode(CGContextRef context, CGBlendMode mode)方法進行設置。
前面的示例中已經演示過純色填充、漸變填充,而有時咱們須要按必定的自定義樣式進行填充,這種方式有點相似於貼瓷磚的方式。咱們知道若是家裏貼地板或瓷磚時,一般咱們會先選擇一種瓷磚樣式,根據房間面積咱們購買不一樣量的瓷磚。可是無論買多少,這些瓷磚的樣式都是如出一轍的。填充模式就是爲了達到這種效果而產生的:咱們只須要繪製一個瓷磚的樣式,而後讓程序自動調用這種樣式填充指定大小的區域。
Quartz 2D支持兩種填充模式:有顏色填充和無顏色填充。兩種模式使用起來區別很小,有顏色填充就是在繪製瓷磚時就指定顏色,在調用填充時就不用再指定瓷磚顏色;無顏色填充模式就是繪製瓷磚時不用指定任何顏色,在調用填充時再指定具體填充顏色。相比較無顏色填充模式而言,有顏色填充模式更加的靈活,推薦使用。
下面咱們具體看一下如何按指定模式進行圖形填充:
1.在使用填充模式時首先要構建一個符合CGPatternDrawPatternCallback簽名的方法,這個方法專門用來建立「瓷磚」。注意:若是使用有顏色填充模式,須要設置填充色。例如咱們定義一個方法drawTile繪製如下瓷磚(有顏色填充):
2.接着須要指定一個填充的顏色空間,這個顏色空間跟前面繪製漸變的顏色空間不太同樣,前面建立漸變使用的顏色空間是設備無關的,咱們須要基於這個顏色空間建立一個顏色空間專門用於填充(注意對於有顏色填充建立填充顏色空間參數爲NULL,不用基於設備無關的顏色空間建立)。
3.而後咱們就可使用CGPatternCreate方法建立一個填充模式,建立填充模式時須要注意其中的參數,在代碼中已經作了一一解釋(這裏注意對於有顏色填充模式isColored設置爲true,不然爲false)。
4.最後調用CGContextSetFillPattern方法給圖形上下文指定填充模式(這個時候注意最後一個參數,若是是有顏色填充模式最後一個參數爲透明度alpa的地址,對於無顏色填充模式最後一個參數是當前填充顏色空間的顏色數組)。
5.繪製圖形,這裏咱們繪製一個矩形。
6.釋放資源。
下面是具體代碼(包含兩種填充模式代碼,能夠一一運行)
// // UIView4.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCView.h" #define TILE_SIZE 20 @implementation KCView -(void)drawRect:(CGRect)rect{ CGContextRef context=UIGraphicsGetCurrentContext(); [self drawBackgroundWithColoredPattern:context]; // [self drawBackgroundWithPattern:context]; } #pragma mark - 有顏色填充模式 void drawColoredTile(void *info,CGContextRef context){ //有顏色填充,這裏設置填充色 CGContextSetRGBFillColor(context, 254.0/255.0, 52.0/255.0, 90.0/255.0, 1); CGContextFillRect(context, CGRectMake(0, 0, TILE_SIZE, TILE_SIZE)); CGContextFillRect(context, CGRectMake(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE)); } -(void)drawBackgroundWithColoredPattern:(CGContextRef)context{ //設備無關的顏色空間 // CGColorSpaceRef rgbSpace= CGColorSpaceCreateDeviceRGB(); //模式填充顏色空間,注意對於有顏色填充模式,這裏傳NULL CGColorSpaceRef colorSpace=CGColorSpaceCreatePattern(NULL); //將填充色顏色空間設置爲模式填充的顏色空間 CGContextSetFillColorSpace(context, colorSpace); //填充模式回調函數結構體 CGPatternCallbacks callback={0,&drawColoredTile,NULL}; /*填充模式 info://傳遞給callback的參數 bounds:瓷磚大小 matrix:形變 xStep:瓷磚橫向間距 yStep:瓷磚縱向間距 tiling:貼磚的方法 isClored:繪製的瓷磚是否已經指定了顏色(對於有顏色瓷磚此處指定位true) callbacks:回調函數 */ CGPatternRef pattern=CGPatternCreate(NULL, CGRectMake(0, 0, 2*TILE_SIZE, 2*TILE_SIZE), CGAffineTransformIdentity,2*TILE_SIZE+ 5,2*TILE_SIZE+ 5, kCGPatternTilingNoDistortion, true, &callback); CGFloat alpha=1; //注意最後一個參數對於有顏色瓷磚指定爲透明度的參數地址,對於無顏色瓷磚則指定當前顏色空間對應的顏色數組 CGContextSetFillPattern(context, pattern, &alpha); UIRectFill(CGRectMake(0, 0, 320, 568)); // CGColorSpaceRelease(rgbSpace); CGColorSpaceRelease(colorSpace); CGPatternRelease(pattern); } #pragma mark - 無顏色填充模式 //填充瓷磚的回調函數(必須知足CGPatternCallbacks簽名) void drawTile(void *info,CGContextRef context){ CGContextFillRect(context, CGRectMake(0, 0, TILE_SIZE, TILE_SIZE)); CGContextFillRect(context, CGRectMake(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE)); } -(void)drawBackgroundWithPattern:(CGContextRef)context{ //設備無關的顏色空間 CGColorSpaceRef rgbSpace= CGColorSpaceCreateDeviceRGB(); //模式填充顏色空間 CGColorSpaceRef colorSpace=CGColorSpaceCreatePattern(rgbSpace); //將填充色顏色空間設置爲模式填充的顏色空間 CGContextSetFillColorSpace(context, colorSpace); //填充模式回調函數結構體 CGPatternCallbacks callback={0,&drawTile,NULL}; /*填充模式 info://傳遞給callback的參數 bounds:瓷磚大小 matrix:形變 xStep:瓷磚橫向間距 yStep:瓷磚縱向間距 tiling:貼磚的方法(瓷磚擺放的方式) isClored:繪製的瓷磚是否已經指定了顏色(對於無顏色瓷磚此處指定位false) callbacks:回調函數 */ CGPatternRef pattern=CGPatternCreate(NULL, CGRectMake(0, 0, 2*TILE_SIZE, 2*TILE_SIZE), CGAffineTransformIdentity,2*TILE_SIZE+ 5,2*TILE_SIZE+ 5, kCGPatternTilingNoDistortion, false, &callback); CGFloat components[]={254.0/255.0,52.0/255.0,90.0/255.0,1.0}; //注意最後一個參數對於無顏色填充模式指定爲當前顏色空間顏色數據 CGContextSetFillPattern(context, pattern, components); // CGContextSetStrokePattern(context, pattern, components); UIRectFill(CGRectMake(0, 0, 320, 568)); CGColorSpaceRelease(rgbSpace); CGColorSpaceRelease(colorSpace); CGPatternRelease(pattern); } @end
運行效果:
這裏強調一點,在drawTile回調方法中不要使用UIKit封裝方法進行圖形繪製(例如UIRectFill等),因爲這個方法由Core Graphics內部調用,而Core Graphics考慮到跨平臺問題,內部是不容許調用UIKit方法的。
咱們知道在UIKit開發中UIView有一個transform屬性用於控件的形變,其實在繪圖中咱們也常常用到圖形形變,這個時候能夠藉助圖形上下文的形變方法來完成。在弄清形變以前咱們要清楚圖形上下文的座標原點,由於不管是位移仍是旋轉都是相對於座標原點進行的。其實Quartz 2D的座標系同UIKit並不同,它的座標原點在屏幕左下方,可是爲了統一編程方式,UIKit對其進行了轉換,座標原點統一在屏幕左上角。注意在設置圖形上下文形變以前必定要注意保存上下文的初始狀態,在使用完以後進行恢復。不然在處理多個圖形形變的時候很容易弄不清楚究竟是基於怎樣的座標系進行繪圖,容易找不到原點(作過html5 canvas繪圖的朋友對這一點應該很熟悉,在html5中繪圖也常常進行狀態保存和恢復)。下面經過一個圖片的變換演示一下圖形上下文的形變(其餘圖形也是同樣的,就再也不演示):
// // KCView2.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCView2.h" @implementation KCView2 -(void)drawRect:(CGRect)rect{ CGContextRef context=UIGraphicsGetCurrentContext(); [self drawImage:context]; } #pragma mark 圖形上下文形變 -(void)drawImage:(CGContextRef)context{ //保存初始狀態 CGContextSaveGState(context); //形變第一步:圖形上下文向右平移40 CGContextTranslateCTM(context, 100, 0); //形變第二步:縮放0.8 CGContextScaleCTM(context, 0.8, 0.8); //形變第三步:旋轉 CGContextRotateCTM(context, M_PI_4/4); UIImage *image=[UIImage imageNamed:@"photo1.jpg"]; [image drawInRect:CGRectMake(0, 50, 240, 300)]; //恢復到初始狀態 CGContextRestoreGState(context); } @end
最終運行效果見第四幅截圖,下圖描繪出了整個程序的運行過程(移動->縮放->旋轉):
在前面基本繪圖部分,繪製圖像時使用了UIKit中封裝的方法進行了圖像繪製,咱們不妨看一下使用Quartz 2D內置方法繪製是什麼效果。
-(void)drawImage2:(CGContextRef)context{ UIImage *image=[UIImage imageNamed:@"image2.jpg"]; //圖像繪製 CGRect rect= CGRectMake(10, 50, 300, 450); CGContextDrawImage(context, rect, image.CGImage); }
運行效果:
看起來整個圖像是倒過來的,緣由正是前面說的:在Core Graphics中座標系的y軸正方向是向上的,座標原點在屏幕左下角,y軸方向恰好和UIKit中y軸方向相反。而使用UIKit進行繪圖之因此沒有問題是由於UIKit中進行了處理,事實上對於其餘圖形即便使用Core Graphics繪製也沒有問題,由於UIKit統一了編程方式。可是使用Core Graphics中內置方法繪製圖像是存在這種問題的,如何解決呢?
其實圖形上下文只要沿着x軸旋轉180度,而後向上平移適當的高度便可(可是注意不要沿着z軸旋轉,這樣得不到想要的結果)。但是經過前面介紹的CGContextRotateCTM方法只能經過沿着z軸旋轉,此時不妨使用另一種方法,那就是在y軸方向縮放-1,一樣能夠達到想要的效果:
-(void)drawImage2:(CGContextRef)context{ UIImage *image=[UIImage imageNamed:@"image2.jpg"]; CGSize size=[UIScreen mainScreen].bounds.size; CGContextSaveGState(context); CGFloat height=450,y=50; //上下文形變 CGContextScaleCTM(context, 1.0, -1.0);//在y軸縮放-1至關於沿着x張旋轉180 CGContextTranslateCTM(context, 0, -(size.height-(size.height-2*y-height)));//向上平移 //圖像繪製 CGRect rect= CGRectMake(10, y, 300, height); CGContextDrawImage(context, rect, image.CGImage); CGContextRestoreGState(context); }
在UIView的drawRect:中繪製的圖形會在控件顯示的時候調用(並且顯示時會重繪全部圖形),有時候咱們但願繪製內容的顯示是實時的,此時咱們就須要調用繪圖方法從新繪製,可是在iOS開發中不容許開發者直接調用drawRect:方法,刷新繪製內容須要調用setNeedsDisplay方法。下面以一個調整字體大小的界面演示一下視圖的刷新。
KCView.h
// // KCView.h // RefreshView // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <UIKit/UIKit.h> @interface KCView : UIView @property (nonatomic,copy) NSString *title; @property (nonatomic,assign) CGFloat fontSize; @end
KCView.m
// // KCView.m // RefreshView // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCView.h" @implementation KCView -(void)drawRect:(CGRect)rect{ NSString *str=_title; UIFont *font=[UIFont fontWithName:@"Marker Felt" size:_fontSize]; UIColor *foreignColor=[UIColor redColor]; [str drawInRect:CGRectMake(100, 120, 300, 200) withAttributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:foreignColor}]; } @end
KCMainViewController.m
// // KCMainViewController.m // RefreshView // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h" #import "KCView.h" @interface KCMainViewController ()<UIPickerViewDataSource,UIPickerViewDelegate>{ KCView *_contentView; NSArray *_fontSize; } @end @implementation KCMainViewController - (void)viewDidLoad { [super viewDidLoad]; [self initLayout]; [self addPickerView]; } -(void)initLayout{ _fontSize=@[@15,@18,@20,@22,@25,@28,@30,@32,@35,@40]; _contentView=[[KCView alloc]initWithFrame:CGRectMake(0, 0, 320, 300)]; _contentView.backgroundColor=[UIColor whiteColor]; _contentView.title=@"Hello world!"; _contentView.fontSize=[_fontSize[0] intValue]; [self.view addSubview:_contentView]; } -(void)addPickerView{ UIPickerView *picker=[[UIPickerView alloc]initWithFrame:CGRectMake(0, 300, 320, 268)]; picker.dataSource=self; picker.delegate=self; [self.view addSubview:picker]; } -(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{ return 1; } -(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{ return _fontSize.count; } -(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{ return [NSString stringWithFormat:@"%@號字體",_fontSize[row] ]; } -(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{ _contentView.fontSize=[[_fontSize objectAtIndex:row] intValue]; //刷新視圖 [_contentView setNeedsDisplay]; } @end
運行效果:
前面咱們也說過,Quartz 2D的圖形上下方除了能夠繪製到層上還能夠繪製到位圖、PDF等,這裏咱們就介紹一下如何利用Quartz 2D繪製圖像到位圖及PDF中。
上面的示例中一直都是在drawRect:方法中利用UIGraphicsGetCurrentContext()方法取得上下文,要獲得位圖或者PDF的上下文能夠利用UIGraphicsBeginImageContext(CGSize size)和UIGraphicsBeginPDFPageWithInfo(CGRect bounds, NSDictionary *pageInfo)方法。位圖圖形上下文和PDF圖形上下文UIKit是不會負責建立的,因此須要用戶手動建立,而且在使用完後關閉它。在使用UIKit中系統建立的圖形上下文的時候,咱們只能在drawRect:方法中使用,因爲這兩類圖形上下文是由咱們手動建立的所以能夠放到任何方法中調用。此外,這兩個方法開啓的圖形上下文並無返回值,若是咱們要獲得咱們建立的圖形上下文只要在建立上下文以後、關閉以前調用UIGraphicsGetCurrentContext()方法,此時取得的上下文便是咱們本身建立的圖形上下文。
下面利用位圖圖形上下文給一個圖片添加水印,在下面的程序中咱們首先建立上下文,而後在上下文中繪製圖片、直線和文本,最後從當前位圖上下文中取得最終造成的新圖片顯示到界面。
// // KCMainViewController.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <CoreText/CoreText.h> #import "KCMainViewController.h" #import "KCView.h" #import "KCView2.h" #import "KCView3.h" @interface KCMainViewController () @end @implementation KCMainViewController - (void)viewDidLoad { [super viewDidLoad]; UIImage *image=[self drawImageAtImageContext]; UIImageView *imageView=[[UIImageView alloc]initWithImage:image]; imageView.center=CGPointMake(160, 284); [self.view addSubview:imageView]; } #pragma mark 利用位圖上下文添加水印效果 -(UIImage *)drawImageAtImageContext{ //得到一個位圖圖形上下文 CGSize size=CGSizeMake(300, 188);//畫布大小 UIGraphicsBeginImageContext(size); UIImage *image=[UIImage imageNamed:@"photo2.png"]; [image drawInRect:CGRectMake(0, 0, 300, 188)];//注意繪圖的位置是相對於畫布頂點而言,不是屏幕 //添加水印 CGContextRef context=UIGraphicsGetCurrentContext(); CGContextMoveToPoint(context, 200, 178); CGContextAddLineToPoint(context, 270, 178); [[UIColor redColor]setStroke]; CGContextSetLineWidth(context, 2); CGContextDrawPath(context, kCGPathStroke); NSString *str=@"Kenshin Cui"; [str drawInRect:CGRectMake(200, 158, 100, 30) withAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"Marker Felt" size:15],NSForegroundColorAttributeName:[UIColor redColor]}]; //返回繪製的新圖形 UIImage *newImage=UIGraphicsGetImageFromCurrentImageContext(); //最後必定不要忘記關閉對應的上下文 UIGraphicsEndImageContext(); //保存圖片 // NSData *data= UIImagePNGRepresentation(newImage); // [data writeToFile:@"/Users/kenshincui/Desktop/myPic.png" atomically:YES]; return newImage; } @end
運行效果:
注意:上面這種方式繪製的圖像除了能夠顯示在界面上還能夠調用對應方法進行保存(代碼註釋中已經包含保存方法);除此以外這種方法相比在drawRect:方法中繪製圖形效率更高,它不用每次展現時都調用全部圖形繪製方法。
繪製到PDF則要啓用pdf圖形上下文,PDF圖形上下文的建立使用方式跟位圖圖形上下文是相似的,須要注意的一點就是繪製內容到PDF時須要建立分頁,每頁內容的開始都要調用一次IGraphicsBeginPDFPage();方法。下面的示例演示了文本繪製和圖片繪製(其餘圖形繪製也是相似的):
// // KCMainViewController.m // Quartz2D // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <CoreText/CoreText.h> #import "KCMainViewController.h" #import "KCView.h" #import "KCView2.h" @interface KCMainViewController () @end @implementation KCMainViewController - (void)viewDidLoad { [super viewDidLoad]; [self drawContentToPdfContext]; } #pragma mark 利用pdf圖形上下文繪製內容到pdf文檔 -(void)drawContentToPdfContext{ //沙盒路徑(也就是咱們應用程序文件運行的路徑) NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *path=[[paths firstObject] stringByAppendingPathComponent:@"myPDF.pdf"]; NSLog(@"%@",path); //啓用pdf圖形上下文 /** path:保存路徑 bounds:pdf文檔大小,若是設置爲CGRectZero則使用默認值:612*792 pageInfo:頁面設置,爲nil則不設置任何信息 */ UIGraphicsBeginPDFContextToFile(path,CGRectZero,[NSDictionary dictionaryWithObjectsAndKeys:@"Kenshin Cui",kCGPDFContextAuthor, nil]); //因爲pdf文檔是分頁的,因此首先要建立一頁畫布供咱們繪製 UIGraphicsBeginPDFPage(); NSString *title=@"Welcome to Apple Support"; NSMutableParagraphStyle *style=[[NSMutableParagraphStyle alloc]init]; NSTextAlignment align=NSTextAlignmentCenter; style.alignment=align; [title drawInRect:CGRectMake(26, 20, 300, 50) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18],NSParagraphStyleAttributeName:style}]; NSString *content=@"Learn about Apple products, view online manuals, get the latest downloads, and more. Connect with other Apple users, or get service, support, and professional advice from Apple."; NSMutableParagraphStyle *style2=[[NSMutableParagraphStyle alloc]init]; style2.alignment=NSTextAlignmentLeft; // style2.firstLineHeadIndent=20; [content drawInRect:CGRectMake(26, 56, 300, 255) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15],NSForegroundColorAttributeName:[UIColor grayColor],NSParagraphStyleAttributeName:style2}]; UIImage *image=[UIImage imageNamed:@"applecare_folks_tall.png"]; [image drawInRect:CGRectMake(316, 20, 290, 305)]; UIImage *image2=[UIImage imageNamed:@"applecare_page1.png"]; [image2 drawInRect:CGRectMake(6, 320, 600, 281)]; //建立新的一頁繼續繪製其餘內容 UIGraphicsBeginPDFPage(); UIImage *image3=[UIImage imageNamed:@"applecare_page2.png"]; [image3 drawInRect:CGRectMake(6, 20, 600, 629)]; //結束pdf上下文 UIGraphicsEndPDFContext(); } @end
生成的pdf文檔:
知識補充
1.Core Graphics是基於C語言的一套框架,開發時沒法像使用Obj-C同樣調用;
2.在Quartz 2D中凡是使用帶有「Create」或者「Copy」關鍵字方法建立的對象,在使用後必定要使用對應的方法釋放(因爲這個框架基於C語言編寫沒法自動釋放內存);
3.Quartz 2D是跨平臺的,所以其中的方法中不能使用UIKit中的對象(UIKit只有iOS可用),例如用到的顏色只能用CGColorRef而不能用UIColor,可是UIKit中提供了對應的轉換方法;
4.在C語言中枚舉通常以「k」開頭,因爲Quartz 2D基於C語言開發,因此它也不例外(參數中不少枚舉都是k開頭的);
5.因爲Quartz 2D是Core Graphics的一部分,因此API多數以CG開頭;
6.在使用Quartz 2D繪圖API中全部以「Ref」結尾對象,在聲明時都沒必要聲明爲指針類型;
7.在使用Quartz 2D繪圖API時,凡是「UI」開頭的相關繪圖函數,都是UIKit對Core Graphics的封裝(主要爲了簡化繪圖操做);
利用Quartz 2D咱們能夠繪製各種圖形、圖像,功能確實強大。用過photoshop的朋友都知道,使用photoshop能夠製做各類濾鏡特效,那麼在iOS中可否實現濾鏡呢?在iOS5.0以前這些算法基本所有要靠程序員編程實現,實現過程至關複雜。從iOS5.0開始蘋果官方已經提供了Core Image框架來幫助開發者進行特效製做。
先來看一下濾鏡使用過程當中經常使用的基類對象:
CIContext:圖像上下文,用於管理整個圖片處理過程,不一樣的圖形上下文將利用不一樣的圖像處理硬件進行圖像處理(在iOS中能夠經過不一樣的方式建立圖像上下文,例如能夠建立基於CPU的圖像上下方、建立基於GPU的圖像上下方以及建立OpenGL優化過的圖像上下文)。
CIFilter:圖像處理濾鏡,每種濾鏡有不一樣的參數設置。
CIImage:Core Image框架中的圖像類型,主要用於輸入和輸出圖像。
在使用濾鏡以前咱們先要弄清平臺主要支持哪些濾鏡,以及這些濾鏡的方法和參數如何設置,此時不妨使用下面的方法進行打印查看:
#pragma mark 查看全部內置濾鏡 -(void)showAllFilters{ NSArray *filterNames=[CIFilter filterNamesInCategory:kCICategoryBuiltIn]; for (NSString *filterName in filterNames) { CIFilter *filter=[CIFilter filterWithName:filterName]; NSLog(@"\rfilter:%@\rattributes:%@",filterName,[filter attributes]); } }
在iOS7中打印會發現有127中濾鏡,若是咱們把每種濾鏡都介紹一遍恐怕用幾章內容也很難介紹詳細,事實上也沒有這個必要。這些濾鏡使用方法是相似的,只是參數設置有所區別。在iOS文檔中能夠搜索「core image filter reference」一節的內容,裏面有每種濾鏡的詳細介紹和圖片使用效果。
使用Core Image框架建立濾鏡效果通常分爲如下幾步:
1.建立圖像上下文CIContext
2.建立濾鏡CIFilter
3.建立過濾原圖片CIImage
4.調用CIFilter的setValue: forKey:方法爲濾鏡指定源圖片
5.設置濾鏡參數【可選】
6.取得輸出圖片顯示或保存
你們都知道在美圖秀秀中有一個「加強」功能,利用它能夠調整照片的飽和度、亮度、對比度,其實在Core Image中也有這樣一款濾鏡,下面就以顏色濾鏡來演示一下Core Image中濾鏡的使用。
// // KCMainViewController.m // CoreImage // // Created by Kenshin Cui on 14-3-17. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h" #define CONSTROLPANEL_FONTSIZE 12 @interface KCMainViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate>{ UIImagePickerController *_imagePickerController;//系統照片選擇控制器 UIImageView *_imageView;//圖片顯示控件 CIContext *_context;//Core Image上下文 CIImage *_image;//咱們要編輯的圖像 CIImage *_outputImage;//處理後的圖像 CIFilter *_colorControlsFilter;//色彩濾鏡 } @end @implementation KCMainViewController - (void)viewDidLoad { [super viewDidLoad]; [self initLayout]; } #pragma mark 初始化佈局 -(void)initLayout{ //初始化圖片選擇器 _imagePickerController=[[UIImagePickerController alloc]init]; _imagePickerController.delegate =self; //建立圖片顯示控件 _imageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 64, 320, 502)]; _imageView.contentMode=UIViewContentModeScaleAspectFit; [self.view addSubview:_imageView]; //上方導航按鈕 self.navigationItem.title=@"Enhance"; self.navigationItem.leftBarButtonItem=[[UIBarButtonItem alloc]initWithTitle:@"Open" style:UIBarButtonItemStyleDone target:self action:@selector(openPhoto:)]; self.navigationItem.rightBarButtonItem=[[UIBarButtonItem alloc]initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(savePhoto:)]; //下方控制面板 UIView *controlView=[[UIView alloc]initWithFrame:CGRectMake(0, 450, 320, 118)]; // controlView.alpha=0.2; // controlView.backgroundColor=[UIColor colorWithRed:46.0/255.0 green:178.0/255.0 blue:235.0/255.0 alpha:1]; [self.view addSubview:controlView]; //飽和度(默認爲1,大於飽和度增長小於1則下降) UILabel *lbSaturation=[[UILabel alloc]initWithFrame:CGRectMake(10, 10, 60, 25)]; lbSaturation.text=@"Saturation"; lbSaturation.font=[UIFont systemFontOfSize:CONSTROLPANEL_FONTSIZE]; [controlView addSubview:lbSaturation]; UISlider *sldStaturation=[[UISlider alloc]initWithFrame:CGRectMake(80, 10, 230, 30)];//注意UISlider高度雖然沒法調整,不少朋友會說高度設置位0便可,事實上在iOS7中設置爲0後是沒法拖動的 [controlView addSubview:sldStaturation]; sldStaturation.minimumValue=0; sldStaturation.maximumValue=2; sldStaturation.value=1; [sldStaturation addTarget:self action:@selector(changeStaturation:) forControlEvents:UIControlEventValueChanged]; //亮度(默認爲0) UILabel *lbBrightness=[[UILabel alloc]initWithFrame:CGRectMake(10, 40, 60, 25)]; lbBrightness.text=@"Brightness"; lbBrightness.font=[UIFont systemFontOfSize:CONSTROLPANEL_FONTSIZE]; [controlView addSubview:lbBrightness]; UISlider *sldBrightness=[[UISlider alloc]initWithFrame:CGRectMake(80, 40, 230, 30)]; [controlView addSubview:sldBrightness]; sldBrightness.minimumValue=-1; sldBrightness.maximumValue=1; sldBrightness.value=0; [sldBrightness addTarget:self action:@selector(changeBrightness:) forControlEvents:UIControlEventValueChanged]; //對比度(默認爲1) UILabel *lbContrast=[[UILabel alloc]initWithFrame:CGRectMake(10, 70, 60, 25)]; lbContrast.text=@"Contrast"; lbContrast.font=[UIFont systemFontOfSize:CONSTROLPANEL_FONTSIZE]; [controlView addSubview:lbContrast]; UISlider *sldContrast=[[UISlider alloc]initWithFrame:CGRectMake(80, 70, 230, 30)]; [controlView addSubview:sldContrast]; sldContrast.minimumValue=0; sldContrast.maximumValue=2; sldContrast.value=1; [sldContrast addTarget:self action:@selector(changeContrast:) forControlEvents:UIControlEventValueChanged]; //初始化CIContext //建立基於CPU的圖像上下文 // NSNumber *number=[NSNumber numberWithBool:YES]; // NSDictionary *option=[NSDictionary dictionaryWithObject:number forKey:kCIContextUseSoftwareRenderer]; // _context=[CIContext contextWithOptions:option]; _context=[CIContext contextWithOptions:nil];//使用GPU渲染,推薦,但注意GPU的CIContext沒法跨應用訪問,例如直接在UIImagePickerController的完成方法中調用上下文處理就會自動降級爲CPU渲染,因此推薦如今完成方法中保存圖像,而後在主程序中調用 // EAGLContext *eaglContext=[[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES1]; // _context=[CIContext contextWithEAGLContext:eaglContext];//OpenGL優化過的圖像上下文 //取得濾鏡 _colorControlsFilter=[CIFilter filterWithName:@"CIColorControls"]; } #pragma mark 打開圖片選擇器 -(void)openPhoto:(UIBarButtonItem *)btn{ //打開圖片選擇器 [self presentViewController:_imagePickerController animated:YES completion:nil]; } #pragma mark 保存圖片 -(void)savePhoto:(UIBarButtonItem *)btn{ //保存照片到相冊 UIImageWriteToSavedPhotosAlbum(_imageView.image, nil, nil, nil); UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Sytem Info" message:@"Save Success!" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; [alert show]; } #pragma mark 圖片選擇器選擇圖片代理方法 -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ //關閉圖片選擇器 [self dismissViewControllerAnimated:YES completion:nil]; //取得選擇圖片 UIImage *selectedImage=[info objectForKey:UIImagePickerControllerOriginalImage]; _imageView.image=selectedImage; //初始化CIImage源圖像 _image=[CIImage imageWithCGImage:selectedImage.CGImage]; [_colorControlsFilter setValue:_image forKey:@"inputImage"];//設置濾鏡的輸入圖片 } #pragma mark 將輸出圖片設置到UIImageView -(void)setImage{ CIImage *outputImage= [_colorControlsFilter outputImage];//取得輸出圖像 CGImageRef temp=[_context createCGImage:outputImage fromRect:[outputImage extent]]; _imageView.image=[UIImage imageWithCGImage:temp];//轉化爲CGImage顯示在界面中 CGImageRelease(temp);//釋放CGImage對象 } #pragma mark 調整飽和度 -(void)changeStaturation:(UISlider *)slider{ [_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputSaturation"];//設置濾鏡參數 [self setImage]; } #pragma mark 調整亮度 -(void)changeBrightness:(UISlider *)slider{ [_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputBrightness"]; [self setImage]; } #pragma mark 調整對比度 -(void)changeContrast:(UISlider *)slider{ [_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputContrast"]; [self setImage]; } @end
運行效果:
再次給你們強調一下: