< Summary

Class:DCL.GifDecoderProcessor
Assembly:GifProcessor
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/AssetManager/Gif/GifProcessor/GifDecoderProcessor.cs
Covered lines:61
Uncovered lines:4
Coverable lines:65
Total lines:183
Line coverage:93.8% (61 of 65)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
RawImage(...)0%110100%
GifDecoderProcessor(...)0%110100%
GifDecoderProcessor(...)0%2100%
DisposeGif()0%2.062075%
Load()0%550100%
StartDecoding()0%15.0815092.86%
ProcessGifData()0%440100%
DownloadGifAndReadStream()0%440100%
ReadStream(...)0%110100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/AssetManager/Gif/GifProcessor/GifDecoderProcessor.cs

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Collections.Generic;
 4using System.Diagnostics;
 5using System.IO;
 6using System.Threading;
 7using Cysharp.Threading.Tasks;
 8using DCL.Helpers;
 9using ThreeDISevenZeroR.UnityGifDecoder;
 10using ThreeDISevenZeroR.UnityGifDecoder.Model;
 11using UnityEngine;
 12using Debug = UnityEngine.Debug;
 13
 14namespace DCL
 15{
 16    public class GifWebRequestException : Exception
 17    {
 18        public GifWebRequestException(string message) : base(message) { }
 19    }
 20
 21    public class GifDecoderProcessor : IGifProcessor
 22    {
 23        private struct RawImage
 24        {
 25            public readonly Color32[] colors;
 26            public readonly float delay;
 27            public RawImage(GifImage gifImage)
 28            {
 29                //(Kinerius): We have to clone the colors as the pointer gets modifier as we read the gif
 27030                colors = gifImage.colors.Clone() as Color32[];
 27031                delay = gifImage.SafeDelaySeconds;
 27032            }
 33        }
 34
 35        private static bool isRunning;
 36        private bool isRunningInternal;
 1237        private readonly ThrottlingCounter throttlingCounter = new ThrottlingCounter();
 38
 39        private readonly string url;
 40        private readonly Stream stream;
 41        private readonly IWebRequestController webRequestController;
 42        private GifFrameData[] gifFrameData;
 43
 1244        public GifDecoderProcessor(string url, IWebRequestController webRequestController)
 45        {
 1246            this.url = url;
 1247            this.webRequestController = webRequestController;
 1248        }
 49
 050        public GifDecoderProcessor(Stream stream) { this.stream = stream; }
 51
 52        public void DisposeGif()
 53        {
 1254            gifFrameData = null;
 1255            webRequestController.Dispose();
 1256            stream?.Dispose();
 057        }
 58
 59        public async UniTask Load(Action<GifFrameData[]> loadSuccsess, Action<Exception> fail, CancellationToken token)
 60        {
 61            try
 62            {
 3663                await StartDecoding(loadSuccsess, fail, token);
 564            }
 765            catch (Exception e) when (!(e is OperationCanceledException))
 66            {
 367                Debug.LogException(e);
 368                fail(e);
 369            }
 870        }
 71
 72        private async UniTask StartDecoding(Action<GifFrameData[]> loadSuccsess, Action<Exception> fail, CancellationTok
 73        {
 74            try
 75            {
 76                // (Kinerius): I noticed that overall the loading of multiple gifs at the same time is faster when only
 77                //          one is loading, this also avoids the "burst" of gifs loading at the same time, overall
 78                //          improving the smoothness and the experience, this could be further improved by prioritizing
 79                //          the processing of gifs whether im close or looking at them like GLFTs.
 4580                await UniTask.WaitUntil(() => isRunning == false, cancellationToken: token);
 81
 982                isRunning = false;
 983                isRunningInternal = true;
 84
 985                var stopwatch = new Stopwatch();
 986                stopwatch.Start();
 87
 88                GifStream gifStream;
 89
 990                if (stream != null)
 91                {
 092                    gifStream = new GifStream(stream);
 093                }
 94                else
 95                {
 2796                    gifStream = new GifStream( await DownloadGifAndReadStream(token));
 97                }
 98
 1899                var images = await ReadStream(gifStream, token);
 100
 6101                token.ThrowIfCancellationRequested();
 102
 18103                await TaskUtils.RunThrottledCoroutine(ProcessGifData(images, gifStream.Header.width, gifStream.Header.he
 104                               .AttachExternalCancellation(token);
 105
 5106                loadSuccsess(gifFrameData);
 107
 5108                stopwatch.Stop();
 5109            }
 7110            catch (Exception e) when (!(e is OperationCanceledException))
 111            {
 3112                throw;
 113            }
 114            finally
 115            {
 12116                if (isRunningInternal)
 9117                    isRunning = false;
 118            }
 5119        }
 120
 121        private IEnumerator ProcessGifData(List<RawImage> rawImages, int width, int height)
 122        {
 6123            gifFrameData = new GifFrameData[rawImages.Count];
 6124            SkipFrameIfDepletedTimeBudget skipFrameIfDepletedTimeBudget = new SkipFrameIfDepletedTimeBudget();
 125
 552126            for (var i = 0; i < rawImages.Count; i++)
 127            {
 270128                var frame = new Texture2D(
 129                    width,
 130                    height,
 131                    TextureFormat.ARGB32, false);
 132
 270133                frame.SetPixels32(rawImages[i].colors);
 270134                frame.Compress(false);
 270135                frame.Apply();
 136
 270137                gifFrameData[i] = new GifFrameData()
 138                {
 139                    texture = frame,
 140                    delay = rawImages[i].delay
 141                };
 142
 270143                yield return skipFrameIfDepletedTimeBudget;
 144            }
 6145        }
 146
 147        private async UniTask<byte[]> DownloadGifAndReadStream(CancellationToken token)
 148        {
 9149            var operation = webRequestController.Get(url, timeout: 15, disposeOnCompleted: false);
 36150            await UniTask.WaitUntil( () => operation.isDone || operation.isDisposed || operation.isSucceded, cancellatio
 151
 9152            if (!operation.isSucceded)
 153            {
 3154                throw new GifWebRequestException(url);
 155            }
 156
 6157            return operation.webRequest.downloadHandler.data;
 6158        }
 159
 160        private static UniTask<List<RawImage>> ReadStream(GifStream gifStream, CancellationToken token)
 161        {
 6162            return TaskUtils.Run( () =>
 163            {
 6164                var rawImages = new List<RawImage>();
 165
 834166                while (gifStream.HasMoreData)
 167                {
 828168                    if (gifStream.CurrentToken == GifStream.Token.Image)
 169                    {
 270170                        GifImage gifImage = gifStream.ReadImage();
 270171                        rawImages.Add(new RawImage(gifImage));
 270172                    }
 173                    else
 174                    {
 558175                        gifStream.SkipToken();
 176                    }
 177                }
 178
 6179                return rawImages;
 180            }, token);
 181        }
 182    }
 183}