mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Fix animation cache
This commit is contained in:
parent
adb9767edd
commit
47239681a7
@ -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",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
BIN
Tests/AnimationCacheTest/Resources/sticker.webm
Normal file
BIN
Tests/AnimationCacheTest/Resources/sticker.webm
Normal file
Binary file not shown.
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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__
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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,42 +281,47 @@ 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) {
|
||||||
let width = roundUp(proposedWidth, multiple: 16)
|
|
||||||
let height = roundUp(proposedHeight, multiple: 16)
|
|
||||||
|
|
||||||
let surface: ImageARGB
|
|
||||||
if let current = self.currentSurface {
|
|
||||||
if current.argbPlane.width == width && current.argbPlane.height == height {
|
|
||||||
surface = current
|
|
||||||
} else {
|
|
||||||
self.isFailed = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
surface = ImageARGB(width: width, height: height, rowAlignment: 32)
|
|
||||||
self.currentSurface = surface
|
|
||||||
}
|
|
||||||
|
|
||||||
let duration = surface.argbPlane.data.withUnsafeMutableBytes { bytes -> Double? in
|
|
||||||
return drawingBlock(AnimationCacheItemDrawingSurface(
|
|
||||||
argb: bytes.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
bytesPerRow: surface.argbPlane.bytesPerRow,
|
|
||||||
length: bytes.count
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let duration = duration else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try addInternal(with: { yuvaSurface in
|
try self.lock.throwingLocked {
|
||||||
surface.toYUVA420(target: yuvaSurface)
|
let width = roundUp(proposedWidth, multiple: 16)
|
||||||
|
let height = roundUp(proposedHeight, multiple: 16)
|
||||||
|
|
||||||
return duration
|
let surface: ImageARGB
|
||||||
}, width: width, height: height, insertKeyframe: insertKeyframe)
|
if let current = self.currentSurface {
|
||||||
|
if current.argbPlane.width == width && current.argbPlane.height == height {
|
||||||
|
surface = current
|
||||||
|
surface.argbPlane.data.withUnsafeMutableBytes { bytes -> Void in
|
||||||
|
memset(bytes.baseAddress!, 0, bytes.count)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.isFailed = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
surface = ImageARGB(width: width, height: height, rowAlignment: 32)
|
||||||
|
self.currentSurface = surface
|
||||||
|
}
|
||||||
|
|
||||||
|
let duration = surface.argbPlane.data.withUnsafeMutableBytes { bytes -> Double? in
|
||||||
|
return drawingBlock(AnimationCacheItemDrawingSurface(
|
||||||
|
argb: bytes.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
bytesPerRow: surface.argbPlane.bytesPerRow,
|
||||||
|
length: bytes.count
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let duration = duration else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try addInternal(with: { yuvaSurface in
|
||||||
|
surface.toYUVA420(target: yuvaSurface)
|
||||||
|
|
||||||
|
return duration
|
||||||
|
}, 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 addInternal(with: { yuvaSurface in
|
try self.lock.throwingLocked {
|
||||||
return drawingBlock(yuvaSurface)
|
try addInternal(with: { yuvaSurface in
|
||||||
}, width: width, height: height, insertKeyframe: insertKeyframe)
|
return drawingBlock(yuvaSurface)
|
||||||
|
}, width: width, height: height, insertKeyframe: insertKeyframe)
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,202 +349,194 @@ 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
|
}
|
||||||
}
|
|
||||||
|
var isFirstFrame = false
|
||||||
var isFirstFrame = false
|
|
||||||
|
let yuvaSurface: ImageYUVA420
|
||||||
let yuvaSurface: ImageYUVA420
|
if let current = self.currentYUVASurface {
|
||||||
if let current = self.currentYUVASurface {
|
if current.yPlane.width == width && current.yPlane.height == height {
|
||||||
if current.yPlane.width == width && current.yPlane.height == height {
|
yuvaSurface = current
|
||||||
yuvaSurface = current
|
|
||||||
} else {
|
|
||||||
self.isFailed = true
|
|
||||||
throw WriteError.generic
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
isFirstFrame = true
|
|
||||||
|
|
||||||
yuvaSurface = ImageYUVA420(width: width, height: height, rowAlignment: nil)
|
|
||||||
self.currentYUVASurface = yuvaSurface
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentFrameFloat: FloatCoefficientsYUVA420
|
|
||||||
if let current = self.currentFrameFloat {
|
|
||||||
if current.yPlane.width == width && current.yPlane.height == height {
|
|
||||||
currentFrameFloat = current
|
|
||||||
} else {
|
|
||||||
self.isFailed = true
|
|
||||||
throw WriteError.generic
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
currentFrameFloat = FloatCoefficientsYUVA420(width: width, height: height)
|
|
||||||
self.currentFrameFloat = currentFrameFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
let previousFrameFloat: FloatCoefficientsYUVA420
|
|
||||||
if let current = self.previousFrameFloat {
|
|
||||||
if current.yPlane.width == width && current.yPlane.height == height {
|
|
||||||
previousFrameFloat = current
|
|
||||||
} else {
|
|
||||||
self.isFailed = true
|
|
||||||
throw WriteError.generic
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
previousFrameFloat = FloatCoefficientsYUVA420(width: width, height: height)
|
|
||||||
self.previousFrameFloat = previousFrameFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
let deltaFrameFloat: FloatCoefficientsYUVA420
|
|
||||||
if let current = self.deltaFrameFloat {
|
|
||||||
if current.yPlane.width == width && current.yPlane.height == height {
|
|
||||||
deltaFrameFloat = current
|
|
||||||
} else {
|
|
||||||
self.isFailed = true
|
|
||||||
throw WriteError.generic
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
deltaFrameFloat = FloatCoefficientsYUVA420(width: width, height: height)
|
|
||||||
self.deltaFrameFloat = deltaFrameFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
let dctData: DctData
|
|
||||||
if let current = self.currentDctData {
|
|
||||||
dctData = current
|
|
||||||
} else {
|
|
||||||
dctData = DctData(generatingTablesAtQualityLuma: self.dctQualityLuma, chroma: self.dctQualityChroma, delta: self.dctQualityDelta)
|
|
||||||
self.currentDctData = dctData
|
|
||||||
}
|
|
||||||
|
|
||||||
let duration = drawingBlock(yuvaSurface)
|
|
||||||
|
|
||||||
guard let duration = duration else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let dctCoefficients: DctCoefficientsYUVA420
|
|
||||||
if let current = self.currentDctCoefficients {
|
|
||||||
if current.yPlane.width == width && current.yPlane.height == height {
|
|
||||||
dctCoefficients = current
|
|
||||||
} else {
|
|
||||||
self.isFailed = true
|
|
||||||
throw WriteError.generic
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dctCoefficients = DctCoefficientsYUVA420(width: width, height: height)
|
|
||||||
self.currentDctCoefficients = dctCoefficients
|
|
||||||
}
|
|
||||||
|
|
||||||
let differenceCoefficients: DctCoefficientsYUVA420
|
|
||||||
if let current = self.differenceCoefficients {
|
|
||||||
if current.yPlane.width == width && current.yPlane.height == height {
|
|
||||||
differenceCoefficients = current
|
|
||||||
} else {
|
|
||||||
self.isFailed = true
|
|
||||||
throw WriteError.generic
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
differenceCoefficients = DctCoefficientsYUVA420(width: width, height: height)
|
|
||||||
self.differenceCoefficients = differenceCoefficients
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG && false
|
|
||||||
var insertKeyframe = insertKeyframe
|
|
||||||
insertKeyframe = true
|
|
||||||
#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
|
|
||||||
if let current = self.previousYUVASurface {
|
|
||||||
previousYUVASurface = current
|
|
||||||
} else {
|
|
||||||
previousYUVASurface = ImageYUVA420(width: dctCoefficients.yPlane.width, height: dctCoefficients.yPlane.height, rowAlignment: nil)
|
|
||||||
self.previousYUVASurface = previousYUVASurface
|
|
||||||
}
|
|
||||||
dctCoefficients.idct(dctData: dctData, target: previousYUVASurface)
|
|
||||||
previousYUVASurface.toCoefficients(target: previousFrameFloat)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isFirstFrame {
|
|
||||||
file.write(5 as UInt32)
|
|
||||||
|
|
||||||
file.write(UInt32(dctCoefficients.yPlane.width))
|
|
||||||
file.write(UInt32(dctCoefficients.yPlane.height))
|
|
||||||
|
|
||||||
let lumaDctTable = dctData.lumaTable.serializedData()
|
|
||||||
file.write(UInt32(lumaDctTable.count))
|
|
||||||
let _ = file.write(lumaDctTable)
|
|
||||||
|
|
||||||
let chromaDctTable = dctData.chromaTable.serializedData()
|
|
||||||
file.write(UInt32(chromaDctTable.count))
|
|
||||||
let _ = file.write(chromaDctTable)
|
|
||||||
|
|
||||||
let deltaDctTable = dctData.deltaTable.serializedData()
|
|
||||||
file.write(UInt32(deltaDctTable.count))
|
|
||||||
let _ = file.write(deltaDctTable)
|
|
||||||
|
|
||||||
self.contentLengthOffset = Int(file.position())
|
|
||||||
file.write(0 as UInt32)
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
let frameLength = dctCoefficients.yPlane.data.count + dctCoefficients.uPlane.data.count + dctCoefficients.vPlane.data.count + dctCoefficients.aPlane.data.count
|
|
||||||
try compressedWriter.writeUInt32(UInt32(frameLength))
|
|
||||||
|
|
||||||
try compressedWriter.writeUInt32(isKeyframe ? 1 : 0)
|
|
||||||
|
|
||||||
for i in 0 ..< 4 {
|
|
||||||
let dctPlane: DctCoefficientPlane
|
|
||||||
switch i {
|
|
||||||
case 0:
|
|
||||||
dctPlane = dctCoefficients.yPlane
|
|
||||||
case 1:
|
|
||||||
dctPlane = dctCoefficients.uPlane
|
|
||||||
case 2:
|
|
||||||
dctPlane = dctCoefficients.vPlane
|
|
||||||
case 3:
|
|
||||||
dctPlane = dctCoefficients.aPlane
|
|
||||||
default:
|
|
||||||
preconditionFailure()
|
|
||||||
}
|
|
||||||
|
|
||||||
try compressedWriter.writeUInt32(UInt32(dctPlane.data.count))
|
|
||||||
try dctPlane.data.withUnsafeBytes { bytes in
|
|
||||||
try compressedWriter.write(bytes: bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), count: bytes.count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.frames.append(FrameMetadata(duration: duration))
|
|
||||||
} catch {
|
|
||||||
self.isFailed = true
|
self.isFailed = true
|
||||||
throw WriteError.generic
|
throw WriteError.generic
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
isFirstFrame = true
|
||||||
|
|
||||||
|
yuvaSurface = ImageYUVA420(width: width, height: height, rowAlignment: nil)
|
||||||
|
self.currentYUVASurface = yuvaSurface
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentFrameFloat: FloatCoefficientsYUVA420
|
||||||
|
if let current = self.currentFrameFloat {
|
||||||
|
if current.yPlane.width == width && current.yPlane.height == height {
|
||||||
|
currentFrameFloat = current
|
||||||
|
} else {
|
||||||
|
self.isFailed = true
|
||||||
|
throw WriteError.generic
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentFrameFloat = FloatCoefficientsYUVA420(width: width, height: height)
|
||||||
|
self.currentFrameFloat = currentFrameFloat
|
||||||
|
}
|
||||||
|
|
||||||
|
let previousFrameCoefficients: DctCoefficientsYUVA420
|
||||||
|
if let current = self.previousFrameCoefficients {
|
||||||
|
if current.yPlane.width == width && current.yPlane.height == height {
|
||||||
|
previousFrameCoefficients = current
|
||||||
|
} else {
|
||||||
|
self.isFailed = true
|
||||||
|
throw WriteError.generic
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
previousFrameCoefficients = DctCoefficientsYUVA420(width: width, height: height)
|
||||||
|
self.previousFrameCoefficients = previousFrameCoefficients
|
||||||
|
}
|
||||||
|
|
||||||
|
let deltaFrameFloat: FloatCoefficientsYUVA420
|
||||||
|
if let current = self.deltaFrameFloat {
|
||||||
|
if current.yPlane.width == width && current.yPlane.height == height {
|
||||||
|
deltaFrameFloat = current
|
||||||
|
} else {
|
||||||
|
self.isFailed = true
|
||||||
|
throw WriteError.generic
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deltaFrameFloat = FloatCoefficientsYUVA420(width: width, height: height)
|
||||||
|
self.deltaFrameFloat = deltaFrameFloat
|
||||||
|
}
|
||||||
|
|
||||||
|
let dctData: DctData
|
||||||
|
if let current = self.currentDctData {
|
||||||
|
dctData = current
|
||||||
|
} else {
|
||||||
|
dctData = DctData(generatingTablesAtQualityLuma: self.dctQualityLuma, chroma: self.dctQualityChroma, delta: self.dctQualityDelta)
|
||||||
|
self.currentDctData = dctData
|
||||||
|
}
|
||||||
|
|
||||||
|
let duration = drawingBlock(yuvaSurface)
|
||||||
|
|
||||||
|
guard let duration = duration else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let dctCoefficients: DctCoefficientsYUVA420
|
||||||
|
if let current = self.currentDctCoefficients {
|
||||||
|
if current.yPlane.width == width && current.yPlane.height == height {
|
||||||
|
dctCoefficients = current
|
||||||
|
} else {
|
||||||
|
self.isFailed = true
|
||||||
|
throw WriteError.generic
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dctCoefficients = DctCoefficientsYUVA420(width: width, height: height)
|
||||||
|
self.currentDctCoefficients = dctCoefficients
|
||||||
|
}
|
||||||
|
|
||||||
|
let differenceCoefficients: DctCoefficientsYUVA420
|
||||||
|
if let current = self.differenceCoefficients {
|
||||||
|
if current.yPlane.width == width && current.yPlane.height == height {
|
||||||
|
differenceCoefficients = current
|
||||||
|
} else {
|
||||||
|
self.isFailed = true
|
||||||
|
throw WriteError.generic
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
differenceCoefficients = DctCoefficientsYUVA420(width: width, height: height)
|
||||||
|
self.differenceCoefficients = differenceCoefficients
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG && false
|
||||||
|
var insertKeyframe = insertKeyframe
|
||||||
|
insertKeyframe = true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
let previousYUVASurface: ImageYUVA420
|
||||||
|
if let current = self.previousYUVASurface {
|
||||||
|
previousYUVASurface = current
|
||||||
|
} else {
|
||||||
|
previousYUVASurface = ImageYUVA420(width: dctCoefficients.yPlane.width, height: dctCoefficients.yPlane.height, rowAlignment: nil)
|
||||||
|
self.previousYUVASurface = previousYUVASurface
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
file.write(6 as UInt32)
|
||||||
|
|
||||||
|
file.write(UInt32(dctCoefficients.yPlane.width))
|
||||||
|
file.write(UInt32(dctCoefficients.yPlane.height))
|
||||||
|
|
||||||
|
let lumaDctTable = dctData.lumaTable.serializedData()
|
||||||
|
file.write(UInt32(lumaDctTable.count))
|
||||||
|
let _ = file.write(lumaDctTable)
|
||||||
|
|
||||||
|
let chromaDctTable = dctData.chromaTable.serializedData()
|
||||||
|
file.write(UInt32(chromaDctTable.count))
|
||||||
|
let _ = file.write(chromaDctTable)
|
||||||
|
|
||||||
|
let deltaDctTable = dctData.deltaTable.serializedData()
|
||||||
|
file.write(UInt32(deltaDctTable.count))
|
||||||
|
let _ = file.write(deltaDctTable)
|
||||||
|
|
||||||
|
self.contentLengthOffset = Int(file.position())
|
||||||
|
file.write(0 as UInt32)
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let frameLength = dctCoefficients.yPlane.data.count + dctCoefficients.uPlane.data.count + dctCoefficients.vPlane.data.count + dctCoefficients.aPlane.data.count
|
||||||
|
try compressedWriter.writeUInt32(UInt32(frameLength))
|
||||||
|
|
||||||
|
try compressedWriter.writeUInt32(isKeyframe ? 1 : 0)
|
||||||
|
|
||||||
|
for i in 0 ..< 4 {
|
||||||
|
let dctPlane: DctCoefficientPlane
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
dctPlane = dctCoefficients.yPlane
|
||||||
|
case 1:
|
||||||
|
dctPlane = dctCoefficients.uPlane
|
||||||
|
case 2:
|
||||||
|
dctPlane = dctCoefficients.vPlane
|
||||||
|
case 3:
|
||||||
|
dctPlane = dctCoefficients.aPlane
|
||||||
|
default:
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
try compressedWriter.writeUInt32(UInt32(dctPlane.data.count))
|
||||||
|
try dctPlane.data.withUnsafeBytes { bytes in
|
||||||
|
try compressedWriter.write(bytes: bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), count: bytes.count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.frames.append(FrameMetadata(duration: duration))
|
||||||
|
} catch {
|
||||||
|
self.isFailed = true
|
||||||
|
throw WriteError.generic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user