在UIImageView上添加一个圆形蒙版层

时间:2021-11-10 23:50:54

I'm building a Photo filter app (like Instagram, Camera+ and many more..), may main screen is a UIImageView that presenting the image to the user, and a bottom bar with some filters and other options.
One of the option is blur, where the user can use his fingers to pinch or move a circle that represent the non-blur part (radius and position) - all the pixels outside of this circle will be blurred.

我正在开发一个照片过滤器应用程序(比如Instagram、Camera+等),may main screen是一个UIImageView,可以将图片呈现给用户,它的底部有一些过滤器和其他选项。其中一个选项是blur,用户可以用手指捏或移动代表非模糊部分(半径和位置)的圆圈——圆圈外的所有像素都将被模糊。

When the user touch the screen I want to add a semi transparent layer above my image that represent the blurred part, with a fully transparent circle that represent the non-blur part.

当用户触摸屏幕时,我想在我的图像上方添加一个半透明的图层,它代表模糊的部分,用一个完全透明的圆圈表示非模糊部分。

So my question is, how do I add this layer? I suppose I need to use some view above my image view, and to use some mask to get my circle shape? I would really appreciate a good tip here.

我的问题是,如何添加这一层呢?我想我需要在我的图像视图上面使用一些视图,并使用一些蒙版来获得我的圆形形状?如果能给我个好建议,我将不胜感激。

One More Thing
I need the circle will not be cut straight, but have a kind of gradient fade. something like Instagram:
在UIImageView上添加一个圆形蒙版层

还有一点我需要的是圆不会被剪直,而是有一种渐变。像Instagram:

And what's very important is to get this effect with good performance, I'd succeed getting this effect with drawRect: but the performance was very bad on old devices (iphone 4, iPod)

最重要的是要有好的表现才能达到这个效果,我想用drawRect来达到这个效果:但是在旧设备上(iphone 4, iPod)的表现非常糟糕

3 个解决方案

#1


94  

Sharp Mask

Whenever you want to draw a path that consists of a shape (or series of shapes) as a hole in another shape, the key is almost always using an 'even odd winding rule'.

无论何时你想画出一个由形状(或一系列形状)组成的洞,它的形状就像一个洞,它几乎总是用一个“偶数条弯曲规则”。

From the Winding Rules section of the Cocoa Drawing Guide:

可可制图指南的卷绕规则部分:

A winding rule is simply an algorithm that tracks information about each contiguous region that makes up the path's overall fill area. A ray is drawn from a point inside a given region to any point outside the path bounds. The total number of crossed path lines (including implicit lines) and the direction of each path line are then interpreted using rules which determine if the region should be filled.

绕线规则只是一种算法,它跟踪构成路径整体填充区域的每个相邻区域的信息。射线是从给定区域内的点绘制到路径边界之外的任何点。然后使用规则解释交叉路径线的总数(包括隐式线)和每个路径线的方向,这些规则确定是否应该填充该区域。

I appreciate that description isn't really helpful without the rules as context and diagrams to make it easier to understand so I urge you to read the links I've provided above. For the sake of creating our circle mask layer the following diagrams depict what an even odd winding rule allows us to accomplish:

我很欣赏,如果没有上下文和图这样的规则,描述并不是真正有用的,所以我建议您阅读我上面提供的链接。为了创建我们的圆掩模层,下面的图描述了一个偶奇缠绕规则允许我们完成的事情:

Non Zero Winding Rule

在UIImageView上添加一个圆形蒙版层

Even Odd Winding Rule

在UIImageView上添加一个圆形蒙版层

Now it's simply a matter of creating the translucent mask using a CAShapeLayer that can be repositioned and expanded and contracted through user iteraction.

现在只需使用CAShapeLayer创建半透明的蒙版,可以通过用户迭代操作重新定位、扩展和收缩。

Code

#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);
}

...

在UIImageView上添加一个圆形蒙版层

(This diagram may help understand the naming conventions in the code)

(此图可能有助于理解代码中的命名约定)


Gradient Mask

The Gradients section of Apple's Quartz 2D Programming Guide details how to draw radial gradients which we can use to create a mask with a feathered edge. This invovlves drawing a CALayers content directly by subclassing it or implementing its drawing delegate. Here we subclass it to encapsulate the data related to it i.e. origin and diameter.

苹果的Quartz 2D编程指南的渐变部分详细介绍了如何绘制径向渐变,我们可以用它来创建一个带羽状边缘的掩模。这个invovlves通过子类化或实现它的绘图委托直接绘制CALayers内容。这里我们对它进行子类化,以封装与它相关的数据,即原点和直径。

Code

BlurFilterMask.h

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

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 (Wherever you are implementing the blur filer masking functionality)

ViewController。m(无论您在哪里实现模糊分析器屏蔽功能)

#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];
}

...

在UIImageView上添加一个圆形蒙版层

(This diagram may help understand the naming conventions in the code)

(此图可能有助于理解代码中的命名约定)


Note

Ensure the multipleTouchEnabled property of the UIImageView hosting your image is set to YES/true:

确保承载您的图像的UIImageView的multipleTouchEnabled属性设置为YES/true:

在UIImageView上添加一个圆形蒙版层


Note

For sake of clarity in answering the OPs question this answer continues to use the naming conventions originally used. This may be slightly misleading to others. 'Mask' is this context does not refer to an image mask but mask in a more general sense. This answer doesn't use any image masking operations.

为了清晰地回答OPs问题,这个答案继续使用最初使用的命名约定。这对其他人来说可能有点误导。“蒙版”指的不是图像蒙版,而是广义的蒙版。这个答案不使用任何图像掩蔽操作。

#2


1  

Sounds like you want to use GPUImageGaussianSelectiveBlurFilter which is contained inside the GPUImage framework. It should be a faster more efficient way to achieve what you want.

听起来您想使用gpuimagegaussianselectiveblur过滤器,它包含在GPUImage框架中。它应该是一种更快更有效的实现你想要的东西的方式。

You can hook up the excludeCircleRadius property to a UIPinchGestureRecognizer in order to allow the user to change the size of the non-blurred circle. Then use the 'excludeCirclePoint' property in conjuction with a UIPanGestureRecognizer to allow the user to move the center of the non-blurred circle.

您可以将exclusive decircleradius属性连接到uipinchsturerecognizer,以便允许用户更改非模糊圆的大小。然后使用“排除分界点”属性与UIPanGestureRecognizer结合,让用户移动非模糊圈的中心。

Read more about how to apply the filter here:

阅读更多关于如何应用过滤器的信息:

https://github.com/BradLarson/GPUImage#processing-a-still-image

https://github.com/BradLarson/GPUImage processing-a-still-image

#3


1  

In Swift if anyone needs it (added pan gesture as well):

在Swift中,如果有人需要它(也添加了pan手势):

BlurFilterMask.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

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()
}

#1


94  

Sharp Mask

Whenever you want to draw a path that consists of a shape (or series of shapes) as a hole in another shape, the key is almost always using an 'even odd winding rule'.

无论何时你想画出一个由形状(或一系列形状)组成的洞,它的形状就像一个洞,它几乎总是用一个“偶数条弯曲规则”。

From the Winding Rules section of the Cocoa Drawing Guide:

可可制图指南的卷绕规则部分:

A winding rule is simply an algorithm that tracks information about each contiguous region that makes up the path's overall fill area. A ray is drawn from a point inside a given region to any point outside the path bounds. The total number of crossed path lines (including implicit lines) and the direction of each path line are then interpreted using rules which determine if the region should be filled.

绕线规则只是一种算法,它跟踪构成路径整体填充区域的每个相邻区域的信息。射线是从给定区域内的点绘制到路径边界之外的任何点。然后使用规则解释交叉路径线的总数(包括隐式线)和每个路径线的方向,这些规则确定是否应该填充该区域。

I appreciate that description isn't really helpful without the rules as context and diagrams to make it easier to understand so I urge you to read the links I've provided above. For the sake of creating our circle mask layer the following diagrams depict what an even odd winding rule allows us to accomplish:

我很欣赏,如果没有上下文和图这样的规则,描述并不是真正有用的,所以我建议您阅读我上面提供的链接。为了创建我们的圆掩模层,下面的图描述了一个偶奇缠绕规则允许我们完成的事情:

Non Zero Winding Rule

在UIImageView上添加一个圆形蒙版层

Even Odd Winding Rule

在UIImageView上添加一个圆形蒙版层

Now it's simply a matter of creating the translucent mask using a CAShapeLayer that can be repositioned and expanded and contracted through user iteraction.

现在只需使用CAShapeLayer创建半透明的蒙版,可以通过用户迭代操作重新定位、扩展和收缩。

Code

#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);
}

...

在UIImageView上添加一个圆形蒙版层

(This diagram may help understand the naming conventions in the code)

(此图可能有助于理解代码中的命名约定)


Gradient Mask

The Gradients section of Apple's Quartz 2D Programming Guide details how to draw radial gradients which we can use to create a mask with a feathered edge. This invovlves drawing a CALayers content directly by subclassing it or implementing its drawing delegate. Here we subclass it to encapsulate the data related to it i.e. origin and diameter.

苹果的Quartz 2D编程指南的渐变部分详细介绍了如何绘制径向渐变,我们可以用它来创建一个带羽状边缘的掩模。这个invovlves通过子类化或实现它的绘图委托直接绘制CALayers内容。这里我们对它进行子类化,以封装与它相关的数据,即原点和直径。

Code

BlurFilterMask.h

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

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 (Wherever you are implementing the blur filer masking functionality)

ViewController。m(无论您在哪里实现模糊分析器屏蔽功能)

#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];
}

...

在UIImageView上添加一个圆形蒙版层

(This diagram may help understand the naming conventions in the code)

(此图可能有助于理解代码中的命名约定)


Note

Ensure the multipleTouchEnabled property of the UIImageView hosting your image is set to YES/true:

确保承载您的图像的UIImageView的multipleTouchEnabled属性设置为YES/true:

在UIImageView上添加一个圆形蒙版层


Note

For sake of clarity in answering the OPs question this answer continues to use the naming conventions originally used. This may be slightly misleading to others. 'Mask' is this context does not refer to an image mask but mask in a more general sense. This answer doesn't use any image masking operations.

为了清晰地回答OPs问题,这个答案继续使用最初使用的命名约定。这对其他人来说可能有点误导。“蒙版”指的不是图像蒙版,而是广义的蒙版。这个答案不使用任何图像掩蔽操作。

#2


1  

Sounds like you want to use GPUImageGaussianSelectiveBlurFilter which is contained inside the GPUImage framework. It should be a faster more efficient way to achieve what you want.

听起来您想使用gpuimagegaussianselectiveblur过滤器,它包含在GPUImage框架中。它应该是一种更快更有效的实现你想要的东西的方式。

You can hook up the excludeCircleRadius property to a UIPinchGestureRecognizer in order to allow the user to change the size of the non-blurred circle. Then use the 'excludeCirclePoint' property in conjuction with a UIPanGestureRecognizer to allow the user to move the center of the non-blurred circle.

您可以将exclusive decircleradius属性连接到uipinchsturerecognizer,以便允许用户更改非模糊圆的大小。然后使用“排除分界点”属性与UIPanGestureRecognizer结合,让用户移动非模糊圈的中心。

Read more about how to apply the filter here:

阅读更多关于如何应用过滤器的信息:

https://github.com/BradLarson/GPUImage#processing-a-still-image

https://github.com/BradLarson/GPUImage processing-a-still-image

#3


1  

In Swift if anyone needs it (added pan gesture as well):

在Swift中,如果有人需要它(也添加了pan手势):

BlurFilterMask.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

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()
}