Files
luma/Sources/LumaCore/VirtualMachines/CoreGraphicsFrameEncoder.swift

47 lines
2.2 KiB
Swift

#if canImport(CoreGraphics)
import CoreGraphics
import Foundation
import ImageIO
import UniformTypeIdentifiers
public enum CoreGraphicsFrameEncoder {
public static func install() {
VirtualMachineFrameEncoder.png = { frame, width in
png(of: frame, width: width)
}
}
private static func png(of frame: VirtualMachineFrame, width: Int) -> Data? {
guard let image = image(of: frame), let scaled = scaled(image, toWidth: width) else { return nil }
let buffer = NSMutableData()
guard let destination = CGImageDestinationCreateWithData(buffer as CFMutableData, UTType.png.identifier as CFString, 1, nil)
else { return nil }
CGImageDestinationAddImage(destination, scaled, nil)
return CGImageDestinationFinalize(destination) ? buffer as Data : nil
}
private static func image(of frame: VirtualMachineFrame) -> CGImage? {
let alpha: CGImageAlphaInfo = frame.format == .bgra8888 ? .premultipliedFirst : .noneSkipFirst
guard let provider = CGDataProvider(data: frame.withPixels { Data($0) } as CFData) else { return nil }
return CGImage(
width: frame.width, height: frame.height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: frame.stride,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGBitmapInfo(rawValue: alpha.rawValue | CGBitmapInfo.byteOrder32Little.rawValue),
provider: provider, decode: nil, shouldInterpolate: true, intent: .defaultIntent)
}
private static func scaled(_ image: CGImage, toWidth targetWidth: Int) -> CGImage? {
guard targetWidth < image.width else { return image }
let targetHeight = max(1, image.height * targetWidth / image.width)
guard
let context = CGContext(
data: nil, width: targetWidth, height: targetHeight, bitsPerComponent: 8, bytesPerRow: 0,
space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue)
else { return nil }
context.interpolationQuality = .high
context.draw(image, in: CGRect(x: 0, y: 0, width: targetWidth, height: targetHeight))
return context.makeImage()
}
}
#endif