2

I am attempting to output a CIImage to disk, and am looking for the most efficient way possible. I found this question, which suggests using the writeJPEGRepresentation/writePNGRepresentation methods of CIContext. However, whenever I attempt to use those methods, they produce blank images.

In the case of JPEG, it produces a pure black image. PNG produces a pure transparent image. The HEIF method also produced a pure black image. Same result if I use the jpegRepresentation or pngRepresentation methods which generate a Data, instead of writing directly to disk.

If I instead render these objects using createCGImage, and then write out the data from the CGImage, they look fine.

Any ideas why this might be happening?

Here's a simple example that illustrates the problem:

let ciImage = CIImage(contentsOf: photoURL, options: [CIImageOption.applyOrientationProperty: true])!

let context = CIContext()
try! context.writeJPEGRepresentation(of: ciImage, to: FileManager.default.temporaryDirectory.appendingPathComponent("testFromCIImage.jpeg"), colorSpace: CGColorSpace(name: CGColorSpace.sRGB)!, options: [:])

let cgImage: CGImage = context.createCGImage(ciImage, from: ciImage.extent)!
let uiImage = UIImage(cgImage: cgImage)
try! uiImage.jpegData(compressionQuality: 1.0)!.write(to: FileManager.default.temporaryDirectory.appendingPathComponent("testFromUIImage.jpeg"))

testFromCIImage.jpeg appears as a blank image, with the same size as the original input image. testFromUIImage.jpeg appears identical to the input image.

I have created a sample project that illustrates the issue, along with a few sample images.

Update

I have found only one condition where the image outputs correctly - when the source image is a PNG, and the colorSpace parameter of writeJPEGRepresentation matches the color space of the source image. I have not had success with any JPEG, even when the color space matches.

Chris Vasselli
  • 13,064
  • 4
  • 46
  • 49
  • This is very odd indeed. Have you tried retaining the `CIContext` outside of the scope you are calling `writeJPEGRepresentation` in? – Frank Rupprecht May 02 '20 at 13:16
  • @FrankSchlegel Yep, I tried that, but no luck. Good thought! – Chris Vasselli May 02 '20 at 16:04
  • Could you please post a bit more code/context or, ideally, a minimal sample app we could play around with? – Frank Rupprecht May 02 '20 at 16:07
  • @FrankSchlegel just updated the question with a sample project, and a little more information. – Chris Vasselli May 02 '20 at 22:03
  • Hmm, I guess we have a classical "works for me" problem here: On my iPhone 11 Pro Max, iOS 13.41, all images are written and read again correctly... What's your test environment? – Frank Rupprecht May 03 '20 at 07:22
  • Of course, I was making a classic mistake and testing in the simulator and not on a real device. On a real device, it seems to work for me too. So I would guess this is just a bug with the simulator, unless you think there's any reason it would be expected behavior. If you want to write this up as an answer I'll mark it correct! – Chris Vasselli May 03 '20 at 20:35

1 Answers1

2

As you said, this seems to be an issue exclusive to the Simulator. However, I did some more digging and found the following:

When inspecting what Core Image is doing (by setting the CI_PRINT_TREE environment variable) I found that the tree looks normal (though the resulting image is black). However, for the working configurations nothing was printed at all! That seems to me that Core Image is not actually doing any rendering but maybe Image I/O is used directly for writing the JPG.

Which led me to try forcing Core Image to use the software renderer (instead of its default Metal renderer) by passing CIContextOption.useSoftwareRenderer on context creation. However, this is totally ignored. The resulting context is still a Metal context...

So yeah, it seems the only feasible workaround is via CGImage right now. It would be great if you would file a bug with Apple.

Frank Rupprecht
  • 9,191
  • 31
  • 56