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阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容

  • iOS开发中有的时候需要将图片设置模糊,来实现特定的效果获取更好的用户体验, iOS7之后半透明模糊效果得到大范围...
    零距离仰望星空阅读 46,482评论 47 223
  • 1、禁止手机睡眠[UIApplication sharedApplication].idleTimerDisabl...
    DingGa阅读 1,110评论 1 6
  • 1.NSString过滤特殊字符串定义一个特殊字符的集合NSCharacterSet set = [NSChara...
    奋拓达阅读 709评论 0 0
  • 说母亲,她是一个很欠缺安全感的人。在一个男性为主导的社会,甚至于家庭,她所有的一切都来自于另一个人的给予(...
    danaom阅读 270评论 0 0
  • 因为孤独是生命的常态,所以陪伴才显得长情。生活在灯红酒绿的城市里,不知道你是否找到了自己的方向?在人潮人海中奔波,...
    柚小兮阅读 873评论 0 2