SlideDir.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /*
  2. * Copyright 2018 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. #include "tools/viewer/SlideDir.h"
  8. #include "include/core/SkCanvas.h"
  9. #include "include/core/SkCubicMap.h"
  10. #include "include/core/SkTypeface.h"
  11. #include "modules/sksg/include/SkSGDraw.h"
  12. #include "modules/sksg/include/SkSGGroup.h"
  13. #include "modules/sksg/include/SkSGPaint.h"
  14. #include "modules/sksg/include/SkSGPlane.h"
  15. #include "modules/sksg/include/SkSGRect.h"
  16. #include "modules/sksg/include/SkSGRenderNode.h"
  17. #include "modules/sksg/include/SkSGScene.h"
  18. #include "modules/sksg/include/SkSGText.h"
  19. #include "modules/sksg/include/SkSGTransform.h"
  20. #include "src/core/SkMakeUnique.h"
  21. #include "tools/timer/TimeUtils.h"
  22. #include <cmath>
  23. #include <utility>
  24. namespace {
  25. static constexpr float kAspectRatio = 1.5f;
  26. static constexpr float kLabelSize = 12.0f;
  27. static constexpr SkSize kPadding = { 12.0f , 24.0f };
  28. static constexpr float kFocusDuration = 500;
  29. static constexpr SkSize kFocusInset = { 100.0f, 100.0f };
  30. static constexpr SkPoint kFocusCtrl0 = { 0.3f, 1.0f };
  31. static constexpr SkPoint kFocusCtrl1 = { 0.0f, 1.0f };
  32. static constexpr SkColor kFocusShade = 0xa0000000;
  33. // TODO: better unfocus binding?
  34. static constexpr SkUnichar kUnfocusKey = ' ';
  35. class SlideAdapter final : public sksg::RenderNode {
  36. public:
  37. explicit SlideAdapter(sk_sp<Slide> slide)
  38. : fSlide(std::move(slide)) {
  39. SkASSERT(fSlide);
  40. }
  41. sk_sp<sksg::Animator> makeForwardingAnimator() {
  42. // Trivial sksg::Animator -> skottie::Animation tick adapter
  43. class ForwardingAnimator final : public sksg::Animator {
  44. public:
  45. explicit ForwardingAnimator(sk_sp<SlideAdapter> adapter)
  46. : fAdapter(std::move(adapter)) {}
  47. protected:
  48. void onTick(float t) override {
  49. fAdapter->tick(SkScalarRoundToInt(t));
  50. }
  51. private:
  52. sk_sp<SlideAdapter> fAdapter;
  53. };
  54. return sk_make_sp<ForwardingAnimator>(sk_ref_sp(this));
  55. }
  56. protected:
  57. SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
  58. const auto isize = fSlide->getDimensions();
  59. return SkRect::MakeIWH(isize.width(), isize.height());
  60. }
  61. void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
  62. SkAutoCanvasRestore acr(canvas, true);
  63. canvas->clipRect(SkRect::Make(fSlide->getDimensions()), true);
  64. // TODO: commit the context?
  65. fSlide->draw(canvas);
  66. }
  67. const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; }
  68. private:
  69. void tick(SkMSec t) {
  70. fSlide->animate(t * 1e6);
  71. this->invalidate();
  72. }
  73. const sk_sp<Slide> fSlide;
  74. using INHERITED = sksg::RenderNode;
  75. };
  76. SkMatrix SlideMatrix(const sk_sp<Slide>& slide, const SkRect& dst) {
  77. const auto slideSize = slide->getDimensions();
  78. return SkMatrix::MakeRectToRect(SkRect::MakeIWH(slideSize.width(), slideSize.height()),
  79. dst,
  80. SkMatrix::kCenter_ScaleToFit);
  81. }
  82. } // namespace
  83. struct SlideDir::Rec {
  84. sk_sp<Slide> fSlide;
  85. sk_sp<sksg::RenderNode> fSlideRoot;
  86. sk_sp<sksg::Matrix<SkMatrix>> fMatrix;
  87. SkRect fRect;
  88. };
  89. class SlideDir::FocusController final : public sksg::Animator {
  90. public:
  91. FocusController(const SlideDir* dir, const SkRect& focusRect)
  92. : fDir(dir)
  93. , fRect(focusRect)
  94. , fTarget(nullptr)
  95. , fMap(kFocusCtrl1, kFocusCtrl0)
  96. , fState(State::kIdle) {
  97. fShadePaint = sksg::Color::Make(kFocusShade);
  98. fShade = sksg::Draw::Make(sksg::Plane::Make(), fShadePaint);
  99. }
  100. bool hasFocus() const { return fState == State::kFocused; }
  101. void startFocus(const Rec* target) {
  102. if (fState != State::kIdle)
  103. return;
  104. fTarget = target;
  105. // Move the shade & slide to front.
  106. fDir->fRoot->removeChild(fTarget->fSlideRoot);
  107. fDir->fRoot->addChild(fShade);
  108. fDir->fRoot->addChild(fTarget->fSlideRoot);
  109. fM0 = SlideMatrix(fTarget->fSlide, fTarget->fRect);
  110. fM1 = SlideMatrix(fTarget->fSlide, fRect);
  111. fOpacity0 = 0;
  112. fOpacity1 = 1;
  113. fTimeBase = 0;
  114. fState = State::kFocusing;
  115. // Push initial state to the scene graph.
  116. this->onTick(fTimeBase);
  117. }
  118. void startUnfocus() {
  119. SkASSERT(fTarget);
  120. using std::swap;
  121. swap(fM0, fM1);
  122. swap(fOpacity0, fOpacity1);
  123. fTimeBase = 0;
  124. fState = State::kUnfocusing;
  125. }
  126. bool onMouse(SkScalar x, SkScalar y, InputState state, ModifierKey modifiers) {
  127. SkASSERT(fTarget);
  128. if (!fRect.contains(x, y)) {
  129. this->startUnfocus();
  130. return true;
  131. }
  132. // Map coords to slide space.
  133. const auto xform = SkMatrix::MakeRectToRect(fRect,
  134. SkRect::MakeSize(fDir->fWinSize),
  135. SkMatrix::kCenter_ScaleToFit);
  136. const auto pt = xform.mapXY(x, y);
  137. return fTarget->fSlide->onMouse(pt.x(), pt.y(), state, modifiers);
  138. }
  139. bool onChar(SkUnichar c) {
  140. SkASSERT(fTarget);
  141. return fTarget->fSlide->onChar(c);
  142. }
  143. protected:
  144. void onTick(float t) {
  145. if (!this->isAnimating())
  146. return;
  147. if (!fTimeBase) {
  148. fTimeBase = t;
  149. }
  150. const auto rel_t = (t - fTimeBase) / kFocusDuration,
  151. map_t = SkTPin(fMap.computeYFromX(rel_t), 0.0f, 1.0f);
  152. SkMatrix m;
  153. for (int i = 0; i < 9; ++i) {
  154. m[i] = fM0[i] + map_t * (fM1[i] - fM0[i]);
  155. }
  156. SkASSERT(fTarget);
  157. fTarget->fMatrix->setMatrix(m);
  158. const auto shadeOpacity = fOpacity0 + map_t * (fOpacity1 - fOpacity0);
  159. fShadePaint->setOpacity(shadeOpacity);
  160. if (rel_t < 1)
  161. return;
  162. switch (fState) {
  163. case State::kFocusing:
  164. fState = State::kFocused;
  165. break;
  166. case State::kUnfocusing:
  167. fState = State::kIdle;
  168. fDir->fRoot->removeChild(fShade);
  169. break;
  170. case State::kIdle:
  171. case State::kFocused:
  172. SkASSERT(false);
  173. break;
  174. }
  175. }
  176. private:
  177. enum class State {
  178. kIdle,
  179. kFocusing,
  180. kUnfocusing,
  181. kFocused,
  182. };
  183. bool isAnimating() const { return fState == State::kFocusing || fState == State::kUnfocusing; }
  184. const SlideDir* fDir;
  185. const SkRect fRect;
  186. const Rec* fTarget;
  187. SkCubicMap fMap;
  188. sk_sp<sksg::RenderNode> fShade;
  189. sk_sp<sksg::PaintNode> fShadePaint;
  190. SkMatrix fM0 = SkMatrix::I(),
  191. fM1 = SkMatrix::I();
  192. float fOpacity0 = 0,
  193. fOpacity1 = 1,
  194. fTimeBase = 0;
  195. State fState = State::kIdle;
  196. using INHERITED = sksg::Animator;
  197. };
  198. SlideDir::SlideDir(const SkString& name, SkTArray<sk_sp<Slide>>&& slides, int columns)
  199. : fSlides(std::move(slides))
  200. , fColumns(columns) {
  201. fName = name;
  202. }
  203. static sk_sp<sksg::RenderNode> MakeLabel(const SkString& txt,
  204. const SkPoint& pos,
  205. const SkMatrix& dstXform) {
  206. const auto size = kLabelSize / std::sqrt(dstXform.getScaleX() * dstXform.getScaleY());
  207. auto text = sksg::Text::Make(nullptr, txt);
  208. text->setEdging(SkFont::Edging::kAntiAlias);
  209. text->setSize(size);
  210. text->setAlign(SkTextUtils::kCenter_Align);
  211. text->setPosition(pos + SkPoint::Make(0, size));
  212. return sksg::Draw::Make(std::move(text), sksg::Color::Make(SK_ColorBLACK));
  213. }
  214. void SlideDir::load(SkScalar winWidth, SkScalar winHeight) {
  215. // Build a global scene using transformed animation fragments:
  216. //
  217. // [Group(root)]
  218. // [Transform]
  219. // [Group]
  220. // [AnimationWrapper]
  221. // [Draw]
  222. // [Text]
  223. // [Color]
  224. // [Transform]
  225. // [Group]
  226. // [AnimationWrapper]
  227. // [Draw]
  228. // [Text]
  229. // [Color]
  230. // ...
  231. //
  232. fWinSize = SkSize::Make(winWidth, winHeight);
  233. const auto cellWidth = winWidth / fColumns;
  234. fCellSize = SkSize::Make(cellWidth, cellWidth / kAspectRatio);
  235. sksg::AnimatorList sceneAnimators;
  236. fRoot = sksg::Group::Make();
  237. for (int i = 0; i < fSlides.count(); ++i) {
  238. const auto& slide = fSlides[i];
  239. slide->load(winWidth, winHeight);
  240. const auto slideSize = slide->getDimensions();
  241. const auto cell = SkRect::MakeXYWH(fCellSize.width() * (i % fColumns),
  242. fCellSize.height() * (i / fColumns),
  243. fCellSize.width(),
  244. fCellSize.height()),
  245. slideRect = cell.makeInset(kPadding.width(), kPadding.height());
  246. auto slideMatrix = sksg::Matrix<SkMatrix>::Make(SlideMatrix(slide, slideRect));
  247. auto adapter = sk_make_sp<SlideAdapter>(slide);
  248. auto slideGrp = sksg::Group::Make();
  249. slideGrp->addChild(sksg::Draw::Make(sksg::Rect::Make(SkRect::MakeIWH(slideSize.width(),
  250. slideSize.height())),
  251. sksg::Color::Make(0xfff0f0f0)));
  252. slideGrp->addChild(adapter);
  253. slideGrp->addChild(MakeLabel(slide->getName(),
  254. SkPoint::Make(slideSize.width() / 2, slideSize.height()),
  255. slideMatrix->getMatrix()));
  256. auto slideRoot = sksg::TransformEffect::Make(std::move(slideGrp), slideMatrix);
  257. sceneAnimators.push_back(adapter->makeForwardingAnimator());
  258. fRoot->addChild(slideRoot);
  259. fRecs.push_back({ slide, slideRoot, slideMatrix, slideRect });
  260. }
  261. fScene = sksg::Scene::Make(fRoot, std::move(sceneAnimators));
  262. const auto focusRect = SkRect::MakeSize(fWinSize).makeInset(kFocusInset.width(),
  263. kFocusInset.height());
  264. fFocusController = skstd::make_unique<FocusController>(this, focusRect);
  265. }
  266. void SlideDir::unload() {
  267. for (const auto& slide : fSlides) {
  268. slide->unload();
  269. }
  270. fRecs.reset();
  271. fScene.reset();
  272. fFocusController.reset();
  273. fRoot.reset();
  274. fTimeBase = 0;
  275. }
  276. SkISize SlideDir::getDimensions() const {
  277. return SkSize::Make(fWinSize.width(),
  278. fCellSize.height() * (1 + (fSlides.count() - 1) / fColumns)).toCeil();
  279. }
  280. void SlideDir::draw(SkCanvas* canvas) {
  281. fScene->render(canvas);
  282. }
  283. bool SlideDir::animate(double nanos) {
  284. SkMSec msec = TimeUtils::NanosToMSec(nanos);
  285. if (fTimeBase == 0) {
  286. // Reset the animation time.
  287. fTimeBase = msec;
  288. }
  289. const auto t = msec - fTimeBase;
  290. fScene->animate(t);
  291. fFocusController->tick(t);
  292. return true;
  293. }
  294. bool SlideDir::onChar(SkUnichar c) {
  295. if (fFocusController->hasFocus()) {
  296. if (c == kUnfocusKey) {
  297. fFocusController->startUnfocus();
  298. return true;
  299. }
  300. return fFocusController->onChar(c);
  301. }
  302. return false;
  303. }
  304. bool SlideDir::onMouse(SkScalar x, SkScalar y, InputState state,
  305. ModifierKey modifiers) {
  306. if (state == InputState::kMove || ModifierKeyIsSet(modifiers))
  307. return false;
  308. if (fFocusController->hasFocus()) {
  309. return fFocusController->onMouse(x, y, state, modifiers);
  310. }
  311. const auto* cell = this->findCell(x, y);
  312. if (!cell)
  313. return false;
  314. static constexpr SkScalar kClickMoveTolerance = 4;
  315. switch (state) {
  316. case InputState::kDown:
  317. fTrackingCell = cell;
  318. fTrackingPos = SkPoint::Make(x, y);
  319. break;
  320. case InputState::kUp:
  321. if (cell == fTrackingCell &&
  322. SkPoint::Distance(fTrackingPos, SkPoint::Make(x, y)) < kClickMoveTolerance) {
  323. fFocusController->startFocus(cell);
  324. }
  325. break;
  326. default:
  327. break;
  328. }
  329. return false;
  330. }
  331. const SlideDir::Rec* SlideDir::findCell(float x, float y) const {
  332. // TODO: use SG hit testing instead of layout info?
  333. const auto size = this->getDimensions();
  334. if (x < 0 || y < 0 || x >= size.width() || y >= size.height()) {
  335. return nullptr;
  336. }
  337. const int col = static_cast<int>(x / fCellSize.width()),
  338. row = static_cast<int>(y / fCellSize.height()),
  339. idx = row * fColumns + col;
  340. return idx < fRecs.count() ? &fRecs[idx] : nullptr;
  341. }