ioscalayercgimage

mask two UIImages by using cgImage.mask not working, but works for imageview.layer.mask


I have two images, which both have alpha channel. I want to comine them together. It do works for UIImageView, but I want to do it by using cgimage, which without creat a UIImageView. I tried cgimage like this is not working:

    func  combine3(_ bg: UIImage, cover: UIImage) -> UIImage?{
        let size = CGSize(width: self.view.frame.width, height: self.view.frame.height)
        UIGraphicsBeginImageContext(size)
        
        let maskRef = cover.cgImage!
        let mask = CGImage.init(maskWidth: maskRef.width, height: maskRef.height, bitsPerComponent: maskRef.bitsPerComponent, bitsPerPixel: maskRef.bitsPerPixel, bytesPerRow: maskRef.bytesPerRow, provider: maskRef.dataProvider!, decode: nil, shouldInterpolate: false)
        let masked = bg.cgImage?.masking(mask!)
        let outPutImage = UIImage(cgImage: masked!)
        
        UIGraphicsEndImageContext()
        return outPutImage
    }

but for UIImageView, it works pretty good:

        let bg = creatBGImageFinal()!
        bgIV.image = bg

        let cover = createCoverImageFinal(progress: 0)!
        let layer = CALayer()
        layer.contents = cover.cgImage
        layer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        maskIV.layer.mask = layer

bg: enter image description here cover,which is a picture with an white circle in the middle, the others is Transparent: enter image description here result: enter image description here


Solution

  • First, reverse the mask:

    The rule for cgImage masking is Dst = 1 - Src.

    Then draw the masked image onto image context with opacity:

    func combine3(_ bg: UIImage, cover: UIImage, size: CGSize) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, true, 1)
        defer {
            UIGraphicsEndImageContext()
        }
        let context = UIGraphicsGetCurrentContext()!
        let maskRef = cover.cgImage!
        let mask = CGImage.init(maskWidth: maskRef.width, height: maskRef.height, bitsPerComponent: maskRef.bitsPerComponent, bitsPerPixel: maskRef.bitsPerPixel, bytesPerRow: maskRef.bytesPerRow, provider: maskRef.dataProvider!, decode: nil, shouldInterpolate: false)!
        let masked = bg.cgImage!.masking(mask)!
        // adjust for lower-left-origin CG coordinates
        context.translateBy(x: 0, y: size.height)
        context.scaleBy(x: 1, y: -1)
        context.draw(masked, in: CGRect(origin: .zero, size: size))
        return UIGraphicsGetImageFromCurrentImageContext()
    }
    

    Result: