diff --git a/Telegram-iOS.xcworkspace/xcshareddata/xcschemes/Telegram-iOS-Hockeyapp-Internal.xcscheme b/Telegram-iOS.xcworkspace/xcshareddata/xcschemes/Telegram-iOS-Hockeyapp-Internal.xcscheme index cda2b15e44..2db06cb59b 100644 --- a/Telegram-iOS.xcworkspace/xcshareddata/xcschemes/Telegram-iOS-Hockeyapp-Internal.xcscheme +++ b/Telegram-iOS.xcworkspace/xcshareddata/xcschemes/Telegram-iOS-Hockeyapp-Internal.xcscheme @@ -79,11 +79,14 @@ buildConfiguration = "DebugHockeyapp" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + enableAddressSanitizer = "YES" enableASanStackUseAfterReturn = "YES" + enableUBSanitizer = "YES" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" + stopOnEveryUBSanitizerIssue = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES" queueDebuggingEnabled = "No"> diff --git a/submodules/Display/Display/GenerateImage.swift b/submodules/Display/Display/GenerateImage.swift index ae6baba564..7c02da4e7c 100644 --- a/submodules/Display/Display/GenerateImage.swift +++ b/submodules/Display/Display/GenerateImage.swift @@ -15,7 +15,7 @@ private let deviceColorSpace: CGColorSpace = { let deviceScale = UIScreen.main.scale -public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer) -> Void) -> UIImage? { +public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer, Int) -> Void) -> UIImage? { let scaledSize = CGSize(width: size.width * scale, height: size.height * scale) let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15) let length = bytesPerRow * Int(scaledSize.height) @@ -27,7 +27,7 @@ public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: ( return nil } - pixelGenerator(scaledSize, bytes) + pixelGenerator(scaledSize, bytes, bytesPerRow) let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) diff --git a/submodules/Postbox/Postbox/MessageHistoryMetadataTable.swift b/submodules/Postbox/Postbox/MessageHistoryMetadataTable.swift index 8467212a8d..c464fa7fb0 100644 --- a/submodules/Postbox/Postbox/MessageHistoryMetadataTable.swift +++ b/submodules/Postbox/Postbox/MessageHistoryMetadataTable.swift @@ -90,7 +90,7 @@ final class MessageHistoryMetadataTable: Table { private func chatListGroupInitializedKey(_ key: InitializedChatListKey) -> ValueBoxKey { self.sharedChatListGroupHistoryInitializedKey.setInt32(0, value: key.groupId.rawValue) - self.sharedChatListGroupHistoryInitializedKey.setInt8(8, value: MetadataPrefix.ChatListGroupInitialized.rawValue) + self.sharedChatListGroupHistoryInitializedKey.setInt8(4, value: MetadataPrefix.ChatListGroupInitialized.rawValue) return self.sharedChatListGroupHistoryInitializedKey } diff --git a/submodules/RLottie/Sources/LottieInstance.h b/submodules/RLottie/Sources/LottieInstance.h index 5eb3fb3d41..dd57dcddbe 100644 --- a/submodules/RLottie/Sources/LottieInstance.h +++ b/submodules/RLottie/Sources/LottieInstance.h @@ -11,7 +11,7 @@ @property (nonatomic, readonly) CGSize dimensions; - (instancetype _Nullable)initWithData:(NSData * _Nonnull)data cacheKey:(NSString * _Nonnull)cacheKey; -- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height; +- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height bytesPerRow:(int32_t)bytesPerRow; @end diff --git a/submodules/RLottie/Sources/LottieInstance.mm b/submodules/RLottie/Sources/LottieInstance.mm index e1859ef694..83a10557ff 100644 --- a/submodules/RLottie/Sources/LottieInstance.mm +++ b/submodules/RLottie/Sources/LottieInstance.mm @@ -38,8 +38,8 @@ return self; } -- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height { - rlottie::Surface surface((uint32_t *)buffer, width, height, width * 4); +- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height bytesPerRow:(int32_t) bytesPerRow{ + rlottie::Surface surface((uint32_t *)buffer, width, height, bytesPerRow); _animation->renderSync(index, surface); } diff --git a/submodules/TelegramUI/TelegramUI/AnimatedStickerNode.swift b/submodules/TelegramUI/TelegramUI/AnimatedStickerNode.swift index 168b692774..48f4cfb86a 100644 --- a/submodules/TelegramUI/TelegramUI/AnimatedStickerNode.swift +++ b/submodules/TelegramUI/TelegramUI/AnimatedStickerNode.swift @@ -82,13 +82,15 @@ private final class AnimatedStickerFrame { let type: AnimationRendererFrameType let width: Int let height: Int + let bytesPerRow: Int let isLastFrame: Bool - init(data: Data, type: AnimationRendererFrameType, width: Int, height: Int, isLastFrame: Bool) { + init(data: Data, type: AnimationRendererFrameType, width: Int, height: Int, bytesPerRow: Int, isLastFrame: Bool) { self.data = data self.type = type self.width = width self.height = height + self.bytesPerRow = bytesPerRow self.isLastFrame = isLastFrame } } @@ -105,6 +107,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource private let data: Data private var scratchBuffer: Data let width: Int + let bytesPerRow: Int let height: Int let frameRate: Int private let initialOffset: Int @@ -120,6 +123,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource var offset = 0 var width = 0 var height = 0 + var bytesPerRow = 0 var frameRate = 0 if !self.data.withUnsafeBytes({ (bytes: UnsafePointer) -> Bool in @@ -129,19 +133,23 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource offset += 4 var widthValue: Int32 = 0 var heightValue: Int32 = 0 + var bytesPerRowValue: Int32 = 0 memcpy(&widthValue, bytes.advanced(by: offset), 4) offset += 4 memcpy(&heightValue, bytes.advanced(by: offset), 4) offset += 4 + memcpy(&bytesPerRowValue, bytes.advanced(by: offset), 4) + offset += 4 width = Int(widthValue) height = Int(heightValue) + bytesPerRow = Int(bytesPerRowValue) return true }) { return nil } - assert(width % 16 == 0) + self.bytesPerRow = bytesPerRow self.width = width self.height = height @@ -150,8 +158,8 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource self.initialOffset = offset self.offset = offset - self.decodeBuffer = Data(count: width * 4 * height) - self.frameBuffer = Data(count: width * 4 * height) + self.decodeBuffer = Data(count: self.bytesPerRow * height) + self.frameBuffer = Data(count: self.bytesPerRow * height) let frameBufferLength = self.frameBuffer.count self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in memset(bytes, 0, frameBufferLength) @@ -203,7 +211,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource } } - return AnimatedStickerFrame(data: frameData!, type: .yuva, width: self.width, height: self.height, isLastFrame: isLastFrame) + return AnimatedStickerFrame(data: frameData!, type: .yuva, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, isLastFrame: isLastFrame) } } @@ -212,6 +220,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource private let data: Data private let width: Int private let height: Int + private let bytesPerRow: Int private let frameCount: Int let frameRate: Int private var currentFrame: Int @@ -222,6 +231,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource self.data = data self.width = width self.height = height + self.bytesPerRow = (4 * Int(width) + 15) & (~15) self.currentFrame = 0 guard let rawData = TGGUnzipData(data, 8 * 1024 * 1024) else { return nil @@ -232,7 +242,6 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource self.animation = animation self.frameCount = Int(animation.frameCount) self.frameRate = Int(animation.frameRate) - assert(width % 16 == 0) } deinit { @@ -242,12 +251,12 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource func takeFrame() -> AnimatedStickerFrame { let frameIndex = self.currentFrame % self.frameCount self.currentFrame += 1 - var frameData = Data(count: self.width * self.height * 4) + var frameData = Data(count: self.bytesPerRow * self.height) frameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - memset(bytes, 0, self.width * self.height * 4) - self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height)) + memset(bytes, 0, self.bytesPerRow * self.height) + self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height), bytesPerRow: Int32(self.bytesPerRow)) } - return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, isLastFrame: frameIndex == self.frameCount) + return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, isLastFrame: frameIndex == self.frameCount) } } @@ -354,6 +363,9 @@ final class AnimatedStickerNode: ASDisplayNode { } func setup(account: Account, resource: MediaResource, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) { + if width < 2 || height < 2 { + return + } self.playbackMode = playbackMode switch mode { case .direct: @@ -430,7 +442,7 @@ final class AnimatedStickerNode: ASDisplayNode { guard let strongSelf = self else { return } - strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, data: frame.data, type: frame.type, completion: { + strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, completion: { guard let strongSelf = self else { return } diff --git a/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift b/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift index b5429c89c8..d3d6ad1502 100644 --- a/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift +++ b/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift @@ -83,10 +83,12 @@ func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, cacheKey: St } let context = DrawingContext(size: size, scale: 1.0, clear: true) - player.renderFrame(with: 0, into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height)) + player.renderFrame(with: 0, into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(context.bytesPerRow)) - let yuvaLength = Int(size.width) * Int(size.height) * 2 + Int(size.width) * Int(size.height) / 2 - assert(yuvaLength % 8 == 0) + let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1) + assert(yuvaPixelsPerAlphaRow % 2 == 0) + + let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2 var yuvaFrameData = malloc(yuvaLength)! memset(yuvaFrameData, 0, yuvaLength) @@ -94,8 +96,8 @@ func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, cacheKey: St free(yuvaFrameData) } - encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height)) - decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height)) + encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow)) + decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow)) if let colorImage = context.generateImage() { let colorData = NSMutableData() @@ -189,23 +191,29 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize, let scale = size.width / 512.0 + let bytesPerRow = (4 * Int(size.width) + 15) & (~15) + var currentFrame: Int32 = 0 var fps: Int32 = player.frameRate let _ = fileContext.write(&fps, count: 4) var widthValue: Int32 = Int32(size.width) var heightValue: Int32 = Int32(size.height) + var bytesPerRowValue: Int32 = Int32(bytesPerRow) let _ = fileContext.write(&widthValue, count: 4) let _ = fileContext.write(&heightValue, count: 4) + let _ = fileContext.write(&bytesPerRowValue, count: 4) - let frameLength = Int(size.width) * Int(size.height) * 4 + let frameLength = bytesPerRow * Int(size.height) assert(frameLength % 16 == 0) let currentFrameData = malloc(frameLength)! memset(currentFrameData, 0, frameLength) - let yuvaLength = Int(size.width) * Int(size.height) * 2 + Int(size.width) * Int(size.height) / 2 - assert(yuvaLength % 8 == 0) + let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1) + assert(yuvaPixelsPerAlphaRow % 2 == 0) + + let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2 var yuvaFrameData = malloc(yuvaLength)! memset(yuvaFrameData, 0, yuvaLength) @@ -234,12 +242,12 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize, let drawStartTime = CACurrentMediaTime() memset(currentFrameData, 0, frameLength) - player.renderFrame(with: Int32(currentFrame), into: currentFrameData.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height)) + player.renderFrame(with: Int32(currentFrame), into: currentFrameData.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(bytesPerRow)) drawingTime += CACurrentMediaTime() - drawStartTime let appendStartTime = CACurrentMediaTime() - encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), currentFrameData.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height)) + encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), currentFrameData.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(bytesPerRow)) appendingTime += CACurrentMediaTime() - appendStartTime @@ -250,6 +258,9 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize, lhs.pointee = rhs.pointee ^ lhs.pointee lhs = lhs.advanced(by: 1) rhs = rhs.advanced(by: 1) + } + for i in (yuvaLength / 8) * 8 ..< yuvaLength { + } deltaTime += CACurrentMediaTime() - deltaStartTime diff --git a/submodules/TelegramUI/TelegramUI/AnimationRenderer.swift b/submodules/TelegramUI/TelegramUI/AnimationRenderer.swift index 7f728dd7f7..1c171dac83 100644 --- a/submodules/TelegramUI/TelegramUI/AnimationRenderer.swift +++ b/submodules/TelegramUI/TelegramUI/AnimationRenderer.swift @@ -8,5 +8,5 @@ enum AnimationRendererFrameType { } protocol AnimationRenderer { - func render(queue: Queue, width: Int, height: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) + func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) } diff --git a/submodules/TelegramUI/TelegramUI/CachedResourceRepresentations.swift b/submodules/TelegramUI/TelegramUI/CachedResourceRepresentations.swift index bd03f1b2bd..cdd435ad6a 100644 --- a/submodules/TelegramUI/TelegramUI/CachedResourceRepresentations.swift +++ b/submodules/TelegramUI/TelegramUI/CachedResourceRepresentations.swift @@ -271,7 +271,7 @@ final class CachedAnimatedStickerRepresentation: CachedMediaResourceRepresentati let height: Int32 var uniqueId: String { - return "animated-sticker-\(self.width)x\(self.height)-v5" + return "animated-sticker-\(self.width)x\(self.height)-v6" } init(width: Int32, height: Int32) { diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift index e17b80f464..32fb9fdb8c 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift @@ -224,7 +224,8 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { } self.addSubnode(animationNode) } - self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.account.postbox, file: item.stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0))) + let dimensions = item.stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0) + self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.account.postbox, file: item.stickerItem.file, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)))) self.updateVisibility() self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start()) } else { @@ -293,7 +294,9 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { self.animationNode?.visibility = isPlaying if let item = self.item, isPlaying, !self.didSetUpAnimationNode { self.didSetUpAnimationNode = true - self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: 160, height: 160, mode: .cached) + let dimensions = item.stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) + self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) } } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift index 5b2fc88f74..37c42a991c 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift @@ -127,7 +127,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let telegramFile = media as? TelegramMediaFile { if self.telegramFile?.id != telegramFile.id { self.telegramFile = telegramFile - self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: CGSize(width: 384.0, height: 384.0), thumbnail: false)) + let dimensions = telegramFile.dimensions ?? CGSize(width: 512.0, height: 512.0) + self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0)), thumbnail: false)) self.updateVisibility() self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .message(message: MessageReference(item.message), media: telegramFile)).start()) } @@ -172,7 +173,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if !item.controllerInteraction.stickerSettings.loopAnimatedStickers { playbackMode = .once } - self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, playbackMode: playbackMode, mode: .cached) + let dimensions = telegramFile.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedSize = dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0)) + self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: .cached) } else if let emojiResource = self.emojiResource { var playbackMode: AnimatedStickerPlaybackMode = .loop if item.context.sharedContext.immediateExperimentalUISettings.playAnimatedEmojiOnce { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift index c0f9be3518..fa6096a608 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -458,8 +458,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } } else { if file.isAnimatedSticker { + let dimensions = file.dimensions ?? CGSize(width: 512.0, height: 512.0) updateImageSignal = { synchronousLoad in - return chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: CGSize(width: 400.0, height: 400.0)) + return chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: dimensions.aspectFitted(CGSize(width: 400.0, height: 400.0))) } } else if file.isSticker { updateImageSignal = { synchronousLoad in @@ -685,7 +686,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { strongSelf.imageNode.isHidden = true } strongSelf.animatedStickerNode = animatedStickerNode - animatedStickerNode.setup(account: context.account, resource: updatedAnimatedStickerFile.resource, width: 384, height: 384, mode: .cached) + let dimensions = updatedAnimatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0)) + animatedStickerNode.setup(account: context.account, resource: updatedAnimatedStickerFile.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) strongSelf.insertSubnode(animatedStickerNode, aboveSubnode: strongSelf.imageNode) animatedStickerNode.visibility = strongSelf.visibility } diff --git a/submodules/TelegramUI/TelegramUI/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatPinnedMessageTitlePanelNode.swift index fdaa8981bf..d620db3a8e 100644 --- a/submodules/TelegramUI/TelegramUI/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatPinnedMessageTitlePanelNode.swift @@ -217,7 +217,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { updateImageSignal = chatMessagePhotoThumbnail(account: context.account, photoReference: imageReference) } else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) { if fileReference.media.isAnimatedSticker { - updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, file: fileReference.media, small: false, size: CGSize(width: 160.0, height: 160.0)) + let dimensions = fileReference.media.dimensions ?? CGSize(width: 512.0, height: 512.0) + updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, file: fileReference.media, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))) updatedFetchMediaSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: fileReference.resourceReference(fileReference.media.resource)) } else if fileReference.media.isVideo { updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference) diff --git a/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift index 285440a7d5..54b531a1b2 100644 --- a/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift +++ b/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift @@ -380,7 +380,9 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode animationNode.started = { [weak self] in self?.imageNode.alpha = 0.0 } - animationNode.setup(account: item.account, resource: animatedStickerFile.resource, width: 160, height: 160, mode: .cached) + let dimensions = animatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) + animationNode.setup(account: item.account, resource: animatedStickerFile.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) } } diff --git a/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift b/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift index 1649b2b680..f081f0bf7b 100755 --- a/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift +++ b/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift @@ -106,7 +106,9 @@ final class HorizontalStickerGridItemNode: GridItemNode { animationNode.started = { [weak self] in self?.imageNode.alpha = 0.0 } - animationNode.setup(account: account, resource: item.file.resource, width: 160, height: 160, mode: .cached) + let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) + animationNode.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start()) } else { diff --git a/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift b/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift index 571d79455c..278ba25185 100644 --- a/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift +++ b/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift @@ -113,7 +113,9 @@ final class TrendingTopItemNode: ASDisplayNode { animationNode.started = { [weak self] in self?.imageNode.alpha = 0.0 } - animationNode.setup(account: account, resource: item.file.resource, width: 160, height: 160, mode: .cached) + let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) + animationNode.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start()) } else { self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads) diff --git a/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift b/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift index 016090accb..aec6d9471d 100644 --- a/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift +++ b/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift @@ -279,8 +279,10 @@ public final class NotificationViewControllerImpl { } view?.addSubnode(animatedStickerNode) } - strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: CGSize(width: 512.0, height: 512.0))) - animatedStickerNode.setup(account: accountAndImage.0, resource: fileReference.media.resource, width: 512, height: 512, mode: .direct) + let dimensions = fileReference.media.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 512.0, height: 512.0)) + strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions)) + animatedStickerNode.setup(account: accountAndImage.0, resource: fileReference.media.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct) animatedStickerNode.visibility = true accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true)) diff --git a/submodules/TelegramUI/TelegramUI/SoftwareAnimationRenderer.swift b/submodules/TelegramUI/TelegramUI/SoftwareAnimationRenderer.swift index 85dfc322d3..f13c50adea 100644 --- a/submodules/TelegramUI/TelegramUI/SoftwareAnimationRenderer.swift +++ b/submodules/TelegramUI/TelegramUI/SoftwareAnimationRenderer.swift @@ -6,13 +6,16 @@ import SwiftSignalKit import TelegramUIPrivateModule final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer { - func render(queue: Queue, width: Int, height: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) { + func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) { queue.async { [weak self] in - let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData in + let calculatedBytesPerRow = (4 * Int(width) + 15) & (~15) + assert(bytesPerRow == calculatedBytesPerRow) + + let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData, bytesPerRow in switch type { case .yuva: data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height)) + decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height), Int32(bytesPerRow)) } case .argb: data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in diff --git a/submodules/TelegramUI/TelegramUI/StickerPackPreviewController.swift b/submodules/TelegramUI/TelegramUI/StickerPackPreviewController.swift index c7bb483771..b2715d323c 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPackPreviewController.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPackPreviewController.swift @@ -165,7 +165,8 @@ final class StickerPackPreviewController: ViewController { let signal = Signal { subscriber in let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: FileMediaReference.standalone(media: item.file).resourceReference(item.file.resource)).start() let data = account.postbox.mediaBox.resourceData(item.file.resource).start() - let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, file: item.file, small: false, size: CGSize(width: 160.0, height: 160.0), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in + let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, file: item.file, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in let hasContent = next._0 != nil || next._1 != nil subscriber.putNext(hasContent) if hasContent { diff --git a/submodules/TelegramUI/TelegramUI/StickerPackPreviewGridItem.swift b/submodules/TelegramUI/TelegramUI/StickerPackPreviewGridItem.swift index 06b0bdc2b1..693c1cbd18 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPackPreviewGridItem.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPackPreviewGridItem.swift @@ -93,7 +93,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode { if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem { if let dimensions = stickerItem.file.dimensions { if stickerItem.file.isAnimatedSticker { - self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0))) + let dimensions = stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0) + self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)))) if self.animationNode == nil { let animationNode = AnimatedStickerNode() @@ -103,7 +104,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode { self?.imageNode.isHidden = true } } - self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) + self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start()) } else { diff --git a/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift b/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift index 92945922a9..82abfd0722 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift @@ -159,7 +159,9 @@ final class StickerPaneSearchStickerItemNode: GridItemNode { self.animationNode = animationNode self.addSubnode(animationNode) } - self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached) + let dimensions = stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)) + self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) self.animationNode?.visibility = self.isVisibleInGrid self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start()) } else { diff --git a/submodules/TelegramUI/TelegramUI/StickerPreviewPeekContent.swift b/submodules/TelegramUI/TelegramUI/StickerPreviewPeekContent.swift index 43bd9ca954..736885ee4a 100644 --- a/submodules/TelegramUI/TelegramUI/StickerPreviewPeekContent.swift +++ b/submodules/TelegramUI/TelegramUI/StickerPreviewPeekContent.swift @@ -86,7 +86,10 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController let animationNode = AnimatedStickerNode() self.animationNode = animationNode - self.animationNode?.setup(account: account, resource: item.file.resource, width: 400, height: 400, mode: .direct) + let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0) + let fittedDimensions = dimensions.aspectFitted(CGSize(width: 400.0, height: 400.0)) + + self.animationNode?.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct) self.animationNode?.visibility = true self.animationNode?.addSubnode(self.textNode) } else { diff --git a/submodules/TelegramUI/TelegramUI/YUV.h b/submodules/TelegramUI/TelegramUI/YUV.h index 9208d75f39..ab00691a46 100644 --- a/submodules/TelegramUI/TelegramUI/YUV.h +++ b/submodules/TelegramUI/TelegramUI/YUV.h @@ -1,4 +1,4 @@ #import -void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height); -void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height); +void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height, int bytesPerRow); +void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height, int bytesPerRow); diff --git a/submodules/TelegramUI/TelegramUI/YUV.m b/submodules/TelegramUI/TelegramUI/YUV.m index a53dd0c585..78381d714e 100644 --- a/submodules/TelegramUI/TelegramUI/YUV.m +++ b/submodules/TelegramUI/TelegramUI/YUV.m @@ -1,7 +1,7 @@ #import "YUV.h" #import -void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height) { +void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height, int bytesPerRow) { static vImage_ARGBToYpCbCr info; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -15,7 +15,7 @@ void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height) src.data = (void *)argb; src.width = width; src.height = height; - src.rowBytes = width * 4; + src.rowBytes = bytesPerRow; uint8_t permuteMap[4] = {3, 2, 1, 0}; error = vImagePermuteChannels_ARGB8888(&src, &src, permuteMap, kvImageDoNotTile); @@ -48,7 +48,7 @@ void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height) } } -void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height) { +void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height, int bytesPerRow) { static vImage_YpCbCrToARGB info; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -74,7 +74,7 @@ void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height) dest.data = (void *)argb; dest.width = width; dest.height = height; - dest.rowBytes = width * 4; + dest.rowBytes = bytesPerRow; error = vImageConvert_420Yp8_CbCr8ToARGB8888(&srcYp, &srcCbCr, &dest, &info, NULL, 0xff, kvImageDoNotTile);