diff --git a/submodules/Display/Display/GenerateImage.swift b/submodules/Display/Display/GenerateImage.swift index dac84c3a37..e1da6c0196 100644 --- a/submodules/Display/Display/GenerateImage.swift +++ b/submodules/Display/Display/GenerateImage.swift @@ -4,12 +4,11 @@ import UIKit let deviceColorSpace = CGColorSpaceCreateDeviceRGB() let deviceScale = UIScreen.main.scale -public func generateImagePixel(_ size: CGSize, pixelGenerator: (CGSize, UnsafeMutablePointer) -> Void) -> UIImage? { - let scale = deviceScale +public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer) -> 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) - let bytes = malloc(length)!.assumingMemoryBound(to: Int8.self) + let bytes = malloc(length)!.assumingMemoryBound(to: UInt8.self) guard let provider = CGDataProvider(dataInfo: bytes, data: bytes, size: length, releaseData: { bytes, _, _ in free(bytes) }) @@ -287,7 +286,7 @@ public class DrawingContext { } } - public init(size: CGSize, scale: CGFloat = 0.0, clear: Bool = false) { + public init(size: CGSize, scale: CGFloat = 0.0, premultiplied: Bool = true, clear: Bool = false) { let actualScale: CGFloat if scale.isZero { actualScale = deviceScale @@ -301,7 +300,11 @@ public class DrawingContext { self.bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15) self.length = bytesPerRow * Int(scaledSize.height) - self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) + if premultiplied { + self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) + } else { + self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.first.rawValue) + } self.bytes = malloc(length)! if clear { diff --git a/submodules/LegacyComponents/LegacyComponents/LegacyComponentsInternal.m b/submodules/LegacyComponents/LegacyComponents/LegacyComponentsInternal.m index 5ef77d899a..bcc18ecbb0 100644 --- a/submodules/LegacyComponents/LegacyComponents/LegacyComponentsInternal.m +++ b/submodules/LegacyComponents/LegacyComponents/LegacyComponentsInternal.m @@ -44,42 +44,8 @@ int iosMajorVersion() { static bool initialized = false; static int version = 7; - if (!initialized) - { - switch ([[[UIDevice currentDevice] systemVersion] intValue]) - { - case 4: - version = 4; - break; - case 5: - version = 5; - break; - case 6: - version = 6; - break; - case 7: - version = 7; - break; - case 8: - version = 8; - break; - case 9: - version = 9; - break; - case 10: - version = 10; - break; - case 11: - version = 11; - break; - case 12: - version = 12; - break; - default: - version = 9; - break; - } - + if (!initialized) { + version = [[[UIDevice currentDevice] systemVersion] intValue]; initialized = true; } return version; @@ -93,8 +59,9 @@ int iosMinorVersion() { NSString *versionString = [[UIDevice currentDevice] systemVersion]; NSRange range = [versionString rangeOfString:@"."]; - if (range.location != NSNotFound) + if (range.location != NSNotFound) { version = [[versionString substringFromIndex:range.location + 1] intValue]; + } initialized = true; } diff --git a/submodules/Postbox/Postbox/Postbox.swift b/submodules/Postbox/Postbox/Postbox.swift index cc911fa769..6f5097236a 100644 --- a/submodules/Postbox/Postbox/Postbox.swift +++ b/submodules/Postbox/Postbox/Postbox.swift @@ -940,7 +940,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, #if DEBUG //debugSaveState(basePath: basePath, name: "previous1") - //debugRestoreState(basePath: basePath, name: "previous1") + debugRestoreState(basePath: basePath, name: "previous1") #endif let startTime = CFAbsoluteTimeGetCurrent() diff --git a/submodules/Postbox/Postbox/TimeBasedCleanup.swift b/submodules/Postbox/Postbox/TimeBasedCleanup.swift index 0210a549dc..93f8aefa1e 100644 --- a/submodules/Postbox/Postbox/TimeBasedCleanup.swift +++ b/submodules/Postbox/Postbox/TimeBasedCleanup.swift @@ -59,6 +59,9 @@ private final class TimeBasedCleanupImpl { public func setMaxStoreTime(_ maxStoreTime: Int32) { if self.maxStoreTime != maxStoreTime { self.maxStoreTime = maxStoreTime + #if DEBUG + return; + #endif self.resetScan(maxStoreTime: maxStoreTime) } } diff --git a/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift b/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift index 7dc0a53968..d565c29669 100644 --- a/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift +++ b/submodules/TelegramUI/TelegramUI/AnimatedStickerUtils.swift @@ -110,52 +110,141 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize) var currentFrame: Int32 = 0 let container = LOTAnimationLayerContainer(model: model, size: size) - + let singleContext = DrawingContext(size: size, scale: 1.0, clear: true) - - let fps: Int32 = model.framerate?.int32Value ?? 30 - let frameDuration = CMTimeMake(1, fps) - var frameData = Data(count: singleContext.length) - let frameDataCount = frameData.count + var fps: Int32 = model.framerate?.int32Value ?? 30 + let _ = fileContext.write(&fps, count: 4) - let scratchData = malloc(compression_encode_scratch_buffer_size(COMPRESSION_LZ4))! - defer { - free(scratchData) - } - - while startFrame + currentFrame < endFrame { - let lastFrameTime = CMTimeMake(Int64(currentFrame - startFrame), fps) - let presentationTime = currentFrame == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration) + if true { + let frameLength = singleContext.length + assert(frameLength % 16 == 0) - let drawStartTime = CACurrentMediaTime() - singleContext.withContext { context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.saveGState() - context.scaleBy(x: scale, y: scale) - container?.renderFrame(startFrame + currentFrame, in: context) - context.restoreGState() - } - drawingTime += CACurrentMediaTime() - drawStartTime + let previousFrameData = malloc(frameLength)! + memset(previousFrameData, 0, frameLength) - let appendStartTime = CACurrentMediaTime() - frameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - let length = compression_encode_buffer(bytes, frameDataCount, singleContext.bytes.assumingMemoryBound(to: UInt8.self), singleContext.length, scratchData, COMPRESSION_LZ4) - var frameLengthValue: Int32 = Int32(length) - let _ = fileContext.write(&frameLengthValue, count: 4) - let _ = fileContext.write(bytes, count: length) + defer { + free(previousFrameData) } - appendingTime += CACurrentMediaTime() - appendStartTime - currentFrame += 1 - } - - if startFrame + currentFrame == endFrame { - subscriber.putNext(path) - subscriber.putCompletion() - print("animation render time \(CACurrentMediaTime() - startTime)") - print("of which drawing time \(drawingTime)") - print("of which appending time \(appendingTime)") + var compressedFrameData = Data(count: frameLength) + let compressedFrameDataLength = compressedFrameData.count + + let scratchData = malloc(compression_encode_scratch_buffer_size(COMPRESSION_LZ4))! + defer { + free(scratchData) + } + + while startFrame + currentFrame < endFrame { + let drawStartTime = CACurrentMediaTime() + singleContext.withContext { context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.saveGState() + context.scaleBy(x: scale, y: scale) + container?.renderFrame(startFrame + currentFrame, in: context) + context.restoreGState() + } + + var lhs = previousFrameData.assumingMemoryBound(to: UInt64.self) + var rhs = singleContext.bytes.assumingMemoryBound(to: UInt64.self) + for _ in 0 ..< frameLength / 8 { + lhs.pointee = rhs.pointee ^ lhs.pointee + lhs = lhs.advanced(by: 1) + rhs = rhs.advanced(by: 1) + } + + drawingTime += CACurrentMediaTime() - drawStartTime + + let appendStartTime = CACurrentMediaTime() + compressedFrameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + let length = compression_encode_buffer(bytes, compressedFrameDataLength, previousFrameData.assumingMemoryBound(to: UInt8.self), frameLength, scratchData, COMPRESSION_LZ4) + var frameLengthValue: Int32 = Int32(length) + let _ = fileContext.write(&frameLengthValue, count: 4) + let _ = fileContext.write(bytes, count: length) + } + + memcpy(previousFrameData, singleContext.bytes, frameLength) + + appendingTime += CACurrentMediaTime() - appendStartTime + currentFrame += 1 + } + + if startFrame + currentFrame >= endFrame { + subscriber.putNext(path) + subscriber.putCompletion() + print("animation render time \(CACurrentMediaTime() - startTime)") + print("of which drawing time \(drawingTime)") + print("of which appending time \(appendingTime)") + } + } else { + let bgrg422Length = Int(size.width) * 2 * Int(size.height) + let aLength = Int(size.width) * Int(size.height) + let frameLength = bgrg422Length + aLength + + assert(frameLength % 16 == 0) + + let currentFrameData = malloc(frameLength)! + let previousFrameData = malloc(frameLength)! + memset(previousFrameData, 0, frameLength) + + defer { + free(currentFrameData) + free(previousFrameData) + } + + let fps: Int32 = model.framerate?.int32Value ?? 30 + + var compressedFrameData = Data(count: bgrg422Length + aLength) + let compressedFrameDataLength = compressedFrameData.count + + let scratchData = malloc(compression_encode_scratch_buffer_size(COMPRESSION_LZ4))! + defer { + free(scratchData) + } + + while startFrame + currentFrame < endFrame { + let drawStartTime = CACurrentMediaTime() + singleContext.withContext { context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.saveGState() + context.scaleBy(x: scale, y: scale) + container?.renderFrame(startFrame + currentFrame, in: context) + context.restoreGState() + } + + encodeRGBAToBRGR422A(currentFrameData.assumingMemoryBound(to: UInt8.self).advanced(by: 0), currentFrameData.assumingMemoryBound(to: UInt8.self).advanced(by: bgrg422Length), singleContext.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height)) + + var lhs = previousFrameData.assumingMemoryBound(to: UInt64.self) + var rhs = currentFrameData.assumingMemoryBound(to: UInt64.self) + for _ in 0 ..< frameLength / 8 { + lhs.pointee = rhs.pointee ^ lhs.pointee + lhs = lhs.advanced(by: 1) + rhs = rhs.advanced(by: 1) + } + + drawingTime += CACurrentMediaTime() - drawStartTime + + let appendStartTime = CACurrentMediaTime() + compressedFrameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + let length = compression_encode_buffer(bytes, compressedFrameDataLength, previousFrameData.assumingMemoryBound(to: UInt8.self), frameLength, scratchData, COMPRESSION_LZ4) + var frameLengthValue: Int32 = Int32(length) + let _ = fileContext.write(&frameLengthValue, count: 4) + let _ = fileContext.write(bytes, count: length) + } + + memcpy(previousFrameData, currentFrameData, frameLength) + + appendingTime += CACurrentMediaTime() - appendStartTime + currentFrame += 1 + } + + if startFrame + currentFrame >= endFrame { + subscriber.putNext(path) + subscriber.putCompletion() + print("animation render time \(CACurrentMediaTime() - startTime)") + print("of which drawing time \(drawingTime)") + print("of which appending time \(appendingTime)") + } } } } diff --git a/submodules/TelegramUI/TelegramUI/AnimationRenderer.swift b/submodules/TelegramUI/TelegramUI/AnimationRenderer.swift new file mode 100644 index 0000000000..0af8fc5306 --- /dev/null +++ b/submodules/TelegramUI/TelegramUI/AnimationRenderer.swift @@ -0,0 +1,6 @@ +import Foundation +import AsyncDisplayKit + +protocol AnimationRenderer { + func render(width: Int, height: Int, bytes: UnsafeRawPointer, length: Int) +} diff --git a/submodules/TelegramUI/TelegramUI/CachedResourceRepresentations.swift b/submodules/TelegramUI/TelegramUI/CachedResourceRepresentations.swift index 7a81a57876..513de44094 100644 --- a/submodules/TelegramUI/TelegramUI/CachedResourceRepresentations.swift +++ b/submodules/TelegramUI/TelegramUI/CachedResourceRepresentations.swift @@ -217,7 +217,7 @@ final class CachedEmojiRepresentation: CachedMediaResourceRepresentation { final class CachedAnimatedStickerRepresentation: CachedMediaResourceRepresentation { var uniqueId: String { - return "animated-sticker-v2" + return "animated-sticker-v3" } func isEqual(to: CachedMediaResourceRepresentation) -> Bool { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift index 401ee5968f..0710b2bf90 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift @@ -5,50 +5,29 @@ import Display import SwiftSignalKit import Postbox import TelegramCore -import AVFoundation import CoreImage import TelegramPresentationData import Compression -private class AlphaFrameFilter: CIFilter { - static var kernel: CIColorKernel? = { - return CIColorKernel(source: """ -kernel vec4 alphaFrame(__sample s, __sample m) { - return vec4( s.rgb, 1.0 - m.r ); -} -""") - }() +private final class AnimationFrameCache { + private var cache: [Int: NSPurgeableData] = [:] - var inputImage: CIImage? - var maskImage: CIImage? - - override var outputImage: CIImage? { - let kernel = AlphaFrameFilter.kernel! - guard let inputImage = inputImage, let maskImage = maskImage else { - return nil + func get(index: Int, _ f: (NSPurgeableData?) -> Void) { + guard let data = self.cache[index] else { + f(nil) + return + } + if data.beginContentAccess() { + f(data) + data.endContentAccess() + } else { + self.cache.removeValue(forKey: index) + f(nil) } - let args = [inputImage as AnyObject, maskImage as AnyObject] - return kernel.apply(extent: inputImage.extent, arguments: args) } -} - -private func createVideoComposition(for playerItem: AVPlayerItem, ready: @escaping () -> Void) -> AVVideoComposition? { - let videoSize = CGSize(width: playerItem.presentationSize.width, height: playerItem.presentationSize.height / 2.0) - if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - let composition = AVMutableVideoComposition(asset: playerItem.asset, applyingCIFiltersWithHandler: { request in - let sourceRect = CGRect(origin: .zero, size: videoSize) - let alphaRect = sourceRect.offsetBy(dx: 0, dy: sourceRect.height) - let filter = AlphaFrameFilter() - filter.inputImage = request.sourceImage.cropped(to: alphaRect) - .transformed(by: CGAffineTransform(translationX: 0, y: -sourceRect.height)) - filter.maskImage = request.sourceImage.cropped(to: sourceRect) - request.finish(with: filter.outputImage!, context: nil) - ready() - }) - composition.renderSize = videoSize - return composition - } else { - return nil + + func set(index: Int, bytes: UnsafeRawPointer, length: Int) { + self.cache[index] = NSPurgeableData(bytes: bytes, length: length) } } @@ -59,10 +38,15 @@ private final class StickerAnimationNode: ASDisplayNode { private let fetchDisposable = MetaDisposable() var started: () -> Void = {} + private var reportedStarted = false private var timer: SwiftSignalKit.Timer? - var data: Data? + private var data: Data? + private var frameCache = AnimationFrameCache() + + private var renderer: (AnimationRenderer & ASDisplayNode)? + var visibility = false { didSet { if self.visibility { @@ -83,6 +67,19 @@ private final class StickerAnimationNode: ASDisplayNode { self.timer?.invalidate() } + override func didLoad() { + super.didLoad() + + #if targetEnvironment(simulator) + self.renderer = SoftwareAnimationRenderer() + #else + self.renderer = SoftwareAnimationRenderer() + //self.renderer = MetalAnimationRenderer() + #endif + self.renderer?.frame = CGRect(origin: CGPoint(), size: self.bounds.size) + self.addSubnode(self.renderer!) + } + func setup(account: Account, fileReference: FileMediaReference) { self.disposable.set(chatMessageAnimationData(postbox: account.postbox, fileReference: fileReference, synchronousLoad: false).start(next: { [weak self] data in if let strongSelf = self, data.complete { @@ -108,26 +105,155 @@ private final class StickerAnimationNode: ASDisplayNode { let dataCount = data.count self.timer?.invalidate() var scratchBuffer = Data(count: compression_decode_scratch_buffer_size(COMPRESSION_LZ4)) - let context = DrawingContext(size: CGSize(width: 400.0, height: 400.0), scale: 1.0, clear: false) + + let width = 320 + let height = 320 + var offset = 0 - let timer = SwiftSignalKit.Timer(timeout: 1.0 / 30.0, repeat: true, completion: { [weak self] in - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - var frameLength: Int32 = 0 - memcpy(&frameLength, bytes.advanced(by: offset), 4) - scratchBuffer.withUnsafeMutableBytes { (scratchBytes: UnsafeMutablePointer) -> Void in - compression_decode_buffer(context.bytes.assumingMemoryBound(to: UInt8.self), context.length, bytes.advanced(by: offset + 4), Int(frameLength), UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZ4) - } - if let image = context.generateImage() { - self?.contents = image.cgImage - } - offset += 4 + Int(frameLength) - if offset == dataCount { - offset = 0 - } + + var fps: Int32 = 0 + data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + memcpy(&fps, bytes, 4) + offset += 4 + } + + if true { + var decodeBuffer = Data(count: width * 4 * height) + var frameBuffer = Data(count: width * 4 * height) + let decodeBufferLength = decodeBuffer.count + frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + memset(bytes, 0, decodeBufferLength) } - }, queue: Queue.mainQueue()) - self.timer = timer - timer.start() + + var frameIndex = 0 + let timer = SwiftSignalKit.Timer(timeout: 1.0 / Double(fps), repeat: true, completion: { [weak self] in + guard let strongSelf = self else { + return + } + data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + var frameLength: Int32 = 0 + memcpy(&frameLength, bytes.advanced(by: offset), 4) + + var usedCache = false + strongSelf.frameCache.get(index: frameIndex, { data in + if let data = data { + usedCache = true + + strongSelf.renderer?.render(width: 320, height: 320, bytes: data.bytes, length: data.length) + + if !strongSelf.reportedStarted { + strongSelf.reportedStarted = true + strongSelf.started() + } + } + }) + + if !usedCache { + scratchBuffer.withUnsafeMutableBytes { (scratchBytes: UnsafeMutablePointer) -> Void in + decodeBuffer.withUnsafeMutableBytes { (decodeBytes: UnsafeMutablePointer) -> Void in + frameBuffer.withUnsafeMutableBytes { (frameBytes: UnsafeMutablePointer) -> Void in + compression_decode_buffer(decodeBytes, decodeBufferLength, bytes.advanced(by: offset + 4), Int(frameLength), UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZ4) + + var lhs = UnsafeMutableRawPointer(frameBytes).assumingMemoryBound(to: UInt64.self) + var rhs = UnsafeRawPointer(decodeBytes).assumingMemoryBound(to: UInt64.self) + for _ in 0 ..< decodeBufferLength / 8 { + lhs.pointee = lhs.pointee ^ rhs.pointee + lhs = lhs.advanced(by: 1) + rhs = rhs.advanced(by: 1) + } + + strongSelf.renderer?.render(width: 320, height: 320, bytes: frameBytes, length: decodeBufferLength) + + strongSelf.frameCache.set(index: frameIndex, bytes: frameBytes, length: decodeBufferLength) + } + } + } + + if !strongSelf.reportedStarted { + strongSelf.reportedStarted = true + strongSelf.started() + } + } + + offset += 4 + Int(frameLength) + frameIndex += 1 + if offset == dataCount { + offset = 4 + frameIndex = 0 + } + } + }, queue: Queue.mainQueue()) + self.timer = timer + timer.start() + } else { + var decodeBuffer = Data(count: width * 2 * height + width * height) + var frameBuffer = Data(count: width * 2 * height + width * height) + let decodeBufferLength = decodeBuffer.count + frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + memset(bytes, 0, decodeBufferLength) + } + + var frameIndex = 0 + let timer = SwiftSignalKit.Timer(timeout: 1.0 / Double(offset), repeat: true, completion: { [weak self] in + guard let strongSelf = self else { + return + } + data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + var frameLength: Int32 = 0 + memcpy(&frameLength, bytes.advanced(by: offset), 4) + + var usedCache = false + strongSelf.frameCache.get(index: frameIndex, { data in + if let data = data { + usedCache = true + + strongSelf.renderer?.render(width: 320, height: 320, bytes: data.bytes, length: data.length) + + if !strongSelf.reportedStarted { + strongSelf.reportedStarted = true + strongSelf.started() + } + } + }) + + if !usedCache { + scratchBuffer.withUnsafeMutableBytes { (scratchBytes: UnsafeMutablePointer) -> Void in + decodeBuffer.withUnsafeMutableBytes { (decodeBytes: UnsafeMutablePointer) -> Void in + frameBuffer.withUnsafeMutableBytes { (frameBytes: UnsafeMutablePointer) -> Void in + compression_decode_buffer(decodeBytes, decodeBufferLength, bytes.advanced(by: offset + 4), Int(frameLength), UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZ4) + + var lhs = UnsafeMutableRawPointer(frameBytes).assumingMemoryBound(to: UInt64.self) + var rhs = UnsafeRawPointer(decodeBytes).assumingMemoryBound(to: UInt64.self) + for _ in 0 ..< Int(decodeBufferLength) / 8 { + lhs.pointee = lhs.pointee ^ rhs.pointee + lhs = lhs.advanced(by: 1) + rhs = rhs.advanced(by: 1) + } + + strongSelf.renderer?.render(width: 320, height: 320, bytes: frameBytes, length: decodeBufferLength) + + strongSelf.frameCache.set(index: frameIndex, bytes: frameBytes, length: decodeBufferLength) + } + } + } + + if !strongSelf.reportedStarted { + strongSelf.reportedStarted = true + strongSelf.started() + } + } + + offset += 4 + Int(frameLength) + frameIndex += 1 + if offset == dataCount { + offset = 0 + frameIndex = 0 + } + } + }, queue: Queue.mainQueue()) + self.timer = timer + timer.start() + } } } @@ -135,6 +261,10 @@ private final class StickerAnimationNode: ASDisplayNode { self.timer?.invalidate() self.timer = nil } + + func updateLayout(size: CGSize) { + self.renderer?.frame = CGRect(origin: CGPoint(), size: size) + } } class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { @@ -213,14 +343,16 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { private var visibilityPromise = ValuePromise(false, ignoreRepeated: true) override var visibility: ListViewItemNodeVisibility { didSet { - if self.visibility != oldValue { - switch self.visibility { - case .visible: - self.animationNode.visibility = true - self.visibilityPromise.set(true) - case .none: - self.animationNode.visibility = false - self.visibilityPromise.set(false) + let wasVisible = oldValue != .none + let isVisible = self.visibility != .none + + if wasVisible != isVisible { + if isVisible { + self.animationNode.visibility = true + self.visibilityPromise.set(true) + } else { + self.animationNode.visibility = false + self.visibilityPromise.set(false) } } } @@ -255,14 +387,14 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in let incoming = item.message.effectivelyIncoming(item.context.account.peerId) - var imageSize: CGSize = CGSize(width: 162.0, height: 162.0) - if let telegramFile = telegramFile { + var imageSize: CGSize = CGSize(width: 160.0, height: 160.0) + /*if let telegramFile = telegramFile { if let dimensions = telegramFile.dimensions { imageSize = dimensions.aspectFitted(displaySize) } else if let thumbnailSize = telegramFile.previewRepresentations.first?.dimensions { imageSize = thumbnailSize.aspectFitted(displaySize) } - } + }*/ let avatarInset: CGFloat var hasAvatar = false @@ -434,6 +566,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { strongSelf.imageNode.frame = updatedImageFrame strongSelf.animationNode.frame = updatedImageFrame.insetBy(dx: imageInset, dy: imageInset) + strongSelf.animationNode.updateLayout(size: updatedImageFrame.insetBy(dx: imageInset, dy: imageInset).size) imageApply() if let updatedShareButtonNode = updatedShareButtonNode { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift index 27969f1d9b..bf5cc8a42f 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageAttachedContentNode.swift @@ -236,8 +236,8 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { var visibility: ListViewItemNodeVisibility = .none { didSet { - self.contentImageNode?.visibility = self.visibility - self.contentInstantVideoNode?.visibility = self.visibility + self.contentImageNode?.visibility = self.visibility != .none + self.contentInstantVideoNode?.visibility = self.visibility != .none } } @@ -784,7 +784,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { strongSelf.openMedia?(mode) } } - contentImageNode.visibility = strongSelf.visibility + contentImageNode.visibility = strongSelf.visibility != .none } let _ = contentImageApply(transition, synchronousLoads) let contentImageFrame: CGRect @@ -800,7 +800,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { adjustedStatusFrame = CGRect(origin: CGPoint(x: contentImageFrame.width - statusFrame.size.width - 2.0, y: contentImageFrame.height - statusFrame.size.height - 2.0), size: statusFrame.size) } } else if let contentImageNode = strongSelf.contentImageNode { - contentImageNode.visibility = .none + contentImageNode.visibility = false contentImageNode.removeFromSupernode() strongSelf.contentImageNode = nil } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift index f465b4c29c..48b900b27d 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift @@ -39,8 +39,11 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { override var visibility: ListViewItemNodeVisibility { didSet { - if self.visibility != oldValue { - self.interactiveVideoNode.visibility = self.visibility + let wasVisible = oldValue != .none + let isVisible = self.visibility != .none + + if wasVisible != isVisible { + self.interactiveVideoNode.visibility = isVisible } } } diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift index 5640247564..49d6794ee0 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift @@ -61,14 +61,14 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { private let fetchedThumbnailDisposable = MetaDisposable() private var shouldAcquireVideoContext: Bool { - if case .visible = self.visibility { + if self.visibility { return true } else { return false } } - var visibility: ListViewItemNodeVisibility = .none { + var visibility: Bool = false { didSet { if self.visibility != oldValue { self.videoNode?.canAttachContent = self.shouldAcquireVideoContext diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift index 5d22185bc2..69fc8c3285 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -86,26 +86,21 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { private var secretTimer: SwiftSignalKit.Timer? var visibilityPromise = ValuePromise(false, ignoreRepeated: true) - var visibility: ListViewItemNodeVisibility = .none { + var visibility: Bool = false { didSet { if let videoNode = self.videoNode { - switch self.visibility { - case .visible: - if !videoNode.canAttachContent { - videoNode.canAttachContent = true - if videoNode.hasAttachedContext { - videoNode.play() - } + if self.visibility { + if !videoNode.canAttachContent { + videoNode.canAttachContent = true + if videoNode.hasAttachedContext { + videoNode.play() } - case .none: - videoNode.canAttachContent = false + } + } else { + videoNode.canAttachContent = false } } - var isVisible = false - if case .visible = self.visibility { - isVisible = true - } - self.visibilityPromise.set(isVisible) + self.visibilityPromise.set(self.visibility) } } @@ -638,7 +633,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { videoNode.updateLayout(size: arguments.drawingSize, transition: .immediate) videoNode.frame = imageFrame - if case .visible = strongSelf.visibility { + if strongSelf.visibility { if !videoNode.canAttachContent { videoNode.canAttachContent = true if videoNode.hasAttachedContext { diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageMediaBubbleContentNode.swift index 8a6d39b051..eef867b54a 100644 --- a/submodules/TelegramUI/TelegramUI/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatMessageMediaBubbleContentNode.swift @@ -22,7 +22,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { override var visibility: ListViewItemNodeVisibility { didSet { - self.interactiveImageNode.visibility = self.visibility + self.interactiveImageNode.visibility = self.visibility != .none } } diff --git a/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift b/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift index f811e225fc..95a094b3b5 100644 --- a/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift +++ b/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift @@ -889,7 +889,7 @@ private func fetchAnimatedStickerRepresentation(account: Account, resource: Medi return Signal({ subscriber in if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { if #available(iOS 9.0, *) { - return experimentalConvertCompressedLottieToCombinedMp4(data: data, size: CGSize(width: 400.0, height: 400.0)).start(next: { path in + return experimentalConvertCompressedLottieToCombinedMp4(data: data, size: CGSize(width: 320.0, height: 320.0)).start(next: { path in subscriber.putNext(CachedMediaResourceRepresentationResult(temporaryPath: path)) subscriber.putCompletion() }) diff --git a/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift b/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift index 8c934c3470..6f6a552ae6 100644 --- a/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift +++ b/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift @@ -105,8 +105,11 @@ class MediaInputPaneTrendingItemNode: ListViewItemNode { override var visibility: ListViewItemNodeVisibility { didSet { - if self.visibility != oldValue { - if case .visible = self.visibility { + let wasVisible = oldValue != .none + let isVisible = self.visibility != .none + + if isVisible != wasVisible { + if isVisible { if let item = self.item, item.unread { self.readDisposable.set(( markFeaturedStickerPacksAsSeenInteractively(postbox: item.account.postbox, ids: [item.info.id]) diff --git a/submodules/TelegramUI/TelegramUI/MetalAnimationRenderer.swift b/submodules/TelegramUI/TelegramUI/MetalAnimationRenderer.swift new file mode 100644 index 0000000000..7251e4eb19 --- /dev/null +++ b/submodules/TelegramUI/TelegramUI/MetalAnimationRenderer.swift @@ -0,0 +1,177 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Metal + +#if !targetEnvironment(simulator) + +final class MetalAnimationRenderer: ASDisplayNode, AnimationRenderer { + private let device: MTLDevice + private let pipelineState: MTLRenderPipelineState + private let commandQueue: MTLCommandQueue + private let vertexBuffer: MTLBuffer + private let colorTexture: MTLTexture + private let alphaTexture: MTLTexture + private let samplerColor: MTLSamplerState + private let samplerAlpha: MTLSamplerState + + private var metalLayer: CAMetalLayer { + return self.layer as! CAMetalLayer + } + + override init() { + let device = MTLCreateSystemDefaultDevice()! + + self.device = device + + do { + let library = try device.makeLibrary(source: + """ +using namespace metal; + +struct VertexIn { + packed_float3 position; + packed_float2 texCoord; +}; + +struct VertexOut { + float4 position [[position]]; + float2 texCoord; +}; + +vertex VertexOut basic_vertex( + const device VertexIn* vertex_array [[ buffer(0) ]], + unsigned int vid [[ vertex_id ]] +) { + VertexIn VertexIn = vertex_array[vid]; + + VertexOut VertexOut; + VertexOut.position = float4(VertexIn.position, 1.0); + VertexOut.texCoord = VertexIn.texCoord; + + return VertexOut; +} + +fragment float4 basic_fragment( + VertexOut interpolated [[stage_in]], + texture2d texColor [[ texture(0) ]], + sampler samplerColor [[ sampler(0) ]], + texture2d texA [[ texture(1) ]], + sampler samplerA [[ sampler(1) ]] +) { + float4 color = texColor.sample(samplerColor, interpolated.texCoord); + float4 alpha = texA.sample(samplerA, interpolated.texCoord); + return float4(color.r * alpha.a, color.g * alpha.a, color.b * alpha.a, alpha.a); +} +""", options: nil) + + let fragmentProgram = library.makeFunction(name: "basic_fragment") + let vertexProgram = library.makeFunction(name: "basic_vertex") + + let pipelineStateDescriptor = MTLRenderPipelineDescriptor() + pipelineStateDescriptor.vertexFunction = vertexProgram + pipelineStateDescriptor.fragmentFunction = fragmentProgram + pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + + self.pipelineState = try! device.makeRenderPipelineState(descriptor: pipelineStateDescriptor) + + self.commandQueue = device.makeCommandQueue()! + + let vertexData: [Float] = [ + -1.0, -1.0, 0.0, 0.0, 1.0, + -1.0, 1.0, 0.0, 0.0, 0.0, + 1.0, -1.0, 0.0, 1.0, 1.0, + 1.0, -1.0, 0.0, 1.0, 1.0, + -1.0, 1.0, 0.0, 0.0, 0.0, + 1.0, 1.0, 0.0, 1.0, 0.0 + ] + + let dataSize = vertexData.count * MemoryLayout.size(ofValue: vertexData[0]) + self.vertexBuffer = device.makeBuffer(bytes: vertexData, length: dataSize, options: [])! + + let colorTextureDesc: MTLTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgrg422, width: 320, height: 320, mipmapped: false) + colorTextureDesc.sampleCount = 1 + if #available(iOS 9.0, *) { + colorTextureDesc.storageMode = .private + colorTextureDesc.usage = .shaderRead + } + colorTextureDesc.textureType = .type2D + + self.colorTexture = device.makeTexture(descriptor: colorTextureDesc)! + + let alphaTextureDesc: MTLTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .a8Unorm, width: 320, height: 320, mipmapped: false) + alphaTextureDesc.sampleCount = 1 + if #available(iOS 9.0, *) { + alphaTextureDesc.storageMode = .private + alphaTextureDesc.usage = .shaderRead + } + alphaTextureDesc.textureType = .type2D + + self.alphaTexture = device.makeTexture(descriptor: alphaTextureDesc)! + + let sampler = MTLSamplerDescriptor() + sampler.minFilter = MTLSamplerMinMagFilter.nearest + sampler.magFilter = MTLSamplerMinMagFilter.nearest + sampler.mipFilter = MTLSamplerMipFilter.nearest + sampler.maxAnisotropy = 1 + sampler.sAddressMode = MTLSamplerAddressMode.clampToEdge + sampler.tAddressMode = MTLSamplerAddressMode.clampToEdge + sampler.rAddressMode = MTLSamplerAddressMode.clampToEdge + sampler.normalizedCoordinates = true + sampler.lodMinClamp = 0.0 + sampler.lodMaxClamp = .greatestFiniteMagnitude + self.samplerColor = device.makeSamplerState(descriptor: sampler)! + self.samplerAlpha = device.makeSamplerState(descriptor: sampler)! + } catch let e { + print(e) + preconditionFailure() + } + + super.init() + + self.setLayerBlock { () -> CALayer in + return CAMetalLayer() + } + + self.metalLayer.device = self.device + self.metalLayer.pixelFormat = .bgra8Unorm + self.metalLayer.framebufferOnly = true + self.metalLayer.isOpaque = false + self.metalLayer.contentsScale = 2.0 + } + + func render(width: Int, height: Int, bytes: UnsafeRawPointer, length: Int) { + if self.metalLayer.bounds.width.isZero { + return + } + + let bgrgLength = width * 2 * height + let alphaLength = width * height + + self.colorTexture.replace(region: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0, withBytes: bytes.assumingMemoryBound(to: UInt8.self), bytesPerRow: width * 2) + self.alphaTexture.replace(region: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0, withBytes: bytes.assumingMemoryBound(to: UInt8.self).advanced(by: bgrgLength), bytesPerRow: width) + + let renderPassDescriptor = MTLRenderPassDescriptor() + let drawable = self.metalLayer.nextDrawable()! + renderPassDescriptor.colorAttachments[0].texture = drawable.texture + renderPassDescriptor.colorAttachments[0].loadAction = .clear + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0) + + let commandBuffer = commandQueue.makeCommandBuffer()! + + let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)! + renderEncoder.setRenderPipelineState(self.pipelineState) + renderEncoder.setVertexBuffer(self.vertexBuffer, offset: 0, index: 0) + renderEncoder.setFragmentTexture(self.colorTexture, index: 0) + renderEncoder.setFragmentSamplerState(self.samplerColor, index: 0) + renderEncoder.setFragmentTexture(self.alphaTexture, index: 1) + renderEncoder.setFragmentSamplerState(self.samplerAlpha, index: 1) + renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6, instanceCount: 1) + renderEncoder.endEncoding() + + commandBuffer.present(drawable) + commandBuffer.commit() + } +} + +#endif diff --git a/submodules/TelegramUI/TelegramUI/SoftwareAnimationRenderer.swift b/submodules/TelegramUI/TelegramUI/SoftwareAnimationRenderer.swift new file mode 100644 index 0000000000..31104db17e --- /dev/null +++ b/submodules/TelegramUI/TelegramUI/SoftwareAnimationRenderer.swift @@ -0,0 +1,19 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import TelegramUIPrivateModule + +final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer { + func render(width: Int, height: Int, bytes: UnsafeRawPointer, length: Int) { + let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData in + if true { + memcpy(pixelData, bytes, length) + } else { + encodeBRGR422AToRGBA(bytes.assumingMemoryBound(to: UInt8.self), bytes.assumingMemoryBound(to: UInt8.self).advanced(by: width * 2 * height), pixelData, Int32(width), Int32(height)) + } + }) + + self.contents = image?.cgImage + } +} diff --git a/submodules/TelegramUI/TelegramUI/TelegramUIPrivate/module.modulemap b/submodules/TelegramUI/TelegramUI/TelegramUIPrivate/module.modulemap index 077a2f392e..959651e6e7 100644 --- a/submodules/TelegramUI/TelegramUI/TelegramUIPrivate/module.modulemap +++ b/submodules/TelegramUI/TelegramUI/TelegramUIPrivate/module.modulemap @@ -22,4 +22,5 @@ module TelegramUIPrivateModule { header "../TGPresentationAutoNightPreferences.h" header "../TGProxyItem.h" header "../UIImage+ImageEffects.h" + header "../YUV.h" } diff --git a/submodules/TelegramUI/TelegramUI/YUV.h b/submodules/TelegramUI/TelegramUI/YUV.h new file mode 100644 index 0000000000..a13fe86332 --- /dev/null +++ b/submodules/TelegramUI/TelegramUI/YUV.h @@ -0,0 +1,7 @@ +#import + +void encodeRGBAToBRGR422A(uint8_t * _Nonnull bgrg422, uint8_t * _Nonnull a, uint8_t const * _Nonnull argb, int width, int height); +void encodeBRGR422AToRGBA(uint8_t const * _Nonnull bgrg422, uint8_t const * _Nonnull a, uint8_t * _Nonnull argb, int width, int height); + +NSData * _Nonnull encodeSparseBuffer(uint8_t const * _Nonnull bytes, int length); +void decodeSparseeBuffer(uint8_t * _Nonnull bytes, uint8_t const * _Nonnull buffer); diff --git a/submodules/TelegramUI/TelegramUI/YUV.m b/submodules/TelegramUI/TelegramUI/YUV.m new file mode 100644 index 0000000000..43277cabd0 --- /dev/null +++ b/submodules/TelegramUI/TelegramUI/YUV.m @@ -0,0 +1,67 @@ +#import "YUV.h" + +void encodeRGBAToBRGR422A(uint8_t *bgrg422, uint8_t *a, uint8_t const *argb, int width, int height) { + int i, j; + int lineWidth = width * 2; + for (j = 0; j < height; j++) { + for (i = 0; i < width; i += 2) { + int A1 = argb[(j * width + i) * 4 + 0]; + int R1 = argb[(j * width + i) * 4 + 3]; + int G1 = argb[(j * width + i) * 4 + 2]; + int B1 = argb[(j * width + i) * 4 + 1]; + + int A2 = argb[(j * width + i) * 4 + 4]; + int R2 = argb[(j * width + i) * 4 + 7]; + int G2 = argb[(j * width + i) * 4 + 6]; + int B2 = argb[(j * width + i) * 4 + 5]; + + bgrg422[j * lineWidth + (i / 2) * 4 + 0] = (uint8_t)((B1 + B2) >> 1); + bgrg422[j * lineWidth + (i / 2) * 4 + 1] = G1; + bgrg422[j * lineWidth + (i / 2) * 4 + 2] = (uint8_t)((R1 + R2) >> 1); + bgrg422[j * lineWidth + (i / 2) * 4 + 3] = G2; + + a[j * width + i + 0] = A1; + a[j * width + i + 1] = A2; + } + } +} + +void encodeBRGR422AToRGBA(uint8_t const * _Nonnull bgrg422, uint8_t const * _Nonnull const a, uint8_t * _Nonnull argb, int width, int height) { + int i, j; + int lineWidth = width * 2; + for (j = 0; j < height; j++) { + for (i = 0; i < width; i += 2) { + argb[(j * width + i) * 4 + 0] = a[j * width + i + 0]; + argb[(j * width + i) * 4 + 3] = bgrg422[j * lineWidth + (i / 2) * 4 + 2]; + argb[(j * width + i) * 4 + 2] = bgrg422[j * lineWidth + (i / 2) * 4 + 1]; + argb[(j * width + i) * 4 + 1] = bgrg422[j * lineWidth + (i / 2) * 4 + 0]; + + argb[(j * width + i) * 4 + 4] = a[j * width + i + 1]; + argb[(j * width + i) * 4 + 7] = bgrg422[j * lineWidth + (i / 2) * 4 + 2]; + argb[(j * width + i) * 4 + 6] = bgrg422[j * lineWidth + (i / 2) * 4 + 3]; + argb[(j * width + i) * 4 + 5] = bgrg422[j * lineWidth + (i / 2) * 4 + 0]; + } + } +} + +NSData * _Nonnull encodeSparseBuffer(uint8_t const * _Nonnull bytes, int length) { + NSMutableData *result = [[NSMutableData alloc] init]; + int offset = 0; + int currentStart = 0; + int currentType = 0; + while (offset != length) { + if (bytes[offset] == 0) { + if (currentType != 0) { + + } + } else { + + } + offset += 1; + } + return result; +} + +void decodeSparseeBuffer(uint8_t * _Nonnull bytes, uint8_t const * _Nonnull buffer) { + +} diff --git a/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj b/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj index ab9fc61672..b6758ebd22 100644 --- a/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj +++ b/submodules/TelegramUI/TelegramUI_Xcode.xcodeproj/project.pbxproj @@ -260,6 +260,11 @@ D0147BA7206E8B4F00E40378 /* SecureIdAuthAcceptNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0147BA6206E8B4F00E40378 /* SecureIdAuthAcceptNode.swift */; }; D0147BA9206EA35000E40378 /* SecureIdDocumentGalleryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0147BA8206EA35000E40378 /* SecureIdDocumentGalleryController.swift */; }; D0147BAB206EA6C100E40378 /* SecureIdDocumentImageGalleryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0147BAA206EA6C100E40378 /* SecureIdDocumentImageGalleryItem.swift */; }; + D01590A622BD460C0017C33E /* MetalAnimationRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01590A522BD460C0017C33E /* MetalAnimationRenderer.swift */; }; + D01590A822BD462C0017C33E /* SoftwareAnimationRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01590A722BD462C0017C33E /* SoftwareAnimationRenderer.swift */; }; + D01590AB22BD467B0017C33E /* AnimationRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01590AA22BD467B0017C33E /* AnimationRenderer.swift */; }; + D01590AE22BD58AD0017C33E /* YUV.h in Headers */ = {isa = PBXBuildFile; fileRef = D01590AC22BD58AD0017C33E /* YUV.h */; }; + D01590AF22BD58AD0017C33E /* YUV.m in Sources */ = {isa = PBXBuildFile; fileRef = D01590AD22BD58AD0017C33E /* YUV.m */; }; D015E04F225D2E5900CB9E8A /* WebP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D015E04E225D2E5900CB9E8A /* WebP.framework */; }; D017734C22049BF800DA06A7 /* UpgradedAccounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D017734B22049BF800DA06A7 /* UpgradedAccounts.swift */; }; D01776B31F1D69A80044446D /* RadialStatusNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01776B21F1D69A80044446D /* RadialStatusNode.swift */; }; @@ -1456,6 +1461,11 @@ D0147BA6206E8B4F00E40378 /* SecureIdAuthAcceptNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdAuthAcceptNode.swift; sourceTree = ""; }; D0147BA8206EA35000E40378 /* SecureIdDocumentGalleryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdDocumentGalleryController.swift; sourceTree = ""; }; D0147BAA206EA6C100E40378 /* SecureIdDocumentImageGalleryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdDocumentImageGalleryItem.swift; sourceTree = ""; }; + D01590A522BD460C0017C33E /* MetalAnimationRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalAnimationRenderer.swift; sourceTree = ""; }; + D01590A722BD462C0017C33E /* SoftwareAnimationRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftwareAnimationRenderer.swift; sourceTree = ""; }; + D01590AA22BD467B0017C33E /* AnimationRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationRenderer.swift; sourceTree = ""; }; + D01590AC22BD58AD0017C33E /* YUV.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YUV.h; sourceTree = ""; }; + D01590AD22BD58AD0017C33E /* YUV.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YUV.m; sourceTree = ""; }; D015E04E225D2E5900CB9E8A /* WebP.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WebP.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D017494D1E1059570057C89A /* StringWithAppliedEntities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringWithAppliedEntities.swift; sourceTree = ""; }; D01749501E1067E40057C89A /* HashtagSearchController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HashtagSearchController.swift; sourceTree = ""; }; @@ -2788,6 +2798,18 @@ name = "Instant Page Gallery"; sourceTree = ""; }; + D01590A922BD46690017C33E /* Animation */ = { + isa = PBXGroup; + children = ( + D01590AA22BD467B0017C33E /* AnimationRenderer.swift */, + D01590A522BD460C0017C33E /* MetalAnimationRenderer.swift */, + D01590A722BD462C0017C33E /* SoftwareAnimationRenderer.swift */, + D01590AC22BD58AD0017C33E /* YUV.h */, + D01590AD22BD58AD0017C33E /* YUV.m */, + ); + name = Animation; + sourceTree = ""; + }; D017494F1E1067C00057C89A /* Hashtag Search */ = { isa = PBXGroup; children = ( @@ -4494,6 +4516,7 @@ D0F69E181D6B8AD10046BCD6 /* Items */ = { isa = PBXGroup; children = ( + D01590A922BD46690017C33E /* Animation */, D0F69E1B1D6B8B030046BCD6 /* ChatMessageActionItemNode.swift */, D0F69E1C1D6B8B030046BCD6 /* ChatMessageAvatarAccessoryItem.swift */, D0F69E1D1D6B8B030046BCD6 /* ChatMessageBubbleContentCalclulateImageCorners.swift */, @@ -4867,6 +4890,7 @@ D08803C51F6064CF00DD7951 /* TelegramUI.h in Headers */, D0E9BA171F05574500F079A4 /* STPPaymentCardTextFieldViewModel.h in Headers */, D0EB42001F30ED4F00838FE6 /* LegacyImageProcessors.h in Headers */, + D01590AE22BD58AD0017C33E /* YUV.h in Headers */, D008177B22B46B7E008A895F /* TGContactModel.h in Headers */, D0E9BA291F0557A600F079A4 /* STPFormEncodable.h in Headers */, D0E9BA141F05574500F079A4 /* STPCardValidationState.h in Headers */, @@ -5314,6 +5338,7 @@ 09C9EA3821A044B500E90146 /* StringForDuration.swift in Sources */, D0EC6D241EB9F58800EBF1C3 /* CachedResourceRepresentations.swift in Sources */, 09619B8E21A34C0100493558 /* InstantPageScrollableNode.swift in Sources */, + D01590A622BD460C0017C33E /* MetalAnimationRenderer.swift in Sources */, D01BAA201ECC9A2500295217 /* CallListNodeLocation.swift in Sources */, D0EC6D251EB9F58800EBF1C3 /* FetchCachedRepresentations.swift in Sources */, D0EC6D261EB9F58800EBF1C3 /* TransformOutgoingMessageMedia.swift in Sources */, @@ -5509,6 +5534,7 @@ D0EC6D811EB9F58800EBF1C3 /* ChatController.swift in Sources */, D0FFF7F81F55B83600BEBC01 /* InstantPageAudioNode.swift in Sources */, D0B37C5E1F8D26A8004252DF /* ThemeSettingsChatPreviewItem.swift in Sources */, + D01590A822BD462C0017C33E /* SoftwareAnimationRenderer.swift in Sources */, D093D7DB2062CFF500BC3599 /* SecureIdAuthFormContentNode.swift in Sources */, D0EC6D821EB9F58800EBF1C3 /* ChatControllerInteraction.swift in Sources */, D0EC6D831EB9F58800EBF1C3 /* ChatControllerNode.swift in Sources */, @@ -5542,6 +5568,7 @@ D0AEAE252080D6830013176E /* PaneSearchContainerNode.swift in Sources */, D01DBA9B209CC6AD00C64E64 /* ChatLinkPreview.swift in Sources */, D044A0FB20BDC40C00326FAC /* CachedChannelAdmins.swift in Sources */, + D01590AF22BD58AD0017C33E /* YUV.m in Sources */, D0EC6D901EB9F58900EBF1C3 /* ChatMessageBubbleContentNode.swift in Sources */, 09874E582107A4C300E190B8 /* VimeoEmbedImplementation.swift in Sources */, D0EC6D911EB9F58900EBF1C3 /* ChatMessageBubbleItemNode.swift in Sources */, @@ -5986,6 +6013,7 @@ D0AB262F21C3D3DE008F6685 /* CreatePollController.swift in Sources */, D0EC6E581EB9F58900EBF1C3 /* PeerSelectionController.swift in Sources */, D093D7D92062A9CA00BC3599 /* SecureIdAuthControllerState.swift in Sources */, + D01590AB22BD467B0017C33E /* AnimationRenderer.swift in Sources */, D0EC6E591EB9F58900EBF1C3 /* PeerSelectionControllerNode.swift in Sources */, D0EC6E5B1EB9F58900EBF1C3 /* CallController.swift in Sources */, D0AB262921C307D7008F6685 /* ChatMessagePollBubbleContentNode.swift in Sources */, @@ -6180,6 +6208,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( @@ -6377,6 +6406,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( @@ -6619,6 +6649,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( @@ -6736,6 +6767,7 @@ APPLICATION_EXTENSION_API_ONLY = NO; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CODE_SIGN_STYLE = Manual; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( @@ -6814,6 +6846,7 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_INSTALL_OBJC_HEADER = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include"; }; @@ -6870,6 +6903,7 @@ APPLICATION_EXTENSION_API_ONLY = NO; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; CODE_SIGN_STYLE = Manual; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = (