Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clip UIImage to UIBezierPath (not masking)

I'm trying to clip a UIImage based on a given UIBezierPath, but the resulting image retains the original shape and size. I want the shape and size to resemble the path, i.e. the visible content of the new image.

Take a look at this following example:

Input image, input path, actual result, and desired result

The following is the code I am using to mask an image given a path:

func imageByApplyingClippingBezierPath(_ path: UIBezierPath) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(CGSize(
        width: size.width,
        height: size.height
    ), false, 0.0)
    let context = UIGraphicsGetCurrentContext()!
    context.saveGState()

    path.addClip()
    draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

    let newImage = UIGraphicsGetImageFromCurrentImageContext()!

    context.restoreGState()
    UIGraphicsEndImageContext()

    return newImage
}

One solution would be to crop the image after masking it, but ideally I want this to be done as simple and efficiently as possible in one go.

Any solution/ tips would be appreciated.

like image 766
Aleksander Avatar asked Nov 26 '16 05:11

Aleksander


1 Answers

Thanks to Rob I was able to implement a solution using the cropping(to:) method of CGImage.

This is a two step process where first I mask the image using the given path, and then crop the result using the bounds of the path.

The following is my final working source code for implementing a clipping UIBezierPath UIImage extension:

extension UIImage {

    func imageByApplyingClippingBezierPath(_ path: UIBezierPath) -> UIImage {
        // Mask image using path
        let maskedImage = imageByApplyingMaskingBezierPath(path)

        // Crop image to frame of path
        let croppedImage = UIImage(cgImage: maskedImage.cgImage!.cropping(to: path.bounds)!)
        return croppedImage
    }

    func imageByApplyingMaskingBezierPath(_ path: UIBezierPath) -> UIImage {
        // Define graphic context (canvas) to paint on
        UIGraphicsBeginImageContext(size)
        let context = UIGraphicsGetCurrentContext()!
        context.saveGState()

        // Set the clipping mask
        path.addClip()
        draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

        let maskedImage = UIGraphicsGetImageFromCurrentImageContext()!

        // Restore previous drawing context
        context.restoreGState()
        UIGraphicsEndImageContext()

        return maskedImage
    }

}
like image 132
Aleksander Avatar answered Oct 01 '22 19:10

Aleksander