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 #include "SkAnimTimer.h"
     11 #include "SkCanvas.h"
     12 #include "SkMakeUnique.h"
     13 #include "Skottie.h"
     14 #include "SkOSFile.h"
     15 #include "SkOSPath.h"
     16 #include "SkSGColor.h"
     17 #include "SkSGDraw.h"
     18 #include "SkSGGroup.h"
     19 #include "SkSGRenderNode.h"
     20 #include "SkSGScene.h"
     21 #include "SkSGText.h"
     22 #include "SkSGTransform.h"
     23 #include "SkStream.h"
     24 #include "SkTypeface.h"
     25 
     26 #include <cmath>
     27 
     28 static constexpr int CELL_WIDTH  = 240;
     29 static constexpr int CELL_HEIGHT = 160;
     30 static constexpr int COL_COUNT   = 4;
     31 static constexpr int SPACER_X    = 12;
     32 static constexpr int SPACER_Y    = 24;
     33 static constexpr int MARGIN      = 8;
     34 
     35 class SkottieSlide2::AnimationWrapper final : public sksg::RenderNode {
     36 public:
     37     explicit AnimationWrapper(std::unique_ptr<skottie::Animation> anim)
     38         : fAnimation(std::move(anim)) {
     39         SkASSERT(fAnimation);
     40     }
     41 
     42     void tick(SkMSec t) {
     43         fAnimation->animationTick(t);
     44         this->invalidate();
     45     }
     46 
     47     void setShowInval(bool show) { fAnimation->setShowInval(show); }
     48 
     49     // Trivial sksg::Animator -> skottie::Animation tick adapter
     50     class ForwardingAnimator final : public sksg::Animator {
     51     public:
     52         ForwardingAnimator(sk_sp<AnimationWrapper> wrapper) : fWrapper(std::move(wrapper)) {}
     53 
     54     protected:
     55         void onTick(float t) override {
     56             fWrapper->tick(SkScalarRoundToInt(t));
     57         }
     58 
     59     private:
     60         sk_sp<AnimationWrapper> fWrapper;
     61 
     62         using INHERITED = sksg::Animator;
     63     };
     64 
     65 protected:
     66     SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
     67         return SkRect::MakeSize(fAnimation->size());
     68     }
     69 
     70     void onRender(SkCanvas* canvas) const override {
     71         fAnimation->render(canvas);
     72     }
     73 
     74 private:
     75     const std::unique_ptr<skottie::Animation> fAnimation;
     76 
     77     using INHERITED = sksg::RenderNode;
     78 };
     79 
     80 SkottieSlide2::Rec::Rec(sk_sp<AnimationWrapper> wrapper)
     81     : fWrapper(std::move(wrapper)) {}
     82 
     83 SkottieSlide2::Rec::Rec(Rec&& o) = default;
     84 
     85 SkottieSlide2::SkottieSlide2(const SkString& path)
     86     : fPath(path)
     87 {
     88     fName.set("skottie-dir");
     89 }
     90 
     91 // Build a global scene using tranformed animation fragments:
     92 //
     93 // [Group(root)]
     94 //     [Transform]
     95 //         [Group]
     96 //             [AnimationWrapper]
     97 //             [Draw]
     98 //                 [Text]
     99 //                 [Color]
    100 //     [Transform]
    101 //         [Group]
    102 //             [AnimationWrapper]
    103 //             [Draw]
    104 //                 [Text]
    105 //                 [Color]
    106 //     ...
    107 //
    108 // Note: for now animation wrappers are also tracked externally in fAnims, for tick dispatching.
    109 
    110 static sk_sp<sksg::RenderNode> MakeLabel(const SkString& txt,
    111                                          const SkRect& src,
    112                                          const SkMatrix& dstXform) {
    113     auto text = sksg::Text::Make(nullptr, txt);
    114     text->setFlags(SkPaint::kAntiAlias_Flag);
    115     text->setSize(12 / std::sqrt(dstXform.getScaleX() * dstXform.getScaleY()));
    116     text->setAlign(SkPaint::kCenter_Align);
    117     text->setPosition(SkPoint::Make(src.width() / 2, src.height() + text->getSize()));
    118 
    119     return sksg::Draw::Make(std::move(text), sksg::Color::Make(SK_ColorBLACK));
    120 }
    121 
    122 void SkottieSlide2::load(SkScalar, SkScalar) {
    123     SkString name;
    124     SkOSFile::Iter iter(fPath.c_str(), "json");
    125 
    126     int x = 0, y = 0;
    127 
    128     auto scene_root = sksg::Group::Make();
    129     sksg::AnimatorList scene_animators;
    130 
    131     while (iter.next(&name)) {
    132         SkString path = SkOSPath::Join(fPath.c_str(), name.c_str());
    133         if (auto anim  = skottie::Animation::MakeFromFile(path.c_str())) {
    134             const auto src = SkRect::MakeSize(anim->size()),
    135                        dst = SkRect::MakeXYWH(MARGIN + x * (CELL_WIDTH + SPACER_X),
    136                                               MARGIN + y * (CELL_HEIGHT + SPACER_Y),
    137                                               CELL_WIDTH, CELL_HEIGHT);
    138             const auto m   = SkMatrix::MakeRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit);
    139             auto wrapper   = sk_make_sp<AnimationWrapper>(std::move(anim));
    140             auto group     = sksg::Group::Make();
    141 
    142             group->addChild(wrapper);
    143             group->addChild(MakeLabel(name, src, m));
    144 
    145             auto xform     = sksg::Transform::Make(std::move(group), m);
    146 
    147             scene_animators.push_back(
    148                 skstd::make_unique<AnimationWrapper::ForwardingAnimator>(wrapper));
    149             scene_root->addChild(xform);
    150             fAnims.emplace_back(std::move(wrapper));
    151 
    152             if (++x == COL_COUNT) {
    153                 x = 0;
    154                 y += 1;
    155             }
    156         }
    157     }
    158 
    159     fScene = sksg::Scene::Make(std::move(scene_root), std::move(scene_animators));
    160 }
    161 
    162 void SkottieSlide2::unload() {
    163     fAnims.reset();
    164     fScene.reset();
    165     fTimeBase = 0;
    166 }
    167 
    168 SkISize SkottieSlide2::getDimensions() const {
    169     const int rows = (fAnims.count() + COL_COUNT - 1) / COL_COUNT;
    170     return {
    171         MARGIN + (COL_COUNT - 1) * SPACER_X + COL_COUNT * CELL_WIDTH + MARGIN,
    172         MARGIN + (rows - 1) * SPACER_Y + rows * CELL_HEIGHT + MARGIN,
    173     };
    174 }
    175 
    176 void SkottieSlide2::draw(SkCanvas* canvas) {
    177     fScene->render(canvas);
    178 }
    179 
    180 bool SkottieSlide2::animate(const SkAnimTimer& timer) {
    181     if (fTimeBase == 0) {
    182         // Reset the animation time.
    183         fTimeBase = timer.msec();
    184     }
    185     fScene->animate(timer.msec() - fTimeBase);
    186 
    187     return true;
    188 }
    189 
    190 bool SkottieSlide2::onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state,
    191                            uint32_t modifiers) {
    192     if (fTrackingCell < 0 && state == sk_app::Window::kDown_InputState) {
    193         fTrackingCell = this->findCell(x, y);
    194     }
    195     if (fTrackingCell >= 0 && state == sk_app::Window::kUp_InputState) {
    196         int index = this->findCell(x, y);
    197         if (fTrackingCell == index) {
    198             fAnims[index].fShowAnimationInval = !fAnims[index].fShowAnimationInval;
    199             fAnims[index].fWrapper->setShowInval(fAnims[index].fShowAnimationInval);
    200         }
    201         fTrackingCell = -1;
    202     }
    203     return fTrackingCell >= 0;
    204 }
    205 
    206 int SkottieSlide2::findCell(float x, float y) const {
    207     x -= MARGIN;
    208     y -= MARGIN;
    209     int index = -1;
    210     if (x >= 0 && y >= 0) {
    211         int ix = (int)x;
    212         int iy = (int)y;
    213         int col = ix / (CELL_WIDTH + SPACER_X);
    214         int row = iy / (CELL_HEIGHT + SPACER_Y);
    215         index = row * COL_COUNT + col;
    216         if (index >= fAnims.count()) {
    217             index = -1;
    218         }
    219     }
    220     return index;
    221 }
    222