I have an image on which I applied blur effect using the following code:
@IBAction func blurSliderSlides(_ sender: UISlider) {
let currentValue = Int(sender.value)
let currentFilter = CIFilter(name: "CIGaussianBlur")
currentFilter!.setValue(CIImage(image: mainImage), forKey: kCIInputImageKey)
currentFilter!.setValue(currentValue, forKey: kCIInputRadiusKey)
let cropFilter = CIFilter(name: "CICrop")
cropFilter!.setValue(currentFilter!.outputImage, forKey: kCIInputImageKey)
cropFilter!.setValue(CIVector(cgRect: (CIImage(image: mainImage)?.extent)!), forKey: "inputRectangle")
let output = cropFilter!.outputImage
let cgimg = context.createCGImage(output!, from: output!.extent)
processedImage = UIImage(cgImage: cgimg!)
backgroundImage.image = processedImage
}
Now I have three buttons square circle and rectangle. when ever use tap any of button that type of view is created on mid of blurred image and user can make that view large or small using slider. but all I want when subview is added of any shape the background image should be unblur from that point where view is created.
subview created on background image:
@IBAction func squareButtonTapped(_ sender: Any) {
squareView.removeFromSuperview()
squareView.frame = CGRect(x: backgroundImage.bounds.midX, y: backgroundImage.bounds.midY, width: CGFloat(backgroundImage.frame.size.height * 20 / 100), height: CGFloat(backgroundImage.frame.size.width * 10 / 100))
backgroundImage.addSubview(squareView)
}
using slider subview can be changed:
@IBAction func blurTypeSliderSlides(_ sender: UISlider) {
squareView.transform = CGAffineTransform(scaleX: CGFloat(sender.value / 10), y: CGFloat(sender.value / 10))
}
how to remove blur effect from background image at point where subview is added. I have searched a lot but nothing find helpful as my requirement. can someone please help. Thanks in advance.
CodePudding user response:
It looks like you have a UIImage
and a UIImageView
declared as a property of your controller... and in viewDidLoad()
you're loading that image, something like:
mainImage = UIImage(named: "background")
I'm guessing that, because in your func blurSliderSlides(_ sender: UISlider)
you have this line:
currentFilter!.setValue(CIImage(image: mainImage), forKey: kCIInputImageKey)
and at the end:
backgroundImage.image = processedImage
So, when you add the "shape" subview, set the .image
to the original:
@IBAction func squareButtonTapped(_ sender: Any) {
squareView.removeFromSuperview()
squareView.frame = CGRect(x: backgroundImage.bounds.midX, y: backgroundImage.bounds.midY, width: CGFloat(backgroundImage.frame.size.height * 20 / 100), height: CGFloat(backgroundImage.frame.size.width * 10 / 100))
backgroundImage.addSubview(squareView)
// replace the "blur" image with the original
backgroundImage.image = mainImage
}
Edit - after clarification in comments...
You don't want to think in terms of "adding subviews."
Instead, use two image views... one containing the original image, and another containing the blurred image, overlaid on top. Then use a layer mask (with a "hole cut") on the blurred image view to let the original "show through."
So,
And it can look like this at run-time:
Here is some example code you can try out. It has one slider which controls the "cut-out oval" as a percentage of the image view width:
class BlurMaskVC: UIViewController {
var mainImage: UIImage!
var originalImageView: UIImageView!
var blurredImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// make sure we can load the image
guard let img = UIImage(named: "bkg640x360") else {
fatalError("Could not load image!!!")
}
mainImage = img
originalImageView = UIImageView()
blurredImageView = UIImageView()
originalImageView.image = mainImage
blurredImageView.image = mainImage
originalImageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(originalImageView)
blurredImageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(blurredImageView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain original image view Top / Leading / Trailing
originalImageView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
originalImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
originalImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
// let's use the image's aspect ratio
originalImageView.heightAnchor.constraint(equalTo: originalImageView.widthAnchor, multiplier: mainImage.size.height / mainImage.size.width),
// constrain blurred image view to match the original image view
// so it's overlaid directly on top
blurredImageView.topAnchor.constraint(equalTo: originalImageView.topAnchor, constant: 0.0),
blurredImageView.leadingAnchor.constraint(equalTo: originalImageView.leadingAnchor, constant: 0.0),
blurredImageView.trailingAnchor.constraint(equalTo: originalImageView.trailingAnchor, constant: 0.0),
blurredImageView.bottomAnchor.constraint(equalTo: originalImageView.bottomAnchor, constant: 0.0),
])
// a slider to set the "percentage" of the image view to "un-blur"
let areaSlider = UISlider()
areaSlider.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(areaSlider)
NSLayoutConstraint.activate([
areaSlider.topAnchor.constraint(equalTo: originalImageView.bottomAnchor, constant: 20.0),
areaSlider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
areaSlider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
])
areaSlider.addTarget(self, action: #selector(updateBlurMask(_:)), for: .valueChanged)
// since this example does not have a "blur" slider,
// let's set the blur to 20
doBlur(20)
}
func doBlur(_ currentValue: Int) {
let context = CIContext(options: nil)
guard let inputImage = CIImage(image: mainImage) else {
fatalError("Could not get CIImage from mainImage!!!")
}
let originalOrientation = mainImage.imageOrientation
let originalScale = mainImage.scale
if let filter = CIFilter(name: "CIGaussianBlur") {
filter.setValue(inputImage, forKey: kCIInputImageKey)
filter.setValue(currentValue, forKey: kCIInputRadiusKey)
guard let outputImage = filter.outputImage,
let cgImage = context.createCGImage(outputImage, from: inputImage.extent)
else {
fatalError("Could not generate Processed Image!!!")
}
let processedImage = UIImage(cgImage: cgImage, scale: originalScale, orientation: originalOrientation)
blurredImageView.image = processedImage
}
}
@objc func updateBlurMask(_ sender: UISlider) {
let b: CGRect = blurredImageView.bounds
// let's make a "square" rect with the max of blurImageView's width, height
let m: CGFloat = max(b.width, b.height)
let maxR: CGRect = CGRect(x: 0.0, y: 0.0, width: m, height: m)
// use the value of the slider - 0.0. to 1.0
// as a percentage of the width
// to scale the max rect
let v: CGFloat = CGFloat(sender.value)
let tr = CGAffineTransform(scaleX: v, y: v)
var r: CGRect = maxR.applying(tr)
// center it
r.origin.x = (b.width - r.width) * 0.5
r.origin.y = (b.height - r.height) * 0.5
// a path for the full image view rect
let fullPath = UIBezierPath(rect: blurredImageView.bounds)
// a path for the oval in a percentage of the full rect
let pth = UIBezierPath(ovalIn: r)
// append the paths
fullPath.append(pth)
// this "cuts a hole" in the path
fullPath.usesEvenOddFillRule = true
// shape layer to use as a mask
let maskLayer = CAShapeLayer()
// again, we're going to "cut a hole" in the path
maskLayer.fillRule = .evenOdd
// set the path
maskLayer.path = fullPath.cgPath
// can be any opaque color
maskLayer.fillColor = UIColor.white.cgColor
// set the layer mask
blurredImageView.layer.mask = maskLayer
}
}
You should have no trouble using the updateBlurMask()
func as a basis for your other shapes.