5

I need to save and load metal textures to a file. Example code below is below. I'm noticing the RGB values are changing as it gets saved and reloaded again.

metal texture pixel: RGBA: 42,79,12,95  
after save and reload:     66,88,37,95

That got brighter and grayer. Next save it starts getting darker. I'm on an iPad Pro so wondering if this is a colorspace issue. Any pointers on why this might be happening and how to fix it?

In the line below that is saving the cgImage, I can inspect the raw pixel data and see that the RGBA is 66,88,37.

// saving...
let ciCtx = CIContext()
let ciImage = CIImage(mtlTexture: metalTexture, options: [:])
// [ … transfrom to flip y-coordinate …]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let cgImage = ciCtx.createCGImage(ciImage, from: fullRect, format: kCIFormatRGBA8, colorSpace: colorSpace)!
let imageDest = CGImageDestinationCreateWithData(mData, kUTTypePNG, 1, nil)!
CGImageDestinationAddImage(imageDest, cgImage, nil)
CGImageDestinationFinalize(imageDest)

// loading...
let src = CGImageSourceCreateWithData(imgData, nil)
let img = CGImageSourceCreateImageAtIndex(src, 0, nil)
let loader = MTKTextureLoader(device: self.metalDevice)
let texture = try! loader.newTexture(cgImage: img, options: [:])
Rob N
  • 15,024
  • 17
  • 92
  • 165
  • Generally speaking, PNG data is stored sRGB-encoded, but you're telling Core Image to treat the texture data as if it were in the colorspace of the device, which might be Display P3 (you can determine this by printing `colorspace`). In any event, a device colorspace is rarely what you want. It probably makes sense to provide the sRGB colorspace (`CGColorSpace(name: CGColorSpace.sRGB)`) to CI instead. When the texture loader loads the image, it should infer that the image data is sRGB, and you should see better results. – warrenm Apr 08 '18 at 16:38
  • When I print that `colorSpace` object it gives: ` (kCGColorSpaceDeviceRGB)`, which does not appear to be Display P3. I just tried using both .sRGB and .displayP3 to create that colorspace, and both lead to colors changing as I save and reload. There has to be way to save a pixelmap and get back the same RGB values. – Rob N Apr 08 '18 at 21:16

1 Answers1

13

I came across a very similar issue. I think if you pass some options to CIImage() rather than doing no colorspace management , i.e. "options: [:]" you'll get rid of the color offset issue.

let kciOptions = [kCIImageColorSpace: CGColorSpaceCreateDeviceRGB(),
                      kCIContextOutputPremultiplied: true,
                      kCIContextUseSoftwareRenderer: false] as [String : Any]
let ciImage = CIImage(mtlTexture: metalTexture, options: kciOptions)

The above worked for me when I was having this issue.

Plutovman
  • 677
  • 5
  • 22
  • Thanks @Plutovman! Do you happen to know the equivalent in Objective C code? The NSDictionary object will not accept CGColorSpaceRef as a value. – Jonathan Zrake Jan 07 '19 at 18:13
  • I unfortunately do not, as my Objective-C knowledge is rather minimal. I'm sure you'll find your way in no time. Best. – Plutovman Jan 08 '19 at 00:12
  • 2
    for objective c use: @{kCIImageColorSpace:(__bridge_transfer id)CGColorSpaceCreateDeviceRGB()} – karel Apr 23 '20 at 11:00