< Summary

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

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
GifWebRequestException(...)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    {
 618        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
 30                colors = gifImage.colors.Clone() as Color32[];
 31                delay = gifImage.SafeDelaySeconds;
 32            }
 33        }
 34
 35        private static bool isRunning;
 36        private bool isRunningInternal;
 37        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
 44        public GifDecoderProcessor(string url, IWebRequestController webRequestController)
 45        {
 46            this.url = url;
 47            this.webRequestController = webRequestController;
 48        }
 49
 50        public GifDecoderProcessor(Stream stream) { this.stream = stream; }
 51
 52        public void DisposeGif()
 53        {
 54            gifFrameData = null;
 55            webRequestController.Dispose();
 56            stream?.Dispose();
 57        }
 58
 59        public async UniTask Load(Action<GifFrameData[]> loadSuccsess, Action<Exception> fail, CancellationToken token)
 60        {
 61            try
 62            {
 63                await StartDecoding(loadSuccsess, fail, token);
 64            }
 65            catch (Exception e) when (!(e is OperationCanceledException))
 66            {
 67                Debug.LogException(e);
 68                fail(e);
 69            }
 70        }
 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.
 80                await UniTask.WaitUntil(() => isRunning == false, cancellationToken: token);
 81
 82                isRunning = false;
 83                isRunningInternal = true;
 84
 85                var stopwatch = new Stopwatch();
 86                stopwatch.Start();
 87
 88                GifStream gifStream;
 89
 90                if (stream != null)
 91                {
 92                    gifStream = new GifStream(stream);
 93                }
 94                else
 95                {
 96                    gifStream = new GifStream( await DownloadGifAndReadStream(token));
 97                }
 98
 99                var images = await ReadStream(gifStream, token);
 100
 101                token.ThrowIfCancellationRequested();
 102
 103                await TaskUtils.RunThrottledCoroutine(ProcessGifData(images, gifStream.Header.width, gifStream.Header.he
 104                               .AttachExternalCancellation(token);
 105
 106                loadSuccsess(gifFrameData);
 107
 108                stopwatch.Stop();
 109            }
 110            catch (Exception e) when (!(e is OperationCanceledException))
 111            {
 112                throw;
 113            }
 114            finally
 115            {
 116                if (isRunningInternal)
 117                    isRunning = false;
 118            }
 119        }
 120
 121        private IEnumerator ProcessGifData(List<RawImage> rawImages, int width, int height)
 122        {
 123            gifFrameData = new GifFrameData[rawImages.Count];
 124            SkipFrameIfDepletedTimeBudget skipFrameIfDepletedTimeBudget = new SkipFrameIfDepletedTimeBudget();
 125
 126            for (var i = 0; i < rawImages.Count; i++)
 127            {
 128                var frame = new Texture2D(
 129                    width,
 130                    height,
 131                    TextureFormat.ARGB32, false);
 132
 133                frame.SetPixels32(rawImages[i].colors);
 134                frame.Compress(false);
 135                frame.Apply();
 136
 137                gifFrameData[i] = new GifFrameData()
 138                {
 139                    texture = frame,
 140                    delay = rawImages[i].delay
 141                };
 142
 143                yield return skipFrameIfDepletedTimeBudget;
 144            }
 145        }
 146
 147        private async UniTask<byte[]> DownloadGifAndReadStream(CancellationToken token)
 148        {
 149            var operation = webRequestController.Get(url, timeout: 15, disposeOnCompleted: false);
 150            await UniTask.WaitUntil( () => operation.isDone || operation.isDisposed || operation.isSucceded, cancellatio
 151
 152            if (!operation.isSucceded)
 153            {
 154                throw new GifWebRequestException(url);
 155            }
 156
 157            return operation.webRequest.downloadHandler.data;
 158        }
 159
 160        private static UniTask<List<RawImage>> ReadStream(GifStream gifStream, CancellationToken token)
 161        {
 162            return TaskUtils.Run( () =>
 163            {
 164                var rawImages = new List<RawImage>();
 165
 166                while (gifStream.HasMoreData)
 167                {
 168                    if (gifStream.CurrentToken == GifStream.Token.Image)
 169                    {
 170                        GifImage gifImage = gifStream.ReadImage();
 171                        rawImages.Add(new RawImage(gifImage));
 172                    }
 173                    else
 174                    {
 175                        gifStream.SkipToken();
 176                    }
 177                }
 178
 179                return rawImages;
 180            }, token);
 181        }
 182    }
 183}