I am extracting a JPEG2000 (JP2) facial image from an NFC passport chip (ISO/IEC 19794-5) and attempting to create a UIImage from it.
On iOS 16, the following code works fine:
import ImageIO
import UIKit
func getUIImage(from imageData: [UInt8]) -> UIImage? {
let data = Data(imageData)
guard let imageSource = CGImageSourceCreateWithData(data as CFData, nil),
let cgImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) else {
print("Failed to decode JP2 image!")
return nil
}
return UIImage(cgImage: cgImage)
}
However, on iOS 18, this fails with errors like:
initialize:1415: *** invalid JPEG2000 file ***
makeImagePlus:3752: *** ERROR: 'JP2 ' - failed to create image [-50]
CGImageSourceCreateImageAtIndex: *** ERROR: failed to create image [-59]
Questions:
- Did Apple remove or modify JPEG2000 support in iOS 18?
- Is there an official workaround for decoding JPEG2000 on iOS 18?
- Should I use Vision/Metal/Core Image instead?
- Is there a recommended way to convert JPEG2000 to JPEG/PNG before creating a UIImage?
- Are there any Apple-provided APIs that maintain backward compatibility for JPEG2000 decoding?
Additional Info:
The UInt8 array has a valid JPEG2000 header (0x00 0x00 0x00 0x0C 6A 50 ...). The image works on iOS 16 but fails on iOS 18. Tested on iPhone running iOS 18.0 beta.
Any insights on how to handle JPEG2000 decoding in iOS 18 would be greatly appreciated! 🚀
Running the following code on iOS 18.3.1 on an iPhone 16 I see jpeg-2000 is listed among both the source and destination types supported by Image I/O:
struct ContentView: View {
var destination_types: [String] = (CGImageDestinationCopyTypeIdentifiers() as! [String]).sorted()
var source_types: [String] = ((CGImageSourceCopyTypeIdentifiers() as! [String]).filter { !$0.lowercased().contains("raw") }).sorted()
var source_raw_types: [String] = ((CGImageSourceCopyTypeIdentifiers() as! [String]).filter { $0.lowercased().contains("raw") }).sorted()
var body: some View {
VStack {
Text("Image I/O Source Types").font(.title3)
let _ = print("Image I/O Source Types\n\(source_types)")
List {
ForEach(source_types, id: \.self) { the_type in
Text(the_type)
}
}
Divider()
Text("Image I/O Source RAW Types").font(.title3)
let _ = print("Image I/O Source RAW Types\n\(source_raw_types)")
List {
ForEach(source_raw_types, id: \.self) { the_type in
Text(the_type)
}
}
Divider()
Text("Image I/O Destination Types").font(.title3)
let _ = print("Image I/O Destination Types\n\(destination_types)")
List {
ForEach(destination_types, id: \.self) { the_type in
Text(the_type)
}
}
}
.padding()
}
}
Maybe there is a problem with the image file you are trying to read (or a bug in the Image I/O preventing it from being read). If you are able to read this image in other software (like, say, GIMP or PhotoShop), then I suggest filing a bug report that includes a copy of the image file so we can take a look.
If you file a bug report, please include a small Xcode project that includes a copy of your image with some directions that can be used to reproduce the problem and post the FB number here. If you post the bug number here I'll check the status next time I do a sweep of forums posts where I've suggested bug reports.
Bug Reporting: How and Why? has tips on creating your bug report.