[skottie] Multi-frame image support

Extend the image asset provider API to support animated/multi-frame images.

Add a GM based on SkAnimCodecPlayer + animated public domain GIF
(source: https://giphy.com/explore/public-domain).

Bug: skia:
Change-Id: Iaa596e01a7626ca6574db1ebc90632f5a9a02bdc
Reviewed-on: https://skia-review.googlesource.com/159162
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/modules/skottie/gm/SkottieGM.cpp b/modules/skottie/gm/SkottieGM.cpp
index 229df62..6e88837 100644
--- a/modules/skottie/gm/SkottieGM.cpp
+++ b/modules/skottie/gm/SkottieGM.cpp
@@ -7,8 +7,10 @@
 
 #include "gm.h"
 #include "Resources.h"
+#include "SkAnimCodecPlayer.h"
 #include "SkAnimTimer.h"
 #include "SkColor.h"
+#include "SkMakeUnique.h"
 #include "Skottie.h"
 #include "SkottieProperty.h"
 
@@ -88,7 +90,6 @@
 DEF_GM(return new SkottieWebFontGM;)
 
 class SkottieColorizeGM : public skiagm::GM {
-public:
 protected:
     SkString onShortName() override {
         return SkString("skottie_colorize");
@@ -126,7 +127,6 @@
         return true;
     }
 
-protected:
     bool onHandleKey(SkUnichar uni) override {
         static constexpr SkColor kColors[] = {
             SK_ColorBLACK,
@@ -173,3 +173,81 @@
 };
 
 DEF_GM(return new SkottieColorizeGM;)
+
+class SkottieMultiFrameGM : public skiagm::GM {
+public:
+protected:
+    SkString onShortName() override {
+        return SkString("skottie_multiframe");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kSize, kSize);
+    }
+
+    void onOnceBeforeDraw() override {
+        if (auto stream = GetResourceAsStream("skottie/skottie_sample_multiframe.json")) {
+            fAnimation = Animation::Builder()
+                            .setResourceProvider(sk_make_sp<MultiFrameResourceProvider>())
+                            .make(stream.get());
+        }
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        if (!fAnimation) {
+            return;
+        }
+
+        auto dest = SkRect::MakeWH(kSize, kSize);
+        fAnimation->render(canvas, &dest);
+    }
+
+    bool onAnimate(const SkAnimTimer& timer) override {
+        if (!fAnimation) {
+            return false;
+        }
+
+        const auto duration = fAnimation->duration();
+        fAnimation->seek(std::fmod(timer.secs(), duration) / duration);
+        return true;
+    }
+
+private:
+    class MultiFrameImageAsset final : public skottie::ImageAsset {
+    public:
+        MultiFrameImageAsset() {
+            if (auto codec = SkCodec::MakeFromData(GetResourceAsData("images/flightAnim.gif"))) {
+                fPlayer = skstd::make_unique<SkAnimCodecPlayer>(std::move(codec));
+            }
+        }
+
+        bool isMultiFrame() override { return fPlayer ? fPlayer->duration() > 0 : false; }
+
+        sk_sp<SkImage> getFrame(float t) override {
+            if (!fPlayer) {
+                return nullptr;
+            }
+
+            fPlayer->seek(static_cast<uint32_t>(t * 1000));
+            return fPlayer->getFrame();
+        }
+
+    private:
+        std::unique_ptr<SkAnimCodecPlayer> fPlayer;
+    };
+
+    class MultiFrameResourceProvider final : public skottie::ResourceProvider {
+    public:
+        sk_sp<ImageAsset> loadImageAsset(const char[], const char[]) const override {
+            return sk_make_sp<MultiFrameImageAsset>();
+        }
+    };
+
+    static constexpr SkScalar kSize = 800;
+
+    sk_sp<Animation> fAnimation;
+
+    using INHERITED = skiagm::GM;
+};
+
+DEF_GM(return new SkottieMultiFrameGM;)