diff --git a/Tests/AnimationCacheTest/BUILD b/Tests/AnimationCacheTest/BUILD
index 7649ea11df..b42fca4697 100644
--- a/Tests/AnimationCacheTest/BUILD
+++ b/Tests/AnimationCacheTest/BUILD
@@ -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",
     ],
 )
diff --git a/Tests/AnimationCacheTest/Resources/sticker.webm b/Tests/AnimationCacheTest/Resources/sticker.webm
new file mode 100644
index 0000000000..4d01aa5b8e
Binary files /dev/null and b/Tests/AnimationCacheTest/Resources/sticker.webm differ
diff --git a/Tests/AnimationCacheTest/Sources/ViewController.swift b/Tests/AnimationCacheTest/Sources/ViewController.swift
index 964b832277..bfd2798873 100644
--- a/Tests/AnimationCacheTest/Sources/ViewController.swift
+++ b/Tests/AnimationCacheTest/Sources/ViewController.swift
@@ -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
             }
diff --git a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/PublicHeaders/ImageDCT/YuvConversion.h b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/PublicHeaders/ImageDCT/YuvConversion.h
index cd81059078..f935e3cafd 100644
--- a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/PublicHeaders/ImageDCT/YuvConversion.h
+++ b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/PublicHeaders/ImageDCT/YuvConversion.h
@@ -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__
 }
diff --git a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/DCT.cpp b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/DCT.cpp
index 9775f1ee66..41d11043e3 100644
--- a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/DCT.cpp
+++ b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/DCT.cpp
@@ -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];
                 }
-            }
+            }*/
         }
     }
 }
diff --git a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/YuvConversion.m b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/YuvConversion.m
index dd7123e2ce..0f2532e212 100644
--- a/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/YuvConversion.m
+++ b/submodules/TelegramUI/Components/AnimationCache/ImageDCT/Sources/YuvConversion.m
@@ -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
+}
diff --git a/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift b/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift
index eba675200b..b278c3fae7 100644
--- a/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift
+++ b/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift
@@ -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
     }
     
diff --git a/submodules/TelegramUI/Components/AnimationCache/Sources/ImageData.swift b/submodules/TelegramUI/Components/AnimationCache/Sources/ImageData.swift
index 6ba90246d9..965e2f57b3 100644
--- a/submodules/TelegramUI/Components/AnimationCache/Sources/ImageData.swift
+++ b/submodules/TelegramUI/Components/AnimationCache/Sources/ImageData.swift
@@ -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)
+    }
 }
diff --git a/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift b/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift
index a22701c090..7bb2c4e0d6 100644
--- a/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift
+++ b/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift
@@ -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 {
diff --git a/submodules/TelegramUI/Components/VideoAnimationCache/Sources/VideoAnimationCache.swift b/submodules/TelegramUI/Components/VideoAnimationCache/Sources/VideoAnimationCache.swift
index 8a6d53c1d9..b1543de3b6 100644
--- a/submodules/TelegramUI/Components/VideoAnimationCache/Sources/VideoAnimationCache.swift
+++ b/submodules/TelegramUI/Components/VideoAnimationCache/Sources/VideoAnimationCache.swift
@@ -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