import Foundation import AsyncDisplayKit import SwiftSignalKit import Display import TelegramCore public struct TransformImageArguments: Equatable { public let corners: ImageCorners public let imageSize: CGSize public let boundingSize: CGSize public let intrinsicInsets: UIEdgeInsets public var drawingSize: CGSize { let cornersExtendedEdges = self.corners.extendedEdges return CGSize(width: self.boundingSize.width + cornersExtendedEdges.left + cornersExtendedEdges.right + self.intrinsicInsets.left + self.intrinsicInsets.right, height: self.boundingSize.height + cornersExtendedEdges.top + cornersExtendedEdges.bottom + self.intrinsicInsets.top + self.intrinsicInsets.bottom) } public var drawingRect: CGRect { let cornersExtendedEdges = self.corners.extendedEdges return CGRect(x: cornersExtendedEdges.left + self.intrinsicInsets.left, y: cornersExtendedEdges.top + self.intrinsicInsets.top, width: self.boundingSize.width, height: self.boundingSize.height); } public var insets: UIEdgeInsets { let cornersExtendedEdges = self.corners.extendedEdges return UIEdgeInsets(top: cornersExtendedEdges.top + self.intrinsicInsets.top, left: cornersExtendedEdges.left + self.intrinsicInsets.left, bottom: cornersExtendedEdges.bottom + self.intrinsicInsets.bottom, right: cornersExtendedEdges.right + self.intrinsicInsets.right) } } public func ==(lhs: TransformImageArguments, rhs: TransformImageArguments) -> Bool { return lhs.imageSize == rhs.imageSize && lhs.boundingSize == rhs.boundingSize && lhs.corners == rhs.corners } public class TransformImageNode: ASDisplayNode { public var imageUpdated: (() -> Void)? public var alphaTransitionOnFirstUpdate = false private var disposable = MetaDisposable() private var argumentsPromise = ValuePromise(ignoreRepeated: true) deinit { self.disposable.dispose() } func setSignal(account: Account, signal: Signal<(TransformImageArguments) -> DrawingContext, NoError>, dispatchOnDisplayLink: Bool = true) { let argumentsPromise = self.argumentsPromise let result = combineLatest(signal, argumentsPromise.get()) |> deliverOn(Queue.concurrentDefaultQueue() /*account.graphicsThreadPool*/) |> mapToThrottled { transform, arguments -> Signal in return deferred { return Signal.single(transform(arguments).generateImage()) } } self.disposable.set((result |> deliverOnMainQueue).start(next: {[weak self] next in if dispatchOnDisplayLink { displayLinkDispatcher.dispatch { [weak self] in if let strongSelf = self { if strongSelf.alphaTransitionOnFirstUpdate && strongSelf.contents == nil { strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) } strongSelf.contents = next?.cgImage if let imageUpdated = strongSelf.imageUpdated { imageUpdated() } } } } else { if let strongSelf = self { if strongSelf.alphaTransitionOnFirstUpdate && strongSelf.contents == nil { strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) } strongSelf.contents = next?.cgImage if let imageUpdated = strongSelf.imageUpdated { imageUpdated() } } } })) } public func asyncLayout() -> (TransformImageArguments) -> (() -> Void) { return { arguments in self.argumentsPromise.set(arguments) return { } } } }