[skottie] External logger support

Add a skottie::Logger interface, allowing clients to register for log events
from the animation builder.

Convert existing log messages to the new machinery.

Change-Id: If9083f89b27f197bfc0d8d81860bbacb6b764be3
Reviewed-on: https://skia-review.googlesource.com/c/158580
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/tools/viewer/SkottieSlide.cpp b/tools/viewer/SkottieSlide.cpp
index 51b0311..251faaa 100644
--- a/tools/viewer/SkottieSlide.cpp
+++ b/tools/viewer/SkottieSlide.cpp
@@ -59,18 +59,54 @@
 }
 
 void SkottieSlide::load(SkScalar w, SkScalar h) {
+    class Logger final : public skottie::Logger {
+    public:
+        struct LogEntry {
+            SkString fMessage,
+                     fJSON;
+        };
+
+        void log(skottie::Logger::Level lvl, const char message[], const char json[]) override {
+            auto& log = lvl == skottie::Logger::Level::kError ? fErrors : fWarnings;
+            log.push_back({ SkString(message), json ? SkString(json) : SkString() });
+        }
+
+        void report() const {
+            SkDebugf("Animation loaded with %lu error%s, %lu warning%s.\n",
+                     fErrors.size(), fErrors.size() == 1 ? "" : "s",
+                     fWarnings.size(), fWarnings.size() == 1 ? "" : "s");
+
+            const auto& show = [](const LogEntry& log, const char prefix[]) {
+                SkDebugf("%s%s", prefix, log.fMessage.c_str());
+                if (!log.fJSON.isEmpty())
+                    SkDebugf(" : %s", log.fJSON.c_str());
+                SkDebugf("\n");
+            };
+
+            for (const auto& err : fErrors)   show(err, "  !! ");
+            for (const auto& wrn : fWarnings) show(wrn, "  ?? ");
+        }
+
+    private:
+        std::vector<LogEntry> fErrors,
+                              fWarnings;
+    };
+
+    auto logger = sk_make_sp<Logger>();
     skottie::Animation::Builder builder;
-    fAnimation      = builder.makeFromFile(fPath.c_str());
+
+    fAnimation      = builder.setLogger(logger).makeFromFile(fPath.c_str());
     fAnimationStats = builder.getStats();
     fWinSize        = SkSize::Make(w, h);
     fTimeBase       = 0; // force a time reset
 
     if (fAnimation) {
         fAnimation->setShowInval(fShowAnimationInval);
-        SkDebugf("loaded Bodymovin animation v: %s, size: [%f %f]\n",
+        SkDebugf("Loaded Bodymovin animation v: %s, size: [%f %f]\n",
                  fAnimation->version().c_str(),
                  fAnimation->size().width(),
                  fAnimation->size().height());
+        logger->report();
     } else {
         SkDebugf("failed to load Bodymovin animation: %s\n", fPath.c_str());
     }