Experimental animated stickers

This commit is contained in:
Peter 2019-06-22 00:05:01 +02:00
parent 05425b680a
commit 550be6b560
21 changed files with 685 additions and 178 deletions

View File

@ -4,12 +4,11 @@ import UIKit
let deviceColorSpace = CGColorSpaceCreateDeviceRGB()
let deviceScale = UIScreen.main.scale
public func generateImagePixel(_ size: CGSize, pixelGenerator: (CGSize, UnsafeMutablePointer<Int8>) -> Void) -> UIImage? {
let scale = deviceScale
public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer<UInt8>) -> 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 {

View File

@ -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;
}

View File

@ -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()

View File

@ -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)
}
}

View File

@ -113,49 +113,138 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize)
let singleContext = DrawingContext(size: size, scale: 1.0, clear: true)
let fps: Int32 = model.framerate?.int32Value ?? 30
let frameDuration = CMTimeMake(1, fps)
var fps: Int32 = model.framerate?.int32Value ?? 30
let _ = fileContext.write(&fps, count: 4)
var frameData = Data(count: singleContext.length)
let frameDataCount = frameData.count
if true {
let frameLength = singleContext.length
assert(frameLength % 16 == 0)
let scratchData = malloc(compression_encode_scratch_buffer_size(COMPRESSION_LZ4))!
defer {
free(scratchData)
}
let previousFrameData = malloc(frameLength)!
memset(previousFrameData, 0, frameLength)
while startFrame + currentFrame < endFrame {
let lastFrameTime = CMTimeMake(Int64(currentFrame - startFrame), fps)
let presentationTime = currentFrame == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration)
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 appendStartTime = CACurrentMediaTime()
frameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> 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
}
var compressedFrameData = Data(count: frameLength)
let compressedFrameDataLength = compressedFrameData.count
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)")
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<UInt8>) -> 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<UInt8>) -> 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)")
}
}
}
}

View File

@ -0,0 +1,6 @@
import Foundation
import AsyncDisplayKit
protocol AnimationRenderer {
func render(width: Int, height: Int, bytes: UnsafeRawPointer, length: Int)
}

View File

@ -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 {

View File

@ -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<UInt8>) -> Void in
var frameLength: Int32 = 0
memcpy(&frameLength, bytes.advanced(by: offset), 4)
scratchBuffer.withUnsafeMutableBytes { (scratchBytes: UnsafeMutablePointer<UInt8>) -> 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<UInt8>) -> 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<UInt8>) -> 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<UInt8>) -> 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<UInt8>) -> Void in
decodeBuffer.withUnsafeMutableBytes { (decodeBytes: UnsafeMutablePointer<UInt8>) -> Void in
frameBuffer.withUnsafeMutableBytes { (frameBytes: UnsafeMutablePointer<UInt8>) -> 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<UInt8>) -> 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<UInt8>) -> 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<UInt8>) -> Void in
decodeBuffer.withUnsafeMutableBytes { (decodeBytes: UnsafeMutablePointer<UInt8>) -> Void in
frameBuffer.withUnsafeMutableBytes { (frameBytes: UnsafeMutablePointer<UInt8>) -> 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<Bool>(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 {

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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

View File

@ -86,26 +86,21 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
private var secretTimer: SwiftSignalKit.Timer?
var visibilityPromise = ValuePromise<Bool>(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 {

View File

@ -22,7 +22,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
override var visibility: ListViewItemNodeVisibility {
didSet {
self.interactiveImageNode.visibility = self.visibility
self.interactiveImageNode.visibility = self.visibility != .none
}
}

View File

@ -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()
})

View File

@ -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])

View File

@ -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<float> texColor [[ texture(0) ]],
sampler samplerColor [[ sampler(0) ]],
texture2d<float> 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

View File

@ -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
}
}

View File

@ -22,4 +22,5 @@ module TelegramUIPrivateModule {
header "../TGPresentationAutoNightPreferences.h"
header "../TGProxyItem.h"
header "../UIImage+ImageEffects.h"
header "../YUV.h"
}

View File

@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
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);

View File

@ -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) {
}

View File

@ -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 = "<group>"; };
D0147BA8206EA35000E40378 /* SecureIdDocumentGalleryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdDocumentGalleryController.swift; sourceTree = "<group>"; };
D0147BAA206EA6C100E40378 /* SecureIdDocumentImageGalleryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureIdDocumentImageGalleryItem.swift; sourceTree = "<group>"; };
D01590A522BD460C0017C33E /* MetalAnimationRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalAnimationRenderer.swift; sourceTree = "<group>"; };
D01590A722BD462C0017C33E /* SoftwareAnimationRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftwareAnimationRenderer.swift; sourceTree = "<group>"; };
D01590AA22BD467B0017C33E /* AnimationRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationRenderer.swift; sourceTree = "<group>"; };
D01590AC22BD58AD0017C33E /* YUV.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YUV.h; sourceTree = "<group>"; };
D01590AD22BD58AD0017C33E /* YUV.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YUV.m; sourceTree = "<group>"; };
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 = "<group>"; };
D01749501E1067E40057C89A /* HashtagSearchController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HashtagSearchController.swift; sourceTree = "<group>"; };
@ -2788,6 +2798,18 @@
name = "Instant Page Gallery";
sourceTree = "<group>";
};
D01590A922BD46690017C33E /* Animation */ = {
isa = PBXGroup;
children = (
D01590AA22BD467B0017C33E /* AnimationRenderer.swift */,
D01590A522BD460C0017C33E /* MetalAnimationRenderer.swift */,
D01590A722BD462C0017C33E /* SoftwareAnimationRenderer.swift */,
D01590AC22BD58AD0017C33E /* YUV.h */,
D01590AD22BD58AD0017C33E /* YUV.m */,
);
name = Animation;
sourceTree = "<group>";
};
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 = (