Explore gamma correction related properties on iOS
// test in playground targeting iOS | |
import UIKit | |
import QuartzCore | |
let sRGBSpace = CGColorSpace(name: CGColorSpace.sRGB)! | |
let linearSpace = CGColorSpace(name: CGColorSpace.linearSRGB)! | |
func dumpImageFirstPixel(_ image: UIImage) { | |
guard let cgImage = image.cgImage, | |
let dataBuffer = cgImage.dataProvider?.data as Data?, | |
let colorspaceName = cgImage.colorSpace?.name else { | |
return | |
} | |
print("\(Array(dataBuffer.prefix(through: 3))) in colorspace \(colorspaceName)") | |
} | |
// test 1 | |
// blend red and green by default | |
let imageSize = CGSize(width: 20, height: 20) | |
let format = UIGraphicsImageRendererFormat() | |
format.scale = 1 | |
let renderer = UIGraphicsImageRenderer(size: imageSize, format: format) | |
let image1 = renderer.image { context in | |
let bounds = renderer.format.bounds | |
UIColor.red.setFill() | |
context.fill(bounds) | |
UIColor.green.withAlphaComponent(0.5).setFill() | |
context.fill(bounds, blendMode: .normal) // by default .copy may be used | |
} | |
dumpImageFirstPixel(image1) | |
// outputs [127, 128, 0, 255], but it's relatively dark | |
// test 2 | |
// force input a non-linear color, the system will perform transformation for us | |
var buffer: [CGFloat] = [0.5, 0.5, 0, 1] | |
let linearBlendedColor = CGColor(colorSpace: linearSpace, components: &buffer)! | |
let linearUIColor = UIColor(cgColor: linearBlendedColor) | |
let image2 = renderer.image { context in | |
let bounds = renderer.format.bounds | |
linearUIColor.setFill() | |
context.fill(bounds) | |
} | |
dumpImageFirstPixel(image2) | |
// outputs [188, 188, 0, 255] | |
// test 3 | |
// use a linear color space directly | |
let alphaInfo: CGImageAlphaInfo = .premultipliedLast | |
let linearContext = CGContext(data: nil, | |
width: Int(imageSize.width), | |
height: Int(imageSize.height), | |
bitsPerComponent: 8, | |
bytesPerRow: 0, | |
space: linearSpace, | |
bitmapInfo: alphaInfo.rawValue)! | |
// we can use UIColor.red/green because we know the value 255 will not be transformed, | |
// other colors should be created using CGColor | |
linearContext.setFillColor(UIColor.red.cgColor) | |
linearContext.fill(CGRect(origin: .zero, size: imageSize)) | |
linearContext.setFillColor(UIColor.green.withAlphaComponent(0.5).cgColor) | |
linearContext.setBlendMode(.normal) | |
linearContext.fill(CGRect(origin: .zero, size: imageSize)) | |
let linearSpaceImage = linearContext.makeImage()! | |
let image3 = UIImage(cgImage: linearSpaceImage) | |
dumpImageFirstPixel(image3) | |
// outputs [127, 128, 0, 255] | |
// in 3D rendering, if we wan't to achieve accurate physically based effects, we should display colors in image2 & 3 on monitor, not image1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment