mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Entity input: improved animation cache and rendering
This commit is contained in:
@@ -11,25 +11,39 @@ import TelegramCore
|
||||
import Postbox
|
||||
import AnimationCache
|
||||
import LottieAnimationCache
|
||||
import VideoAnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import ShimmerEffect
|
||||
import TextFormat
|
||||
|
||||
public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
|
||||
public static let queue = Queue()
|
||||
|
||||
public struct Key: Hashable {
|
||||
public var id: MediaId
|
||||
public var id: Int64
|
||||
public var index: Int
|
||||
|
||||
public init(id: MediaId, index: Int) {
|
||||
public init(id: Int64, index: Int) {
|
||||
self.id = id
|
||||
self.index = index
|
||||
}
|
||||
}
|
||||
|
||||
private let file: TelegramMediaFile
|
||||
private let context: AccountContext
|
||||
private let groupId: String
|
||||
private let emoji: ChatTextInputTextCustomEmojiAttribute
|
||||
private let cache: AnimationCache
|
||||
private let renderer: MultiAnimationRenderer
|
||||
private let placeholderColor: UIColor
|
||||
|
||||
private let pointSize: CGSize
|
||||
private let pixelSize: CGSize
|
||||
|
||||
private var file: TelegramMediaFile?
|
||||
private var infoDisposable: Disposable?
|
||||
private var disposable: Disposable?
|
||||
private var fetchDisposable: Disposable?
|
||||
private var loadDisposable: Disposable?
|
||||
|
||||
private var isInHierarchyValue: Bool = false
|
||||
public var isVisibleForAnimations: Bool = false {
|
||||
@@ -39,45 +53,36 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
|
||||
}
|
||||
}
|
||||
}
|
||||
private var displayLink: ConstantDisplayLinkAnimator?
|
||||
|
||||
public init(context: AccountContext, groupId: String, attemptSynchronousLoad: Bool, file: TelegramMediaFile, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor) {
|
||||
self.file = file
|
||||
public init(context: AccountContext, groupId: String, attemptSynchronousLoad: Bool, emoji: ChatTextInputTextCustomEmojiAttribute, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor) {
|
||||
self.context = context
|
||||
self.groupId = groupId
|
||||
self.emoji = emoji
|
||||
self.cache = cache
|
||||
self.renderer = renderer
|
||||
self.placeholderColor = placeholderColor
|
||||
|
||||
let scale = min(2.0, UIScreenScale)
|
||||
self.pointSize = CGSize(width: 24, height: 24)
|
||||
self.pixelSize = CGSize(width: self.pointSize.width * scale, height: self.pointSize.height * scale)
|
||||
|
||||
super.init()
|
||||
|
||||
let scale = min(2.0, UIScreenScale)
|
||||
let pixelSize = CGSize(width: 24 * scale, height: 24 * scale)
|
||||
|
||||
if attemptSynchronousLoad {
|
||||
if !renderer.loadFirstFrameSynchronously(groupId: groupId, target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize) {
|
||||
let size = CGSize(width: pixelSize.width / scale, height: pixelSize.height / scale)
|
||||
if let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: size, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: placeholderColor) {
|
||||
self.contents = image.cgImage
|
||||
}
|
||||
self.infoDisposable = (context.engine.stickers.loadedStickerPack(reference: emoji.stickerPack, forceActualized: false)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
self.disposable = renderer.add(groupId: groupId, target: self, cache: cache, itemId: file.resource.id.stringRepresentation, size: pixelSize, fetch: { size, writer in
|
||||
let source = AnimatedStickerResourceSource(account: context.account, resource: file.resource, fitzModifier: nil, isVideo: false)
|
||||
|
||||
let dataDisposable = source.directDataPath(attemptSynchronously: false).start(next: { result in
|
||||
guard let result = result else {
|
||||
return
|
||||
switch result {
|
||||
case let .result(_, items, _):
|
||||
for item in items {
|
||||
if item.file.fileId.id == emoji.fileId {
|
||||
strongSelf.updateFile(file: item.file, attemptSynchronousLoad: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: result)) else {
|
||||
writer.finish()
|
||||
return
|
||||
}
|
||||
cacheLottieAnimation(data: data, width: Int(size.width), height: Int(size.height), writer: writer)
|
||||
})
|
||||
|
||||
let fetchDisposable = freeMediaFileInteractiveFetched(account: context.account, fileReference: .standalone(media: file)).start()
|
||||
|
||||
return ActionDisposable {
|
||||
dataDisposable.dispose()
|
||||
fetchDisposable.dispose()
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -91,6 +96,8 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.loadDisposable?.dispose()
|
||||
self.infoDisposable?.dispose()
|
||||
self.disposable?.dispose()
|
||||
self.fetchDisposable?.dispose()
|
||||
}
|
||||
@@ -110,13 +117,70 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
|
||||
|
||||
self.shouldBeAnimating = shouldBePlaying
|
||||
}
|
||||
|
||||
private func updateFile(file: TelegramMediaFile, attemptSynchronousLoad: Bool) {
|
||||
if self.file?.fileId == file.fileId {
|
||||
return
|
||||
}
|
||||
|
||||
self.file = file
|
||||
|
||||
if attemptSynchronousLoad {
|
||||
if !self.renderer.loadFirstFrameSynchronously(groupId: self.groupId, target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize) {
|
||||
if let image = generateStickerPlaceholderImage(data: file.immediateThumbnailData, size: self.pointSize, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), backgroundColor: nil, foregroundColor: self.placeholderColor) {
|
||||
self.contents = image.cgImage
|
||||
}
|
||||
}
|
||||
|
||||
self.loadAnimation()
|
||||
} else {
|
||||
self.loadDisposable = self.renderer.loadFirstFrame(groupId: self.groupId, target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, completion: { [weak self] _ in
|
||||
self?.loadAnimation()
|
||||
})
|
||||
self.loadAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
private func loadAnimation() {
|
||||
guard let file = self.file else {
|
||||
return
|
||||
}
|
||||
|
||||
let context = self.context
|
||||
self.disposable = renderer.add(groupId: self.groupId, target: self, cache: self.cache, itemId: file.resource.id.stringRepresentation, size: self.pixelSize, fetch: { size, writer in
|
||||
let source = AnimatedStickerResourceSource(account: context.account, resource: file.resource, fitzModifier: nil, isVideo: false)
|
||||
|
||||
let dataDisposable = source.directDataPath(attemptSynchronously: false).start(next: { result in
|
||||
guard let result = result else {
|
||||
return
|
||||
}
|
||||
|
||||
if file.isVideoSticker {
|
||||
cacheVideoAnimation(path: result, width: Int(size.width), height: Int(size.height), writer: writer)
|
||||
} else {
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: result)) else {
|
||||
writer.finish()
|
||||
return
|
||||
}
|
||||
cacheLottieAnimation(data: data, width: Int(size.width), height: Int(size.height), writer: writer)
|
||||
}
|
||||
})
|
||||
|
||||
let fetchDisposable = freeMediaFileResourceInteractiveFetched(account: context.account, fileReference: stickerPackFileReference(file), resource: file.resource).start()
|
||||
|
||||
return ActionDisposable {
|
||||
dataDisposable.dispose()
|
||||
fetchDisposable.dispose()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public final class EmojiTextAttachmentView: UIView {
|
||||
private let contentLayer: InlineStickerItemLayer
|
||||
|
||||
public init(context: AccountContext, file: TelegramMediaFile, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor) {
|
||||
self.contentLayer = InlineStickerItemLayer(context: context, groupId: "textInputView", attemptSynchronousLoad: true, file: file, cache: cache, renderer: renderer, placeholderColor: placeholderColor)
|
||||
public init(context: AccountContext, emoji: ChatTextInputTextCustomEmojiAttribute, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor) {
|
||||
self.contentLayer = InlineStickerItemLayer(context: context, groupId: "textInputView", attemptSynchronousLoad: true, emoji: emoji, cache: cache, renderer: renderer, placeholderColor: placeholderColor)
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
@@ -131,6 +195,6 @@ public final class EmojiTextAttachmentView: UIView {
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.contentLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: -2.0), size: CGSize(width: self.bounds.width - 0.0, height: self.bounds.height + 9.0))
|
||||
self.contentLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.bounds.width, height: self.bounds.height))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user