Swift中的像素数组到UIImage
Posted
技术标签:
【中文标题】Swift中的像素数组到UIImage【英文标题】:Pixel Array to UIImage in Swift 【发布时间】:2015-06-20 20:13:29 【问题描述】:我一直在试图弄清楚如何在 Swift 中将 rgb 像素数据数组转换为 UIImage。
我将每个像素的 rgb 数据保存在一个简单的结构中:
public struct PixelData
var a: Int
var r: Int
var g: Int
var b: Int
我已使用以下功能,但生成的图像不正确:
func imageFromARGB32Bitmap(pixels:[PixelData], width: Int, height: Int)-> UIImage
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo:CGBitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedFirst.rawValue)
let bitsPerComponent:Int = 8
let bitsPerPixel:Int = 32
assert(pixels.count == Int(width * height))
var data = pixels // Copy to mutable []
let providerRef = CGDataProviderCreateWithCFData(
NSData(bytes: &data, length: data.count * sizeof(PixelData))
)
let cgim = CGImageCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
width * Int(sizeof(PixelData)),
rgbColorSpace,
bitmapInfo,
providerRef,
nil,
true,
kCGRenderingIntentDefault
)
return UIImage(CGImage: cgim)!
关于如何将 rgb 数组正确转换为 UIImage 的任何提示或指针?
【问题讨论】:
【参考方案1】:注意:这是 ios 创建UIImage
的解决方案。有关 macOS 和 NSImage
的解决方案,请参阅 this answer。
您唯一的问题是PixelData
结构中的数据类型必须是UInt8
。我在 Playground 中使用以下内容创建了一个测试图像:
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)
Swift 4 更新:
我更新了 imageFromARGB32Bitmap
以使用 Swift 4。该函数现在返回一个 UIImage?
,如果出现任何问题,guard
用于返回 nil
。
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)
使其成为 UIImage 的便捷初始化器:
这个函数可以很好地作为UIImage
的convenience
初始化器。这是实现:
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)
下面是它的用法示例:
// Generate a 500x500 image of randomly colored pixels
let height = 500
let width = 500
var 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)
【讨论】:
【参考方案2】: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)
【讨论】:
这确实很好,但有点啰嗦。我建议将断言更改为简单的guard
语句,并使用 guard let
而不是 if
ing 来表示 nils。在我的实现中,我还使用了UInt
来防止负宽度和高度。以上是关于Swift中的像素数组到UIImage的主要内容,如果未能解决你的问题,请参考以下文章