Core Graphics實現(xiàn)各種藝術(shù)字效果

1.初覽

在正式開始前先簡單介紹下CoreGraphics,我會把CoreGraphics分解成概念和套路兩個部分,具體框架設(shè)計思路和API用法不會涉及,可移步參考Quartz 2D Programming Guide

  • 概念
    概念好比武學(xué)中的內(nèi)功心法,理解了這些,才能發(fā)揮出招式真正的威力,CoreGraphics中比較核心的概念是Graphics Contexts,這將是本文引入的唯一的一個概念。
  • Graphics Contexts(圖形上下文)
    Context是個比較抽象的東西,它不僅僅是一個可以繪制的圖層,還包含為當(dāng)前圖層設(shè)置的參數(shù),如陰影,線條粗細(xì),繪制模式等??梢灶惐瘸梢粋€新建的Photoshop圖層以及當(dāng)前筆觸,顏色等配置。對于移動平臺,有三種常見的Context
    1.View Graphics Context: 由UIView自動創(chuàng)建,你重寫UIView drawRect方法時,你的內(nèi)容會畫在這個上下文上。
    2.Bitmap Graphics Context: 繪制在該上下文的內(nèi)容會以點(diǎn)陣形式存儲在一塊內(nèi)存中。簡單說,就是為圖片開辟一塊內(nèi)存,然后在里面畫東西,上下文幫你把圖片內(nèi)存抽象成一個Context(圖層)了。
    3.PDF Graphics Context:顧名思義,跟PDF文件相關(guān),本文不會涉及。
  • 套路
    就是慣用套路,這相當(dāng)于武學(xué)中的招式。我一直有個疑問,如果一門武學(xué)沒有招式,怎么判斷這是哪個門子的武學(xué)? CoreGraphics里面就有相關(guān)的招式,這里將帶入幾個具備代表性的招式,以后看到別人用CoreGraphics寫的看似無招的代碼,其實仔細(xì)品讀后會發(fā)現(xiàn)所謂的無招只是沒有固定套路,招式還是在的。

第一招:拿取當(dāng)前Graphics Context。

CGContextRef context = UIGraphicsGetCurrentContext();

通常是起始招式,接下來一般會用來為上下文設(shè)置參數(shù)比如說設(shè)置畫線時的寬度CGContextSetLineWidth(context, 1),把上下文內(nèi)容截取成一個位圖CGBitmapContextCreateImage(context)等等。

第二招:開辟Bitmap Graphics Context

UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0);
draw something ...
UIGraphicsEndImageContext();

注意上下構(gòu)成一套組合,是開啟然后關(guān)閉一個Bitmap Graphics Context,在這區(qū)域內(nèi)的所有繪制操作都是對于該Bitmap Context的,在代碼塊里面使用第一招UIGraphicsGetCurrentContext()拿到的就是這個Bitmap Graphics Context。

第三招: 保存和恢復(fù)當(dāng)前Context狀態(tài)

Set line width 5, black hair color ...
Draw hair...
CGContextSaveGState(context);//save line width 5, black color
Set line width 8, red color...
Draw hair ornaments...
CGContextRestoreGState(context);//restore line width 5, black color
Continue to draw hair...

這招又是一個組合塊,會產(chǎn)生什么效果呢?舉個栗子,你正在描繪一個人物的頭部畫像,畫頭發(fā)=>畫裝飾物=>修飾頭發(fā)=>修飾裝飾物...這樣需要來回切換著畫筆狀態(tài),實際過程中會有很多參數(shù)需要配置,這個招式讓我們能保存恢復(fù)某些狀態(tài)。當(dāng)你使用CGContextSaveGState后接下來你更改畫筆狀態(tài),畫完后再使用CGContextRestoreGState可以將狀態(tài)恢復(fù)到使用Save方法之前。關(guān)于哪些狀態(tài)可以保存,請參考CGContextSaveGState Discussion部分

最后一招:扭轉(zhuǎn)乾坤???

CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(rect)));

這是你熟悉又陌生的線性變換操作,因為Core Graphics(原點(diǎn)左下角,y軸向上為正)使用的坐標(biāo)系和UIKit(原點(diǎn)左上角,y軸向下為正)的坐標(biāo)系是不一樣的,在重寫UIView drawRect的時候直接畫上去的內(nèi)容是一個相對x軸的鏡像。因此需要做一次線性變換來得到正確的方位,該操作是將y變成-1*y 然后沿y軸平移,平移距離為CGRectGetHeight(rect)。

好了招式都介紹完了。

2.繪制

接下來要把學(xué)到的內(nèi)容用于實戰(zhàn)了,希望通過實戰(zhàn)演練,大家能加深理解,逐漸達(dá)到無招的狀態(tài)。代碼長度不一,只列出關(guān)鍵部分,文底有全套實現(xiàn)地址。

  • 發(fā)光

效果圖:

glow.gif

實現(xiàn):

//為文字設(shè)置陰影
CGContextSetShadowWithColor(context, CGSizeZero, self.glowSize, self.glowColor.CGColor);

第一招拿到context后,這個效果的核心代碼就只有1句了,我都不好意思做解釋。你甚至可以直接修改UILabel自帶的陰影屬性來達(dá)到這個效果。

  • 描邊

效果圖:

stroke.gif

實現(xiàn):

//設(shè)置邊線寬度
CGContextSetLineWidth(context, self.outlineWidth);
//設(shè)置線條轉(zhuǎn)角樣式
CGContextSetLineJoin(context, kCGLineJoinRound);
//設(shè)置繪圖模式為描線
CGContextSetTextDrawingMode(context, kCGTextStroke);

這個效果的核心只有3行,通過第一招拿到context,然后配置context。接下來調(diào)用super的draw方法,這時候就畫了字的描邊。如果要如圖一樣的黑色填充,把DrawingMode改成Fill的模式,再調(diào)用一遍super的draw方法即可。

  • 漸變

效果圖:

gradient.gif

實現(xiàn):

//>>>第一部分
//第二招開始
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0);
//把當(dāng)前內(nèi)容繪制在Bitmap Context上
[super drawTextInRect:rect];
//第一招
CGContextRef context = UIGraphicsGetCurrentContext();
//以當(dāng)前context內(nèi)容生成一張圖片
CGImageRef mask = CGBitmapContextCreateImage(context);
//第二招結(jié)束
UIGraphicsEndImageContext();

//>>>第二部分
//第一招,此時是View Graphics context了
context = UIGraphicsGetCurrentContext();
//最后一招,扭轉(zhuǎn)乾坤
CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(rect)));
//注意是ClipTo,因此只有mask部分能被繪制上內(nèi)容
CGContextClipToMask(context, rect, mask);

//>>>第三部分
//創(chuàng)建漸變
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)color, NULL);
//繪制漸變,漸變只顯示mask部分
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation|kCGGradientDrawsAfterEndLocation);

這個效果大體分為3個部分,第一部分把原來的字形畫在一張圖片里用作mask。第二部分使用mask裁剪當(dāng)前view的context。第三部分在當(dāng)前view的context上繪制一個線性漸變。除線性漸變外,還有徑向漸變,具體可以參考結(jié)尾的Git代碼。

  • 鏤空

效果圖:

hollow.gif

實現(xiàn):

//第一部分
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0);
[super drawTextInRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGImageRef image = CGBitmapContextCreateImage(context);
UIGraphicsEndImageContext();

//第二部分
context = UIGraphicsGetCurrentContext();
CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(rect)));
//使用第一部分得到的image創(chuàng)建一個mask,這里得到的是一個反向的遮罩
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerComponent(image), CGImageGetBitsPerPixel(image), CGImageGetBytesPerRow(image), CGImageGetDataProvider(image), CGImageGetDecode(image), CGImageGetShouldInterpolate(image));
CGContextClipToMask(context, rect, mask);

//第三部分
//設(shè)置為填充色
[self.maskColor set];
//使用顏色填充區(qū)域
CGContextFillRect(context, rect);

該實現(xiàn)跟漸變大體相似,不過這里創(chuàng)建了一個反向的mask,把字體區(qū)域給鏤空了。然后其他部分填充為一個純色,當(dāng)然也可以用圖片填充這個區(qū)域。還有一種鏤空的實現(xiàn)是通過修改BlendMode剔除像素,這里不贅述了。

  • 3D

效果圖:

3d.gif

實現(xiàn):

//循環(huán)繪制text到context,每次偏移幾個像素
[self.text drawInRect:CGRectMake(rect.origin.x + i, rect.origin.y + i, rect.size.width, rect.size.height) withAttributes:attrs];

從動畫中應(yīng)該也可以看出端倪,所謂的3D效果其實是很多圖層疊加起來的,因此真正的核心代碼只有這一句。但相應(yīng)的為了實現(xiàn)這種透視感,每一層的顏色和陰影都有些許變化。

  • 涂層

效果圖:

marked.gif

實現(xiàn):

//循環(huán)繪制圖片,圖片偏移i = i + randomValue
[self.strokeTexture drawInRect:CGRectMake(i, midY - self.strokeWidth/2.f, self.strokeWidth, self.strokeWidth) blendMode:kCGBlendModeNormal alpha:self.maskAlpha];

這個效果的核心代碼也只有這一句,基本思想是用一張筆刷灰度圖,修改該灰度圖的TintColor,然后繪制在context上,通過隨機(jī)調(diào)整間隔,就達(dá)到了深淺相間的效果??刂莆淖趾蛨D片的繪制順序,就形成了上下效果。

  • 故障

效果圖:

glitch.gif

實現(xiàn):
這個效果的實現(xiàn)沒有引入什么新的操作,是一些基本操作的組合。
1.用3中顏色先把文字畫3遍

CGRect bottomRect = CGRectMake(rect.origin.x + self.bottomOffset.x, rect.origin.y + self.bottomOffset.y, rect.size.width, rect.size.height);
CGRect middleRect = CGRectMake(rect.origin.x + self.middleOffset.x, rect.origin.y + self.middleOffset.y, rect.size.width, rect.size.height);
self.textColor = self.bottomColor;
[super drawTextInRect:bottomRect];
self.textColor = self.middleColor;
[super drawTextInRect:middleRect];
self.textColor = self.topColor;
[super drawTextInRect:rect];

得到下面的效果

step1.gif

2.為圖片添加切片

//得到切片圖片
CGImageRef sliceRef = CGImageCreateWithImageInRect(contentImage, imageSlice);
//把原上下文切片部分內(nèi)容剔除
CGContextClearRect(context, contentSlice);
//把切片畫到原上下文被剔除部分,左右隨機(jī)平移一定距離
CGContextDrawImage(context, translatedRect, rotateRef);
step2.gif
step2_2.gif

3.添加隨機(jī)的線段
代碼還有很大優(yōu)化空間,不列舉了,說下基本實現(xiàn)思路吧

  • 構(gòu)建一個循環(huán)體
  • 循環(huán)體內(nèi)隨機(jī)生成CGRect
  • 過濾掉重疊的Rect
step3.gif
  • 材質(zhì)

效果圖:

materia.png

實現(xiàn):
這不是蒙圖實現(xiàn)!這不是蒙圖實現(xiàn)!這不是蒙圖實現(xiàn)!蒙圖很難實現(xiàn)這種有高低落差的光影效果。

//使用CIHeightFieldFromMask生成高低落差圖
CIImage *inputImage = [CIImage imageWithCGImage:imageRef];
CIFilter *filter = [CIFilter filterWithName:@"CIHeightFieldFromMask"];
[filter setValue:inputImage forKey:kCIInputImageKey];
CIImage *outputImage = filter.outputImage;
CGImageRelease(imageRef); imageRef = NULL;

//使用CIShadedMaterial拼接材質(zhì)
CIImage *materia = [CIImage imageWithCGImage:self.materiaImage.CGImage];
CIFilter *filterMaterial = [CIFilter filterWithName:@"CIShadedMaterial"];
[filterMaterial setValue:outputImage forKey:kCIInputImageKey];
[filterMaterial setValue:materia forKey:kCIInputShadingImageKey];
CIImage *finalEffect = filterMaterial.outputImage;
UIImage *finalImage = [UIImage imageWithCIImage:finalEffect];
[finalImage drawInRect:rect];

通過使用不同的材質(zhì)球,來顯示不同的材質(zhì)效果,上圖使用的材質(zhì)圖

Golden.png

這個效果主要用到的是Core Image里面的filter,目的是引入實現(xiàn)特殊字體的另一個思路,通過第二招將文字變成圖片,然后就可以組合使用Core Image Filter來實現(xiàn)更加復(fù)雜的效果。CoreImageFilterReference

附上代碼地址ArtFontDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載請帶上出處, 謝謝. 一個 Graphics Context 代表一個繪制目標(biāo), 它包含繪制系統(tǒng)用于完成繪制指...
    Falme丶閱讀 1,876評論 0 2
  • 復(fù)雜的組織都是專門化的 Catharine R. Stimpson 到目前為止,我們已經(jīng)探討過 CALayer 類...
    小東門兒閱讀 673評論 0 7
  • 在新媒體和自媒體時刻變革的時代里,個人和企業(yè)到底如何玩轉(zhuǎn)自媒體營銷呢? 1、 什么是新媒體呢? 以數(shù)字技術(shù)為基礎(chǔ),...
    華一說閱讀 2,348評論 27 29
  • 大學(xué)的期末,往往在考完的那一瞬就會有奧特曼打完怪獸,天亮了的感覺。高興,興奮,瘋癲......聚餐,電影,旅行.....
    山茱萸閱讀 291評論 0 2
  • 誰也沒有告訴 要去哪里 他走到車站
    你說那是什么閱讀 263評論 0 0

友情鏈接更多精彩內(nèi)容