iosswiftimagecgimageciimage

Creating a new image using UIGraphicsBeginImageContext distorts the image color


I'm trying to draw 2 images into the same canvas and for some images I see a slight color distortion in the resulting UIImage, the image "brightens up" a little bit and I can't figure-out the reason.

Here's a simplified code I use which produces color distortion even if drawing a single image:

// originalImageURI is a local path
guard let originalImage = normalizedImageOrientation(image: originalImageURI) else { ... }

UIGraphicsBeginImageContext(originalImage.size)
originalImage.draw(at: .zero)
let combinedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

And in this example, combinedImage has color distortion.

I tried using https://github.com/MetalPetal/MetalPetal but doing the same: import and render the original image produces color distortion as well:

let mtiImage = MTIImage(cgImage: originalImage.cgImage!)

let options = MTIContextOptions()
guard let device = MTLCreateSystemDefaultDevice(), let context = try? MTIContext(device: device, options: options) else { ... }

let combinedImage = try context.makeCGImage(from: mtiImage)
let resultImage = UIImage(cgImage: combinedImage)

And in this example, result image has color distortion.

Example of how color distortion look for the stock simulator image:

color distortion sample

Any advice on how to approach this problem?

Thank you!


Solution

  • Unfortunately, there are several factors that can yield this sort of shift (e.g., color spaces, rendering intents, pixel interpolation if resized, etc.). It’s all very complicated.

    I would recommend retiring the deprecated UIGraphicsBeginImageContext and instead use the contemporary UIGraphicsImageRenderer. And I recommend specifying the format when instantiating it, supplying it the original image’s imageRendererFormat, to minimize these issues. E.g.:

    let rect = CGRect(origin: .zero, size: size)
    let newImage = UIGraphicsImageRenderer(bounds: rect, format: originalImage.imageRendererFormat).image { _ in
        originalImage.draw(in: rect)
    }
    

    In this screen snapshot, the top image is the original, the bottom is the one generated by UIGraphicsImageRenderer with a common format, and I compare the same pixel on the two images and they do not appear to suffer any color shift:

    enter image description here