skip to Main Content

I have two UIImages – let’s call them base and overhead. Their size is the same.

I need a UIImage that is made like this: when overhead’s pixel is solid or semi-transparent, use clear pixel. When overhead’s pixel is fully transparent, use the base’s pixel.

I do see that there are solutions for the opposite effect – clear when overhead’s pixels are transparent, base otherwise. What is the best way to combine images this way?

The result can be made while base is still generated (with UIGraphicsImageRenderer, using Context operations) – separate base is not needed.

Here’s the example
Base
enter image description here

Overhead (the transparency looks grey here, sorry)
enter image description here

base with overhead drawn on top of it (some parts are transparent, some are not)
enter image description here

Desired result
enter image description here

2

Answers


  1. Chosen as BEST ANSWER

    Found a way to do this using mask invertion from this question UIView invert mask MaskView. After combining this with CIFilter.blendWithAlphaMask(), the desired result was made. But is this the most efficient way?


  2. You can do this with UIGraphicsImageRenderer and make it a bit easier than using CIFilter

    You said your "base" and "overhead" images will be the same size, so I grabbed the images you posted and made the gray circles transparent.

    Base Image (white is opaque white, although it doesn’t really matter):

    enter image description here

    Overhead Image (white is transparent):

    enter image description here

    We can use this extension:

    extension UIImage {
        func invertMasked(with maskImg: UIImage) -> UIImage {
            let format = UIGraphicsImageRendererFormat()
            format.scale = self.scale
            
            let renderer = UIGraphicsImageRenderer(size: self.size, format: format)
            return renderer.image { ctx in
                self.draw(at: .zero)
                maskImg.draw(at: .zero, blendMode: .destinationOut, alpha: 1.0)
            }
        }
    }
    

    and call it like this:

    let maskedImage = baseImage.invertMasked(with: overheadImage)
    

    Here’s a quick example:

    class InvMaskVC: UIViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            guard let baseImage = UIImage(named: "baseImage"),
                  let overheadImage = UIImage(named: "overheadImage")
            else {
                fatalError("Could not load images!")
            }
            
            let stackView = UIStackView()
            stackView.axis = .vertical
            stackView.spacing = 20.0
            
            stackView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(stackView)
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
    
                stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
                stackView.leftAnchor.constraint(equalTo: g.leftAnchor, constant: 40.0),
                stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
            ])
            
            
            let maskedImage = baseImage.invertMasked(with: overheadImage)
                    
            [baseImage, overheadImage, maskedImage].forEach { img in
                let iView = UIImageView(image: img)
                iView.backgroundColor = .red
                iView.heightAnchor.constraint(equalTo: iView.widthAnchor, multiplier: img.size.height / img.size.width).isActive = true
                stackView.addArrangedSubview(iView)
            }
            
        }
        
    }
    

    Gives this output:

    enter image description here

    • Top image view holds baseImage
    • Middle image view holds overheadImage
    • Bottom image view holds maskedImage

    Image views all have a red background, so we can see the frames (and see the transparency).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search