Home | History | Annotate | Download | only in viewer
      1 /*
      2  * Copyright 2017 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkottieSlide.h"
      9 
     10 #if defined(SK_ENABLE_SKOTTIE)
     11 
     12 #include "SkAnimTimer.h"
     13 #include "SkCanvas.h"
     14 #include "SkFont.h"
     15 #include "SkOSPath.h"
     16 #include "Skottie.h"
     17 #include "SkottieUtils.h"
     18 
     19 #include <cmath>
     20 
     21 static void draw_stats_box(SkCanvas* canvas, const skottie::Animation::Builder::Stats& stats) {
     22     static constexpr SkRect kR = { 10, 10, 280, 120 };
     23     static constexpr SkScalar kTextSize = 20;
     24 
     25     SkPaint paint;
     26     paint.setAntiAlias(true);
     27     paint.setColor(0xffeeeeee);
     28 
     29     SkFont font(nullptr, kTextSize);
     30 
     31     canvas->drawRect(kR, paint);
     32 
     33     paint.setColor(SK_ColorBLACK);
     34 
     35     const auto json_size = SkStringPrintf("Json size: %lu bytes",
     36                                           stats.fJsonSize);
     37     canvas->drawString(json_size, kR.x() + 10, kR.y() + kTextSize * 1, font, paint);
     38     const auto animator_count = SkStringPrintf("Animator count: %lu",
     39                                                stats.fAnimatorCount);
     40     canvas->drawString(animator_count, kR.x() + 10, kR.y() + kTextSize * 2, font, paint);
     41     const auto json_parse_time = SkStringPrintf("Json parse time: %.3f ms",
     42                                                 stats.fJsonParseTimeMS);
     43     canvas->drawString(json_parse_time, kR.x() + 10, kR.y() + kTextSize * 3, font, paint);
     44     const auto scene_parse_time = SkStringPrintf("Scene build time: %.3f ms",
     45                                                  stats.fSceneParseTimeMS);
     46     canvas->drawString(scene_parse_time, kR.x() + 10, kR.y() + kTextSize * 4, font, paint);
     47     const auto total_load_time = SkStringPrintf("Total load time: %.3f ms",
     48                                                 stats.fTotalLoadTimeMS);
     49     canvas->drawString(total_load_time, kR.x() + 10, kR.y() + kTextSize * 5, font, paint);
     50 
     51     paint.setStyle(SkPaint::kStroke_Style);
     52     canvas->drawRect(kR, paint);
     53 }
     54 
     55 SkottieSlide::SkottieSlide(const SkString& name, const SkString& path)
     56     : fPath(path) {
     57     fName = name;
     58 }
     59 
     60 void SkottieSlide::load(SkScalar w, SkScalar h) {
     61     class Logger final : public skottie::Logger {
     62     public:
     63         struct LogEntry {
     64             SkString fMessage,
     65                      fJSON;
     66         };
     67 
     68         void log(skottie::Logger::Level lvl, const char message[], const char json[]) override {
     69             auto& log = lvl == skottie::Logger::Level::kError ? fErrors : fWarnings;
     70             log.push_back({ SkString(message), json ? SkString(json) : SkString() });
     71         }
     72 
     73         void report() const {
     74             SkDebugf("Animation loaded with %lu error%s, %lu warning%s.\n",
     75                      fErrors.size(), fErrors.size() == 1 ? "" : "s",
     76                      fWarnings.size(), fWarnings.size() == 1 ? "" : "s");
     77 
     78             const auto& show = [](const LogEntry& log, const char prefix[]) {
     79                 SkDebugf("%s%s", prefix, log.fMessage.c_str());
     80                 if (!log.fJSON.isEmpty())
     81                     SkDebugf(" : %s", log.fJSON.c_str());
     82                 SkDebugf("\n");
     83             };
     84 
     85             for (const auto& err : fErrors)   show(err, "  !! ");
     86             for (const auto& wrn : fWarnings) show(wrn, "  ?? ");
     87         }
     88 
     89     private:
     90         std::vector<LogEntry> fErrors,
     91                               fWarnings;
     92     };
     93 
     94     auto logger = sk_make_sp<Logger>();
     95     skottie::Animation::Builder builder;
     96 
     97     fAnimation      = builder
     98             .setLogger(logger)
     99             .setResourceProvider(
    100                 skottie_utils::FileResourceProvider::Make(SkOSPath::Dirname(fPath.c_str())))
    101             .makeFromFile(fPath.c_str());
    102     fAnimationStats = builder.getStats();
    103     fWinSize        = SkSize::Make(w, h);
    104     fTimeBase       = 0; // force a time reset
    105 
    106     if (fAnimation) {
    107         fAnimation->setShowInval(fShowAnimationInval);
    108         SkDebugf("Loaded Bodymovin animation v: %s, size: [%f %f]\n",
    109                  fAnimation->version().c_str(),
    110                  fAnimation->size().width(),
    111                  fAnimation->size().height());
    112         logger->report();
    113     } else {
    114         SkDebugf("failed to load Bodymovin animation: %s\n", fPath.c_str());
    115     }
    116 }
    117 
    118 void SkottieSlide::unload() {
    119     fAnimation.reset();
    120 }
    121 
    122 SkISize SkottieSlide::getDimensions() const {
    123     // We always scale to fill the window.
    124     return fWinSize.toCeil();
    125 }
    126 
    127 void SkottieSlide::draw(SkCanvas* canvas) {
    128     if (fAnimation) {
    129         SkAutoCanvasRestore acr(canvas, true);
    130         const auto dstR = SkRect::MakeSize(fWinSize);
    131         fAnimation->render(canvas, &dstR);
    132 
    133         if (fShowAnimationStats) {
    134             draw_stats_box(canvas, fAnimationStats);
    135         }
    136     }
    137 }
    138 
    139 bool SkottieSlide::animate(const SkAnimTimer& timer) {
    140     if (fTimeBase == 0) {
    141         // Reset the animation time.
    142         fTimeBase = timer.msec();
    143     }
    144 
    145     if (fAnimation) {
    146         const auto t = timer.msec() - fTimeBase;
    147         const auto d = fAnimation->duration() * 1000;
    148         fAnimation->seek(std::fmod(t, d) / d);
    149     }
    150     return true;
    151 }
    152 
    153 bool SkottieSlide::onChar(SkUnichar c) {
    154     switch (c) {
    155     case 'I':
    156         fShowAnimationStats = !fShowAnimationStats;
    157         break;
    158     default:
    159         break;
    160     }
    161 
    162     return INHERITED::onChar(c);
    163 }
    164 
    165 bool SkottieSlide::onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state, uint32_t) {
    166     switch (state) {
    167     case sk_app::Window::kUp_InputState:
    168         fShowAnimationInval = !fShowAnimationInval;
    169         fShowAnimationStats = !fShowAnimationStats;
    170         fAnimation->setShowInval(fShowAnimationInval);
    171         break;
    172     default:
    173         break;
    174     }
    175 
    176     return false;
    177 }
    178 
    179 #endif // SK_ENABLE_SKOTTIE
    180