mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-05 02:50:42 +00:00
Support non-square animated stickers
This commit is contained in:
parent
6e70be4ec5
commit
b1bb7a630c
@ -79,11 +79,14 @@
|
|||||||
buildConfiguration = "DebugHockeyapp"
|
buildConfiguration = "DebugHockeyapp"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
enableAddressSanitizer = "YES"
|
||||||
enableASanStackUseAfterReturn = "YES"
|
enableASanStackUseAfterReturn = "YES"
|
||||||
|
enableUBSanitizer = "YES"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
|
stopOnEveryUBSanitizerIssue = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
allowLocationSimulation = "YES"
|
allowLocationSimulation = "YES"
|
||||||
queueDebuggingEnabled = "No">
|
queueDebuggingEnabled = "No">
|
||||||
|
@ -15,7 +15,7 @@ private let deviceColorSpace: CGColorSpace = {
|
|||||||
|
|
||||||
let deviceScale = UIScreen.main.scale
|
let deviceScale = UIScreen.main.scale
|
||||||
|
|
||||||
public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer<UInt8>) -> Void) -> UIImage? {
|
public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer<UInt8>, Int) -> Void) -> UIImage? {
|
||||||
let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)
|
let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)
|
||||||
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
|
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
|
||||||
let length = bytesPerRow * Int(scaledSize.height)
|
let length = bytesPerRow * Int(scaledSize.height)
|
||||||
@ -27,7 +27,7 @@ public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pixelGenerator(scaledSize, bytes)
|
pixelGenerator(scaledSize, bytes, bytesPerRow)
|
||||||
|
|
||||||
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
|
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ final class MessageHistoryMetadataTable: Table {
|
|||||||
|
|
||||||
private func chatListGroupInitializedKey(_ key: InitializedChatListKey) -> ValueBoxKey {
|
private func chatListGroupInitializedKey(_ key: InitializedChatListKey) -> ValueBoxKey {
|
||||||
self.sharedChatListGroupHistoryInitializedKey.setInt32(0, value: key.groupId.rawValue)
|
self.sharedChatListGroupHistoryInitializedKey.setInt32(0, value: key.groupId.rawValue)
|
||||||
self.sharedChatListGroupHistoryInitializedKey.setInt8(8, value: MetadataPrefix.ChatListGroupInitialized.rawValue)
|
self.sharedChatListGroupHistoryInitializedKey.setInt8(4, value: MetadataPrefix.ChatListGroupInitialized.rawValue)
|
||||||
return self.sharedChatListGroupHistoryInitializedKey
|
return self.sharedChatListGroupHistoryInitializedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
@property (nonatomic, readonly) CGSize dimensions;
|
@property (nonatomic, readonly) CGSize dimensions;
|
||||||
|
|
||||||
- (instancetype _Nullable)initWithData:(NSData * _Nonnull)data cacheKey:(NSString * _Nonnull)cacheKey;
|
- (instancetype _Nullable)initWithData:(NSData * _Nonnull)data cacheKey:(NSString * _Nonnull)cacheKey;
|
||||||
- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height;
|
- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height bytesPerRow:(int32_t)bytesPerRow;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height {
|
- (void)renderFrameWithIndex:(int32_t)index into:(uint8_t * _Nonnull)buffer width:(int32_t)width height:(int32_t)height bytesPerRow:(int32_t) bytesPerRow{
|
||||||
rlottie::Surface surface((uint32_t *)buffer, width, height, width * 4);
|
rlottie::Surface surface((uint32_t *)buffer, width, height, bytesPerRow);
|
||||||
_animation->renderSync(index, surface);
|
_animation->renderSync(index, surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,13 +82,15 @@ private final class AnimatedStickerFrame {
|
|||||||
let type: AnimationRendererFrameType
|
let type: AnimationRendererFrameType
|
||||||
let width: Int
|
let width: Int
|
||||||
let height: Int
|
let height: Int
|
||||||
|
let bytesPerRow: Int
|
||||||
let isLastFrame: Bool
|
let isLastFrame: Bool
|
||||||
|
|
||||||
init(data: Data, type: AnimationRendererFrameType, width: Int, height: Int, isLastFrame: Bool) {
|
init(data: Data, type: AnimationRendererFrameType, width: Int, height: Int, bytesPerRow: Int, isLastFrame: Bool) {
|
||||||
self.data = data
|
self.data = data
|
||||||
self.type = type
|
self.type = type
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
self.bytesPerRow = bytesPerRow
|
||||||
self.isLastFrame = isLastFrame
|
self.isLastFrame = isLastFrame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,6 +107,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
|||||||
private let data: Data
|
private let data: Data
|
||||||
private var scratchBuffer: Data
|
private var scratchBuffer: Data
|
||||||
let width: Int
|
let width: Int
|
||||||
|
let bytesPerRow: Int
|
||||||
let height: Int
|
let height: Int
|
||||||
let frameRate: Int
|
let frameRate: Int
|
||||||
private let initialOffset: Int
|
private let initialOffset: Int
|
||||||
@ -120,6 +123,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
|||||||
var offset = 0
|
var offset = 0
|
||||||
var width = 0
|
var width = 0
|
||||||
var height = 0
|
var height = 0
|
||||||
|
var bytesPerRow = 0
|
||||||
var frameRate = 0
|
var frameRate = 0
|
||||||
|
|
||||||
if !self.data.withUnsafeBytes({ (bytes: UnsafePointer<UInt8>) -> Bool in
|
if !self.data.withUnsafeBytes({ (bytes: UnsafePointer<UInt8>) -> Bool in
|
||||||
@ -129,19 +133,23 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
|||||||
offset += 4
|
offset += 4
|
||||||
var widthValue: Int32 = 0
|
var widthValue: Int32 = 0
|
||||||
var heightValue: Int32 = 0
|
var heightValue: Int32 = 0
|
||||||
|
var bytesPerRowValue: Int32 = 0
|
||||||
memcpy(&widthValue, bytes.advanced(by: offset), 4)
|
memcpy(&widthValue, bytes.advanced(by: offset), 4)
|
||||||
offset += 4
|
offset += 4
|
||||||
memcpy(&heightValue, bytes.advanced(by: offset), 4)
|
memcpy(&heightValue, bytes.advanced(by: offset), 4)
|
||||||
offset += 4
|
offset += 4
|
||||||
|
memcpy(&bytesPerRowValue, bytes.advanced(by: offset), 4)
|
||||||
|
offset += 4
|
||||||
width = Int(widthValue)
|
width = Int(widthValue)
|
||||||
height = Int(heightValue)
|
height = Int(heightValue)
|
||||||
|
bytesPerRow = Int(bytesPerRowValue)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}) {
|
}) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(width % 16 == 0)
|
self.bytesPerRow = bytesPerRow
|
||||||
|
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
@ -150,8 +158,8 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
|||||||
self.initialOffset = offset
|
self.initialOffset = offset
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
|
|
||||||
self.decodeBuffer = Data(count: width * 4 * height)
|
self.decodeBuffer = Data(count: self.bytesPerRow * height)
|
||||||
self.frameBuffer = Data(count: width * 4 * height)
|
self.frameBuffer = Data(count: self.bytesPerRow * height)
|
||||||
let frameBufferLength = self.frameBuffer.count
|
let frameBufferLength = self.frameBuffer.count
|
||||||
self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||||
memset(bytes, 0, frameBufferLength)
|
memset(bytes, 0, frameBufferLength)
|
||||||
@ -203,7 +211,7 @@ private final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AnimatedStickerFrame(data: frameData!, type: .yuva, width: self.width, height: self.height, isLastFrame: isLastFrame)
|
return AnimatedStickerFrame(data: frameData!, type: .yuva, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, isLastFrame: isLastFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +220,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
|||||||
private let data: Data
|
private let data: Data
|
||||||
private let width: Int
|
private let width: Int
|
||||||
private let height: Int
|
private let height: Int
|
||||||
|
private let bytesPerRow: Int
|
||||||
private let frameCount: Int
|
private let frameCount: Int
|
||||||
let frameRate: Int
|
let frameRate: Int
|
||||||
private var currentFrame: Int
|
private var currentFrame: Int
|
||||||
@ -222,6 +231,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
|||||||
self.data = data
|
self.data = data
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
self.bytesPerRow = (4 * Int(width) + 15) & (~15)
|
||||||
self.currentFrame = 0
|
self.currentFrame = 0
|
||||||
guard let rawData = TGGUnzipData(data, 8 * 1024 * 1024) else {
|
guard let rawData = TGGUnzipData(data, 8 * 1024 * 1024) else {
|
||||||
return nil
|
return nil
|
||||||
@ -232,7 +242,6 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
|||||||
self.animation = animation
|
self.animation = animation
|
||||||
self.frameCount = Int(animation.frameCount)
|
self.frameCount = Int(animation.frameCount)
|
||||||
self.frameRate = Int(animation.frameRate)
|
self.frameRate = Int(animation.frameRate)
|
||||||
assert(width % 16 == 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -242,12 +251,12 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
|||||||
func takeFrame() -> AnimatedStickerFrame {
|
func takeFrame() -> AnimatedStickerFrame {
|
||||||
let frameIndex = self.currentFrame % self.frameCount
|
let frameIndex = self.currentFrame % self.frameCount
|
||||||
self.currentFrame += 1
|
self.currentFrame += 1
|
||||||
var frameData = Data(count: self.width * self.height * 4)
|
var frameData = Data(count: self.bytesPerRow * self.height)
|
||||||
frameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
frameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
|
||||||
memset(bytes, 0, self.width * self.height * 4)
|
memset(bytes, 0, self.bytesPerRow * self.height)
|
||||||
self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height))
|
self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height), bytesPerRow: Int32(self.bytesPerRow))
|
||||||
}
|
}
|
||||||
return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, isLastFrame: frameIndex == self.frameCount)
|
return AnimatedStickerFrame(data: frameData, type: .argb, width: self.width, height: self.height, bytesPerRow: self.bytesPerRow, isLastFrame: frameIndex == self.frameCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,6 +363,9 @@ final class AnimatedStickerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setup(account: Account, resource: MediaResource, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) {
|
func setup(account: Account, resource: MediaResource, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) {
|
||||||
|
if width < 2 || height < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.playbackMode = playbackMode
|
self.playbackMode = playbackMode
|
||||||
switch mode {
|
switch mode {
|
||||||
case .direct:
|
case .direct:
|
||||||
@ -430,7 +442,7 @@ final class AnimatedStickerNode: ASDisplayNode {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, data: frame.data, type: frame.type, completion: {
|
strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, completion: {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,12 @@ func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, cacheKey: St
|
|||||||
}
|
}
|
||||||
|
|
||||||
let context = DrawingContext(size: size, scale: 1.0, clear: true)
|
let context = DrawingContext(size: size, scale: 1.0, clear: true)
|
||||||
player.renderFrame(with: 0, into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height))
|
player.renderFrame(with: 0, into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(context.bytesPerRow))
|
||||||
|
|
||||||
let yuvaLength = Int(size.width) * Int(size.height) * 2 + Int(size.width) * Int(size.height) / 2
|
let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1)
|
||||||
assert(yuvaLength % 8 == 0)
|
assert(yuvaPixelsPerAlphaRow % 2 == 0)
|
||||||
|
|
||||||
|
let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2
|
||||||
var yuvaFrameData = malloc(yuvaLength)!
|
var yuvaFrameData = malloc(yuvaLength)!
|
||||||
memset(yuvaFrameData, 0, yuvaLength)
|
memset(yuvaFrameData, 0, yuvaLength)
|
||||||
|
|
||||||
@ -94,8 +96,8 @@ func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, cacheKey: St
|
|||||||
free(yuvaFrameData)
|
free(yuvaFrameData)
|
||||||
}
|
}
|
||||||
|
|
||||||
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height))
|
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
|
||||||
decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height))
|
decodeYUVAToRGBA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), context.bytes.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(context.bytesPerRow))
|
||||||
|
|
||||||
if let colorImage = context.generateImage() {
|
if let colorImage = context.generateImage() {
|
||||||
let colorData = NSMutableData()
|
let colorData = NSMutableData()
|
||||||
@ -189,23 +191,29 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize,
|
|||||||
|
|
||||||
let scale = size.width / 512.0
|
let scale = size.width / 512.0
|
||||||
|
|
||||||
|
let bytesPerRow = (4 * Int(size.width) + 15) & (~15)
|
||||||
|
|
||||||
var currentFrame: Int32 = 0
|
var currentFrame: Int32 = 0
|
||||||
|
|
||||||
var fps: Int32 = player.frameRate
|
var fps: Int32 = player.frameRate
|
||||||
let _ = fileContext.write(&fps, count: 4)
|
let _ = fileContext.write(&fps, count: 4)
|
||||||
var widthValue: Int32 = Int32(size.width)
|
var widthValue: Int32 = Int32(size.width)
|
||||||
var heightValue: Int32 = Int32(size.height)
|
var heightValue: Int32 = Int32(size.height)
|
||||||
|
var bytesPerRowValue: Int32 = Int32(bytesPerRow)
|
||||||
let _ = fileContext.write(&widthValue, count: 4)
|
let _ = fileContext.write(&widthValue, count: 4)
|
||||||
let _ = fileContext.write(&heightValue, count: 4)
|
let _ = fileContext.write(&heightValue, count: 4)
|
||||||
|
let _ = fileContext.write(&bytesPerRowValue, count: 4)
|
||||||
|
|
||||||
let frameLength = Int(size.width) * Int(size.height) * 4
|
let frameLength = bytesPerRow * Int(size.height)
|
||||||
assert(frameLength % 16 == 0)
|
assert(frameLength % 16 == 0)
|
||||||
|
|
||||||
let currentFrameData = malloc(frameLength)!
|
let currentFrameData = malloc(frameLength)!
|
||||||
memset(currentFrameData, 0, frameLength)
|
memset(currentFrameData, 0, frameLength)
|
||||||
|
|
||||||
let yuvaLength = Int(size.width) * Int(size.height) * 2 + Int(size.width) * Int(size.height) / 2
|
let yuvaPixelsPerAlphaRow = (Int(size.width) + 1) & (~1)
|
||||||
assert(yuvaLength % 8 == 0)
|
assert(yuvaPixelsPerAlphaRow % 2 == 0)
|
||||||
|
|
||||||
|
let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2
|
||||||
var yuvaFrameData = malloc(yuvaLength)!
|
var yuvaFrameData = malloc(yuvaLength)!
|
||||||
memset(yuvaFrameData, 0, yuvaLength)
|
memset(yuvaFrameData, 0, yuvaLength)
|
||||||
|
|
||||||
@ -234,12 +242,12 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize,
|
|||||||
|
|
||||||
let drawStartTime = CACurrentMediaTime()
|
let drawStartTime = CACurrentMediaTime()
|
||||||
memset(currentFrameData, 0, frameLength)
|
memset(currentFrameData, 0, frameLength)
|
||||||
player.renderFrame(with: Int32(currentFrame), into: currentFrameData.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height))
|
player.renderFrame(with: Int32(currentFrame), into: currentFrameData.assumingMemoryBound(to: UInt8.self), width: Int32(size.width), height: Int32(size.height), bytesPerRow: Int32(bytesPerRow))
|
||||||
drawingTime += CACurrentMediaTime() - drawStartTime
|
drawingTime += CACurrentMediaTime() - drawStartTime
|
||||||
|
|
||||||
let appendStartTime = CACurrentMediaTime()
|
let appendStartTime = CACurrentMediaTime()
|
||||||
|
|
||||||
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), currentFrameData.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height))
|
encodeRGBAToYUVA(yuvaFrameData.assumingMemoryBound(to: UInt8.self), currentFrameData.assumingMemoryBound(to: UInt8.self), Int32(size.width), Int32(size.height), Int32(bytesPerRow))
|
||||||
|
|
||||||
appendingTime += CACurrentMediaTime() - appendStartTime
|
appendingTime += CACurrentMediaTime() - appendStartTime
|
||||||
|
|
||||||
@ -250,6 +258,9 @@ func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize,
|
|||||||
lhs.pointee = rhs.pointee ^ lhs.pointee
|
lhs.pointee = rhs.pointee ^ lhs.pointee
|
||||||
lhs = lhs.advanced(by: 1)
|
lhs = lhs.advanced(by: 1)
|
||||||
rhs = rhs.advanced(by: 1)
|
rhs = rhs.advanced(by: 1)
|
||||||
|
}
|
||||||
|
for i in (yuvaLength / 8) * 8 ..< yuvaLength {
|
||||||
|
|
||||||
}
|
}
|
||||||
deltaTime += CACurrentMediaTime() - deltaStartTime
|
deltaTime += CACurrentMediaTime() - deltaStartTime
|
||||||
|
|
||||||
|
@ -8,5 +8,5 @@ enum AnimationRendererFrameType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protocol AnimationRenderer {
|
protocol AnimationRenderer {
|
||||||
func render(queue: Queue, width: Int, height: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void)
|
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void)
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,7 @@ final class CachedAnimatedStickerRepresentation: CachedMediaResourceRepresentati
|
|||||||
let height: Int32
|
let height: Int32
|
||||||
|
|
||||||
var uniqueId: String {
|
var uniqueId: String {
|
||||||
return "animated-sticker-\(self.width)x\(self.height)-v5"
|
return "animated-sticker-\(self.width)x\(self.height)-v6"
|
||||||
}
|
}
|
||||||
|
|
||||||
init(width: Int32, height: Int32) {
|
init(width: Int32, height: Int32) {
|
||||||
|
@ -224,7 +224,8 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
|||||||
}
|
}
|
||||||
self.addSubnode(animationNode)
|
self.addSubnode(animationNode)
|
||||||
}
|
}
|
||||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.account.postbox, file: item.stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0)))
|
let dimensions = item.stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.account.postbox, file: item.stickerItem.file, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))))
|
||||||
self.updateVisibility()
|
self.updateVisibility()
|
||||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start())
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start())
|
||||||
} else {
|
} else {
|
||||||
@ -293,7 +294,9 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
|||||||
self.animationNode?.visibility = isPlaying
|
self.animationNode?.visibility = isPlaying
|
||||||
if let item = self.item, isPlaying, !self.didSetUpAnimationNode {
|
if let item = self.item, isPlaying, !self.didSetUpAnimationNode {
|
||||||
self.didSetUpAnimationNode = true
|
self.didSetUpAnimationNode = true
|
||||||
self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: 160, height: 160, mode: .cached)
|
let dimensions = item.stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||||
|
self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
if let telegramFile = media as? TelegramMediaFile {
|
if let telegramFile = media as? TelegramMediaFile {
|
||||||
if self.telegramFile?.id != telegramFile.id {
|
if self.telegramFile?.id != telegramFile.id {
|
||||||
self.telegramFile = telegramFile
|
self.telegramFile = telegramFile
|
||||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: CGSize(width: 384.0, height: 384.0), thumbnail: false))
|
let dimensions = telegramFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0)), thumbnail: false))
|
||||||
self.updateVisibility()
|
self.updateVisibility()
|
||||||
self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .message(message: MessageReference(item.message), media: telegramFile)).start())
|
self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .message(message: MessageReference(item.message), media: telegramFile)).start())
|
||||||
}
|
}
|
||||||
@ -172,7 +173,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
if !item.controllerInteraction.stickerSettings.loopAnimatedStickers {
|
if !item.controllerInteraction.stickerSettings.loopAnimatedStickers {
|
||||||
playbackMode = .once
|
playbackMode = .once
|
||||||
}
|
}
|
||||||
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, playbackMode: playbackMode, mode: .cached)
|
let dimensions = telegramFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
let fittedSize = dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0))
|
||||||
|
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: .cached)
|
||||||
} else if let emojiResource = self.emojiResource {
|
} else if let emojiResource = self.emojiResource {
|
||||||
var playbackMode: AnimatedStickerPlaybackMode = .loop
|
var playbackMode: AnimatedStickerPlaybackMode = .loop
|
||||||
if item.context.sharedContext.immediateExperimentalUISettings.playAnimatedEmojiOnce {
|
if item.context.sharedContext.immediateExperimentalUISettings.playAnimatedEmojiOnce {
|
||||||
|
@ -458,8 +458,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if file.isAnimatedSticker {
|
if file.isAnimatedSticker {
|
||||||
|
let dimensions = file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
updateImageSignal = { synchronousLoad in
|
updateImageSignal = { synchronousLoad in
|
||||||
return chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: CGSize(width: 400.0, height: 400.0))
|
return chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: dimensions.aspectFitted(CGSize(width: 400.0, height: 400.0)))
|
||||||
}
|
}
|
||||||
} else if file.isSticker {
|
} else if file.isSticker {
|
||||||
updateImageSignal = { synchronousLoad in
|
updateImageSignal = { synchronousLoad in
|
||||||
@ -685,7 +686,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
strongSelf.imageNode.isHidden = true
|
strongSelf.imageNode.isHidden = true
|
||||||
}
|
}
|
||||||
strongSelf.animatedStickerNode = animatedStickerNode
|
strongSelf.animatedStickerNode = animatedStickerNode
|
||||||
animatedStickerNode.setup(account: context.account, resource: updatedAnimatedStickerFile.resource, width: 384, height: 384, mode: .cached)
|
let dimensions = updatedAnimatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0))
|
||||||
|
animatedStickerNode.setup(account: context.account, resource: updatedAnimatedStickerFile.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||||
strongSelf.insertSubnode(animatedStickerNode, aboveSubnode: strongSelf.imageNode)
|
strongSelf.insertSubnode(animatedStickerNode, aboveSubnode: strongSelf.imageNode)
|
||||||
animatedStickerNode.visibility = strongSelf.visibility
|
animatedStickerNode.visibility = strongSelf.visibility
|
||||||
}
|
}
|
||||||
|
@ -217,7 +217,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
updateImageSignal = chatMessagePhotoThumbnail(account: context.account, photoReference: imageReference)
|
updateImageSignal = chatMessagePhotoThumbnail(account: context.account, photoReference: imageReference)
|
||||||
} else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) {
|
} else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) {
|
||||||
if fileReference.media.isAnimatedSticker {
|
if fileReference.media.isAnimatedSticker {
|
||||||
updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, file: fileReference.media, small: false, size: CGSize(width: 160.0, height: 160.0))
|
let dimensions = fileReference.media.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, file: fileReference.media, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)))
|
||||||
updatedFetchMediaSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: fileReference.resourceReference(fileReference.media.resource))
|
updatedFetchMediaSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: fileReference.resourceReference(fileReference.media.resource))
|
||||||
} else if fileReference.media.isVideo {
|
} else if fileReference.media.isVideo {
|
||||||
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
updateImageSignal = chatMessageVideoThumbnail(account: context.account, fileReference: fileReference)
|
||||||
|
@ -380,7 +380,9 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
|||||||
animationNode.started = { [weak self] in
|
animationNode.started = { [weak self] in
|
||||||
self?.imageNode.alpha = 0.0
|
self?.imageNode.alpha = 0.0
|
||||||
}
|
}
|
||||||
animationNode.setup(account: item.account, resource: animatedStickerFile.resource, width: 160, height: 160, mode: .cached)
|
let dimensions = animatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||||
|
animationNode.setup(account: item.account, resource: animatedStickerFile.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,9 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
|||||||
animationNode.started = { [weak self] in
|
animationNode.started = { [weak self] in
|
||||||
self?.imageNode.alpha = 0.0
|
self?.imageNode.alpha = 0.0
|
||||||
}
|
}
|
||||||
animationNode.setup(account: account, resource: item.file.resource, width: 160, height: 160, mode: .cached)
|
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||||
|
animationNode.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||||
|
|
||||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
|
||||||
} else {
|
} else {
|
||||||
|
@ -113,7 +113,9 @@ final class TrendingTopItemNode: ASDisplayNode {
|
|||||||
animationNode.started = { [weak self] in
|
animationNode.started = { [weak self] in
|
||||||
self?.imageNode.alpha = 0.0
|
self?.imageNode.alpha = 0.0
|
||||||
}
|
}
|
||||||
animationNode.setup(account: account, resource: item.file.resource, width: 160, height: 160, mode: .cached)
|
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||||
|
animationNode.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||||
self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
|
self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
|
||||||
} else {
|
} else {
|
||||||
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads)
|
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads)
|
||||||
|
@ -279,8 +279,10 @@ public final class NotificationViewControllerImpl {
|
|||||||
}
|
}
|
||||||
view?.addSubnode(animatedStickerNode)
|
view?.addSubnode(animatedStickerNode)
|
||||||
}
|
}
|
||||||
strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: CGSize(width: 512.0, height: 512.0)))
|
let dimensions = fileReference.media.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
animatedStickerNode.setup(account: accountAndImage.0, resource: fileReference.media.resource, width: 512, height: 512, mode: .direct)
|
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 512.0, height: 512.0))
|
||||||
|
strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions))
|
||||||
|
animatedStickerNode.setup(account: accountAndImage.0, resource: fileReference.media.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
|
||||||
animatedStickerNode.visibility = true
|
animatedStickerNode.visibility = true
|
||||||
|
|
||||||
accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true))
|
accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true))
|
||||||
|
@ -6,13 +6,16 @@ import SwiftSignalKit
|
|||||||
import TelegramUIPrivateModule
|
import TelegramUIPrivateModule
|
||||||
|
|
||||||
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
|
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
|
||||||
func render(queue: Queue, width: Int, height: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
|
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
|
||||||
queue.async { [weak self] in
|
queue.async { [weak self] in
|
||||||
let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData in
|
let calculatedBytesPerRow = (4 * Int(width) + 15) & (~15)
|
||||||
|
assert(bytesPerRow == calculatedBytesPerRow)
|
||||||
|
|
||||||
|
let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData, bytesPerRow in
|
||||||
switch type {
|
switch type {
|
||||||
case .yuva:
|
case .yuva:
|
||||||
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||||
decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height))
|
decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height), Int32(bytesPerRow))
|
||||||
}
|
}
|
||||||
case .argb:
|
case .argb:
|
||||||
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||||
|
@ -165,7 +165,8 @@ final class StickerPackPreviewController: ViewController {
|
|||||||
let signal = Signal<Bool, NoError> { subscriber in
|
let signal = Signal<Bool, NoError> { subscriber in
|
||||||
let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: FileMediaReference.standalone(media: item.file).resourceReference(item.file.resource)).start()
|
let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: FileMediaReference.standalone(media: item.file).resourceReference(item.file.resource)).start()
|
||||||
let data = account.postbox.mediaBox.resourceData(item.file.resource).start()
|
let data = account.postbox.mediaBox.resourceData(item.file.resource).start()
|
||||||
let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, file: item.file, small: false, size: CGSize(width: 160.0, height: 160.0), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in
|
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
let fetchedRepresentation = chatMessageAnimatedStickerDatas(postbox: account.postbox, file: item.file, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0)), fetched: true, onlyFullSize: false, synchronousLoad: false).start(next: { next in
|
||||||
let hasContent = next._0 != nil || next._1 != nil
|
let hasContent = next._0 != nil || next._1 != nil
|
||||||
subscriber.putNext(hasContent)
|
subscriber.putNext(hasContent)
|
||||||
if hasContent {
|
if hasContent {
|
||||||
|
@ -93,7 +93,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem {
|
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem {
|
||||||
if let dimensions = stickerItem.file.dimensions {
|
if let dimensions = stickerItem.file.dimensions {
|
||||||
if stickerItem.file.isAnimatedSticker {
|
if stickerItem.file.isAnimatedSticker {
|
||||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0)))
|
let dimensions = stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))))
|
||||||
|
|
||||||
if self.animationNode == nil {
|
if self.animationNode == nil {
|
||||||
let animationNode = AnimatedStickerNode()
|
let animationNode = AnimatedStickerNode()
|
||||||
@ -103,7 +104,8 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
self?.imageNode.isHidden = true
|
self?.imageNode.isHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached)
|
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||||
|
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||||
self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true
|
self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true
|
||||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
|
||||||
} else {
|
} else {
|
||||||
|
@ -159,7 +159,9 @@ final class StickerPaneSearchStickerItemNode: GridItemNode {
|
|||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
self.addSubnode(animationNode)
|
self.addSubnode(animationNode)
|
||||||
}
|
}
|
||||||
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached)
|
let dimensions = stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||||
|
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
|
||||||
self.animationNode?.visibility = self.isVisibleInGrid
|
self.animationNode?.visibility = self.isVisibleInGrid
|
||||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
|
||||||
} else {
|
} else {
|
||||||
|
@ -86,7 +86,10 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
|
|||||||
let animationNode = AnimatedStickerNode()
|
let animationNode = AnimatedStickerNode()
|
||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
|
|
||||||
self.animationNode?.setup(account: account, resource: item.file.resource, width: 400, height: 400, mode: .direct)
|
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 400.0, height: 400.0))
|
||||||
|
|
||||||
|
self.animationNode?.setup(account: account, resource: item.file.resource, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
|
||||||
self.animationNode?.visibility = true
|
self.animationNode?.visibility = true
|
||||||
self.animationNode?.addSubnode(self.textNode)
|
self.animationNode?.addSubnode(self.textNode)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height);
|
void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height, int bytesPerRow);
|
||||||
void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height);
|
void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height, int bytesPerRow);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#import "YUV.h"
|
#import "YUV.h"
|
||||||
#import <Accelerate/Accelerate.h>
|
#import <Accelerate/Accelerate.h>
|
||||||
|
|
||||||
void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height) {
|
void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height, int bytesPerRow) {
|
||||||
static vImage_ARGBToYpCbCr info;
|
static vImage_ARGBToYpCbCr info;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
@ -15,7 +15,7 @@ void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height)
|
|||||||
src.data = (void *)argb;
|
src.data = (void *)argb;
|
||||||
src.width = width;
|
src.width = width;
|
||||||
src.height = height;
|
src.height = height;
|
||||||
src.rowBytes = width * 4;
|
src.rowBytes = bytesPerRow;
|
||||||
|
|
||||||
uint8_t permuteMap[4] = {3, 2, 1, 0};
|
uint8_t permuteMap[4] = {3, 2, 1, 0};
|
||||||
error = vImagePermuteChannels_ARGB8888(&src, &src, permuteMap, kvImageDoNotTile);
|
error = vImagePermuteChannels_ARGB8888(&src, &src, permuteMap, kvImageDoNotTile);
|
||||||
@ -48,7 +48,7 @@ void encodeRGBAToYUVA(uint8_t *yuva, uint8_t const *argb, int width, int height)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height) {
|
void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height, int bytesPerRow) {
|
||||||
static vImage_YpCbCrToARGB info;
|
static vImage_YpCbCrToARGB info;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
@ -74,7 +74,7 @@ void decodeYUVAToRGBA(uint8_t const *yuva, uint8_t *argb, int width, int height)
|
|||||||
dest.data = (void *)argb;
|
dest.data = (void *)argb;
|
||||||
dest.width = width;
|
dest.width = width;
|
||||||
dest.height = height;
|
dest.height = height;
|
||||||
dest.rowBytes = width * 4;
|
dest.rowBytes = bytesPerRow;
|
||||||
|
|
||||||
error = vImageConvert_420Yp8_CbCr8ToARGB8888(&srcYp, &srcCbCr, &dest, &info, NULL, 0xff, kvImageDoNotTile);
|
error = vImageConvert_420Yp8_CbCr8ToARGB8888(&srcYp, &srcCbCr, &dest, &info, NULL, 0xff, kvImageDoNotTile);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user