美文网首页图像音频
iOS开发--模糊不规则的View

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

作者: Areslee | 来源:发表于2017-03-30 11:46 被阅读0次

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

    相关文章

      网友评论

        本文标题:iOS开发--模糊不规则的View

        本文链接:https://www.haomeiwen.com/subject/aceuottx.html