Files
HaishinKit.swift/HaishinKit/Sources/Screen/ScreenRendererByGPU.swift

88 lines
2.8 KiB
Swift

import Accelerate
import AVFoundation
import CoreImage
import CoreImage.CIFilterBuiltins
final class ScreenRendererByGPU: ScreenRenderer {
var bounds: CGRect = .init(origin: .zero, size: Screen.size)
let imageOptions: [CIImageOption: Any]?
var synchronizationClock: CMClock?
var presentationTimeStamp: CMTime = .zero
let context: CIContext
var backgroundColor = CGColor(red: 0x00, green: 0x00, blue: 0x00, alpha: 0x00) {
didSet {
guard backgroundColor != oldValue else {
return
}
backgroundCIColor = CIColor(cgColor: backgroundColor)
}
}
private var canvas: CIImage = .init()
private var images: [ScreenObject: CIImage] = [:]
private var pixelBuffer: CVPixelBuffer?
private let dynamicRangeMode: DynamicRangeMode
private var backgroundCIColor = CIColor()
private var roundedRectangleFactory = RoundedRectangleFactory()
init(dynamicRangeMode: DynamicRangeMode) {
self.dynamicRangeMode = dynamicRangeMode
context = dynamicRangeMode.makeCIContext()
if let colorSpace = dynamicRangeMode.colorSpace {
imageOptions = [.colorSpace: colorSpace]
} else {
imageOptions = nil
}
}
func setTarget(_ pixelBuffer: CVPixelBuffer?) {
guard let pixelBuffer else {
return
}
self.pixelBuffer = pixelBuffer
canvas = CIImage(color: backgroundCIColor).cropped(to: bounds)
}
func layout(_ screenObject: ScreenObject) {
guard let image: CIImage = screenObject.makeImage(self) else {
return
}
if 0 < screenObject.cornerRadius {
if let mask = roundedRectangleFactory.cornerRadius(screenObject.bounds.size, cornerRadius: screenObject.cornerRadius) {
images[screenObject] = image.applyingFilter("CIBlendWithAlphaMask", parameters: [
"inputMaskImage": mask
])
} else {
images[screenObject] = image
}
} else {
images[screenObject] = image
}
}
func draw(_ screenObject: ScreenObject) {
guard let image = images[screenObject] else {
return
}
let origin = screenObject.bounds.origin
if origin.x == 0 && origin.y == 0 {
canvas = image
.composited(over: canvas)
} else {
canvas = image
.transformed(by: .init(translationX: origin.x, y: bounds.height - origin.y - screenObject.bounds.height))
.composited(over: canvas)
}
}
func render() {
guard let pixelBuffer else {
return
}
context.render(canvas, to: pixelBuffer, bounds: canvas.extent, colorSpace: dynamicRangeMode.colorSpace)
dynamicRangeMode.attach(pixelBuffer)
}
}