blob: 3eb723a0f45861c5e98f8965096638f93af3366a [file] [log] [blame]
/*
* 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;
}