iOS開發--模糊不規則的View

附上demo地址:https://github.com/BHAreslee/shapeView

? ? ? ? 最近在開發的一個項目,要求對面部的眉毛位置進行模糊效果處理。大致分為三個步驟:1:獲取眉毛區域的關鍵點。2:通過連接關鍵點獲取一個封閉的區域。3:在這個封閉區域中增加高斯模糊效果。

目標效果:


每一個UIView之所以能夠顯示出來,其實是CALayer在起作用。兩者就相當于是畫布和畫框的關系。UIView只負責在畫布上畫一幅畫,但最終能否完整將一幅畫展示出來,則是取決于CALayer的形狀,也就是CALayer的frame。UIView主要是對顯示內容的管理而 CALayer 主要側重顯示內容的繪制。

? ? ? ? 那如何獲得一個不規則的View呢?或者說如何讓一幅畫只在不規則的區域中顯示呢?UIView只能通過修改frame改變大小和位置,似乎沒有方式去改變形狀。這個時候我們可以調整畫框即可。我們通過查看CALayer的相關屬性,可以得知CALayer有個子類叫CAShapeLayer。這個CAShapeLayer中有一個屬性是CGPathRef類的path。之前用過UIBezierPath,很快就想到這個path應該就是形狀了。我們可以通過UIBezierPath連線得到。OK,思路有了我們來實現一下吧。

實現:

在控制器中首先添加一個屬性@property (nonatomic, strong) UIImageView *imgView;

然后在viewDidLoad方法中,做一下幾部:

1:創建UIBezierPath對象,并繪制左右眉毛的區域。我以簡單的三角形替代眉毛的區域

UIBezierPath *path1 = [UIBezierPath bezierPath];

path1.lineWidth = 1;

path1.lineCapStyle = kCGLineCapRound;

path1.lineJoinStyle = kCGLineJoinRound;

//畫左眉

CGPoint p1 = CGPointMake(100, 100);

CGPoint p2 = CGPointMake(300, 100);

CGPoint p3 = CGPointMake(300, 300);

[path1 moveToPoint:p1];

[path1 addLineToPoint:p2];

[path1 addLineToPoint:p3];

[path1 closePath];

UIBezierPath *path2 = [UIBezierPath bezierPath];

//畫右眉

CGPoint p4 = CGPointMake(0, 100);

CGPoint p5 = CGPointMake(80, 90);

CGPoint p6 = CGPointMake(100, 300);

[path2 moveToPoint:p4];

[path2 addLineToPoint:p5];

[path2 addLineToPoint:p6];

[path2 closePath];

//此時我們得到了兩個path,我們用一個方法把他合為一個path。讓path1包含path2。

[path1 appendPath:path2];

2.創建兩個UIImageView,一個用來展示原圖,一個用來做模糊效果

//self.imgView用來展示原圖

self.imgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"timg.jpeg"]];

[self.view addSubview:self.imgView];

self.imgView.frame = self.view.bounds;

//imgViewBlur用來模糊

UIImageView *imgViewBlur = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"timg.jpeg"]];

imgViewBlur.frame = self.view.bounds;

//模糊圖片操作,方法blurryImage:withBlurLevel:會在后面列出

imgViewBlur.image = [self blurryImage:imgViewBlur.image withBlurLevel:1];

3.自定義一個bhView,繼承自UIView,并添加一個屬性@property (nonatomic, strong) UIImage *img,在控制器創建bhView對象時,傳入img,給bhView中用drawRect方法繪制。

至于為什么要創建bhView,會在后面說明。

在bhView的.m文件中重寫drawRect

- (void)drawRect:(CGRect)rect {

self.backgroundColor = [UIColor clearColor];

[self.img drawInRect:rect];

}

bhView *bh = [[bhView alloc]initWithFrame:self.view.bounds];

bh.backgroundColor = [UIColor clearColor];

bh.img = imgViewBlur.image;

//我們只能通過調用此方法,來觸發drawRect方法。系統不讓直接調用drawRect

[bh setNeedsDisplay];

//創建CAShapeLayer對象maskLayer

CAShapeLayer* maskLayer = [CAShapeLayer layer];

//把準備好的path1賦值給maskLayer.path的path屬性

maskLayer.path = path1.CGPath;

//在將bhView的對象bh的layer設置為maskLayer

bh.layer.mask = maskLayer;

至此,我們已經得到了兩張圖,一張圖是UIImageView展示出來的,一張圖是通過bhView畫出來的,模糊后并且只在特定區域顯示。



接下來就要合并這兩張圖。

4:合并圖片,通過上下文的方式將兩個圖片繪制在一起,生成一張新圖片

CGSize size = CGSizeMake([UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height);

//開啟上下文

UIGraphicsBeginImageContext(size);

//先畫完整的圖片

[self.imgView.image drawInRect:self.imgView.frame];

//再畫模糊的局部圖片。convertViewToImage:方法實現會貼在后面

[[self convertViewToImage:bh] drawInRect:bh.frame];

//拿到生成的ZImage

UIImage *ZImage = UIGraphicsGetImageFromCurrentImageContext();

//關閉圖形上下文

UIGraphicsEndImageContext();

//給展示圖賦新圖片

self.imgView.image = ZImage;


結束。看看最終效果


下面貼兩個方法

//View轉圖片


-(UIImage*)convertViewToImage:(UIView*)v{

CGSize s = v.bounds.size;

// 下面方法,第一個參數表示區域大小。第二個參數表示是否是非透明的。如果需要顯示半透明效果,需要傳NO,否則傳YES。第三個參數就是屏幕密度了

UIGraphicsBeginImageContextWithOptions(s, NO, [UIScreen mainScreen].scale);

[v.layer renderInContext:UIGraphicsGetCurrentContext()];

UIImage*image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return image;

}


//模糊圖片處理方法如下


- (UIImage *)blurryImage:(UIImage *)image withBlurLevel:(CGFloat)blur{? ? if (image==nil)? ? {? ? ? ? NSLog(@"error:為圖片添加模糊效果時,未能獲取原始圖片");? ? ? ? return nil;? ? }? ? //模糊度,? ? if (blur < 0.025f) {? ? ? ? blur = 0.025f;? ? } else if (blur > 1.0f) {? ? ? ? blur = 1.0f;? ? }? ? ? ? //boxSize必須大于0? ? int boxSize = (int)(blur * 100);? ? boxSize -= (boxSize % 2) + 1;? ? NSLog(@"boxSize:%i",boxSize);? ? //圖像處理? ? CGImageRef img = image.CGImage;? ? //需要引入#import//圖像緩存,輸入緩存,輸出緩存

vImage_Buffer inBuffer, outBuffer;

vImage_Error error;

//像素緩存

void *pixelBuffer;

//數據源提供者,Defines an opaque type that supplies Quartz with data.

CGDataProviderRef inProvider = CGImageGetDataProvider(img);

// provider’s data.

CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);

//寬,高,字節/行,data

inBuffer.width = CGImageGetWidth(img);

inBuffer.height = CGImageGetHeight(img);

inBuffer.rowBytes = CGImageGetBytesPerRow(img);

inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);

//像數緩存,字節行*圖片高

pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));

outBuffer.data = pixelBuffer;

outBuffer.width = CGImageGetWidth(img);

outBuffer.height = CGImageGetHeight(img);

outBuffer.rowBytes = CGImageGetBytesPerRow(img);

// 第三個中間的緩存區,抗鋸齒的效果

void *pixelBuffer2 = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));

vImage_Buffer outBuffer2;

outBuffer2.data = pixelBuffer2;

outBuffer2.width = CGImageGetWidth(img);

outBuffer2.height = CGImageGetHeight(img);

outBuffer2.rowBytes = CGImageGetBytesPerRow(img);

//Convolves a region of interest within an ARGB8888 source image by an implicit M x N kernel that has the effect of a box filter.

error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer2, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

error = vImageBoxConvolve_ARGB8888(&outBuffer2, &inBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

if (error) {

NSLog(@"error from convolution %ld", error);

}

//? ? NSLog(@"字節組成部分:%zu",CGImageGetBitsPerComponent(img));

//顏色空間DeviceRGB

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

//用圖片創建上下文,CGImageGetBitsPerComponent(img),7,8

CGContextRef ctx = CGBitmapContextCreate(

outBuffer.data,

outBuffer.width,

outBuffer.height,

8,

outBuffer.rowBytes,

colorSpace,

CGImageGetBitmapInfo(image.CGImage));

//根據上下文,處理過的圖片,重新組件

CGImageRef imageRef = CGBitmapContextCreateImage (ctx);

UIImage *returnImage = [UIImage imageWithCGImage:imageRef];

//clean up

CGContextRelease(ctx);

CGColorSpaceRelease(colorSpace);

free(pixelBuffer);

free(pixelBuffer2);

CFRelease(inBitmapData);

//CGColorSpaceRelease(colorSpace);? //多余的釋放

CGImageRelease(imageRef);

return returnImage;

}



現在說明為什么藥自定義bhView。我剛開始也是直接創建了兩個UIImageView,一個是原圖,一個模糊部分區域的圖。然后在開上下文合并,發現結果得到的是一張全部模糊的圖。

以下直接繪制是不行的:

?UIGraphicsBeginImageContext(self.imgView.bounds.size);

? [self.imgView.image drawInRect:self.view.bounds];

? [imgViewBlur.image drawInRect:self.view.bounds];

? ?UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();

?? UIGraphicsEndImageContext();

因為,在上下文中繪制的是imgViewBlur.imageimage對象,imgViewBlur.imageimage對象是沒有形狀的,雖然你只能看到兩個模糊的三角形區域。但實際上整個imgViewBlur.image都被模糊了,因為layerd的關系,我們才看不到其他區域而已。

后來我想到,通過使用UIView的drawRect方法同樣可以得到一張圖片,再通過設置layer就能夠只展現模糊的三角區域。再將bhView對象與UIImageView對象合并即可。

? 附上demo地址:https://github.com/BHAreslee/shapeView

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容

  • iOS開發中有的時候需要將圖片設置模糊,來實現特定的效果獲取更好的用戶體驗, iOS7之后半透明模糊效果得到大范圍...
    零距離仰望星空閱讀 46,587評論 47 223
  • 1、禁止手機睡眠[UIApplication sharedApplication].idleTimerDisabl...
    DingGa閱讀 1,138評論 1 6
  • 1.NSString過濾特殊字符串定義一個特殊字符的集合NSCharacterSet set = [NSChara...
    奮拓達閱讀 748評論 0 0
  • 說母親,她是一個很欠缺安全感的人。在一個男性為主導的社會,甚至于家庭,她所有的一切都來自于另一個人的給予(...
    danaom閱讀 277評論 0 0
  • 因為孤獨是生命的常態,所以陪伴才顯得長情。生活在燈紅酒綠的城市里,不知道你是否找到了自己的方向?在人潮人海中奔波,...
    柚小兮閱讀 912評論 0 2