Если это занимает 5 минут, я полагаю, что вы должны вызывать эту процедуру несколько раз, каждый раз выполняя это sourceView
. Вы должны визуализировать весь вид в полный пиксельный буфер один раз и только один раз, и тогда вы сможете эффективно извлечь цвет нескольких пикселей из этого буфера.
Например:
var data: Data?
var width: Int!
var height: Int!
func pixelColor(at point: CGPoint, for sourceView: UIView) -> UIColor? {
if data == nil { fillBuffer(for: sourceView) }
return data?.withUnsafeBytes { rawBufferPointer in
let pixels = rawBufferPointer.bindMemory(to: Pixel.self)
let pixel = pixels[Int(point.y) * width + Int(point.x)]
return pixel.color
}
}
func fillBuffer(for sourceView: UIView) {
// get image snapshot
let bounds = sourceView.bounds
let format = UIGraphicsImageRendererFormat()
format.scale = 1
let image = UIGraphicsImageRenderer(bounds: bounds, format: format).image { _ in
sourceView.drawHierarchy(in: bounds, afterScreenUpdates: false)
}
guard let cgImage = image.cgImage else { return }
// prepare to get pixel buffer
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
width = Int(bounds.width)
height = Int(bounds.height)
// populate and save pixel buffer
if let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4 * width, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) {
context.draw(cgImage, in: bounds)
data = context.data.flatMap { Data(bytes: $0, count: 4 * width * height) }
}
}
Очевидно, визуализируйте это в буфер так, как вы хотите, но основная идея заключается в том, чтобы сохранить пиксельный буфер до некоторого Data
, из которого вы можете быстро получить значения цвета.
Кстати, вместо того, чтобы перемещаться по байтам RGBA по отдельности, я думаю, что полезно использовать struct
, как показано ниже, для визуализации немного более интуитивного кода:
struct Pixel {
let red: UInt8
let green: UInt8
let blue: UInt8
let alpha: UInt8
}
extension Pixel {
var color: UIColor {
return UIColor(red: CGFloat(red) / 255,
green: CGFloat(green) / 255,
blue: CGFloat(blue) / 255,
alpha: CGFloat(alpha) / 255)
}
}
Более интересный вопрос, ИМХО, почему вы восстанавливаете цвет нескольких пикселей. Вы действительно нуждаетесь в цвете для отдельных пикселей, или вы делаете какие-то расчеты на основе результатов (например, гистограммы, средний цвет и т. Д.)? То есть если вам действительно нужны цвета только попиксельно, это нормально, но если есть более широкая проблема, которую вы пытаетесь решить, мы могли бы показать вам более эффективные подходы, чем обработка попиксельно. Они варьируются от распараллеленных подпрограмм, высокопроизводительной обработки изображений vImage и т. Д. Но мы не можем сказать больше, не зная, что вы делаете с этими повторными вызовами для получения цветов пикселей.