draw to buffer
inline scale video frame when possible account for different dimensions
This commit is contained in:
@@ -70,7 +70,7 @@ class ThumbnailsPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
|
||||
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol ThumbnailApi {
|
||||
func setThumbnailToBuffer(pointer: Int64, assetId: String, width: Int64, height: Int64, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
func setThumbnailToBuffer(pointer: Int64, assetId: String, width: Int64, height: Int64, completion: @escaping (Result<[String: Int64], Error>) -> Void)
|
||||
}
|
||||
|
||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||
@@ -89,8 +89,8 @@ class ThumbnailApiSetup {
|
||||
let heightArg = args[3] as! Int64
|
||||
api.setThumbnailToBuffer(pointer: pointerArg, assetId: assetIdArg, width: widthArg, height: heightArg) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
reply(wrapResult(nil))
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
|
||||
@@ -20,50 +20,36 @@ class ThumbnailApiImpl: ThumbnailApi {
|
||||
return requestOptions
|
||||
}()
|
||||
private static let processingQueue = DispatchQueue(label: "thumbnail.processing", qos: .userInteractive, attributes: .concurrent)
|
||||
private static let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
private static let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).rawValue
|
||||
|
||||
func setThumbnailToBuffer(pointer: Int64, assetId: String, width: Int64, height: Int64, completion: @escaping (Result<Void, any Error>) -> Void) {
|
||||
func setThumbnailToBuffer(pointer: Int64, assetId: String, width: Int64, height: Int64, completion: @escaping (Result<[String: Int64], any Error>) -> Void) {
|
||||
guard let bufferPointer = UnsafeMutableRawPointer(bitPattern: Int(pointer))
|
||||
else { completion(.failure(PigeonError(code: "", message: "Could not get buffer pointer for \(assetId)", details: nil))); return }
|
||||
Self.processingQueue.async {
|
||||
do {
|
||||
let asset = try self.getAsset(assetId: assetId)
|
||||
Self.cacheManager.requestImage(
|
||||
for: asset,
|
||||
targetSize: CGSize(width: Double(width), height: Double(height)),
|
||||
contentMode: .aspectFill,
|
||||
options: Self.requestOptions,
|
||||
resultHandler: { (image, info) -> Void in
|
||||
guard let image = image,
|
||||
let cgImage = image.cgImage,
|
||||
let dataProvider = cgImage.dataProvider,
|
||||
let pixelData = dataProvider.data
|
||||
else { completion(.failure(PigeonError(code: "", message: "Could not get pixel data for \(assetId)", details: nil))); return }
|
||||
|
||||
guard let sourceBuffer = CFDataGetBytePtr(pixelData)
|
||||
else { completion(.failure(PigeonError(code: "", message: "Could not get pixel data buffer for \(assetId)", details: nil))); return }
|
||||
let dataLength = CFDataGetLength(pixelData)
|
||||
let bufferLength = width * height * 4
|
||||
guard dataLength <= bufferLength
|
||||
else { completion(.failure(PigeonError(code: "", message: "Buffer is not large enough (\(bufferLength) vs \(dataLength) for \(assetId)", details: nil))); return }
|
||||
|
||||
bufferPointer.copyMemory(from: sourceBuffer, byteCount: dataLength)
|
||||
completion(.success(()))
|
||||
}
|
||||
)
|
||||
} catch {
|
||||
completion(
|
||||
.failure(PigeonError(code: "", message: "Could not get asset data for \(assetId)", details: nil)))
|
||||
}
|
||||
guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: Self.fetchOptions).firstObject
|
||||
else { completion(.failure(PigeonError(code: "", message: "Could not get asset data for \(assetId)", details: nil))); return }
|
||||
Self.cacheManager.requestImage(
|
||||
for: asset,
|
||||
targetSize: CGSize(width: Double(width), height: Double(height)),
|
||||
contentMode: .aspectFill,
|
||||
options: Self.requestOptions,
|
||||
resultHandler: { (image, info) -> Void in
|
||||
guard let image = image,
|
||||
let cgImage = image.cgImage,
|
||||
let context = CGContext(
|
||||
data: bufferPointer,
|
||||
width: cgImage.width,
|
||||
height: cgImage.height,
|
||||
bitsPerComponent: 8,
|
||||
bytesPerRow: cgImage.width * 4,
|
||||
space: Self.rgbColorSpace,
|
||||
bitmapInfo: Self.bitmapInfo
|
||||
) else { completion(.failure(PigeonError(code: "", message: "Could not get pixel data for \(assetId)", details: nil))); return }
|
||||
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: cgImage.width, height: cgImage.height))
|
||||
completion(.success(["width": Int64(cgImage.width), "height": Int64(cgImage.height)]))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func getAsset(assetId: String) throws -> PHAsset {
|
||||
guard
|
||||
let asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: Self.fetchOptions)
|
||||
.firstObject
|
||||
else {
|
||||
throw PigeonError(code: "", message: "Could not fetch asset", details: nil)
|
||||
}
|
||||
return asset
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user