图像音频

iOS开发--模糊不规则的View

2017-03-30  本文已影响0人  Areslee

附上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

上一篇下一篇

猜你喜欢

热点阅读