mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Animated stickers playback improvements
This commit is contained in:
parent
ce9aefcd53
commit
afb14ea953
@ -225,8 +225,9 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
|||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
self.addSubnode(animationNode)
|
self.addSubnode(animationNode)
|
||||||
}
|
}
|
||||||
self.animationNode?.setup(account: item.account, fileReference: FileMediaReference.standalone(media: item.stickerItem.file), width: 140, height: 140)
|
self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: 160, height: 160, mode: .cached)
|
||||||
self.animationNode?.visibility = self.isVisibleInGrid
|
self.animationNode?.visibility = self.isVisibleInGrid
|
||||||
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start())
|
||||||
} else {
|
} else {
|
||||||
if let animationNode = self.animationNode {
|
if let animationNode = self.animationNode {
|
||||||
animationNode.visibility = false
|
animationNode.visibility = false
|
||||||
@ -234,8 +235,8 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
|||||||
animationNode.removeFromSupernode()
|
animationNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
self.imageNode.setSignal(chatMessageSticker(account: item.account, file: item.stickerItem.file, small: true, synchronousLoad: synchronousLoads && isVisible))
|
self.imageNode.setSignal(chatMessageSticker(account: item.account, file: item.stickerItem.file, small: true, synchronousLoad: synchronousLoads && isVisible))
|
||||||
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: chatMessageStickerResource(file: item.stickerItem.file, small: true)).start())
|
||||||
}
|
}
|
||||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: chatMessageStickerResource(file: item.stickerItem.file, small: true)).start())
|
|
||||||
|
|
||||||
self.currentState = (item.account, item.stickerItem, dimensions)
|
self.currentState = (item.account, item.stickerItem, dimensions)
|
||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
|
@ -67,17 +67,54 @@ private let boundingImageSize = CGSize(width: 28.0, height: 28.0)
|
|||||||
private let highlightSize = CGSize(width: 35.0, height: 35.0)
|
private let highlightSize = CGSize(width: 35.0, height: 35.0)
|
||||||
private let verticalOffset: CGFloat = 3.0
|
private let verticalOffset: CGFloat = 3.0
|
||||||
|
|
||||||
|
private enum StickerPackThumbnailItem: Equatable {
|
||||||
|
case still(TelegramMediaImageRepresentation)
|
||||||
|
case animated(MediaResource)
|
||||||
|
|
||||||
|
static func ==(lhs: StickerPackThumbnailItem, rhs: StickerPackThumbnailItem) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .still(representation):
|
||||||
|
if case .still(representation) = rhs {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .animated(lhsResource):
|
||||||
|
if case let .animated(rhsResource) = rhs, lhsResource.isEqual(to: rhsResource) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
||||||
private let imageNode: TransformImageNode
|
private let imageNode: TransformImageNode
|
||||||
|
private var animatedStickerNode: AnimatedStickerNode?
|
||||||
private let highlightNode: ASImageNode
|
private let highlightNode: ASImageNode
|
||||||
|
|
||||||
var inputNodeInteraction: ChatMediaInputNodeInteraction?
|
var inputNodeInteraction: ChatMediaInputNodeInteraction?
|
||||||
var currentCollectionId: ItemCollectionId?
|
var currentCollectionId: ItemCollectionId?
|
||||||
private var currentThumbnailItem: TelegramMediaImageRepresentation?
|
private var currentThumbnailItem: StickerPackThumbnailItem?
|
||||||
private var theme: PresentationTheme?
|
private var theme: PresentationTheme?
|
||||||
|
|
||||||
private let stickerFetchedDisposable = MetaDisposable()
|
private let stickerFetchedDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
override var visibility: ListViewItemNodeVisibility {
|
||||||
|
didSet {
|
||||||
|
self.visibilityStatus = self.visibility != .none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var visibilityStatus: Bool = false {
|
||||||
|
didSet {
|
||||||
|
if self.visibilityStatus != oldValue {
|
||||||
|
self.animatedStickerNode?.visibility = self.visibilityStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.highlightNode = ASImageNode()
|
self.highlightNode = ASImageNode()
|
||||||
self.highlightNode.isLayerBacked = true
|
self.highlightNode.isLayerBacked = true
|
||||||
@ -110,28 +147,58 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
|||||||
self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme)
|
self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
var thumbnailItem: TelegramMediaImageRepresentation?
|
var thumbnailItem: StickerPackThumbnailItem?
|
||||||
var resourceReference: MediaResourceReference?
|
var resourceReference: MediaResourceReference?
|
||||||
if let thumbnail = info.thumbnail {
|
if let thumbnail = info.thumbnail {
|
||||||
thumbnailItem = thumbnail
|
if info.flags.contains(.isAnimated) {
|
||||||
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
|
thumbnailItem = .animated(thumbnail.resource)
|
||||||
} else if let item = item, let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
|
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
|
||||||
thumbnailItem = TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource)
|
} else {
|
||||||
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: resource)
|
thumbnailItem = .still(thumbnail)
|
||||||
|
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
|
||||||
|
}
|
||||||
|
} else if let item = item {
|
||||||
|
if item.file.isAnimatedSticker {
|
||||||
|
thumbnailItem = .animated(item.file.resource)
|
||||||
|
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
|
||||||
|
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
|
||||||
|
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource))
|
||||||
|
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: resource)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.currentThumbnailItem != thumbnailItem {
|
if self.currentThumbnailItem != thumbnailItem {
|
||||||
self.currentThumbnailItem = thumbnailItem
|
self.currentThumbnailItem = thumbnailItem
|
||||||
if let thumbnailItem = thumbnailItem {
|
if let thumbnailItem = thumbnailItem {
|
||||||
let imageSize = thumbnailItem.dimensions.aspectFitted(boundingImageSize)
|
switch thumbnailItem {
|
||||||
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
|
case let .still(representation):
|
||||||
imageApply()
|
let imageSize = representation.dimensions.aspectFitted(boundingImageSize)
|
||||||
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, representation: thumbnailItem))
|
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
|
||||||
|
imageApply()
|
||||||
|
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, representation: representation))
|
||||||
|
|
||||||
if let resourceReference = resourceReference {
|
self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
||||||
self.stickerFetchedDisposable.set(fetchedMediaResource(postbox: account.postbox, reference: resourceReference).start())
|
case let .animated(resource):
|
||||||
|
let imageSize = boundingImageSize
|
||||||
|
|
||||||
|
let animatedStickerNode: AnimatedStickerNode
|
||||||
|
if let current = self.animatedStickerNode {
|
||||||
|
animatedStickerNode = current
|
||||||
|
} else {
|
||||||
|
animatedStickerNode = AnimatedStickerNode()
|
||||||
|
self.animatedStickerNode = animatedStickerNode
|
||||||
|
animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
|
self.addSubnode(animatedStickerNode)
|
||||||
|
animatedStickerNode.setup(account: account, resource: resource, width: 80, height: 80, mode: .cached)
|
||||||
|
animatedStickerNode.visibility = self.visibilityStatus
|
||||||
|
}
|
||||||
|
if let animatedStickerNode = self.animatedStickerNode {
|
||||||
|
animatedStickerNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let resourceReference = resourceReference {
|
||||||
|
self.stickerFetchedDisposable.set(fetchedMediaResource(postbox: account.postbox, reference: resourceReference).start())
|
||||||
}
|
}
|
||||||
self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateIsHighlighted()
|
self.updateIsHighlighted()
|
||||||
|
@ -107,8 +107,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
if let telegramFile = media as? TelegramMediaFile {
|
if let telegramFile = media as? TelegramMediaFile {
|
||||||
if self.telegramFile?.id != telegramFile.id {
|
if self.telegramFile?.id != telegramFile.id {
|
||||||
self.telegramFile = telegramFile
|
self.telegramFile = telegramFile
|
||||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: CGSize(width: 360.0, height: 360.0), thumbnail: false))
|
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: CGSize(width: 400.0, height: 400.0), thumbnail: false))
|
||||||
self.animationNode.setup(account: item.context.account, fileReference: .message(message: MessageReference(item.message), media: telegramFile), width: 360, height: 360)
|
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 400, height: 400, mode: .cached)
|
||||||
|
self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .standalone(media: telegramFile)).start())
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -418,7 +418,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
|
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
|
||||||
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
||||||
refineContentImageLayout = refineLayout
|
refineContentImageLayout = refineLayout
|
||||||
} else if file.isSticker, let _ = file.dimensions {
|
} else if file.isSticker || file.isAnimatedSticker {
|
||||||
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
|
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
|
||||||
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload ? .full : .none, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
|
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload ? .full : .none, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
|
||||||
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
|
||||||
|
@ -46,6 +46,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
private var currentImageArguments: TransformImageArguments?
|
private var currentImageArguments: TransformImageArguments?
|
||||||
private var videoNode: UniversalVideoNode?
|
private var videoNode: UniversalVideoNode?
|
||||||
private var videoContent: NativeVideoContent?
|
private var videoContent: NativeVideoContent?
|
||||||
|
private var animatedStickerNode: AnimatedStickerNode?
|
||||||
private var statusNode: RadialStatusNode?
|
private var statusNode: RadialStatusNode?
|
||||||
var videoNodeDecoration: ChatBubbleVideoDecoration?
|
var videoNodeDecoration: ChatBubbleVideoDecoration?
|
||||||
private var badgeNode: ChatMessageInteractiveMediaBadge?
|
private var badgeNode: ChatMessageInteractiveMediaBadge?
|
||||||
@ -100,6 +101,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
videoNode.canAttachContent = false
|
videoNode.canAttachContent = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.animatedStickerNode?.visibility = self.visibility
|
||||||
self.visibilityPromise.set(self.visibility)
|
self.visibilityPromise.set(self.visibility)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +205,10 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
let imageLayout = self.imageNode.asyncLayout()
|
let imageLayout = self.imageNode.asyncLayout()
|
||||||
|
|
||||||
let currentVideoNode = self.videoNode
|
let currentVideoNode = self.videoNode
|
||||||
|
let currentAnimatedStickerNode = self.animatedStickerNode
|
||||||
|
|
||||||
let hasCurrentVideoNode = currentVideoNode != nil
|
let hasCurrentVideoNode = currentVideoNode != nil
|
||||||
|
let hasCurrentAnimatedStickerNode = currentAnimatedStickerNode != nil
|
||||||
let currentAutomaticDownload = self.automaticDownload
|
let currentAutomaticDownload = self.automaticDownload
|
||||||
let currentAutomaticPlayback = self.automaticPlayback
|
let currentAutomaticPlayback = self.automaticPlayback
|
||||||
|
|
||||||
@ -231,6 +236,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var isInlinePlayableVideo = false
|
var isInlinePlayableVideo = false
|
||||||
|
var isSticker = false
|
||||||
var maxDimensions = layoutConstants.image.maxDimensions
|
var maxDimensions = layoutConstants.image.maxDimensions
|
||||||
var maxHeight = layoutConstants.image.maxDimensions.height
|
var maxHeight = layoutConstants.image.maxDimensions.height
|
||||||
|
|
||||||
@ -255,8 +261,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
maxDimensions = CGSize(width: constrainedSize.width, height: layoutConstants.video.maxVerticalHeight)
|
maxDimensions = CGSize(width: constrainedSize.width, height: layoutConstants.video.maxVerticalHeight)
|
||||||
}
|
}
|
||||||
maxHeight = maxDimensions.height
|
maxHeight = maxDimensions.height
|
||||||
} else if file.isSticker {
|
} else if file.isSticker || file.isAnimatedSticker {
|
||||||
unboundSize = unboundSize.aspectFilled(CGSize(width: 162.0, height: 162.0))
|
unboundSize = unboundSize.aspectFilled(CGSize(width: 162.0, height: 162.0))
|
||||||
|
isSticker = true
|
||||||
}
|
}
|
||||||
isInlinePlayableVideo = file.isVideo && !isSecretMedia
|
isInlinePlayableVideo = file.isVideo && !isSecretMedia
|
||||||
} else if let image = media as? TelegramMediaWebFile, let dimensions = image.dimensions {
|
} else if let image = media as? TelegramMediaWebFile, let dimensions = image.dimensions {
|
||||||
@ -283,10 +290,14 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
|
|
||||||
switch sizeCalculation {
|
switch sizeCalculation {
|
||||||
case let .constrained(constrainedSize):
|
case let .constrained(constrainedSize):
|
||||||
if unboundSize.width > unboundSize.height {
|
if isSticker {
|
||||||
nativeSize = unboundSize.aspectFitted(constrainedSize)
|
nativeSize = unboundSize.aspectFittedOrSmaller(constrainedSize)
|
||||||
} else {
|
} else {
|
||||||
nativeSize = unboundSize.aspectFitted(CGSize(width: constrainedSize.height, height: constrainedSize.width))
|
if unboundSize.width > unboundSize.height {
|
||||||
|
nativeSize = unboundSize.aspectFitted(constrainedSize)
|
||||||
|
} else {
|
||||||
|
nativeSize = unboundSize.aspectFitted(CGSize(width: constrainedSize.height, height: constrainedSize.width))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case .unconstrained:
|
case .unconstrained:
|
||||||
nativeSize = unboundSize
|
nativeSize = unboundSize
|
||||||
@ -375,10 +386,17 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var replaceVideoNode: Bool?
|
var replaceVideoNode: Bool?
|
||||||
|
var replaceAnimatedStickerNode: Bool?
|
||||||
var updateVideoFile: TelegramMediaFile?
|
var updateVideoFile: TelegramMediaFile?
|
||||||
|
var updateAnimatedStickerFile: TelegramMediaFile?
|
||||||
var onlyFullSizeVideoThumbnail: Bool?
|
var onlyFullSizeVideoThumbnail: Bool?
|
||||||
|
|
||||||
var emptyColor: UIColor = message.effectivelyIncoming(context.account.peerId) ? theme.chat.bubble.incomingMediaPlaceholderColor : theme.chat.bubble.outgoingMediaPlaceholderColor
|
var emptyColor: UIColor
|
||||||
|
if isSticker {
|
||||||
|
emptyColor = .clear
|
||||||
|
} else {
|
||||||
|
emptyColor = message.effectivelyIncoming(context.account.peerId) ? theme.chat.bubble.incomingMediaPlaceholderColor : theme.chat.bubble.outgoingMediaPlaceholderColor
|
||||||
|
}
|
||||||
if let wallpaper = media as? WallpaperPreviewMedia, case let .file(_, patternColor) = wallpaper.content {
|
if let wallpaper = media as? WallpaperPreviewMedia, case let .file(_, patternColor) = wallpaper.content {
|
||||||
emptyColor = patternColor ?? UIColor(rgb: 0xd6e2ee, alpha: 0.5)
|
emptyColor = patternColor ?? UIColor(rgb: 0xd6e2ee, alpha: 0.5)
|
||||||
}
|
}
|
||||||
@ -388,6 +406,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
if hasCurrentVideoNode {
|
if hasCurrentVideoNode {
|
||||||
replaceVideoNode = true
|
replaceVideoNode = true
|
||||||
}
|
}
|
||||||
|
if hasCurrentAnimatedStickerNode {
|
||||||
|
replaceAnimatedStickerNode = true
|
||||||
|
}
|
||||||
if isSecretMedia {
|
if isSecretMedia {
|
||||||
updateImageSignal = { synchronousLoad in
|
updateImageSignal = { synchronousLoad in
|
||||||
return chatSecretPhoto(account: context.account, photoReference: .message(message: MessageReference(message), media: image))
|
return chatSecretPhoto(account: context.account, photoReference: .message(message: MessageReference(message), media: image))
|
||||||
@ -416,6 +437,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
if hasCurrentVideoNode {
|
if hasCurrentVideoNode {
|
||||||
replaceVideoNode = true
|
replaceVideoNode = true
|
||||||
}
|
}
|
||||||
|
if hasCurrentAnimatedStickerNode {
|
||||||
|
replaceAnimatedStickerNode = true
|
||||||
|
}
|
||||||
updateImageSignal = { synchronousLoad in
|
updateImageSignal = { synchronousLoad in
|
||||||
return chatWebFileImage(account: context.account, file: image)
|
return chatWebFileImage(account: context.account, file: image)
|
||||||
}
|
}
|
||||||
@ -433,7 +457,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
return chatSecretMessageVideo(account: context.account, videoReference: .message(message: MessageReference(message), media: file))
|
return chatSecretMessageVideo(account: context.account, videoReference: .message(message: MessageReference(message), media: file))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if file.isSticker {
|
if file.isAnimatedSticker {
|
||||||
|
updateImageSignal = { synchronousLoad in
|
||||||
|
return chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: CGSize(width: 400.0, height: 400.0))
|
||||||
|
}
|
||||||
|
} else if file.isSticker {
|
||||||
updateImageSignal = { synchronousLoad in
|
updateImageSignal = { synchronousLoad in
|
||||||
return chatMessageSticker(account: context.account, file: file, small: false)
|
return chatMessageSticker(account: context.account, file: file, small: false)
|
||||||
}
|
}
|
||||||
@ -463,6 +491,21 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
if hasCurrentVideoNode {
|
if hasCurrentVideoNode {
|
||||||
replaceVideoNode = false
|
replaceVideoNode = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if file.isAnimatedSticker {
|
||||||
|
updateAnimatedStickerFile = file
|
||||||
|
if hasCurrentAnimatedStickerNode {
|
||||||
|
if let currentMedia = currentMedia {
|
||||||
|
if !currentMedia.isSemanticallyEqual(to: file) {
|
||||||
|
replaceAnimatedStickerNode = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
replaceAnimatedStickerNode = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
replaceAnimatedStickerNode = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedFetchControls = FetchControls(fetch: { manual in
|
updatedFetchControls = FetchControls(fetch: { manual in
|
||||||
@ -625,6 +668,29 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let currentReplaceAnimatedStickerNode = replaceAnimatedStickerNode {
|
||||||
|
replaceAnimatedStickerNode = nil
|
||||||
|
if currentReplaceAnimatedStickerNode, let animatedStickerNode = strongSelf.animatedStickerNode {
|
||||||
|
animatedStickerNode.removeFromSupernode()
|
||||||
|
strongSelf.animatedStickerNode = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentReplaceAnimatedStickerNode, let updatedAnimatedStickerFile = updateAnimatedStickerFile {
|
||||||
|
let animatedStickerNode = AnimatedStickerNode()
|
||||||
|
animatedStickerNode.isUserInteractionEnabled = false
|
||||||
|
animatedStickerNode.started = {
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.imageNode.isHidden = true
|
||||||
|
}
|
||||||
|
strongSelf.animatedStickerNode = animatedStickerNode
|
||||||
|
animatedStickerNode.setup(account: context.account, resource: updatedAnimatedStickerFile.resource, width: 400, height: 400, mode: .cached)
|
||||||
|
strongSelf.insertSubnode(animatedStickerNode, aboveSubnode: strongSelf.imageNode)
|
||||||
|
animatedStickerNode.visibility = strongSelf.visibility
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let videoNode = strongSelf.videoNode {
|
if let videoNode = strongSelf.videoNode {
|
||||||
if !(replaceVideoNode ?? false), let decoration = videoNode.decoration as? ChatBubbleVideoDecoration, decoration.corners != corners {
|
if !(replaceVideoNode ?? false), let decoration = videoNode.decoration as? ChatBubbleVideoDecoration, decoration.corners != corners {
|
||||||
decoration.updateCorners(corners)
|
decoration.updateCorners(corners)
|
||||||
@ -645,6 +711,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let animatedStickerNode = strongSelf.animatedStickerNode {
|
||||||
|
animatedStickerNode.frame = imageFrame
|
||||||
|
animatedStickerNode.updateLayout(size: imageFrame.size)
|
||||||
|
}
|
||||||
|
|
||||||
if let updateImageSignal = updateImageSignal {
|
if let updateImageSignal = updateImageSignal {
|
||||||
strongSelf.imageNode.setSignal(updateImageSignal(synchronousLoads), attemptSynchronously: synchronousLoads)
|
strongSelf.imageNode.setSignal(updateImageSignal(synchronousLoads), attemptSynchronously: synchronousLoads)
|
||||||
}
|
}
|
||||||
|
@ -353,7 +353,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
|
|||||||
|
|
||||||
loop: for media in self.message.media {
|
loop: for media in self.message.media {
|
||||||
if let telegramFile = media as? TelegramMediaFile {
|
if let telegramFile = media as? TelegramMediaFile {
|
||||||
if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 64 * 1024, !telegramFile.previewRepresentations.isEmpty {
|
if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 64 * 1024 {
|
||||||
viewClassName = ChatMessageAnimatedStickerItemNode.self
|
viewClassName = ChatMessageAnimatedStickerItemNode.self
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,18 @@ import SwiftSignalKit
|
|||||||
import TelegramUIPrivateModule
|
import TelegramUIPrivateModule
|
||||||
|
|
||||||
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
|
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
|
||||||
func render(queue: Queue, width: Int, height: Int, data: Data, completion: @escaping () -> Void) {
|
func render(queue: Queue, width: Int, height: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
|
||||||
queue.async { [weak self] in
|
queue.async { [weak self] in
|
||||||
let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData in
|
let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData in
|
||||||
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
switch type {
|
||||||
decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height))
|
case .yuva:
|
||||||
|
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||||
|
decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height))
|
||||||
|
}
|
||||||
|
case .argb:
|
||||||
|
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||||
|
memcpy(pixelData, bytes, data.count)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.imageNode)
|
self.addSubnode(self.imageNode)
|
||||||
self.addSubnode(self.textNode)
|
//self.addSubnode(self.textNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -115,8 +115,9 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
self.addSubnode(animationNode)
|
self.addSubnode(animationNode)
|
||||||
}
|
}
|
||||||
self.animationNode?.setup(account: account, fileReference: FileMediaReference.standalone(media: stickerItem.file), width: 140, height: 140)
|
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached)
|
||||||
self.animationNode?.visibility = self.isVisibleInGrid
|
self.animationNode?.visibility = self.isVisibleInGrid
|
||||||
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
|
||||||
} else {
|
} else {
|
||||||
if let animationNode = self.animationNode {
|
if let animationNode = self.animationNode {
|
||||||
animationNode.visibility = false
|
animationNode.visibility = false
|
||||||
@ -124,10 +125,9 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
animationNode.removeFromSupernode()
|
animationNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
self.imageNode.setSignal(chatMessageSticker(account: account, file: stickerItem.file, small: true))
|
self.imageNode.setSignal(chatMessageSticker(account: account, file: stickerItem.file, small: true))
|
||||||
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: chatMessageStickerResource(file: stickerItem.file, small: true)).start())
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: chatMessageStickerResource(file: stickerItem.file, small: true)).start())
|
|
||||||
|
|
||||||
self.currentState = (account, stickerItem, dimensions)
|
self.currentState = (account, stickerItem, dimensions)
|
||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,6 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
|
|||||||
|
|
||||||
self.textNode = ASTextNode()
|
self.textNode = ASTextNode()
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
self.imageNode.addSubnode(self.textNode)
|
|
||||||
|
|
||||||
for case let .Sticker(text, _, _) in item.file.attributes {
|
for case let .Sticker(text, _, _) in item.file.attributes {
|
||||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(32.0), textColor: .black)
|
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(32.0), textColor: .black)
|
||||||
@ -87,9 +86,11 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
|
|||||||
let animationNode = AnimatedStickerNode()
|
let animationNode = AnimatedStickerNode()
|
||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
|
|
||||||
self.animationNode?.setup(account: account, fileReference: FileMediaReference.standalone(media: item.file), width: 320, height: 320)
|
self.animationNode?.setup(account: account, resource: item.file.resource, width: 400, height: 400, mode: .direct)
|
||||||
self.animationNode?.visibility = true
|
self.animationNode?.visibility = true
|
||||||
|
self.animationNode?.addSubnode(self.textNode)
|
||||||
} else {
|
} else {
|
||||||
|
self.imageNode.addSubnode(self.textNode)
|
||||||
self.animationNode = nil
|
self.animationNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,8 +219,8 @@ private func chatMessageStickerPackThumbnailData(postbox: Postbox, representatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatMessageAnimationData(postbox: Postbox, fileReference: FileMediaReference, width: Int, height: Int, synchronousLoad: Bool) -> Signal<MediaResourceData, NoError> {
|
func chatMessageAnimationData(postbox: Postbox, resource: MediaResource, width: Int, height: Int, synchronousLoad: Bool) -> Signal<MediaResourceData, NoError> {
|
||||||
let maybeFetched = postbox.mediaBox.cachedResourceRepresentation(fileReference.media.resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), pathExtension: "mp4", complete: false, fetch: false, attemptSynchronously: synchronousLoad)
|
let maybeFetched = postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), complete: false, fetch: false, attemptSynchronously: synchronousLoad)
|
||||||
|
|
||||||
return maybeFetched
|
return maybeFetched
|
||||||
|> take(1)
|
|> take(1)
|
||||||
@ -228,7 +228,7 @@ func chatMessageAnimationData(postbox: Postbox, fileReference: FileMediaReferenc
|
|||||||
if maybeData.complete {
|
if maybeData.complete {
|
||||||
return .single(maybeData)
|
return .single(maybeData)
|
||||||
} else {
|
} else {
|
||||||
return postbox.mediaBox.cachedResourceRepresentation(fileReference.media.resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), pathExtension: "mp4", complete: false)
|
return postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), complete: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -474,12 +474,11 @@ public func chatMessageAnimatedSticker(postbox: Postbox, file: TelegramMediaFile
|
|||||||
let fullSizeData = value._1
|
let fullSizeData = value._1
|
||||||
let fullSizeComplete = value._2
|
let fullSizeComplete = value._2
|
||||||
return { arguments in
|
return { arguments in
|
||||||
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil)
|
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: true)
|
||||||
|
|
||||||
let drawingRect = arguments.drawingRect
|
let drawingRect = arguments.drawingRect
|
||||||
let fittedSize = arguments.imageSize
|
let fittedSize = arguments.imageSize
|
||||||
let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
|
let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
|
||||||
//let fittedRect = arguments.drawingRect
|
|
||||||
|
|
||||||
var fullSizeImage: (UIImage, UIImage)?
|
var fullSizeImage: (UIImage, UIImage)?
|
||||||
if let fullSizeData = fullSizeData, fullSizeComplete {
|
if let fullSizeData = fullSizeData, fullSizeComplete {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user