mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35: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/Display:Display",
|
||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||
"//submodules/TelegramUI/Components/VideoAnimationCache:VideoAnimationCache",
|
||||
"//submodules/TelegramUI/Components/LottieAnimationCache:LottieAnimationCache",
|
||||
"//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 Display
|
||||
import RLottieBinding
|
||||
import AnimationCache
|
||||
import SwiftSignalKit
|
||||
import VideoAnimationCache
|
||||
import LottieAnimationCache
|
||||
|
||||
public final class ViewController: UIViewController {
|
||||
private var imageView: UIImageView?
|
||||
@ -14,6 +15,7 @@ public final class ViewController: UIViewController {
|
||||
private var animationCacheItem: AnimationCacheItem?
|
||||
|
||||
//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: 16, height: 16)
|
||||
|
||||
@ -52,26 +54,19 @@ public final class ViewController: UIViewController {
|
||||
return basePath + "/\(Int64.random(in: 0 ... Int64.max))"
|
||||
})
|
||||
|
||||
let path = Bundle.main.path(forResource: "Test2", ofType: "json")!
|
||||
let data = try! Data(contentsOf: URL(fileURLWithPath: path))
|
||||
let path = Bundle.main.path(forResource: "sticker", ofType: "webm")!
|
||||
|
||||
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
|
||||
writer.queue.async {
|
||||
let lottieInstance = LottieInstance(data: data, fitzModifier: .none, colorReplacements: nil, cacheKey: "")!
|
||||
|
||||
for i in 0 ..< min(600, Int(lottieInstance.frameCount)) {
|
||||
//for _ in 0 ..< 10 {
|
||||
writer.add(with: { surface in
|
||||
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)
|
||||
//}
|
||||
let _ = (self.cache!.get(sourceId: "Item\(Int64.random(in: 0 ... Int64.max))", size: scaledSize, fetch: { options in
|
||||
options.writer.queue.async {
|
||||
if path.hasSuffix(".webm") {
|
||||
cacheVideoAnimation(path: path, width: Int(options.size.width), height: Int(options.size.height), writer: options.writer, firstFrameOnly: options.firstFrameOnly)
|
||||
} else {
|
||||
let data = try! Data(contentsOf: URL(fileURLWithPath: path))
|
||||
cacheLottieAnimation(data: data, width: Int(options.size.width), height: Int(options.size.height), keyframeOnly: false, writer: options.writer, firstFrameOnly: options.firstFrameOnly)
|
||||
}
|
||||
|
||||
writer.finish()
|
||||
options.writer.finish()
|
||||
}
|
||||
|
||||
return EmptyDisposable
|
||||
@ -107,12 +102,12 @@ public final class ViewController: UIViewController {
|
||||
|
||||
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()
|
||||
})*/
|
||||
DispatchQueue.main.async {
|
||||
})
|
||||
/*DispatchQueue.main.async {
|
||||
self.updateImage()
|
||||
}
|
||||
}*/
|
||||
default:
|
||||
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 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 addArraysUInt8Int16(uint8_t const *a, int16_t const *b, uint8_t *dest, int length);
|
||||
|
||||
#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]));
|
||||
}
|
||||
|
||||
static int dct4x4QuantDC = 60;
|
||||
static int dct4x4QuantAC = 60;
|
||||
static int dct4x4QuantDC = 58;
|
||||
static int dct4x4QuantAC = 58;
|
||||
|
||||
void performForward4x4Dct(int16_t const *normalizedCoefficients, int16_t *coefficients, int width, int height, DCTELEM *divisors) {
|
||||
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) {
|
||||
//DCTELEM coefficientBlock[4 * 4];
|
||||
DCTELEM resultBlock[4 * 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 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++) {
|
||||
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);
|
||||
|
||||
@ -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 + 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++) {
|
||||
//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);
|
||||
}
|
||||
|
||||
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) {
|
||||
int16x8_t lhs = vld1q_s16((int16_t *)&a[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) {
|
||||
for (int i = 0; i < length; i += 8) {
|
||||
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 currentYUVASurface: ImageYUVA420?
|
||||
private var currentFrameFloat: FloatCoefficientsYUVA420?
|
||||
private var previousFrameFloat: FloatCoefficientsYUVA420?
|
||||
private var previousFrameCoefficients: DctCoefficientsYUVA420?
|
||||
private var deltaFrameFloat: FloatCoefficientsYUVA420?
|
||||
private var previousYUVASurface: ImageYUVA420?
|
||||
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) {
|
||||
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 {
|
||||
try addInternal(with: { yuvaSurface in
|
||||
surface.toYUVA420(target: yuvaSurface)
|
||||
try self.lock.throwingLocked {
|
||||
let width = roundUp(proposedWidth, multiple: 16)
|
||||
let height = roundUp(proposedHeight, multiple: 16)
|
||||
|
||||
return duration
|
||||
}, width: width, height: height, insertKeyframe: insertKeyframe)
|
||||
let surface: ImageARGB
|
||||
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 {
|
||||
}
|
||||
}
|
||||
@ -326,9 +331,11 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
|
||||
let height = roundUp(proposedHeight, multiple: 16)
|
||||
|
||||
do {
|
||||
try addInternal(with: { yuvaSurface in
|
||||
return drawingBlock(yuvaSurface)
|
||||
}, width: width, height: height, insertKeyframe: insertKeyframe)
|
||||
try self.lock.throwingLocked {
|
||||
try addInternal(with: { yuvaSurface in
|
||||
return drawingBlock(yuvaSurface)
|
||||
}, width: width, height: height, insertKeyframe: insertKeyframe)
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
@ -342,202 +349,194 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
|
||||
throw WriteError.generic
|
||||
}
|
||||
|
||||
try self.lock.throwingLocked {
|
||||
guard !self.isFailed, !self.isFinished, let file = self.file, let compressedWriter = self.compressedWriter else {
|
||||
throw WriteError.generic
|
||||
}
|
||||
|
||||
var isFirstFrame = false
|
||||
|
||||
let yuvaSurface: ImageYUVA420
|
||||
if let current = self.currentYUVASurface {
|
||||
if current.yPlane.width == width && current.yPlane.height == height {
|
||||
yuvaSurface = current
|
||||
} else {
|
||||
self.isFailed = true
|
||||
throw WriteError.generic
|
||||
}
|
||||
guard !self.isFailed, !self.isFinished, let file = self.file, let compressedWriter = self.compressedWriter else {
|
||||
throw WriteError.generic
|
||||
}
|
||||
|
||||
var isFirstFrame = false
|
||||
|
||||
let yuvaSurface: ImageYUVA420
|
||||
if let current = self.currentYUVASurface {
|
||||
if current.yPlane.width == width && current.yPlane.height == height {
|
||||
yuvaSurface = current
|
||||
} 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
|
||||
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 {
|
||||
enum FrameType {
|
||||
case key
|
||||
case delta
|
||||
}
|
||||
|
||||
let index: Int
|
||||
let type: FrameType
|
||||
var remainingDuration: 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.type = type
|
||||
self.duration = duration
|
||||
self.remainingDuration = duration
|
||||
self.dctCoefficients = dctCoefficients
|
||||
self.yuva = yuva
|
||||
}
|
||||
}
|
||||
|
||||
@ -642,6 +634,7 @@ private final class AnimationCacheItemAccessor {
|
||||
private var currentFrame: CurrentFrame?
|
||||
|
||||
private var currentYUVASurface: ImageYUVA420?
|
||||
private var currentCoefficients: DctCoefficientsYUVA420?
|
||||
private let currentDctData: DctData
|
||||
private var sharedDctCoefficients: DctCoefficientsYUVA420?
|
||||
private var deltaCoefficients: DctCoefficientsYUVA420?
|
||||
@ -696,7 +689,7 @@ private final class AnimationCacheItemAccessor {
|
||||
let frameType = Int(try compressedDataReader.readUInt32())
|
||||
|
||||
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
|
||||
} else {
|
||||
dctCoefficients = DctCoefficientsYUVA420(width: self.width, height: self.height)
|
||||
@ -738,7 +731,48 @@ private final class AnimationCacheItemAccessor {
|
||||
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 {
|
||||
self.currentFrame = nil
|
||||
self.compressedDataReader = nil
|
||||
@ -773,75 +807,38 @@ private final class AnimationCacheItemAccessor {
|
||||
return nil
|
||||
}
|
||||
|
||||
let yuvaSurface: ImageYUVA420
|
||||
switch requestedFormat {
|
||||
case .rgba:
|
||||
if let currentYUVASurface = self.currentYUVASurface {
|
||||
yuvaSurface = currentYUVASurface
|
||||
} 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
|
||||
let currentSurface = ImageARGB(width: currentFrame.yuva.yPlane.width, height: currentFrame.yuva.yPlane.height, rowAlignment: 32)
|
||||
currentFrame.yuva.toARGB(target: currentSurface)
|
||||
|
||||
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:
|
||||
return AnimationCacheItemFrame(
|
||||
format: .yuva(
|
||||
y: AnimationCacheItemFrame.Plane(
|
||||
data: yuvaSurface.yPlane.data,
|
||||
width: yuvaSurface.yPlane.width,
|
||||
height: yuvaSurface.yPlane.height,
|
||||
bytesPerRow: yuvaSurface.yPlane.bytesPerRow
|
||||
data: currentFrame.yuva.yPlane.data,
|
||||
width: currentFrame.yuva.yPlane.width,
|
||||
height: currentFrame.yuva.yPlane.height,
|
||||
bytesPerRow: currentFrame.yuva.yPlane.bytesPerRow
|
||||
),
|
||||
u: AnimationCacheItemFrame.Plane(
|
||||
data: yuvaSurface.uPlane.data,
|
||||
width: yuvaSurface.uPlane.width,
|
||||
height: yuvaSurface.uPlane.height,
|
||||
bytesPerRow: yuvaSurface.uPlane.bytesPerRow
|
||||
data: currentFrame.yuva.uPlane.data,
|
||||
width: currentFrame.yuva.uPlane.width,
|
||||
height: currentFrame.yuva.uPlane.height,
|
||||
bytesPerRow: currentFrame.yuva.uPlane.bytesPerRow
|
||||
),
|
||||
v: AnimationCacheItemFrame.Plane(
|
||||
data: yuvaSurface.vPlane.data,
|
||||
width: yuvaSurface.vPlane.width,
|
||||
height: yuvaSurface.vPlane.height,
|
||||
bytesPerRow: yuvaSurface.vPlane.bytesPerRow
|
||||
data: currentFrame.yuva.vPlane.data,
|
||||
width: currentFrame.yuva.vPlane.width,
|
||||
height: currentFrame.yuva.vPlane.height,
|
||||
bytesPerRow: currentFrame.yuva.vPlane.bytesPerRow
|
||||
),
|
||||
a: AnimationCacheItemFrame.Plane(
|
||||
data: yuvaSurface.aPlane.data,
|
||||
width: yuvaSurface.aPlane.width,
|
||||
height: yuvaSurface.aPlane.height,
|
||||
bytesPerRow: yuvaSurface.aPlane.bytesPerRow
|
||||
data: currentFrame.yuva.aPlane.data,
|
||||
width: currentFrame.yuva.aPlane.width,
|
||||
height: currentFrame.yuva.aPlane.height,
|
||||
bytesPerRow: currentFrame.yuva.aPlane.bytesPerRow
|
||||
)
|
||||
),
|
||||
duration: currentFrame.duration
|
||||
@ -1125,7 +1122,7 @@ private func loadItem(path: String) throws -> AnimationCacheItem {
|
||||
}
|
||||
let formatVersion = readUInt32(data: compressedData, offset: offset)
|
||||
offset += 4
|
||||
if formatVersion != 5 {
|
||||
if formatVersion != 6 {
|
||||
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 {
|
||||
@ -126,7 +134,15 @@ final class DctCoefficientPlane: CustomStringConvertible {
|
||||
func subtract(other: DctCoefficientPlane) {
|
||||
self.data.withUnsafeMutableBytes { bytes 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 {
|
||||
@ -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)
|
||||
|
||||
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) {
|
||||
self.yPlane.subtract(other: other.yPlane)
|
||||
self.uPlane.subtract(other: other.uPlane)
|
||||
self.vPlane.subtract(other: other.vPlane)
|
||||
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 {
|
||||
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)
|
||||
|
||||
for i in 0 ..< 4 {
|
||||
@ -566,13 +626,7 @@ extension DctCoefficientsYUVA420 {
|
||||
}
|
||||
}
|
||||
|
||||
func idct(dctData: DctData, rowAlignment: Int?) -> ImageYUVA420 {
|
||||
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) {
|
||||
func dct4x4(dctData: DctData, target: DctCoefficientsYUVA420) {
|
||||
precondition(self.yPlane.width == target.yPlane.width && self.yPlane.height == target.yPlane.height)
|
||||
|
||||
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)
|
||||
|
||||
for i in 0 ..< 4 {
|
||||
@ -653,10 +707,26 @@ extension DctCoefficientsYUVA420 {
|
||||
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) {
|
||||
self.yPlane.toFloatCoefficients(target: target.yPlane)
|
||||
self.uPlane.toFloatCoefficients(target: target.uPlane)
|
||||
self.vPlane.toFloatCoefficients(target: target.vPlane)
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
let frameSkip: Int
|
||||
if animation.frameRate >= 60 {
|
||||
if ProcessInfo.processInfo.processorCount > 2 {
|
||||
|
@ -45,7 +45,7 @@ public func cacheVideoAnimation(path: String, width: Int, height: Int, writer: A
|
||||
}
|
||||
}
|
||||
return frameDuration
|
||||
}, proposedWidth: frame.width, proposedHeight: frame.height, insertKeyframe: true)
|
||||
}, proposedWidth: frame.width, proposedHeight: frame.height, insertKeyframe: false)
|
||||
|
||||
if firstFrameOnly {
|
||||
break
|
||||
|
Loading…
x
Reference in New Issue
Block a user