Fix animation cache

This commit is contained in:
Ali 2022-07-30 23:30:14 +02:00
parent adb9767edd
commit 47239681a7
10 changed files with 506 additions and 348 deletions

View File

@ -31,6 +31,8 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/Display:Display", "//submodules/Display:Display",
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache", "//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
"//submodules/TelegramUI/Components/VideoAnimationCache:VideoAnimationCache",
"//submodules/TelegramUI/Components/LottieAnimationCache:LottieAnimationCache",
"//submodules/rlottie:RLottieBinding", "//submodules/rlottie:RLottieBinding",
], ],
) )

Binary file not shown.

View File

@ -2,9 +2,10 @@ import Foundation
import UIKit import UIKit
import Display import Display
import RLottieBinding
import AnimationCache import AnimationCache
import SwiftSignalKit import SwiftSignalKit
import VideoAnimationCache
import LottieAnimationCache
public final class ViewController: UIViewController { public final class ViewController: UIViewController {
private var imageView: UIImageView? private var imageView: UIImageView?
@ -14,6 +15,7 @@ public final class ViewController: UIViewController {
private var animationCacheItem: AnimationCacheItem? private var animationCacheItem: AnimationCacheItem?
//private let playbackSize = CGSize(width: 512, height: 512) //private let playbackSize = CGSize(width: 512, height: 512)
//private let playbackSize = CGSize(width: 256, height: 256)
private let playbackSize = CGSize(width: 48.0, height: 48.0) private let playbackSize = CGSize(width: 48.0, height: 48.0)
//private let playbackSize = CGSize(width: 16, height: 16) //private let playbackSize = CGSize(width: 16, height: 16)
@ -52,26 +54,19 @@ public final class ViewController: UIViewController {
return basePath + "/\(Int64.random(in: 0 ... Int64.max))" return basePath + "/\(Int64.random(in: 0 ... Int64.max))"
}) })
let path = Bundle.main.path(forResource: "Test2", ofType: "json")! let path = Bundle.main.path(forResource: "sticker", ofType: "webm")!
let data = try! Data(contentsOf: URL(fileURLWithPath: path))
let scaledSize = CGSize(width: self.playbackSize.width * 2.0, height: self.playbackSize.height * 2.0) let scaledSize = CGSize(width: self.playbackSize.width * 2.0, height: self.playbackSize.height * 2.0)
let _ = (self.cache!.get(sourceId: "Item\(Int64.random(in: 0 ... Int64.max))", size: scaledSize, fetch: { size, writer in let _ = (self.cache!.get(sourceId: "Item\(Int64.random(in: 0 ... Int64.max))", size: scaledSize, fetch: { options in
writer.queue.async { options.writer.queue.async {
let lottieInstance = LottieInstance(data: data, fitzModifier: .none, colorReplacements: nil, cacheKey: "")! if path.hasSuffix(".webm") {
cacheVideoAnimation(path: path, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly)
for i in 0 ..< min(600, Int(lottieInstance.frameCount)) { } else {
//for _ in 0 ..< 10 { let data = try! Data(contentsOf: URL(fileURLWithPath: path))
writer.add(with: { surface in cacheLottieAnimation(data: data, width: Int(options.size.width), height: Int(options.size.height), keyframeOnly: false, writer: options.writer, firstFrameOnly: options.firstFrameOnly)
let _ = i
lottieInstance.renderFrame(with: Int32(i), into: surface.argb, width: Int32(surface.width), height: Int32(surface.height), bytesPerRow: Int32(surface.bytesPerRow))
return 1.0 / 60.0
}, proposedWidth: Int(scaledSize.width), proposedHeight: Int(scaledSize.height), insertKeyframe: false)
//}
} }
writer.finish() options.writer.finish()
} }
return EmptyDisposable return EmptyDisposable
@ -107,12 +102,12 @@ public final class ViewController: UIViewController {
self.fpsCount += 1 self.fpsCount += 1
/*DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0 / 60.0, execute: { [weak self] in DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0 / 30.0, execute: { [weak self] in
self?.updateImage() self?.updateImage()
})*/ })
DispatchQueue.main.async { /*DispatchQueue.main.async {
self.updateImage() self.updateImage()
} }*/
default: default:
break break
} }

View File

@ -11,8 +11,12 @@ void splitRGBAIntoYUVAPlanes(uint8_t const *argb, uint8_t *outY, uint8_t *outU,
void combineYUVAPlanesIntoARGB(uint8_t *argb, uint8_t const *inY, uint8_t const *inU, uint8_t const *inV, uint8_t const *inA, int width, int height, int bytesPerRow); void combineYUVAPlanesIntoARGB(uint8_t *argb, uint8_t const *inY, uint8_t const *inU, uint8_t const *inV, uint8_t const *inA, int width, int height, int bytesPerRow);
void scaleImagePlane(uint8_t *outPlane, int outWidth, int outHeight, int outBytesPerRow, uint8_t const *inPlane, int inWidth, int inHeight, int inBytesPerRow); void scaleImagePlane(uint8_t *outPlane, int outWidth, int outHeight, int outBytesPerRow, uint8_t const *inPlane, int inWidth, int inHeight, int inBytesPerRow);
void subtractArraysInt16(int16_t const *a, int16_t const *b, uint16_t *dest, int length); void convertUInt8toInt16(uint8_t const *source, int16_t *dest, int length);
void convertInt16toUInt8(int16_t const *source, uint8_t *dest, int length);
void subtractArraysInt16(int16_t const *a, int16_t const *b, int16_t *dest, int length);
void addArraysInt16(int16_t const *a, int16_t const *b, int16_t *dest, int length);
void subtractArraysUInt8Int16(uint8_t const *a, int16_t const *b, uint8_t *dest, int length); void subtractArraysUInt8Int16(uint8_t const *a, int16_t const *b, uint8_t *dest, int length);
void addArraysUInt8Int16(uint8_t const *a, int16_t const *b, uint8_t *dest, int length);
#ifdef __cplusplus__ #ifdef __cplusplus__
} }

View File

@ -752,8 +752,8 @@ inline void vpx_idct4x4_16_add_neon(const int16x8_t &top64, const int16x8_t &bot
vst1_s16(dest, vget_low_s16(a[1])); vst1_s16(dest, vget_low_s16(a[1]));
} }
static int dct4x4QuantDC = 60; static int dct4x4QuantDC = 58;
static int dct4x4QuantAC = 60; static int dct4x4QuantAC = 58;
void performForward4x4Dct(int16_t const *normalizedCoefficients, int16_t *coefficients, int width, int height, DCTELEM *divisors) { void performForward4x4Dct(int16_t const *normalizedCoefficients, int16_t *coefficients, int width, int height, DCTELEM *divisors) {
DCTELEM block[4 * 4]; DCTELEM block[4 * 4];
@ -804,7 +804,6 @@ void performForward4x4Dct(int16_t const *normalizedCoefficients, int16_t *coeffi
} }
void performInverse4x4Dct(int16_t const * coefficients, int16_t *normalizedCoefficients, int width, int height, DctAuxiliaryData *auxiliaryData, IFAST_MULT_TYPE *ifmtbl) { void performInverse4x4Dct(int16_t const * coefficients, int16_t *normalizedCoefficients, int width, int height, DctAuxiliaryData *auxiliaryData, IFAST_MULT_TYPE *ifmtbl) {
//DCTELEM coefficientBlock[4 * 4];
DCTELEM resultBlock[4 * 4]; DCTELEM resultBlock[4 * 4];
for (int y = 0; y < height; y += 4) { for (int y = 0; y < height; y += 4) {
@ -830,11 +829,16 @@ void performInverse4x4Dct(int16_t const * coefficients, int16_t *normalizedCoeff
int16x8_t top64 = vreinterpretq_s16_u16(qtop16); int16x8_t top64 = vreinterpretq_s16_u16(qtop16);
int16x8_t bottom64 = vreinterpretq_s16_u16(qbottom16); int16x8_t bottom64 = vreinterpretq_s16_u16(qbottom16);
/*for (int blockY = 0; blockY < 4; blockY++) { /*DCTELEM coefficientBlock[4 * 4];
for (int blockY = 0; blockY < 4; blockY++) {
for (int blockX = 0; blockX < 4; blockX++) { for (int blockX = 0; blockX < 4; blockX++) {
coefficientBlock[zigZag4x4Inv[blockY * 4 + blockX]] = coefficients[(y + blockY) * width + (x + blockX)]; coefficientBlock[zigZag4x4Inv[blockY * 4 + blockX]] = coefficients[(y + blockY) * width + (x + blockX)];
} }
}*/ }
top64 = vreinterpretq_s16_u64(vld1q_u64((uint64_t *)&coefficientBlock[0]));
bottom64 = vreinterpretq_s16_u64(vld1q_u64((uint64_t *)&coefficientBlock[8]));*/
vpx_idct4x4_16_add_neon(top64, bottom64, resultBlock, dct4x4QuantAC); vpx_idct4x4_16_add_neon(top64, bottom64, resultBlock, dct4x4QuantAC);
@ -848,11 +852,11 @@ void performInverse4x4Dct(int16_t const * coefficients, int16_t *normalizedCoeff
vst1_u32((uint32_t *)&normalizedCoefficients[(y + 2) * width + x], c); vst1_u32((uint32_t *)&normalizedCoefficients[(y + 2) * width + x], c);
vst1_u32((uint32_t *)&normalizedCoefficients[(y + 3) * width + x], d); vst1_u32((uint32_t *)&normalizedCoefficients[(y + 3) * width + x], d);
for (int blockY = 0; blockY < 4; blockY++) { /*for (int blockY = 0; blockY < 4; blockY++) {
for (int blockX = 0; blockX < 4; blockX++) { for (int blockX = 0; blockX < 4; blockX++) {
//normalizedCoefficients[(y + blockY) * width + (x + blockX)] = resultBlock[blockY * 4 + blockX]; normalizedCoefficients[(y + blockY) * width + (x + blockX)] = resultBlock[blockY * 4 + blockX];
}
} }
}*/
} }
} }
} }

View File

@ -117,7 +117,42 @@ void scaleImagePlane(uint8_t *outPlane, int outWidth, int outHeight, int outByte
vImageScale_Planar8(&src, &dst, nil, kvImageDoNotTile); vImageScale_Planar8(&src, &dst, nil, kvImageDoNotTile);
} }
void subtractArraysInt16(int16_t const *a, int16_t const *b, uint16_t *dest, int length) { void convertUInt8toInt16(uint8_t const *source, int16_t *dest, int length) {
for (int i = 0; i < length; i += 8) {
uint8x8_t lhs8 = vld1_u8(&source[i]);
int16x8_t lhs = vreinterpretq_s16_u16(vmovl_u8(lhs8));
vst1q_s16(&dest[i], lhs);
}
if (length % 8 != 0) {
for (int i = length - (length % 8); i < length; i++) {
dest[i] = (int16_t)source[i];
}
}
}
void convertInt16toUInt8(int16_t const *source, uint8_t *dest, int length) {
for (int i = 0; i < length; i += 8) {
int16x8_t lhs16 = vld1q_s16(&source[i]);
int8x8_t lhs = vqmovun_s16(lhs16);
vst1_u8(&dest[i], lhs);
}
if (length % 8 != 0) {
for (int i = length - (length % 8); i < length; i++) {
int16_t result = source[i];
if (result < 0) {
result = 0;
}
if (result > 255) {
result = 255;
}
dest[i] = (int8_t)result;
}
}
}
void subtractArraysInt16(int16_t const *a, int16_t const *b, int16_t *dest, int length) {
for (int i = 0; i < length; i += 8) { for (int i = 0; i < length; i += 8) {
int16x8_t lhs = vld1q_s16((int16_t *)&a[i]); int16x8_t lhs = vld1q_s16((int16_t *)&a[i]);
int16x8_t rhs = vld1q_s16((int16_t *)&b[i]); int16x8_t rhs = vld1q_s16((int16_t *)&b[i]);
@ -131,6 +166,20 @@ void subtractArraysInt16(int16_t const *a, int16_t const *b, uint16_t *dest, int
} }
} }
void addArraysInt16(int16_t const *a, int16_t const *b, int16_t *dest, int length) {
for (int i = 0; i < length; i += 8) {
int16x8_t lhs = vld1q_s16((int16_t *)&a[i]);
int16x8_t rhs = vld1q_s16((int16_t *)&b[i]);
int16x8_t result = vaddq_s16(lhs, rhs);
vst1q_s16((int16_t *)&dest[i], result);
}
if (length % 8 != 0) {
for (int i = length - (length % 8); i < length; i++) {
dest[i] = a[i] - b[i];
}
}
}
void subtractArraysUInt8Int16(uint8_t const *a, int16_t const *b, uint8_t *dest, int length) { void subtractArraysUInt8Int16(uint8_t const *a, int16_t const *b, uint8_t *dest, int length) {
for (int i = 0; i < length; i += 8) { for (int i = 0; i < length; i += 8) {
uint8x8_t lhs8 = vld1_u8(&a[i]); uint8x8_t lhs8 = vld1_u8(&a[i]);
@ -155,3 +204,41 @@ void subtractArraysUInt8Int16(uint8_t const *a, int16_t const *b, uint8_t *dest,
} }
} }
} }
void addArraysUInt8Int16(uint8_t const *a, int16_t const *b, uint8_t *dest, int length) {
#if false
for (int i = 0; i < length; i++) {
int16_t result = ((int16_t)a[i]) + b[i];
if (result < 0) {
result = 0;
}
if (result > 255) {
result = 255;
}
dest[i] = (int8_t)result;
}
#else
for (int i = 0; i < length; i += 8) {
uint8x8_t lhs8 = vld1_u8(&a[i]);
int16x8_t lhs = vreinterpretq_s16_u16(vmovl_u8(lhs8));
int16x8_t rhs = vld1q_s16((int16_t *)&b[i]);
int16x8_t result = vaddq_s16(lhs, rhs);
uint8x8_t result8 = vqmovun_s16(result);
vst1_u8(&dest[i], result8);
}
if (length % 8 != 0) {
for (int i = length - (length % 8); i < length; i++) {
int16_t result = ((int16_t)a[i]) + b[i];
if (result < 0) {
result = 0;
}
if (result > 255) {
result = 255;
}
dest[i] = (int8_t)result;
}
}
#endif
}

View File

@ -246,7 +246,7 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
private var currentSurface: ImageARGB? private var currentSurface: ImageARGB?
private var currentYUVASurface: ImageYUVA420? private var currentYUVASurface: ImageYUVA420?
private var currentFrameFloat: FloatCoefficientsYUVA420? private var currentFrameFloat: FloatCoefficientsYUVA420?
private var previousFrameFloat: FloatCoefficientsYUVA420? private var previousFrameCoefficients: DctCoefficientsYUVA420?
private var deltaFrameFloat: FloatCoefficientsYUVA420? private var deltaFrameFloat: FloatCoefficientsYUVA420?
private var previousYUVASurface: ImageYUVA420? private var previousYUVASurface: ImageYUVA420?
private var currentDctData: DctData? private var currentDctData: DctData?
@ -281,6 +281,8 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
} }
func add(with drawingBlock: (AnimationCacheItemDrawingSurface) -> Double?, proposedWidth: Int, proposedHeight: Int, insertKeyframe: Bool) { func add(with drawingBlock: (AnimationCacheItemDrawingSurface) -> Double?, proposedWidth: Int, proposedHeight: Int, insertKeyframe: Bool) {
do {
try self.lock.throwingLocked {
let width = roundUp(proposedWidth, multiple: 16) let width = roundUp(proposedWidth, multiple: 16)
let height = roundUp(proposedHeight, multiple: 16) let height = roundUp(proposedHeight, multiple: 16)
@ -288,6 +290,9 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
if let current = self.currentSurface { if let current = self.currentSurface {
if current.argbPlane.width == width && current.argbPlane.height == height { if current.argbPlane.width == width && current.argbPlane.height == height {
surface = current surface = current
surface.argbPlane.data.withUnsafeMutableBytes { bytes -> Void in
memset(bytes.baseAddress!, 0, bytes.count)
}
} else { } else {
self.isFailed = true self.isFailed = true
return return
@ -311,12 +316,12 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
return return
} }
do {
try addInternal(with: { yuvaSurface in try addInternal(with: { yuvaSurface in
surface.toYUVA420(target: yuvaSurface) surface.toYUVA420(target: yuvaSurface)
return duration return duration
}, width: width, height: height, insertKeyframe: insertKeyframe) }, width: width, height: height, insertKeyframe: insertKeyframe)
}
} catch { } catch {
} }
} }
@ -326,9 +331,11 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
let height = roundUp(proposedHeight, multiple: 16) let height = roundUp(proposedHeight, multiple: 16)
do { do {
try self.lock.throwingLocked {
try addInternal(with: { yuvaSurface in try addInternal(with: { yuvaSurface in
return drawingBlock(yuvaSurface) return drawingBlock(yuvaSurface)
}, width: width, height: height, insertKeyframe: insertKeyframe) }, width: width, height: height, insertKeyframe: insertKeyframe)
}
} catch { } catch {
} }
} }
@ -342,7 +349,6 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
throw WriteError.generic throw WriteError.generic
} }
try self.lock.throwingLocked {
guard !self.isFailed, !self.isFinished, let file = self.file, let compressedWriter = self.compressedWriter else { guard !self.isFailed, !self.isFinished, let file = self.file, let compressedWriter = self.compressedWriter else {
throw WriteError.generic throw WriteError.generic
} }
@ -377,17 +383,17 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
self.currentFrameFloat = currentFrameFloat self.currentFrameFloat = currentFrameFloat
} }
let previousFrameFloat: FloatCoefficientsYUVA420 let previousFrameCoefficients: DctCoefficientsYUVA420
if let current = self.previousFrameFloat { if let current = self.previousFrameCoefficients {
if current.yPlane.width == width && current.yPlane.height == height { if current.yPlane.width == width && current.yPlane.height == height {
previousFrameFloat = current previousFrameCoefficients = current
} else { } else {
self.isFailed = true self.isFailed = true
throw WriteError.generic throw WriteError.generic
} }
} else { } else {
previousFrameFloat = FloatCoefficientsYUVA420(width: width, height: height) previousFrameCoefficients = DctCoefficientsYUVA420(width: width, height: height)
self.previousFrameFloat = previousFrameFloat self.previousFrameCoefficients = previousFrameCoefficients
} }
let deltaFrameFloat: FloatCoefficientsYUVA420 let deltaFrameFloat: FloatCoefficientsYUVA420
@ -448,31 +454,6 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
insertKeyframe = true insertKeyframe = true
#endif #endif
let isKeyframe: Bool
if !isFirstFrame && !insertKeyframe {
isKeyframe = false
yuvaSurface.toCoefficients(target: currentFrameFloat)
currentFrameFloat.copy(into: deltaFrameFloat)
deltaFrameFloat.subtract(other: previousFrameFloat)
deltaFrameFloat.toDctCoefficients(target: differenceCoefficients)
differenceCoefficients.dct(dctData: dctData, target: dctCoefficients)
dctCoefficients.idct(dctData: dctData, target: differenceCoefficients)
differenceCoefficients.toFloatCoefficients(target: currentFrameFloat)
currentFrameFloat.subtract(other: previousFrameFloat)
currentFrameFloat.clamp()
currentFrameFloat.copy(into: previousFrameFloat)
} else {
isKeyframe = true
yuvaSurface.dct(dctData: dctData, target: dctCoefficients)
let previousYUVASurface: ImageYUVA420 let previousYUVASurface: ImageYUVA420
if let current = self.previousYUVASurface { if let current = self.previousYUVASurface {
previousYUVASurface = current previousYUVASurface = current
@ -480,12 +461,31 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
previousYUVASurface = ImageYUVA420(width: dctCoefficients.yPlane.width, height: dctCoefficients.yPlane.height, rowAlignment: nil) previousYUVASurface = ImageYUVA420(width: dctCoefficients.yPlane.width, height: dctCoefficients.yPlane.height, rowAlignment: nil)
self.previousYUVASurface = previousYUVASurface self.previousYUVASurface = previousYUVASurface
} }
dctCoefficients.idct(dctData: dctData, target: previousYUVASurface)
previousYUVASurface.toCoefficients(target: previousFrameFloat) let isKeyframe: Bool
if !isFirstFrame && !insertKeyframe {
isKeyframe = false
//previous + delta = current
//delta = current - previous
yuvaSurface.toCoefficients(target: differenceCoefficients)
differenceCoefficients.subtract(other: previousFrameCoefficients)
differenceCoefficients.dct4x4(dctData: dctData, target: dctCoefficients)
//previous + delta = current
dctCoefficients.idct4x4(dctData: dctData, target: differenceCoefficients)
previousFrameCoefficients.add(other: differenceCoefficients)
} else {
isKeyframe = true
yuvaSurface.dct8x8(dctData: dctData, target: dctCoefficients)
dctCoefficients.idct8x8(dctData: dctData, target: yuvaSurface)
yuvaSurface.toCoefficients(target: previousFrameCoefficients)
} }
if isFirstFrame { if isFirstFrame {
file.write(5 as UInt32) file.write(6 as UInt32)
file.write(UInt32(dctCoefficients.yPlane.width)) file.write(UInt32(dctCoefficients.yPlane.width))
file.write(UInt32(dctCoefficients.yPlane.height)) file.write(UInt32(dctCoefficients.yPlane.height))
@ -539,7 +539,6 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
throw WriteError.generic throw WriteError.generic
} }
} }
}
func finish() { func finish() {
do { do {
@ -607,23 +606,16 @@ private final class AnimationCacheItemAccessor {
} }
final class CurrentFrame { final class CurrentFrame {
enum FrameType {
case key
case delta
}
let index: Int let index: Int
let type: FrameType
var remainingDuration: Double var remainingDuration: Double
let duration: Double let duration: Double
let dctCoefficients: DctCoefficientsYUVA420 let yuva: ImageYUVA420
init(index: Int, type: FrameType, duration: Double, dctCoefficients: DctCoefficientsYUVA420) { init(index: Int, duration: Double, yuva: ImageYUVA420) {
self.index = index self.index = index
self.type = type
self.duration = duration self.duration = duration
self.remainingDuration = duration self.remainingDuration = duration
self.dctCoefficients = dctCoefficients self.yuva = yuva
} }
} }
@ -642,6 +634,7 @@ private final class AnimationCacheItemAccessor {
private var currentFrame: CurrentFrame? private var currentFrame: CurrentFrame?
private var currentYUVASurface: ImageYUVA420? private var currentYUVASurface: ImageYUVA420?
private var currentCoefficients: DctCoefficientsYUVA420?
private let currentDctData: DctData private let currentDctData: DctData
private var sharedDctCoefficients: DctCoefficientsYUVA420? private var sharedDctCoefficients: DctCoefficientsYUVA420?
private var deltaCoefficients: DctCoefficientsYUVA420? private var deltaCoefficients: DctCoefficientsYUVA420?
@ -696,7 +689,7 @@ private final class AnimationCacheItemAccessor {
let frameType = Int(try compressedDataReader.readUInt32()) let frameType = Int(try compressedDataReader.readUInt32())
let dctCoefficients: DctCoefficientsYUVA420 let dctCoefficients: DctCoefficientsYUVA420
if let sharedDctCoefficients = self.sharedDctCoefficients, sharedDctCoefficients.yPlane.width == self.width, sharedDctCoefficients.yPlane.height == self.height { if let sharedDctCoefficients = self.sharedDctCoefficients, sharedDctCoefficients.yPlane.width == self.width, sharedDctCoefficients.yPlane.height == self.height, !"".isEmpty {
dctCoefficients = sharedDctCoefficients dctCoefficients = sharedDctCoefficients
} else { } else {
dctCoefficients = DctCoefficientsYUVA420(width: self.width, height: self.height) dctCoefficients = DctCoefficientsYUVA420(width: self.width, height: self.height)
@ -738,7 +731,48 @@ private final class AnimationCacheItemAccessor {
frameOffset += plane.data.count frameOffset += plane.data.count
} }
self.currentFrame = CurrentFrame(index: index, type: frameType == 1 ? .key : .delta, duration: self.durationMapping[index], dctCoefficients: dctCoefficients) let yuvaSurface: ImageYUVA420
if let currentYUVASurface = self.currentYUVASurface {
yuvaSurface = currentYUVASurface
} else {
yuvaSurface = ImageYUVA420(width: dctCoefficients.yPlane.width, height: dctCoefficients.yPlane.height, rowAlignment: nil)
}
let currentCoefficients: DctCoefficientsYUVA420
if let current = self.currentCoefficients {
currentCoefficients = current
} else {
currentCoefficients = DctCoefficientsYUVA420(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height)
self.currentCoefficients = currentCoefficients
}
let deltaCoefficients: DctCoefficientsYUVA420
if let current = self.deltaCoefficients {
deltaCoefficients = current
} else {
deltaCoefficients = DctCoefficientsYUVA420(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height)
self.deltaCoefficients = deltaCoefficients
}
switch frameType {
case 1:
dctCoefficients.idct8x8(dctData: self.currentDctData, target: yuvaSurface)
yuvaSurface.toCoefficients(target: currentCoefficients)
default:
dctCoefficients.idct4x4(dctData: self.currentDctData, target: deltaCoefficients)
currentCoefficients.add(other: deltaCoefficients)
if !"".isEmpty {
let deltaFloatCoefficients = FloatCoefficientsYUVA420(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height)
deltaCoefficients.toFloatCoefficients(target: deltaFloatCoefficients)
deltaFloatCoefficients.add(constant: 128.0)
deltaFloatCoefficients.toYUVA420(target: yuvaSurface)
} else {
currentCoefficients.toYUVA420(target: yuvaSurface)
}
}
self.currentFrame = CurrentFrame(index: index, duration: self.durationMapping[index], yuva: yuvaSurface)
} catch { } catch {
self.currentFrame = nil self.currentFrame = nil
self.compressedDataReader = nil self.compressedDataReader = nil
@ -773,75 +807,38 @@ private final class AnimationCacheItemAccessor {
return nil return nil
} }
let yuvaSurface: ImageYUVA420
switch requestedFormat { switch requestedFormat {
case .rgba: case .rgba:
if let currentYUVASurface = self.currentYUVASurface { let currentSurface = ImageARGB(width: currentFrame.yuva.yPlane.width, height: currentFrame.yuva.yPlane.height, rowAlignment: 32)
yuvaSurface = currentYUVASurface currentFrame.yuva.toARGB(target: currentSurface)
} else {
yuvaSurface = ImageYUVA420(width: currentFrame.dctCoefficients.yPlane.width, height: currentFrame.dctCoefficients.yPlane.height, rowAlignment: nil)
}
case let .yuva(preferredRowAlignment):
yuvaSurface = ImageYUVA420(width: currentFrame.dctCoefficients.yPlane.width, height: currentFrame.dctCoefficients.yPlane.height, rowAlignment: preferredRowAlignment)
}
switch currentFrame.type {
case .key:
currentFrame.dctCoefficients.idct(dctData: self.currentDctData, target: yuvaSurface)
case .delta:
let deltaCoefficients: DctCoefficientsYUVA420
if let current = self.deltaCoefficients {
deltaCoefficients = current
} else {
deltaCoefficients = DctCoefficientsYUVA420(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height)
self.deltaCoefficients = deltaCoefficients
}
currentFrame.dctCoefficients.idct(dctData: self.currentDctData, target: deltaCoefficients)
if !"".isEmpty {
let deltaFloatCoefficients = FloatCoefficientsYUVA420(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height)
deltaCoefficients.toFloatCoefficients(target: deltaFloatCoefficients)
deltaFloatCoefficients.add(constant: 128.0)
deltaFloatCoefficients.toYUVA420(target: yuvaSurface)
} else {
yuvaSurface.subtract(other: deltaCoefficients)
}
}
switch requestedFormat {
case .rgba:
let currentSurface = ImageARGB(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height, rowAlignment: 32)
yuvaSurface.toARGB(target: currentSurface)
self.currentYUVASurface = yuvaSurface
return AnimationCacheItemFrame(format: .rgba(data: currentSurface.argbPlane.data, width: currentSurface.argbPlane.width, height: currentSurface.argbPlane.height, bytesPerRow: currentSurface.argbPlane.bytesPerRow), duration: currentFrame.duration) return AnimationCacheItemFrame(format: .rgba(data: currentSurface.argbPlane.data, width: currentSurface.argbPlane.width, height: currentSurface.argbPlane.height, bytesPerRow: currentSurface.argbPlane.bytesPerRow), duration: currentFrame.duration)
case .yuva: case .yuva:
return AnimationCacheItemFrame( return AnimationCacheItemFrame(
format: .yuva( format: .yuva(
y: AnimationCacheItemFrame.Plane( y: AnimationCacheItemFrame.Plane(
data: yuvaSurface.yPlane.data, data: currentFrame.yuva.yPlane.data,
width: yuvaSurface.yPlane.width, width: currentFrame.yuva.yPlane.width,
height: yuvaSurface.yPlane.height, height: currentFrame.yuva.yPlane.height,
bytesPerRow: yuvaSurface.yPlane.bytesPerRow bytesPerRow: currentFrame.yuva.yPlane.bytesPerRow
), ),
u: AnimationCacheItemFrame.Plane( u: AnimationCacheItemFrame.Plane(
data: yuvaSurface.uPlane.data, data: currentFrame.yuva.uPlane.data,
width: yuvaSurface.uPlane.width, width: currentFrame.yuva.uPlane.width,
height: yuvaSurface.uPlane.height, height: currentFrame.yuva.uPlane.height,
bytesPerRow: yuvaSurface.uPlane.bytesPerRow bytesPerRow: currentFrame.yuva.uPlane.bytesPerRow
), ),
v: AnimationCacheItemFrame.Plane( v: AnimationCacheItemFrame.Plane(
data: yuvaSurface.vPlane.data, data: currentFrame.yuva.vPlane.data,
width: yuvaSurface.vPlane.width, width: currentFrame.yuva.vPlane.width,
height: yuvaSurface.vPlane.height, height: currentFrame.yuva.vPlane.height,
bytesPerRow: yuvaSurface.vPlane.bytesPerRow bytesPerRow: currentFrame.yuva.vPlane.bytesPerRow
), ),
a: AnimationCacheItemFrame.Plane( a: AnimationCacheItemFrame.Plane(
data: yuvaSurface.aPlane.data, data: currentFrame.yuva.aPlane.data,
width: yuvaSurface.aPlane.width, width: currentFrame.yuva.aPlane.width,
height: yuvaSurface.aPlane.height, height: currentFrame.yuva.aPlane.height,
bytesPerRow: yuvaSurface.aPlane.bytesPerRow bytesPerRow: currentFrame.yuva.aPlane.bytesPerRow
) )
), ),
duration: currentFrame.duration duration: currentFrame.duration
@ -1125,7 +1122,7 @@ private func loadItem(path: String) throws -> AnimationCacheItem {
} }
let formatVersion = readUInt32(data: compressedData, offset: offset) let formatVersion = readUInt32(data: compressedData, offset: offset)
offset += 4 offset += 4
if formatVersion != 5 { if formatVersion != 6 {
throw LoadItemError.dataError throw LoadItemError.dataError
} }

View File

@ -66,6 +66,14 @@ extension ImagePlane {
} }
} }
} }
func add(other: DctCoefficientPlane) {
self.data.withUnsafeMutableBytes { bytes in
other.data.withUnsafeBytes { otherBytes in
addArraysUInt8Int16(bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), otherBytes.baseAddress!.assumingMemoryBound(to: Int16.self), bytes.baseAddress!.assumingMemoryBound(to: Int8.self), Int32(bytes.count))
}
}
}
} }
final class ImageARGB { final class ImageARGB {
@ -126,7 +134,15 @@ final class DctCoefficientPlane: CustomStringConvertible {
func subtract(other: DctCoefficientPlane) { func subtract(other: DctCoefficientPlane) {
self.data.withUnsafeMutableBytes { bytes in self.data.withUnsafeMutableBytes { bytes in
other.data.withUnsafeBytes { otherBytes in other.data.withUnsafeBytes { otherBytes in
subtractArraysInt16(otherBytes.baseAddress!.assumingMemoryBound(to: Int16.self), bytes.baseAddress!.assumingMemoryBound(to: Int16.self), bytes.baseAddress!.assumingMemoryBound(to: Int16.self), Int32(bytes.count / 2)) subtractArraysInt16(bytes.baseAddress!.assumingMemoryBound(to: Int16.self), otherBytes.baseAddress!.assumingMemoryBound(to: Int16.self), bytes.baseAddress!.assumingMemoryBound(to: Int16.self), Int32(bytes.count / 2))
}
}
}
func add(other: DctCoefficientPlane) {
self.data.withUnsafeMutableBytes { bytes in
other.data.withUnsafeBytes { otherBytes in
addArraysInt16(bytes.baseAddress!.assumingMemoryBound(to: Int16.self), otherBytes.baseAddress!.assumingMemoryBound(to: Int16.self), bytes.baseAddress!.assumingMemoryBound(to: Int16.self), Int32(bytes.count / 2))
} }
} }
} }
@ -140,6 +156,14 @@ extension DctCoefficientPlane {
} }
} }
} }
func toUInt8(target: ImagePlane) {
self.data.withUnsafeBytes { bytes in
target.data.withUnsafeMutableBytes { otherBytes in
convertInt16toUInt8(bytes.baseAddress!.assumingMemoryBound(to: UInt16.self), otherBytes.baseAddress!.assumingMemoryBound(to: UInt8.self), Int32(bytes.count / 2))
}
}
}
} }
final class DctCoefficientsYUVA420 { final class DctCoefficientsYUVA420 {
@ -469,7 +493,42 @@ extension ImageYUVA420 {
} }
} }
func dct(dctData: DctData, target: DctCoefficientsYUVA420) { func toCoefficients(target: DctCoefficientsYUVA420) {
precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height)
for i in 0 ..< 4 {
let sourcePlane: ImagePlane
let targetPlane: DctCoefficientPlane
switch i {
case 0:
sourcePlane = self.yPlane
targetPlane = target.yPlane
case 1:
sourcePlane = self.uPlane
targetPlane = target.uPlane
case 2:
sourcePlane = self.vPlane
targetPlane = target.vPlane
case 3:
sourcePlane = self.aPlane
targetPlane = target.aPlane
default:
preconditionFailure()
}
sourcePlane.data.withUnsafeBytes { sourceBytes in
let sourcePixels = sourceBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
targetPlane.data.withUnsafeMutableBytes { bytes in
let coefficients = bytes.baseAddress!.assumingMemoryBound(to: Int16.self)
convertUInt8toInt16(sourcePixels, coefficients, Int32(sourceBytes.count))
}
}
}
}
func dct8x8(dctData: DctData, target: DctCoefficientsYUVA420) {
precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height) precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height)
for i in 0 ..< 4 { for i in 0 ..< 4 {
@ -510,22 +569,23 @@ extension ImageYUVA420 {
} }
} }
func dct(dctData: DctData) -> DctCoefficientsYUVA420 {
let results = DctCoefficientsYUVA420(width: self.yPlane.width, height: self.yPlane.height)
self.dct(dctData: dctData, target: results)
return results
}
func subtract(other: DctCoefficientsYUVA420) { func subtract(other: DctCoefficientsYUVA420) {
self.yPlane.subtract(other: other.yPlane) self.yPlane.subtract(other: other.yPlane)
self.uPlane.subtract(other: other.uPlane) self.uPlane.subtract(other: other.uPlane)
self.vPlane.subtract(other: other.vPlane) self.vPlane.subtract(other: other.vPlane)
self.aPlane.subtract(other: other.aPlane) self.aPlane.subtract(other: other.aPlane)
} }
func add(other: DctCoefficientsYUVA420) {
self.yPlane.add(other: other.yPlane)
self.uPlane.add(other: other.uPlane)
self.vPlane.add(other: other.vPlane)
self.aPlane.add(other: other.aPlane)
}
} }
extension DctCoefficientsYUVA420 { extension DctCoefficientsYUVA420 {
func idct(dctData: DctData, target: ImageYUVA420) { func idct8x8(dctData: DctData, target: ImageYUVA420) {
precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height) precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height)
for i in 0 ..< 4 { for i in 0 ..< 4 {
@ -566,13 +626,7 @@ extension DctCoefficientsYUVA420 {
} }
} }
func idct(dctData: DctData, rowAlignment: Int?) -> ImageYUVA420 { func dct4x4(dctData: DctData, target: DctCoefficientsYUVA420) {
let resultImage = ImageYUVA420(width: self.yPlane.width, height: self.yPlane.height, rowAlignment: rowAlignment)
self.idct(dctData: dctData, target: resultImage)
return resultImage
}
func dct(dctData: DctData, target: DctCoefficientsYUVA420) {
precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height) precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height)
for i in 0 ..< 4 { for i in 0 ..< 4 {
@ -609,7 +663,7 @@ extension DctCoefficientsYUVA420 {
} }
} }
func idct(dctData: DctData, target: DctCoefficientsYUVA420) { func idct4x4(dctData: DctData, target: DctCoefficientsYUVA420) {
precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height) precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height)
for i in 0 ..< 4 { for i in 0 ..< 4 {
@ -653,10 +707,26 @@ extension DctCoefficientsYUVA420 {
self.aPlane.subtract(other: other.aPlane) self.aPlane.subtract(other: other.aPlane)
} }
func add(other: DctCoefficientsYUVA420) {
self.yPlane.add(other: other.yPlane)
self.uPlane.add(other: other.uPlane)
self.vPlane.add(other: other.vPlane)
self.aPlane.add(other: other.aPlane)
}
func toFloatCoefficients(target: FloatCoefficientsYUVA420) { func toFloatCoefficients(target: FloatCoefficientsYUVA420) {
self.yPlane.toFloatCoefficients(target: target.yPlane) self.yPlane.toFloatCoefficients(target: target.yPlane)
self.uPlane.toFloatCoefficients(target: target.uPlane) self.uPlane.toFloatCoefficients(target: target.uPlane)
self.vPlane.toFloatCoefficients(target: target.vPlane) self.vPlane.toFloatCoefficients(target: target.vPlane)
self.aPlane.toFloatCoefficients(target: target.aPlane) self.aPlane.toFloatCoefficients(target: target.aPlane)
} }
func toYUVA420(target: ImageYUVA420) {
assert(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height)
self.yPlane.toUInt8(target: target.yPlane)
self.uPlane.toUInt8(target: target.uPlane)
self.vPlane.toUInt8(target: target.vPlane)
self.aPlane.toUInt8(target: target.aPlane)
}
} }

View File

@ -14,7 +14,6 @@ public func cacheLottieAnimation(data: Data, width: Int, height: Int, keyframeOn
return return
} }
let frameSkip: Int let frameSkip: Int
if animation.frameRate >= 60 { if animation.frameRate >= 60 {
if ProcessInfo.processInfo.processorCount > 2 { if ProcessInfo.processInfo.processorCount > 2 {

View File

@ -45,7 +45,7 @@ public func cacheVideoAnimation(path: String, width: Int, height: Int, writer: A
} }
} }
return frameDuration return frameDuration
}, proposedWidth: frame.width, proposedHeight: frame.height, insertKeyframe: true) }, proposedWidth: frame.width, proposedHeight: frame.height, insertKeyframe: false)
if firstFrameOnly { if firstFrameOnly {
break break