mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
[WIP] Story collage
This commit is contained in:
@@ -7,8 +7,8 @@ import SwiftSignalKit
|
||||
|
||||
final class UniversalTextureSource: TextureSource {
|
||||
enum Input {
|
||||
case image(UIImage)
|
||||
case video(AVPlayerItem)
|
||||
case image(UIImage, CGRect?)
|
||||
case video(AVPlayerItem, CGRect?)
|
||||
case entity(MediaEditorComposerEntity)
|
||||
|
||||
fileprivate func createContext(renderTarget: RenderTarget, queue: DispatchQueue, additional: Bool) -> InputContext {
|
||||
@@ -28,7 +28,7 @@ final class UniversalTextureSource: TextureSource {
|
||||
private let queue: DispatchQueue
|
||||
|
||||
private var mainInputContext: InputContext?
|
||||
private var additionalInputContext: InputContext?
|
||||
private var additionalInputContexts: [InputContext] = []
|
||||
|
||||
var forceUpdates = false
|
||||
private var rate: Float = 1.0
|
||||
@@ -48,7 +48,7 @@ final class UniversalTextureSource: TextureSource {
|
||||
}
|
||||
|
||||
var mainImage: UIImage? {
|
||||
if let mainInput = self.mainInputContext?.input, case let .image(image) = mainInput {
|
||||
if let mainInput = self.mainInputContext?.input, case let .image(image, _) = mainInput {
|
||||
return image
|
||||
}
|
||||
return nil
|
||||
@@ -62,15 +62,11 @@ final class UniversalTextureSource: TextureSource {
|
||||
self.update(forced: true)
|
||||
}
|
||||
|
||||
func setAdditionalInput(_ input: Input?) {
|
||||
func setAdditionalInputs(_ inputs: [Input]) {
|
||||
guard let renderTarget = self.renderTarget else {
|
||||
return
|
||||
}
|
||||
if let input {
|
||||
self.additionalInputContext = input.createContext(renderTarget: renderTarget, queue: self.queue, additional: true)
|
||||
} else {
|
||||
self.additionalInputContext = nil
|
||||
}
|
||||
self.additionalInputContexts = inputs.map { $0.createContext(renderTarget: renderTarget, queue: self.queue, additional: true) }
|
||||
self.update(forced: true)
|
||||
}
|
||||
|
||||
@@ -79,7 +75,7 @@ final class UniversalTextureSource: TextureSource {
|
||||
self.rate = rate
|
||||
}
|
||||
|
||||
private var previousAdditionalOutput: MediaEditorRenderer.Input?
|
||||
private var previousAdditionalOutput: [Int: MediaEditorRenderer.Input] = [:]
|
||||
private var readyForMoreData = Atomic<Bool>(value: true)
|
||||
private func update(forced: Bool) {
|
||||
let time = CACurrentMediaTime()
|
||||
@@ -89,7 +85,15 @@ final class UniversalTextureSource: TextureSource {
|
||||
fps = 30
|
||||
}
|
||||
|
||||
let needsDisplayLink = (self.mainInputContext?.needsDisplayLink ?? false) || (self.additionalInputContext?.needsDisplayLink ?? false)
|
||||
var additionalsNeedDisplayLink = false
|
||||
for context in self.additionalInputContexts {
|
||||
if context.needsDisplayLink {
|
||||
additionalsNeedDisplayLink = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let needsDisplayLink = (self.mainInputContext?.needsDisplayLink ?? false) || additionalsNeedDisplayLink
|
||||
if needsDisplayLink {
|
||||
if self.displayLink == nil {
|
||||
let displayLink = CADisplayLink(target: DisplayLinkTarget({ [weak self] in
|
||||
@@ -122,22 +126,32 @@ final class UniversalTextureSource: TextureSource {
|
||||
return
|
||||
}
|
||||
if let main {
|
||||
self.output?.consume(main: main, additional: nil, render: true)
|
||||
self.output?.consume(main: main, additionals: [], render: true)
|
||||
}
|
||||
let _ = self.readyForMoreData.swap(true)
|
||||
})
|
||||
} else {
|
||||
let main = self.mainInputContext?.output(time: time)
|
||||
var additional = self.additionalInputContext?.output(time: time)
|
||||
if let additional {
|
||||
self.previousAdditionalOutput = additional
|
||||
} else if self.additionalInputContext != nil {
|
||||
additional = self.previousAdditionalOutput
|
||||
var additionals: [(Int, InputContext.Output?)] = []
|
||||
var index = 0
|
||||
for context in self.additionalInputContexts {
|
||||
additionals.append((index, context.output(time: time)))
|
||||
index += 1
|
||||
}
|
||||
for (index, output) in additionals {
|
||||
if let output {
|
||||
self.previousAdditionalOutput[index] = output
|
||||
}
|
||||
}
|
||||
for (index, output) in additionals {
|
||||
if output == nil {
|
||||
additionals[index] = (index, self.previousAdditionalOutput[index])
|
||||
}
|
||||
}
|
||||
guard let main else {
|
||||
return
|
||||
}
|
||||
self.output?.consume(main: main, additional: additional, render: true)
|
||||
self.output?.consume(main: main, additionals: additionals.compactMap { $0.1 }, render: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +162,7 @@ final class UniversalTextureSource: TextureSource {
|
||||
|
||||
func invalidate() {
|
||||
self.mainInputContext?.invalidate()
|
||||
self.additionalInputContext?.invalidate()
|
||||
self.additionalInputContexts.forEach { $0.invalidate() }
|
||||
}
|
||||
|
||||
private class DisplayLinkTarget {
|
||||
@@ -168,6 +182,8 @@ protocol InputContext {
|
||||
|
||||
var input: Input { get }
|
||||
|
||||
var rect: CGRect? { get }
|
||||
|
||||
var useAsyncOutput: Bool { get }
|
||||
func output(time: Double) -> Output?
|
||||
func asyncOutput(time: Double, completion: @escaping (Output?) -> Void)
|
||||
@@ -191,12 +207,14 @@ private class ImageInputContext: InputContext {
|
||||
fileprivate var input: Input
|
||||
private var texture: MTLTexture?
|
||||
private var hasTransparency = false
|
||||
fileprivate var rect: CGRect?
|
||||
|
||||
init(input: Input, renderTarget: RenderTarget, queue: DispatchQueue) {
|
||||
guard case let .image(image) = input else {
|
||||
guard case let .image(image, rect) = input else {
|
||||
fatalError()
|
||||
}
|
||||
self.input = input
|
||||
self.rect = rect
|
||||
if let device = renderTarget.mtlDevice {
|
||||
self.texture = loadTexture(image: image, device: device)
|
||||
}
|
||||
@@ -204,7 +222,7 @@ private class ImageInputContext: InputContext {
|
||||
}
|
||||
|
||||
func output(time: Double) -> Output? {
|
||||
return self.texture.flatMap { .texture($0, .zero, self.hasTransparency) }
|
||||
return self.texture.flatMap { .texture($0, .zero, self.hasTransparency, self.rect) }
|
||||
}
|
||||
|
||||
func invalidate() {
|
||||
@@ -220,23 +238,26 @@ private class VideoInputContext: NSObject, InputContext, AVPlayerItemOutputPullD
|
||||
fileprivate var input: Input
|
||||
private var videoOutput: AVPlayerItemVideoOutput?
|
||||
private var textureRotation: TextureRotation = .rotate0Degrees
|
||||
fileprivate var rect: CGRect?
|
||||
|
||||
var playerItem: AVPlayerItem {
|
||||
guard case let .video(playerItem) = self.input else {
|
||||
guard case let .video(playerItem, _) = self.input else {
|
||||
fatalError()
|
||||
}
|
||||
return playerItem
|
||||
}
|
||||
|
||||
init(input: Input, renderTarget: RenderTarget, queue: DispatchQueue, additional: Bool) {
|
||||
guard case .video = input else {
|
||||
guard case let .video(_, rect) = input else {
|
||||
fatalError()
|
||||
}
|
||||
self.input = input
|
||||
self.rect = rect
|
||||
|
||||
super.init()
|
||||
|
||||
//TODO: mirror if self.additionalPlayer == nil && self.mirror
|
||||
self.textureRotation = textureRotatonForAVAsset(self.playerItem.asset, mirror: additional)
|
||||
self.textureRotation = textureRotatonForAVAsset(self.playerItem.asset, mirror: rect == nil ? additional : false)
|
||||
|
||||
let colorProperties: [String: Any] = [
|
||||
AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_709_2,
|
||||
@@ -270,7 +291,7 @@ private class VideoInputContext: NSObject, InputContext, AVPlayerItemOutputPullD
|
||||
if let pixelBuffer = videoOutput.copyPixelBuffer(forItemTime: requestTime, itemTimeForDisplay: &presentationTime) {
|
||||
videoPixelBuffer = VideoPixelBuffer(pixelBuffer: pixelBuffer, rotation: self.textureRotation, timestamp: presentationTime)
|
||||
}
|
||||
return videoPixelBuffer.flatMap { .videoBuffer($0) }
|
||||
return videoPixelBuffer.flatMap { .videoBuffer($0, self.rect) }
|
||||
}
|
||||
|
||||
func invalidate() {
|
||||
@@ -290,6 +311,8 @@ final class EntityInputContext: NSObject, InputContext, AVPlayerItemOutputPullDe
|
||||
internal var input: Input
|
||||
private var textureRotation: TextureRotation = .rotate0Degrees
|
||||
|
||||
var rect: CGRect?
|
||||
|
||||
var entity: MediaEditorComposerEntity {
|
||||
guard case let .entity(entity) = self.input else {
|
||||
fatalError()
|
||||
|
||||
Reference in New Issue
Block a user