Search code examples
iosswiftcore-graphicscore-animation

How to get the intersection of two CGPath?


I am using CAShapeLayer.path and CALayer.mask to set up image mask, i can achieve "difference set" and "union" effect for CGPath by setting

maskLayer.fillRule = kCAFillRuleEvenOdd/kCAFillRuleZero

However, how can i get the intersection of two paths? I cannot achieve it with even-odd rule

Here is an example: enter image description here

let view = UIImageView(frame: CGRectMake(0, 0, 400, 400))
view.image = UIImage(named: "scene.jpg")
let maskLayer = CAShapeLayer()
let maskPath = CGPathCreateMutable()    
CGPathAddEllipseInRect(maskPath, nil, CGRectOffset(CGRectInset(view.bounds, 50, 50), 50, 0))
CGPathAddEllipseInRect(maskPath, nil, CGRectOffset(CGRectInset(view.bounds, 50, 50), -50, 0))
maskLayer.path = maskPath
maskLayer.fillRule = kCAFillRuleEvenOdd
maskLayer.path = maskPath
view.layer.mask = maskLayer

how can i get the middle intersection area to be shown?


Solution

  • extension UIImage {
    
        func doubleCircleMask(diameter: CGFloat)-> UIImage? {
            UIGraphicsBeginImageContextWithOptions(size, false, 0)
            defer { UIGraphicsEndImageContext() }
            let x = size.width/2 - diameter/2
            let y = size.height/2 - diameter/2
            let offset = diameter/4
            let bezierPath: UIBezierPath = .init(ovalIn: .init(origin: .init(x: x-offset, y: y), size: .init(width: diameter, height: diameter)))
            bezierPath.append(.init(ovalIn: .init(origin: .init(x: x+offset, y: y), size: .init(width: diameter, height: diameter))))
            bezierPath.addClip()
            draw(in: .init(origin: .zero, size: size))
            return UIGraphicsGetImageFromCurrentImageContext()
        }
    
        func doubleCircleIntersectionMask(diameter: CGFloat)-> UIImage? {
            UIGraphicsBeginImageContextWithOptions(size, false, 0)
            defer { UIGraphicsEndImageContext() }
            let x = size.width/2 - diameter/2
            let y = size.height/2 - diameter/2
            let offset = diameter/4
            UIBezierPath(ovalIn: .init(origin: .init(x: x-offset, y: y), size: .init(width: diameter, height: diameter))).addClip()
            UIBezierPath(ovalIn: .init(origin: .init(x: x+offset, y: y), size: .init(width: diameter, height: diameter))).addClip()
            draw(in: .init(origin: .zero, size: size))
            return UIGraphicsGetImageFromCurrentImageContext()
        }
    
    }
    

    Testing

    let image = UIImage(data: try! Data(contentsOf: URL(string: "https://i.sstatic.net/Xs4RX.jpg")!))!
    let imageUnion = image.doubleCircleMask(diameter: 300)
    let imageIntersection = image.doubleCircleIntersectionMask(diameter: 300)
    

    enter image description here