| /* |
| * Copyright 2017 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkottieSlide.h" |
| |
| #include "SkAnimTimer.h" |
| #include "SkCanvas.h" |
| #include "SkMakeUnique.h" |
| #include "Skottie.h" |
| #include "SkOSFile.h" |
| #include "SkOSPath.h" |
| #include "SkSGColor.h" |
| #include "SkSGDraw.h" |
| #include "SkSGGroup.h" |
| #include "SkSGRenderNode.h" |
| #include "SkSGScene.h" |
| #include "SkSGText.h" |
| #include "SkSGTransform.h" |
| #include "SkStream.h" |
| #include "SkTypeface.h" |
| |
| #include <cmath> |
| |
| static constexpr int CELL_WIDTH = 240; |
| static constexpr int CELL_HEIGHT = 160; |
| static constexpr int COL_COUNT = 4; |
| static constexpr int SPACER_X = 12; |
| static constexpr int SPACER_Y = 24; |
| static constexpr int MARGIN = 8; |
| |
| class SkottieSlide2::AnimationWrapper final : public sksg::RenderNode { |
| public: |
| explicit AnimationWrapper(std::unique_ptr<skottie::Animation> anim) |
| : fAnimation(std::move(anim)) { |
| SkASSERT(fAnimation); |
| } |
| |
| void tick(SkMSec t) { |
| fAnimation->animationTick(t); |
| this->invalidate(); |
| } |
| |
| void setShowInval(bool show) { fAnimation->setShowInval(show); } |
| |
| // Trivial sksg::Animator -> skottie::Animation tick adapter |
| class ForwardingAnimator final : public sksg::Animator { |
| public: |
| ForwardingAnimator(sk_sp<AnimationWrapper> wrapper) : fWrapper(std::move(wrapper)) {} |
| |
| protected: |
| void onTick(float t) override { |
| fWrapper->tick(SkScalarRoundToInt(t)); |
| } |
| |
| private: |
| sk_sp<AnimationWrapper> fWrapper; |
| |
| using INHERITED = sksg::Animator; |
| }; |
| |
| protected: |
| SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override { |
| return SkRect::MakeSize(fAnimation->size()); |
| } |
| |
| void onRender(SkCanvas* canvas) const override { |
| fAnimation->render(canvas); |
| } |
| |
| private: |
| const std::unique_ptr<skottie::Animation> fAnimation; |
| |
| using INHERITED = sksg::RenderNode; |
| }; |
| |
| SkottieSlide2::Rec::Rec(sk_sp<AnimationWrapper> wrapper) |
| : fWrapper(std::move(wrapper)) {} |
| |
| SkottieSlide2::Rec::Rec(Rec&& o) = default; |
| |
| SkottieSlide2::SkottieSlide2(const SkString& path) |
| : fPath(path) |
| { |
| fName.set("skottie-dir"); |
| } |
| |
| // Build a global scene using tranformed animation fragments: |
| // |
| // [Group(root)] |
| // [Transform] |
| // [Group] |
| // [AnimationWrapper] |
| // [Draw] |
| // [Text] |
| // [Color] |
| // [Transform] |
| // [Group] |
| // [AnimationWrapper] |
| // [Draw] |
| // [Text] |
| // [Color] |
| // ... |
| // |
| // Note: for now animation wrappers are also tracked externally in fAnims, for tick dispatching. |
| |
| static sk_sp<sksg::RenderNode> MakeLabel(const SkString& txt, |
| const SkRect& src, |
| const SkMatrix& dstXform) { |
| auto text = sksg::Text::Make(nullptr, txt); |
| text->setFlags(SkPaint::kAntiAlias_Flag); |
| text->setSize(12 / std::sqrt(dstXform.getScaleX() * dstXform.getScaleY())); |
| text->setAlign(SkPaint::kCenter_Align); |
| text->setPosition(SkPoint::Make(src.width() / 2, src.height() + text->getSize())); |
| |
| return sksg::Draw::Make(std::move(text), sksg::Color::Make(SK_ColorBLACK)); |
| } |
| |
| void SkottieSlide2::load(SkScalar, SkScalar) { |
| SkString name; |
| SkOSFile::Iter iter(fPath.c_str(), "json"); |
| |
| int x = 0, y = 0; |
| |
| auto scene_root = sksg::Group::Make(); |
| sksg::AnimatorList scene_animators; |
| |
| while (iter.next(&name)) { |
| SkString path = SkOSPath::Join(fPath.c_str(), name.c_str()); |
| if (auto anim = skottie::Animation::MakeFromFile(path.c_str())) { |
| const auto src = SkRect::MakeSize(anim->size()), |
| dst = SkRect::MakeXYWH(MARGIN + x * (CELL_WIDTH + SPACER_X), |
| MARGIN + y * (CELL_HEIGHT + SPACER_Y), |
| CELL_WIDTH, CELL_HEIGHT); |
| const auto m = SkMatrix::MakeRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit); |
| auto wrapper = sk_make_sp<AnimationWrapper>(std::move(anim)); |
| auto group = sksg::Group::Make(); |
| |
| group->addChild(wrapper); |
| group->addChild(MakeLabel(name, src, m)); |
| |
| auto xform = sksg::Transform::Make(std::move(group), m); |
| |
| scene_animators.push_back( |
| skstd::make_unique<AnimationWrapper::ForwardingAnimator>(wrapper)); |
| scene_root->addChild(xform); |
| fAnims.emplace_back(std::move(wrapper)); |
| |
| if (++x == COL_COUNT) { |
| x = 0; |
| y += 1; |
| } |
| } |
| } |
| |
| fScene = sksg::Scene::Make(std::move(scene_root), std::move(scene_animators)); |
| } |
| |
| void SkottieSlide2::unload() { |
| fAnims.reset(); |
| fScene.reset(); |
| fTimeBase = 0; |
| } |
| |
| SkISize SkottieSlide2::getDimensions() const { |
| const int rows = (fAnims.count() + COL_COUNT - 1) / COL_COUNT; |
| return { |
| MARGIN + (COL_COUNT - 1) * SPACER_X + COL_COUNT * CELL_WIDTH + MARGIN, |
| MARGIN + (rows - 1) * SPACER_Y + rows * CELL_HEIGHT + MARGIN, |
| }; |
| } |
| |
| void SkottieSlide2::draw(SkCanvas* canvas) { |
| fScene->render(canvas); |
| } |
| |
| bool SkottieSlide2::animate(const SkAnimTimer& timer) { |
| if (fTimeBase == 0) { |
| // Reset the animation time. |
| fTimeBase = timer.msec(); |
| } |
| fScene->animate(timer.msec() - fTimeBase); |
| |
| return true; |
| } |
| |
| bool SkottieSlide2::onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState state, |
| uint32_t modifiers) { |
| if (fTrackingCell < 0 && state == sk_app::Window::kDown_InputState) { |
| fTrackingCell = this->findCell(x, y); |
| } |
| if (fTrackingCell >= 0 && state == sk_app::Window::kUp_InputState) { |
| int index = this->findCell(x, y); |
| if (fTrackingCell == index) { |
| fAnims[index].fShowAnimationInval = !fAnims[index].fShowAnimationInval; |
| fAnims[index].fWrapper->setShowInval(fAnims[index].fShowAnimationInval); |
| } |
| fTrackingCell = -1; |
| } |
| return fTrackingCell >= 0; |
| } |
| |
| int SkottieSlide2::findCell(float x, float y) const { |
| x -= MARGIN; |
| y -= MARGIN; |
| int index = -1; |
| if (x >= 0 && y >= 0) { |
| int ix = (int)x; |
| int iy = (int)y; |
| int col = ix / (CELL_WIDTH + SPACER_X); |
| int row = iy / (CELL_HEIGHT + SPACER_Y); |
| index = row * COL_COUNT + col; |
| if (index >= fAnims.count()) { |
| index = -1; |
| } |
| } |
| return index; |
| } |