Pixel Array to UIImage in Swift
Note: This is a solution for iOS creating a UIImage
. For a solution for macOS and NSImage
, see this answer.
Your only problem is that the data types in your PixelData
structure need to be UInt8
. I created a test image in a Playground with the following:
public struct PixelData { var a: UInt8 var r: UInt8 var g: UInt8 var b: UInt8}var pixels = [PixelData]()let red = PixelData(a: 255, r: 255, g: 0, b: 0)let green = PixelData(a: 255, r: 0, g: 255, b: 0)let blue = PixelData(a: 255, r: 0, g: 0, b: 255)for _ in 1...300 { pixels.append(red)}for _ in 1...300 { pixels.append(green)}for _ in 1...300 { pixels.append(blue)}let image = imageFromARGB32Bitmap(pixels: pixels, width: 30, height: 30)
Update for Swift 4:
I updated imageFromARGB32Bitmap
to work with Swift 4. The function now returns a UIImage?
and guard
is used to return nil
if anything goes wrong.
func imageFromARGB32Bitmap(pixels: [PixelData], width: Int, height: Int) -> UIImage? { guard width > 0 && height > 0 else { return nil } guard pixels.count == width * height else { return nil } let rgbColorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue) let bitsPerComponent = 8 let bitsPerPixel = 32 var data = pixels // Copy to mutable [] guard let providerRef = CGDataProvider(data: NSData(bytes: &data, length: data.count * MemoryLayout<PixelData>.size) ) else { return nil } guard let cgim = CGImage( width: width, height: height, bitsPerComponent: bitsPerComponent, bitsPerPixel: bitsPerPixel, bytesPerRow: width * MemoryLayout<PixelData>.size, space: rgbColorSpace, bitmapInfo: bitmapInfo, provider: providerRef, decode: nil, shouldInterpolate: true, intent: .defaultIntent ) else { return nil } return UIImage(cgImage: cgim)}
Making it a convenience initializer for UIImage:
This function works well as a convenience
initializer for UIImage
. Here is the implementation:
extension UIImage { convenience init?(pixels: [PixelData], width: Int, height: Int) { guard width > 0 && height > 0, pixels.count == width * height else { return nil } var data = pixels guard let providerRef = CGDataProvider(data: Data(bytes: &data, count: data.count * MemoryLayout<PixelData>.size) as CFData) else { return nil } guard let cgim = CGImage( width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: width * MemoryLayout<PixelData>.size, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue), provider: providerRef, decode: nil, shouldInterpolate: true, intent: .defaultIntent) else { return nil } self.init(cgImage: cgim) }}
Here is an example of its usage:
// Generate a 500x500 image of randomly colored pixelslet height = 500let width = 500var pixels: [PixelData] = .init(repeating: .init(a: 0, r: 0, g: 0, b: 0), count: width * height)for index in pixels.indices { pixels[index].a = 255 pixels[index].r = .random(in: 0...255) pixels[index].g = .random(in: 0...255) pixels[index].b = .random(in: 0...255)}let image = UIImage(pixels: pixels, width: width, height: height)
Update for Swift 3
struct PixelData { var a: UInt8 = 0 var r: UInt8 = 0 var g: UInt8 = 0 var b: UInt8 = 0}func imageFromBitmap(pixels: [PixelData], width: Int, height: Int) -> UIImage? { assert(width > 0) assert(height > 0) let pixelDataSize = MemoryLayout<PixelData>.size assert(pixelDataSize == 4) assert(pixels.count == Int(width * height)) let data: Data = pixels.withUnsafeBufferPointer { return Data(buffer: $0) } let cfdata = NSData(data: data) as CFData let provider: CGDataProvider! = CGDataProvider(data: cfdata) if provider == nil { print("CGDataProvider is not supposed to be nil") return nil } let cgimage: CGImage! = CGImage( width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: width * pixelDataSize, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue), provider: provider, decode: nil, shouldInterpolate: true, intent: .defaultIntent ) if cgimage == nil { print("CGImage is not supposed to be nil") return nil } return UIImage(cgImage: cgimage)}