Pixel Array to UIImage in Swift Pixel Array to UIImage in Swift arrays arrays

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)}