[skottie] Make animated image support generally available

Consolidate some helpers under skottie_utils, and update all related
tools to support animated images.

TBR=
Change-Id: If08e97143a11d9a414f3230a49ab4284c508b9d0
Reviewed-on: https://skia-review.googlesource.com/c/169342
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index c889c8b..f23716b 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1705,6 +1705,7 @@
         ":tests",
         ":tool_utils",
         "modules/skottie",
+        "modules/skottie:utils",
         "modules/sksg",
         "//third_party/jsoncpp",
         "//third_party/libpng",
@@ -2146,6 +2147,7 @@
       ":skia",
       ":tool_utils",
       "modules/skottie",
+      "modules/skottie:utils",
       "modules/sksg",
       "modules/sksg:samples",
       "//third_party/imgui",
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index e3774da..14311c4 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -64,6 +64,7 @@
 
 #if defined(SK_ENABLE_SKOTTIE)
     #include "Skottie.h"
+    #include "SkottieUtils.h"
 #endif
 
 #if defined(SK_XML)
@@ -1193,7 +1194,10 @@
 SkottieSrc::SkottieSrc(Path path) : fPath(std::move(path)) {}
 
 Error SkottieSrc::draw(SkCanvas* canvas) const {
-    auto animation = skottie::Animation::MakeFromFile(fPath.c_str());
+    auto animation = skottie::Animation::Builder()
+        .setResourceProvider(
+                skottie_utils::FileResourceProvider::Make(SkOSPath::Dirname(fPath.c_str())))
+        .makeFromFile(fPath.c_str());
     if (!animation) {
         return SkStringPrintf("Unable to parse file: %s", fPath.c_str());
     }
diff --git a/modules/skottie/BUILD.gn b/modules/skottie/BUILD.gn
index a572751..1178183 100644
--- a/modules/skottie/BUILD.gn
+++ b/modules/skottie/BUILD.gn
@@ -29,6 +29,25 @@
 }
 
 if (defined(is_skia_standalone)) {
+  config("utils_config") {
+    include_dirs = [ "utils" ]
+  }
+  source_set("utils") {
+    if (skia_enable_skottie) {
+      testonly = true
+
+      public_configs = [ ":utils_config" ]
+      configs += [ "../../:skia_private" ]
+      sources = [
+        "utils/SkottieUtils.cpp",
+      ]
+      deps = [
+        ":skottie",
+        "../..:skia",
+      ]
+    }
+  }
+
   source_set("tests") {
     if (skia_enable_skottie) {
       testonly = true
@@ -83,6 +102,7 @@
 
     public_deps = [
       ":skottie",
+      ":utils",
     ]
   }
 
@@ -100,6 +120,7 @@
 
       deps = [
         ":skottie",
+        ":utils",
         "../..:gpu_tool_utils",
         "../..:skia",
         "../..:tool_utils",
diff --git a/modules/skottie/gm/SkottieGM.cpp b/modules/skottie/gm/SkottieGM.cpp
index 6e88837..040b8b3 100644
--- a/modules/skottie/gm/SkottieGM.cpp
+++ b/modules/skottie/gm/SkottieGM.cpp
@@ -13,6 +13,7 @@
 #include "SkMakeUnique.h"
 #include "Skottie.h"
 #include "SkottieProperty.h"
+#include "SkottieUtils.h"
 
 #include <cmath>
 #include <vector>
@@ -213,33 +214,11 @@
     }
 
 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>();
+            return skottie_utils::MultiFrameImageAsset::Make(
+                        GetResourceAsData("images/flightAnim.gif"));
         }
     };
 
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp
index 792ca05..66aee73 100644
--- a/modules/skottie/src/Skottie.cpp
+++ b/modules/skottie/src/Skottie.cpp
@@ -12,7 +12,6 @@
 #include "SkFontMgr.h"
 #include "SkImage.h"
 #include "SkMakeUnique.h"
-#include "SkOSPath.h"
 #include "SkPaint.h"
 #include "SkPoint.h"
 #include "SkSGColor.h"
@@ -389,58 +388,10 @@
 }
 
 sk_sp<Animation> Animation::Builder::makeFromFile(const char path[]) {
-    class DirectoryResourceProvider final : public ResourceProvider {
-    public:
-        explicit DirectoryResourceProvider(SkString dir) : fDir(std::move(dir)) {}
-
-        sk_sp<SkData> load(const char resource_path[], const char resource_name[]) const override {
-            const auto full_dir  = SkOSPath::Join(fDir.c_str(), resource_path),
-                       full_path = SkOSPath::Join(full_dir.c_str(), resource_name);
-            return SkData::MakeFromFileName(full_path.c_str());
-        }
-
-        sk_sp<ImageAsset> loadImageAsset(const char resource_path[],
-                                         const char resource_name[]) const override {
-            auto data = this->load(resource_path, resource_name);
-
-            return data
-                ? sk_make_sp<StaticImageAsset>(SkImage::MakeFromEncoded(std::move(data)))
-                : nullptr;
-        }
-
-    private:
-        class StaticImageAsset final : public ImageAsset {
-        public:
-            explicit StaticImageAsset(sk_sp<SkImage> image)
-                : fImage(std::move(image)) {}
-
-            bool isMultiFrame() override { return false; }
-
-            sk_sp<SkImage> getFrame(float) override { return fImage; }
-
-        private:
-            sk_sp<SkImage> fImage;
-        };
-
-        const SkString fDir;
-    };
-
     const auto data = SkData::MakeFromFileName(path);
-    if (!data)
-        return nullptr;
 
-    const auto useLocalProvider = !fResourceProvider;
-    if (useLocalProvider) {
-        fResourceProvider = sk_make_sp<DirectoryResourceProvider>(SkOSPath::Dirname(path));
-    }
-
-    auto animation = this->make(static_cast<const char*>(data->data()), data->size());
-
-    if (useLocalProvider) {
-        fResourceProvider.reset();
-    }
-
-    return animation;
+    return data ? this->make(static_cast<const char*>(data->data()), data->size())
+                : nullptr;
 }
 
 Animation::Animation(std::unique_ptr<sksg::Scene> scene, SkString version, const SkSize& size,
diff --git a/modules/skottie/src/SkottieTool.cpp b/modules/skottie/src/SkottieTool.cpp
index ed394e1..4babf33 100644
--- a/modules/skottie/src/SkottieTool.cpp
+++ b/modules/skottie/src/SkottieTool.cpp
@@ -12,6 +12,7 @@
 #include "SkOSFile.h"
 #include "SkOSPath.h"
 #include "Skottie.h"
+#include "SkottieUtils.h"
 #include "SkPictureRecorder.h"
 #include "SkStream.h"
 #include "SkSurface.h"
@@ -187,6 +188,8 @@
 
     auto anim = skottie::Animation::Builder()
             .setLogger(logger)
+            .setResourceProvider(
+                skottie_utils::FileResourceProvider::Make(SkOSPath::Dirname(FLAGS_input[0])))
             .makeFromFile(FLAGS_input[0]);
     if (!anim) {
         SkDebugf("Could not load animation: '%s'.\n", FLAGS_input[0]);
diff --git a/modules/skottie/utils/SkottieUtils.cpp b/modules/skottie/utils/SkottieUtils.cpp
new file mode 100644
index 0000000..d8f39d3
--- /dev/null
+++ b/modules/skottie/utils/SkottieUtils.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkottieUtils.h"
+
+#include "SkAnimCodecPlayer.h"
+#include "SkData.h"
+#include "SkCodec.h"
+#include "SkImage.h"
+#include "SkMakeUnique.h"
+#include "SkOSFile.h"
+#include "SkOSPath.h"
+
+namespace skottie_utils {
+
+sk_sp<MultiFrameImageAsset> MultiFrameImageAsset::Make(sk_sp<SkData> data) {
+    if (auto codec = SkCodec::MakeFromData(std::move(data))) {
+        return sk_sp<MultiFrameImageAsset>(
+              new MultiFrameImageAsset(skstd::make_unique<SkAnimCodecPlayer>(std::move(codec))));
+    }
+
+    return nullptr;
+}
+
+MultiFrameImageAsset::MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer> player)
+    : fPlayer(std::move(player)) {
+    SkASSERT(fPlayer);
+}
+
+bool MultiFrameImageAsset::isMultiFrame() {
+    return fPlayer->duration() > 0;
+}
+
+sk_sp<SkImage> MultiFrameImageAsset::getFrame(float t) {
+    fPlayer->seek(static_cast<uint32_t>(t * 1000));
+    return fPlayer->getFrame();
+}
+
+sk_sp<FileResourceProvider> FileResourceProvider::Make(SkString base_dir) {
+    return sk_isdir(base_dir.c_str())
+        ? sk_sp<FileResourceProvider>(new FileResourceProvider(std::move(base_dir)))
+        : nullptr;
+}
+
+FileResourceProvider::FileResourceProvider(SkString base_dir) : fDir(std::move(base_dir)) {}
+
+sk_sp<SkData> FileResourceProvider::load(const char resource_path[],
+                                         const char resource_name[]) const {
+    const auto full_dir  = SkOSPath::Join(fDir.c_str()    , resource_path),
+               full_path = SkOSPath::Join(full_dir.c_str(), resource_name);
+    return SkData::MakeFromFileName(full_path.c_str());
+}
+
+sk_sp<skottie::ImageAsset> FileResourceProvider::loadImageAsset(const char resource_path[],
+                                                                const char resource_name[]) const {
+    return MultiFrameImageAsset::Make(this->load(resource_path, resource_name));
+}
+
+} // namespace skottie_utils
diff --git a/modules/skottie/utils/SkottieUtils.h b/modules/skottie/utils/SkottieUtils.h
new file mode 100644
index 0000000..06f134a
--- /dev/null
+++ b/modules/skottie/utils/SkottieUtils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkottieUtils_DEFINED
+#define SkottieUtils_DEFINED
+
+#include "Skottie.h"
+#include "SkString.h"
+
+#include <memory>
+
+class SkAnimCodecPlayer;
+class SkData;
+class SkImage;
+
+namespace skottie_utils {
+
+class MultiFrameImageAsset final : public skottie::ImageAsset {
+public:
+    static sk_sp<MultiFrameImageAsset> Make(sk_sp<SkData>);
+
+    bool isMultiFrame() override;
+
+    sk_sp<SkImage> getFrame(float t) override;
+
+private:
+    explicit MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer>);
+
+    std::unique_ptr<SkAnimCodecPlayer> fPlayer;
+
+    using INHERITED = skottie::ImageAsset;
+};
+
+class FileResourceProvider final : public skottie::ResourceProvider {
+public:
+    static sk_sp<FileResourceProvider> Make(SkString base_dir);
+
+    sk_sp<SkData> load(const char resource_path[], const char resource_name[]) const override;
+
+    sk_sp<skottie::ImageAsset> loadImageAsset(const char[], const char []) const override;
+
+private:
+    explicit FileResourceProvider(SkString);
+
+    const SkString fDir;
+
+    using INHERITED = skottie::ResourceProvider;
+};
+
+} // namespace skottie_utils
+
+#endif // SkottieUtils_DEFINED
diff --git a/public.bzl b/public.bzl
index 703f400..b360a11 100644
--- a/public.bzl
+++ b/public.bzl
@@ -662,11 +662,14 @@
 ################################################################################
 
 SKOTTIE_TOOL_INCLUDES = [
+    "modules/skottie/utils",
     "tools/flags",
 ]
 
 SKOTTIE_TOOL_SRCS = [
     "modules/skottie/src/SkottieTool.cpp",
+    "modules/skottie/utils/SkottieUtils.cpp",
+    "modules/skottie/utils/SkottieUtils.h",
     # TODO(benjaminwagner): Add "flags" target.
     "tools/flags/SkCommandLineFlags.cpp",
     "tools/flags/SkCommandLineFlags.h",
diff --git a/tools/viewer/SkottieSlide.cpp b/tools/viewer/SkottieSlide.cpp
index 251faaa..ca164fb 100644
--- a/tools/viewer/SkottieSlide.cpp
+++ b/tools/viewer/SkottieSlide.cpp
@@ -11,7 +11,9 @@
 
 #include "SkAnimTimer.h"
 #include "SkCanvas.h"
+#include "SkOSPath.h"
 #include "Skottie.h"
+#include "SkottieUtils.h"
 
 #include <cmath>
 
@@ -95,7 +97,11 @@
     auto logger = sk_make_sp<Logger>();
     skottie::Animation::Builder builder;
 
-    fAnimation      = builder.setLogger(logger).makeFromFile(fPath.c_str());
+    fAnimation      = builder
+            .setLogger(logger)
+            .setResourceProvider(
+                skottie_utils::FileResourceProvider::Make(SkOSPath::Dirname(fPath.c_str())))
+            .makeFromFile(fPath.c_str());
     fAnimationStats = builder.getStats();
     fWinSize        = SkSize::Make(w, h);
     fTimeBase       = 0; // force a time reset