123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- /*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "tools/viewer/SlideDir.h"
- #include "include/core/SkCanvas.h"
- #include "include/core/SkCubicMap.h"
- #include "include/core/SkTypeface.h"
- #include "modules/sksg/include/SkSGDraw.h"
- #include "modules/sksg/include/SkSGGroup.h"
- #include "modules/sksg/include/SkSGPaint.h"
- #include "modules/sksg/include/SkSGPlane.h"
- #include "modules/sksg/include/SkSGRect.h"
- #include "modules/sksg/include/SkSGRenderNode.h"
- #include "modules/sksg/include/SkSGScene.h"
- #include "modules/sksg/include/SkSGText.h"
- #include "modules/sksg/include/SkSGTransform.h"
- #include "src/core/SkMakeUnique.h"
- #include "tools/timer/TimeUtils.h"
- #include <cmath>
- #include <utility>
- namespace {
- static constexpr float kAspectRatio = 1.5f;
- static constexpr float kLabelSize = 12.0f;
- static constexpr SkSize kPadding = { 12.0f , 24.0f };
- static constexpr float kFocusDuration = 500;
- static constexpr SkSize kFocusInset = { 100.0f, 100.0f };
- static constexpr SkPoint kFocusCtrl0 = { 0.3f, 1.0f };
- static constexpr SkPoint kFocusCtrl1 = { 0.0f, 1.0f };
- static constexpr SkColor kFocusShade = 0xa0000000;
- // TODO: better unfocus binding?
- static constexpr SkUnichar kUnfocusKey = ' ';
- class SlideAdapter final : public sksg::RenderNode {
- public:
- explicit SlideAdapter(sk_sp<Slide> slide)
- : fSlide(std::move(slide)) {
- SkASSERT(fSlide);
- }
- sk_sp<sksg::Animator> makeForwardingAnimator() {
- // Trivial sksg::Animator -> skottie::Animation tick adapter
- class ForwardingAnimator final : public sksg::Animator {
- public:
- explicit ForwardingAnimator(sk_sp<SlideAdapter> adapter)
- : fAdapter(std::move(adapter)) {}
- protected:
- void onTick(float t) override {
- fAdapter->tick(SkScalarRoundToInt(t));
- }
- private:
- sk_sp<SlideAdapter> fAdapter;
- };
- return sk_make_sp<ForwardingAnimator>(sk_ref_sp(this));
- }
- protected:
- SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
- const auto isize = fSlide->getDimensions();
- return SkRect::MakeIWH(isize.width(), isize.height());
- }
- void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
- SkAutoCanvasRestore acr(canvas, true);
- canvas->clipRect(SkRect::Make(fSlide->getDimensions()), true);
- // TODO: commit the context?
- fSlide->draw(canvas);
- }
- const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; }
- private:
- void tick(SkMSec t) {
- fSlide->animate(t * 1e6);
- this->invalidate();
- }
- const sk_sp<Slide> fSlide;
- using INHERITED = sksg::RenderNode;
- };
- SkMatrix SlideMatrix(const sk_sp<Slide>& slide, const SkRect& dst) {
- const auto slideSize = slide->getDimensions();
- return SkMatrix::MakeRectToRect(SkRect::MakeIWH(slideSize.width(), slideSize.height()),
- dst,
- SkMatrix::kCenter_ScaleToFit);
- }
- } // namespace
- struct SlideDir::Rec {
- sk_sp<Slide> fSlide;
- sk_sp<sksg::RenderNode> fSlideRoot;
- sk_sp<sksg::Matrix<SkMatrix>> fMatrix;
- SkRect fRect;
- };
- class SlideDir::FocusController final : public sksg::Animator {
- public:
- FocusController(const SlideDir* dir, const SkRect& focusRect)
- : fDir(dir)
- , fRect(focusRect)
- , fTarget(nullptr)
- , fMap(kFocusCtrl1, kFocusCtrl0)
- , fState(State::kIdle) {
- fShadePaint = sksg::Color::Make(kFocusShade);
- fShade = sksg::Draw::Make(sksg::Plane::Make(), fShadePaint);
- }
- bool hasFocus() const { return fState == State::kFocused; }
- void startFocus(const Rec* target) {
- if (fState != State::kIdle)
- return;
- fTarget = target;
- // Move the shade & slide to front.
- fDir->fRoot->removeChild(fTarget->fSlideRoot);
- fDir->fRoot->addChild(fShade);
- fDir->fRoot->addChild(fTarget->fSlideRoot);
- fM0 = SlideMatrix(fTarget->fSlide, fTarget->fRect);
- fM1 = SlideMatrix(fTarget->fSlide, fRect);
- fOpacity0 = 0;
- fOpacity1 = 1;
- fTimeBase = 0;
- fState = State::kFocusing;
- // Push initial state to the scene graph.
- this->onTick(fTimeBase);
- }
- void startUnfocus() {
- SkASSERT(fTarget);
- using std::swap;
- swap(fM0, fM1);
- swap(fOpacity0, fOpacity1);
- fTimeBase = 0;
- fState = State::kUnfocusing;
- }
- bool onMouse(SkScalar x, SkScalar y, InputState state, ModifierKey modifiers) {
- SkASSERT(fTarget);
- if (!fRect.contains(x, y)) {
- this->startUnfocus();
- return true;
- }
- // Map coords to slide space.
- const auto xform = SkMatrix::MakeRectToRect(fRect,
- SkRect::MakeSize(fDir->fWinSize),
- SkMatrix::kCenter_ScaleToFit);
- const auto pt = xform.mapXY(x, y);
- return fTarget->fSlide->onMouse(pt.x(), pt.y(), state, modifiers);
- }
- bool onChar(SkUnichar c) {
- SkASSERT(fTarget);
- return fTarget->fSlide->onChar(c);
- }
- protected:
- void onTick(float t) {
- if (!this->isAnimating())
- return;
- if (!fTimeBase) {
- fTimeBase = t;
- }
- const auto rel_t = (t - fTimeBase) / kFocusDuration,
- map_t = SkTPin(fMap.computeYFromX(rel_t), 0.0f, 1.0f);
- SkMatrix m;
- for (int i = 0; i < 9; ++i) {
- m[i] = fM0[i] + map_t * (fM1[i] - fM0[i]);
- }
- SkASSERT(fTarget);
- fTarget->fMatrix->setMatrix(m);
- const auto shadeOpacity = fOpacity0 + map_t * (fOpacity1 - fOpacity0);
- fShadePaint->setOpacity(shadeOpacity);
- if (rel_t < 1)
- return;
- switch (fState) {
- case State::kFocusing:
- fState = State::kFocused;
- break;
- case State::kUnfocusing:
- fState = State::kIdle;
- fDir->fRoot->removeChild(fShade);
- break;
- case State::kIdle:
- case State::kFocused:
- SkASSERT(false);
- break;
- }
- }
- private:
- enum class State {
- kIdle,
- kFocusing,
- kUnfocusing,
- kFocused,
- };
- bool isAnimating() const { return fState == State::kFocusing || fState == State::kUnfocusing; }
- const SlideDir* fDir;
- const SkRect fRect;
- const Rec* fTarget;
- SkCubicMap fMap;
- sk_sp<sksg::RenderNode> fShade;
- sk_sp<sksg::PaintNode> fShadePaint;
- SkMatrix fM0 = SkMatrix::I(),
- fM1 = SkMatrix::I();
- float fOpacity0 = 0,
- fOpacity1 = 1,
- fTimeBase = 0;
- State fState = State::kIdle;
- using INHERITED = sksg::Animator;
- };
- SlideDir::SlideDir(const SkString& name, SkTArray<sk_sp<Slide>>&& slides, int columns)
- : fSlides(std::move(slides))
- , fColumns(columns) {
- fName = name;
- }
- static sk_sp<sksg::RenderNode> MakeLabel(const SkString& txt,
- const SkPoint& pos,
- const SkMatrix& dstXform) {
- const auto size = kLabelSize / std::sqrt(dstXform.getScaleX() * dstXform.getScaleY());
- auto text = sksg::Text::Make(nullptr, txt);
- text->setEdging(SkFont::Edging::kAntiAlias);
- text->setSize(size);
- text->setAlign(SkTextUtils::kCenter_Align);
- text->setPosition(pos + SkPoint::Make(0, size));
- return sksg::Draw::Make(std::move(text), sksg::Color::Make(SK_ColorBLACK));
- }
- void SlideDir::load(SkScalar winWidth, SkScalar winHeight) {
- // Build a global scene using transformed animation fragments:
- //
- // [Group(root)]
- // [Transform]
- // [Group]
- // [AnimationWrapper]
- // [Draw]
- // [Text]
- // [Color]
- // [Transform]
- // [Group]
- // [AnimationWrapper]
- // [Draw]
- // [Text]
- // [Color]
- // ...
- //
- fWinSize = SkSize::Make(winWidth, winHeight);
- const auto cellWidth = winWidth / fColumns;
- fCellSize = SkSize::Make(cellWidth, cellWidth / kAspectRatio);
- sksg::AnimatorList sceneAnimators;
- fRoot = sksg::Group::Make();
- for (int i = 0; i < fSlides.count(); ++i) {
- const auto& slide = fSlides[i];
- slide->load(winWidth, winHeight);
- const auto slideSize = slide->getDimensions();
- const auto cell = SkRect::MakeXYWH(fCellSize.width() * (i % fColumns),
- fCellSize.height() * (i / fColumns),
- fCellSize.width(),
- fCellSize.height()),
- slideRect = cell.makeInset(kPadding.width(), kPadding.height());
- auto slideMatrix = sksg::Matrix<SkMatrix>::Make(SlideMatrix(slide, slideRect));
- auto adapter = sk_make_sp<SlideAdapter>(slide);
- auto slideGrp = sksg::Group::Make();
- slideGrp->addChild(sksg::Draw::Make(sksg::Rect::Make(SkRect::MakeIWH(slideSize.width(),
- slideSize.height())),
- sksg::Color::Make(0xfff0f0f0)));
- slideGrp->addChild(adapter);
- slideGrp->addChild(MakeLabel(slide->getName(),
- SkPoint::Make(slideSize.width() / 2, slideSize.height()),
- slideMatrix->getMatrix()));
- auto slideRoot = sksg::TransformEffect::Make(std::move(slideGrp), slideMatrix);
- sceneAnimators.push_back(adapter->makeForwardingAnimator());
- fRoot->addChild(slideRoot);
- fRecs.push_back({ slide, slideRoot, slideMatrix, slideRect });
- }
- fScene = sksg::Scene::Make(fRoot, std::move(sceneAnimators));
- const auto focusRect = SkRect::MakeSize(fWinSize).makeInset(kFocusInset.width(),
- kFocusInset.height());
- fFocusController = skstd::make_unique<FocusController>(this, focusRect);
- }
- void SlideDir::unload() {
- for (const auto& slide : fSlides) {
- slide->unload();
- }
- fRecs.reset();
- fScene.reset();
- fFocusController.reset();
- fRoot.reset();
- fTimeBase = 0;
- }
- SkISize SlideDir::getDimensions() const {
- return SkSize::Make(fWinSize.width(),
- fCellSize.height() * (1 + (fSlides.count() - 1) / fColumns)).toCeil();
- }
- void SlideDir::draw(SkCanvas* canvas) {
- fScene->render(canvas);
- }
- bool SlideDir::animate(double nanos) {
- SkMSec msec = TimeUtils::NanosToMSec(nanos);
- if (fTimeBase == 0) {
- // Reset the animation time.
- fTimeBase = msec;
- }
- const auto t = msec - fTimeBase;
- fScene->animate(t);
- fFocusController->tick(t);
- return true;
- }
- bool SlideDir::onChar(SkUnichar c) {
- if (fFocusController->hasFocus()) {
- if (c == kUnfocusKey) {
- fFocusController->startUnfocus();
- return true;
- }
- return fFocusController->onChar(c);
- }
- return false;
- }
- bool SlideDir::onMouse(SkScalar x, SkScalar y, InputState state,
- ModifierKey modifiers) {
- if (state == InputState::kMove || ModifierKeyIsSet(modifiers))
- return false;
- if (fFocusController->hasFocus()) {
- return fFocusController->onMouse(x, y, state, modifiers);
- }
- const auto* cell = this->findCell(x, y);
- if (!cell)
- return false;
- static constexpr SkScalar kClickMoveTolerance = 4;
- switch (state) {
- case InputState::kDown:
- fTrackingCell = cell;
- fTrackingPos = SkPoint::Make(x, y);
- break;
- case InputState::kUp:
- if (cell == fTrackingCell &&
- SkPoint::Distance(fTrackingPos, SkPoint::Make(x, y)) < kClickMoveTolerance) {
- fFocusController->startFocus(cell);
- }
- break;
- default:
- break;
- }
- return false;
- }
- const SlideDir::Rec* SlideDir::findCell(float x, float y) const {
- // TODO: use SG hit testing instead of layout info?
- const auto size = this->getDimensions();
- if (x < 0 || y < 0 || x >= size.width() || y >= size.height()) {
- return nullptr;
- }
- const int col = static_cast<int>(x / fCellSize.width()),
- row = static_cast<int>(y / fCellSize.height()),
- idx = row * fColumns + col;
- return idx < fRecs.count() ? &fRecs[idx] : nullptr;
- }
|