Skip to content

Instantly share code, notes, and snippets.

@CrystDragon
Last active October 24, 2022 15:03
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CrystDragon/54b51397dd5de7c7378337f3103d5f27 to your computer and use it in GitHub Desktop.
Save CrystDragon/54b51397dd5de7c7378337f3103d5f27 to your computer and use it in GitHub Desktop.
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