Как создать изображение пиксель за пикселем
Я хочу создать UIImage пиксель за пикселем в Swift 3
Я искал, но не смог найти код, который на самом деле работает
Итак, позвольте мне объяснить, у меня есть массив с символами
var array = ["w", "x", "y", "x", "y", "y", "y", "x", "x", "x", "w", "x", "y", "w", "y"] //there will be like 26 millions of those
если это w, цвет пикселя будет синим
если это х, цвет пикселя будет красным
если это у, цвет пикселя будет зеленым
если это v, цвет пикселя будет черным
Я хочу создать изображение из этих персонажей и сохранить его на фотографиях
Какие-нибудь мысли??
Спасибо за ваши ответы
1 ответ
Вы можете создать CGContext
а затем получить data
буфер для этого изображения, а затем заполните этот буфер значениями, соответствующими строковым значениям:
func createImage(width: Int, height: Int, from array: [String], completionHandler: @escaping (UIImage?, String?) -> Void) {
DispatchQueue.global(qos: .utility).async {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bytesPerPixel = 4
let bitsPerComponent = 8
let bytesPerRow = bytesPerPixel * width
let bitmapInfo = RGBA32.bitmapInfo
guard array.count == width * height else {
completionHandler(nil, "Array size \(array.count) is incorrect given dimensions \(width) x \(height)")
return
}
guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
completionHandler(nil, "unable to create context")
return
}
guard let buffer = context.data else {
completionHandler(nil, "unable to get context data")
return
}
let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height)
for (index, string) in array.enumerated() {
switch string {
case "w": pixelBuffer[index] = .blue
case "x": pixelBuffer[index] = .red
case "y": pixelBuffer[index] = .green
case "v": pixelBuffer[index] = .black
default: completionHandler(nil, "Unexpected value: \(string)"); return
}
}
let cgImage = context.makeImage()!
let image = UIImage(cgImage: cgImage)
// or
//
// let image = UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: .up)
completionHandler(image, nil)
}
}
Если имеется 26 миллионов пикселей, вы, вероятно, захотите сделать это асинхронным, чтобы избежать блокировки основной очереди.
Кстати, вышеупомянутое использует это struct
:
struct RGBA32: Equatable {
private var color: UInt32
var redComponent: UInt8 {
return UInt8((color >> 24) & 255)
}
var greenComponent: UInt8 {
return UInt8((color >> 16) & 255)
}
var blueComponent: UInt8 {
return UInt8((color >> 8) & 255)
}
var alphaComponent: UInt8 {
return UInt8((color >> 0) & 255)
}
init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
color = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0)
}
static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {
return lhs.color == rhs.color
}
static let black = RGBA32(red: 0, green: 0, blue: 0, alpha: 255)
static let red = RGBA32(red: 255, green: 0, blue: 0, alpha: 255)
static let green = RGBA32(red: 0, green: 255, blue: 0, alpha: 255)
static let blue = RGBA32(red: 0, green: 0, blue: 255, alpha: 255)
}
Чтобы сохранить изображение, вы можете сделать:
createImage(width: width, height: height, from: array) { image, errorMessage in
guard let image = image, errorMessage == nil else {
print(errorMessage!)
return
}
DispatchQueue.main.async {
self.imageView.image = image
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
}
}
куда
func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: Any?) {
guard error == nil else {
print(error!.localizedDescription)
return
}
print("image saved")
}