在UIImageView上添加一个圆形遮罩层
我正在构build一个照片滤镜应用程序(如Instagram,Camera +等等),主屏幕可能是一个UIImageView
,向用户展示图像,底部有一些滤镜和其他选项。
其中一个选项是模糊 ,用户可以用手指捏住或移动一个代表非模糊部分(半径和位置)的圆圈 – 该圆圈外的所有像素都会模糊。
当用户触摸屏幕时,我想在表示模糊部分的图像上方添加一个半透明图层,并使用完全透明的圆圈表示非模糊部分。
所以我的问题是,我如何添加此图层? 我想我需要在我的图像视图上方使用一些视图,并使用一些蒙版来获得我的圆形状? 我真的很感激这里的一个很好的提示。
还有一件事
我需要的圆不会被直截了,而是有一种渐变的淡入淡出。 像Instagram的东西:
最重要的是要获得这个效果,我会用drawRect:
获得这个效果drawRect:
但是旧设备(iphone 4,iPod)的性能非常糟糕,
锋利的面具
无论何时你想绘制一个由一个形状(或一系列形状)组成的path作为另一个形状的孔,这个键几乎总是使用一个“偶数的缠绕规则”。
从cocoa绘图指南的绕组规则部分:
缠绕规则就是跟踪组成path整个填充区域的每个连续区域的信息的简单algorithm。 光线是从给定区域内的一个点绘制到path边界之外的任何点。 然后使用确定区域是否应填充的规则来解释交叉path线(包括隐含线)的总数和每条path线的方向。
我明白,如果没有规则作为上下文和图表来使描述更容易理解,那么描述并不是很有用,所以我build议您阅读我上面提供的链接。 为了创build我们的圆形遮罩层,下面的图表描述了甚至奇怪的缠绕规则可以让我们完成的事情:
非零缠绕规则
甚至奇数缠绕规则
现在只需要使用一个CAShapeLayer来创build半透明遮罩,通过用户迭代可以重新定位和展开和收缩。
码
#import <QuartzCore/QuartzCore.h> @interface ViewController () @property (strong, nonatomic) IBOutlet UIImageView *imageView; @property (strong) CAShapeLayer *blurFilterMask; @property (assign) CGPoint blurFilterOrigin; @property (assign) CGFloat blurFilterDiameter; @end @implementation ViewController // begin the blur masking operation. - (void)beginBlurMasking { self.blurFilterOrigin = self.imageView.center; self.blurFilterDiameter = MIN(CGRectGetWidth(self.imageView.bounds), CGRectGetHeight(self.imageView.bounds)); CAShapeLayer *blurFilterMask = [CAShapeLayer layer]; // Disable implicit animations for the blur filter mask's path property. blurFilterMask.actions = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"path", nil]; blurFilterMask.fillColor = [UIColor blackColor].CGColor; blurFilterMask.fillRule = kCAFillRuleEvenOdd; blurFilterMask.frame = self.imageView.bounds; blurFilterMask.opacity = 0.5f; self.blurFilterMask = blurFilterMask; [self refreshBlurMask]; [self.imageView.layer addSublayer:blurFilterMask]; UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; [self.imageView addGestureRecognizer:tapGesture]; UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)]; [self.imageView addGestureRecognizer:pinchGesture]; } // Move the origin of the blur mask to the location of the tap. - (void)handleTap:(UITapGestureRecognizer *)sender { self.blurFilterOrigin = [sender locationInView:self.imageView]; [self refreshBlurMask]; } // Expand and contract the clear region of the blur mask. - (void)handlePinch:(UIPinchGestureRecognizer *)sender { // Use some combination of sender.scale and sender.velocity to determine the rate at which you want the circle to expand/contract. self.blurFilterDiameter += sender.velocity; [self refreshBlurMask]; } // Update the blur mask within the UI. - (void)refreshBlurMask { CGFloat blurFilterRadius = self.blurFilterDiameter * 0.5f; CGMutablePathRef blurRegionPath = CGPathCreateMutable(); CGPathAddRect(blurRegionPath, NULL, self.imageView.bounds); CGPathAddEllipseInRect(blurRegionPath, NULL, CGRectMake(self.blurFilterOrigin.x - blurFilterRadius, self.blurFilterOrigin.y - blurFilterRadius, self.blurFilterDiameter, self.blurFilterDiameter)); self.blurFilterMask.path = blurRegionPath; CGPathRelease(blurRegionPath); } ...
(该图可能有助于理解代码中的命名约定)
梯度面具
Apple 石英2D编程指南的渐变部分详细介绍了如何绘制径向渐变,我们可以使用这些渐变创build具有羽化边缘的蒙版。 这通过子类化或实现其绘图委托来直接绘制CALayer的内容。 在这里,我们将它分类来封装与它有关的数据,即原点和直径。
码
BlurFilterMask.h
#import <QuartzCore/QuartzCore.h> @interface BlurFilterMask : CALayer @property (assign) CGPoint origin; // The centre of the blur filter mask. @property (assign) CGFloat diameter; // the diameter of the clear region of the blur filter mask. @end
BlurFilterMask.m
#import "BlurFilterMask.h" // The width in points the gradated region of the blur filter mask will span over. CGFloat const GRADIENT_WIDTH = 50.0f; @implementation BlurFilterMask - (void)drawInContext:(CGContextRef)context { CGFloat clearRegionRadius = self.diameter * 0.5f; CGFloat blurRegionRadius = clearRegionRadius + GRADIENT_WIDTH; CGColorSpaceRef baseColorSpace = CGColorSpaceCreateDeviceRGB(); CGFloat colours[8] = { 0.0f, 0.0f, 0.0f, 0.0f, // Clear region colour. 0.0f, 0.0f, 0.0f, 0.5f }; // Blur region colour. CGFloat colourLocations[2] = { 0.0f, 0.4f }; CGGradientRef gradient = CGGradientCreateWithColorComponents (baseColorSpace, colours, colourLocations, 2); CGContextDrawRadialGradient(context, gradient, self.origin, clearRegionRadius, self.origin, blurRegionRadius, kCGGradientDrawsAfterEndLocation); CGColorSpaceRelease(baseColorSpace); CGGradientRelease(gradient); } @end
ViewController.m(无论您正在实现模糊滤镜遮罩function)
#import "ViewController.h" #import "BlurFilterMask.h" #import <QuartzCore/QuartzCore.h> @interface ViewController () @property (strong, nonatomic) IBOutlet UIImageView *imageView; @property (strong) BlurFilterMask *blurFilterMask; @end @implementation ViewController // Begin the blur filter masking operation. - (void)beginBlurMasking { BlurFilterMask *blurFilterMask = [BlurFilterMask layer]; blurFilterMask.diameter = MIN(CGRectGetWidth(self.imageView.bounds), CGRectGetHeight(self.imageView.bounds)); blurFilterMask.frame = self.imageView.bounds; blurFilterMask.origin = self.imageView.center; blurFilterMask.shouldRasterize = YES; [self.imageView.layer addSublayer:blurFilterMask]; [blurFilterMask setNeedsDisplay]; self.blurFilterMask = blurFilterMask; UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; [self.imageView addGestureRecognizer:tapGesture]; UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)]; [self.imageView addGestureRecognizer:pinchGesture]; } // Move the origin of the blur mask to the location of the tap. - (void)handleTap:(UITapGestureRecognizer *)sender { self.blurFilterMask.origin = [sender locationInView:self.imageView]; [self.blurFilterMask setNeedsDisplay]; } // Expand and contract the clear region of the blur mask. - (void)handlePinch:(UIPinchGestureRecognizer *)sender { // Use some combination of sender.scale and sender.velocity to determine the rate at which you want the mask to expand/contract. self.blurFilterMask.diameter += sender.velocity; [self.blurFilterMask setNeedsDisplay]; } ...
(该图可能有助于理解代码中的命名约定)
注意
确保托pipe图像的UIImageView
的multipleTouchEnabled
属性设置为YES
/ true
:
注意
为了清楚地回答OP的问题,这个答案继续使用原来使用的命名约定。 这可能会误导别人。 “面具”这个上下文不是指图像蒙版,而是更一般的意义上的蒙版。 这个答案不使用任何图像掩盖操作。
听起来就像你想要使用GPUImage框架中包含的GPUImageGaussianSelectiveBlurFilter 。 它应该是一个更快,更高效的方式来实现你想要的。
您可以将excludeCircleRadius
属性连接到UIPinchGestureRecognizer以允许用户更改非模糊圆的大小。 然后使用“excludeCirclePoint”属性和UIPanGestureRecognizer结合使用户移动非模糊圆的中心。
阅读更多关于如何在这里应用filter:
https://github.com/BradLarson/GPUImage#processing-a-still-image
在Swift中如果有人需要它(增加平移手势):
BlurFilterMask.swift
import Foundation import QuartzCore class BlurFilterMask : CALayer { private let GRADIENT_WIDTH : CGFloat = 50.0 var origin : CGPoint? var diameter : CGFloat? override init() { super.init() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func drawInContext(ctx: CGContext) { let clearRegionRadius : CGFloat = self.diameter! * 0.5 let blurRegionRadius : CGFloat = clearRegionRadius + GRADIENT_WIDTH let baseColorSpace = CGColorSpaceCreateDeviceRGB(); let colours : [CGFloat] = [0.0, 0.0, 0.0, 0.0, // Clear region 0.0, 0.0, 0.0, 0.5] // blur region color let colourLocations : [CGFloat] = [0.0, 0.4] let gradient = CGGradientCreateWithColorComponents (baseColorSpace, colours, colourLocations, 2) CGContextDrawRadialGradient(ctx, gradient, self.origin!, clearRegionRadius, self.origin!, blurRegionRadius, .DrawsAfterEndLocation); } }
ViewController.swift
func addMaskOverlay(){ imageView!.userInteractionEnabled = true imageView!.multipleTouchEnabled = true let blurFilterMask = BlurFilterMask() blurFilterMask.diameter = min(CGRectGetWidth(self.imageView!.bounds), CGRectGetHeight(self.imageView!.bounds)) blurFilterMask.frame = self.imageView!.bounds blurFilterMask.origin = self.imageView!.center blurFilterMask.shouldRasterize = true self.imageView!.layer.addSublayer(blurFilterMask) self.blurFilterMask = blurFilterMask self.blurFilterMask!.setNeedsDisplay() self.imageView!.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: "handlePinch:")) self.imageView!.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleTap:")) self.imageView!.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan:")) } func donePressed(){ //save photo and add to textview let parent : LoggedInContainerViewController? = self.parentViewController as? LoggedInContainerViewController let vc : OrderFlowCareInstructionsTextViewController = parent?.viewControllers[(parent?.viewControllers.count)!-2] as! OrderFlowCareInstructionsTextViewController vc.addImageToTextView(imageView?.image) parent?.popViewController() } //MARK: Mask Overlay func handleTap(sender : UITapGestureRecognizer){ self.blurFilterMask!.origin = sender.locationInView(self.imageView!) self.blurFilterMask!.setNeedsDisplay() } func handlePinch(sender : UIPinchGestureRecognizer){ self.blurFilterMask!.diameter = self.blurFilterMask!.diameter! + sender.velocity*3 self.blurFilterMask!.setNeedsDisplay() } func handlePan(sender : UIPanGestureRecognizer){ let translation = sender.translationInView(self.imageView!) let center = CGPoint(x:self.imageView!.center.x + translation.x, y:self.imageView!.center.y + translation.y) self.blurFilterMask!.origin = center self.blurFilterMask!.setNeedsDisplay() }