Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkottieSlide.cpp
Go to the documentation of this file.
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
9
10#if defined(SK_ENABLE_SKOTTIE)
11
13#include "include/core/SkFont.h"
24#include "src/base/SkTime.h"
25#include "src/core/SkOSFile.h"
26#include "src/utils/SkOSPath.h"
27#include "tools/Resources.h"
30
31#include <cmath>
32#include <vector>
33
34#include "imgui.h"
35
36namespace {
37
38class Track final : public skresources::ExternalTrackAsset {
39public:
40 explicit Track(std::unique_ptr<SkAudioPlayer> player) : fPlayer(std::move(player)) {}
41
42private:
43 void seek(float t) override {
44 if (fPlayer->isStopped() && t >=0) {
45 fPlayer->play();
46 }
47
48 if (fPlayer->isPlaying()) {
49 if (t < 0) {
50 fPlayer->stop();
51 } else {
52 static constexpr float kTolerance = 0.075f;
53 const auto player_pos = fPlayer->time();
54
55 if (std::abs(player_pos - t) > kTolerance) {
56 fPlayer->setTime(t);
57 }
58 }
59 }
60 }
61
62 const std::unique_ptr<SkAudioPlayer> fPlayer;
63};
64
65class AudioProviderProxy final : public skresources::ResourceProviderProxyBase {
66public:
67 explicit AudioProviderProxy(sk_sp<skresources::ResourceProvider> rp)
68 : skresources::ResourceProviderProxyBase(std::move(rp)) {}
69
70private:
72 const char name[],
73 const char[] /*id*/) override {
74 if (auto data = this->load(path, name)) {
75 if (auto player = SkAudioPlayer::Make(std::move(data))) {
76 return sk_make_sp<Track>(std::move(player));
77 }
78 }
79
80 return nullptr;
81 }
82};
83
84class Decorator : public SkNoncopyable {
85public:
86 virtual ~Decorator() = default;
87
88 // We pass in the Matrix and have the Decorator handle using it independently
89 // This is so decorators can keep position on screen after moving.
90 virtual void render(SkCanvas*, double, const SkMatrix) = 0;
91};
92
93class SimpleMarker final : public Decorator {
94public:
95 ~SimpleMarker() override = default;
96
97 static std::unique_ptr<Decorator> Make() { return std::make_unique<SimpleMarker>(); }
98
99 void render(SkCanvas* canvas, double t, const SkMatrix transform) override {
100 canvas->concat(transform);
101 SkPaint p;
102 p.setAntiAlias(true);
103
104 p.setColor(SK_ColorGREEN);
105 canvas->drawCircle(0, 0, 5, p);
106
107 p.setColor(SK_ColorRED);
108 p.setStrokeWidth(1.5f);
109 canvas->drawLine(-10, 0, 10, 0, p);
110 canvas->drawLine(0, -10, 0, 10, p);
111 }
112};
113
114class TestingResourceProvider : public skresources::ResourceProvider {
115public:
116 TestingResourceProvider() {}
117
118 sk_sp<SkData> load(const char resource_path[], const char resource_name[]) const override {
119 auto it = fResources.find(resource_name);
120 if (it != fResources.end()) {
121 return it->second;
122 } else {
123 return GetResourceAsData(SkOSPath::Join(resource_path, resource_name).c_str());
124 }
125 }
126
127 sk_sp<skresources::ImageAsset> loadImageAsset(const char resource_path[],
128 const char resource_name[],
129 const char /*resource_id*/[]) const override {
130 auto data = this->load(resource_path, resource_name);
131 // Viewer should have already registered the codecs necessary for MultiFrameImageAsset
133 }
134
135 void addPath(const char resource_name[], const SkPath& path) {
136 fResources[resource_name] = path.serialize();
137 }
138
139private:
140 std::unordered_map<std::string, sk_sp<SkData>> fResources;
141};
142
143static const struct DecoratorRec {
144 const char* fName;
145 std::unique_ptr<Decorator>(*fFactory)();
146} kDecorators[] = {
147 { "Simple marker", SimpleMarker::Make },
148};
149
150class TextTracker final : public skottie::PropertyObserver {
151public:
152 explicit TextTracker(sk_sp<PropertyObserver> delegate) : fDelegate(std::move(delegate)) {}
153
154 std::vector<std::unique_ptr<skottie::TextPropertyHandle>>& props() {
155 return fTextProps;
156 }
157
158private:
159 void onTextProperty(const char node_name[],
160 const LazyHandle<skottie::TextPropertyHandle>& lh) override {
161 fTextProps.push_back(lh());
162
163 if (fDelegate) {
164 fDelegate->onTextProperty(node_name, lh);
165 }
166 }
167
168 const sk_sp<PropertyObserver> fDelegate;
169 std::vector<std::unique_ptr<skottie::TextPropertyHandle>> fTextProps;
170};
171
172} // namespace
173
174class SkottieSlide::TransformTracker : public skottie::PropertyObserver {
175public:
176 void renderUI() {
177 if (ImGui::Begin("Transform Tracker", nullptr)) {
178 if (ImGui::BeginCombo("Transform", fTransformSelect
179 ? std::get<0>(*fTransformSelect).c_str()
180 : nullptr)) {
181 if (ImGui::Selectable("(none)", true)) {
182 fTransformSelect = nullptr;
183 }
184 for (const auto& entry : fTransforms) {
185 const auto* transform_name = std::get<0>(entry).c_str();
186 if (ImGui::Selectable(transform_name, false)) {
187 if (!fTransformSelect ||
188 transform_name != std::get<0>(*fTransformSelect).c_str()) {
189 fTransformSelect = &entry;
190 // Reset the decorator on transform change.
191 fDecorator = fDecoratorSelect->fFactory();
192 }
193 }
194 }
195 ImGui::EndCombo();
196 }
197
198 if (ImGui::BeginCombo("Decoration", fDecoratorSelect->fName)) {
199 for (const auto& dec : kDecorators) {
200 if (ImGui::Selectable(dec.fName, false)) {
201 if (dec.fName != fDecoratorSelect->fName) {
202 fDecoratorSelect = &dec;
203 fDecorator = fDecoratorSelect->fFactory();
204 }
205 }
206 }
207 ImGui::EndCombo();
208 }
209 }
210 ImGui::End();
211 }
212
213 void renderTracker(SkCanvas* canvas, double time, const SkSize& win_size, const SkSize& anim_size) const {
214 if (!fTransformSelect) {
215 return;
216 }
217
218 const auto tprop = std::get<1>(*fTransformSelect)->get();
219
220 const auto m = SkMatrix::Translate(tprop.fPosition.fX, tprop.fPosition.fY)
221 * SkMatrix::RotateDeg(tprop.fRotation)
222 * SkMatrix::Scale (tprop.fScale.fX*0.01f, tprop.fScale.fY*0.01f)
223 * SkMatrix::Translate(tprop.fAnchorPoint.fX, tprop.fAnchorPoint.fY);
224
225 const auto viewer_matrix = SkMatrix::RectToRect(SkRect::MakeSize(anim_size),
226 SkRect::MakeSize(win_size),
228
229 SkAutoCanvasRestore acr(canvas, true);
230 canvas->concat(viewer_matrix);
231
232 SkASSERT(fDecorator);
233 fDecorator->render(canvas, time, m);
234 }
235
236private:
237 void onTransformProperty(const char name[],
238 const LazyHandle<skottie::TransformPropertyHandle>& lh) override {
239
240 fTransforms.push_back(std::make_tuple(SkString(name), lh()));
241 }
242
243 using TransformT = std::tuple<SkString, std::unique_ptr<skottie::TransformPropertyHandle>>;
244
245 std::vector<TransformT> fTransforms;
246 std::unique_ptr<Decorator> fDecorator;
247 const TransformT* fTransformSelect = nullptr;
248 const DecoratorRec* fDecoratorSelect = &kDecorators[0];
249};
250
251// Holds a pointer to a slot manager and the list of slots for the UI widget to track
252class SkottieSlide::SlotManagerInterface {
253public:
254 SlotManagerInterface(sk_sp<skottie::SlotManager> slotManager, sk_sp<skresources::ResourceProvider> rp)
255 : fSlotManager(std::move(slotManager))
256 , fResourceProvider(std::move(rp))
257 {}
258
259
260 void renderUI() {
261 if (ImGui::Begin("Slot Manager", nullptr)) {
262 ImGui::Text("Color Slots");
263 for (size_t i = 0; i < fColorSlots.size(); i++) {
264 auto& cSlot = fColorSlots.at(i);
265 ImGui::PushID(i);
266 ImGui::Text("%s", cSlot.first.c_str());
267 if (ImGui::ColorEdit4("Color", cSlot.second.data())) {
268 this->pushSlots();
269 }
270 ImGui::PopID();
271 }
272 ImGui::Text("Scalar Slots");
273 for (size_t i = 0; i < fScalarSlots.size(); i++) {
274 auto& oSlot = fScalarSlots.at(i);
275 ImGui::PushID(i);
276 ImGui::Text("%s", oSlot.first.c_str());
277 if (ImGui::InputFloat("Scalar", &(oSlot.second))) {
278 this->pushSlots();
279 }
280 ImGui::PopID();
281 }
282 ImGui::Text("Vec2 Slots");
283 for (size_t i = 0; i < fVec2Slots.size(); i++) {
284 auto& vSlot = fVec2Slots.at(i);
285 ImGui::PushID(i);
286 ImGui::Text("%s", vSlot.first.c_str());
287 if (ImGui::InputFloat2("x, y", &(vSlot.second.x))) {
288 this->pushSlots();
289 }
290 ImGui::PopID();
291 }
292 ImGui::Text("Text Slots");
293 for (size_t i = 0; i < fTextStringSlots.size(); i++) {
294 auto& tSlot = fTextStringSlots.at(i);
295 ImGui::PushID(i);
296 ImGui::Text("%s", tSlot.first.c_str());
297 if (ImGui::InputText("Text", tSlot.second.source.data(),
298 tSlot.second.source.size())) {
299 this->pushSlots();
300 }
301 if (ImGui::BeginCombo("Font", tSlot.second.font.data())) {
302 for (const auto& typeface : fTypefaceList) {
303 if (ImGui::Selectable(typeface, false)) {
304 tSlot.second.font = typeface;
305 this->pushSlots();
306 }
307 }
308 ImGui::EndCombo();
309 }
310 ImGui::PopID();
311 }
312
313 ImGui::Text("Image Slots");
314 for (size_t i = 0; i < fImageSlots.size(); i++) {
315 auto& iSlot = fImageSlots.at(i);
316 ImGui::PushID(i);
317 ImGui::Text("%s", iSlot.first.c_str());
318 if (ImGui::BeginCombo("Resource", iSlot.second.data())) {
319 for (const auto& res : fResList) {
320 if (ImGui::Selectable(res.c_str(), false)) {
321 iSlot.second = res.c_str();
322 this->pushSlots();
323 }
324 }
325 ImGui::EndCombo();
326 }
327 ImGui::PopID();
328 }
329 }
330 ImGui::End();
331 }
332
333 void pushSlots() {
334 for(const auto& s : fColorSlots) {
335 fSlotManager->setColorSlot(s.first, SkColor4f{s.second[0], s.second[1],
336 s.second[2], s.second[3]}.toSkColor());
337 }
338 for(const auto& s : fScalarSlots) {
339 fSlotManager->setScalarSlot(s.first, s.second);
340 }
341 for(const auto& s : fVec2Slots) {
342 fSlotManager->setVec2Slot(s.first, {s.second.x, s.second.y});
343 }
344 for(const auto& s : fTextStringSlots) {
345 auto t = fSlotManager->getTextSlot(s.first);
346 t->fText = SkString(s.second.source.data());
347 t->fTypeface = ToolUtils::TestFontMgr()->matchFamilyStyle(s.second.font.c_str(),
348 SkFontStyle());
349 fSlotManager->setTextSlot(s.first, *t);
350 }
351 for(const auto& s : fImageSlots) {
352 auto image = fResourceProvider->loadImageAsset("images/", s.second.c_str(), nullptr);
353 fSlotManager->setImageSlot(s.first, image);
354 }
355 }
356
357 void initializeSlotManagerUI() {
358 prepareImageAssetList(GetResourcePath("skottie/images").c_str());
359 // only initialize if slots are unpopulated
360 if (fColorSlots.empty() && fScalarSlots.empty() && fTextStringSlots.empty()) {
361 auto slotInfos = fSlotManager->getSlotInfo();
362 for (const auto &sid : slotInfos.fColorSlotIDs) {
363 addColorSlot(sid);
364 }
365 for (const auto &sid : slotInfos.fScalarSlotIDs) {
366 addScalarSlot(sid);
367 }
368 for (const auto &sid : slotInfos.fVec2SlotIDs) {
369 addVec2Slot(sid);
370 }
371 for (const auto &sid : slotInfos.fImageSlotIDs) {
372 addImageSlot(sid);
373 }
374 for (const auto &sid : slotInfos.fTextSlotIDs) {
375 addTextSlot(sid);
376 }
377 }
378 }
379
380private:
381 static constexpr int kBufferLen = 256;
382
383 sk_sp<skottie::SlotManager> fSlotManager;
384 const sk_sp<skresources::ResourceProvider> fResourceProvider;
385 std::vector<SkString> fResList;
386 static constexpr std::array<const char*, 4> fTypefaceList = {"Arial",
387 "Courier New",
388 "Roboto-Regular",
389 "Georgia"};
390
391 using GuiTextBuffer = std::array<char, kBufferLen>;
392
393 void addColorSlot(SkString slotID) {
394 auto c = fSlotManager->getColorSlot(slotID);
395 SkColor4f color4f = SkColor4f::FromColor(*c);
396 fColorSlots.push_back(std::make_pair(slotID, color4f.array()));
397 }
398
399 void addScalarSlot(SkString slotID) {
400 fScalarSlots.push_back(std::make_pair(slotID, *fSlotManager->getScalarSlot(slotID)));
401 }
402
403 void addVec2Slot(SkString slotID) {
404 fVec2Slots.push_back(std::make_pair(slotID, *fSlotManager->getVec2Slot(slotID)));
405 }
406
407 void addTextSlot(SkString slotID) {
408 std::array<char, kBufferLen> textSource = {'\0'};
409 SkString s = fSlotManager->getTextSlot(slotID)->fText;
410 std::copy(s.data(), s.data() + s.size(), textSource.data());
411 TextSlotData data = {textSource, fTypefaceList[0]};
412 fTextStringSlots.push_back(std::make_pair(slotID, data));
413 }
414
415 void addImageSlot(SkString slotID) {
416 fImageSlots.push_back(std::make_pair(slotID, fResList[0].data()));
417 }
418
419 void prepareImageAssetList(const char* dirname) {
420 fResList.clear();
421 SkOSFile::Iter iter(dirname, ".png");
422 for (SkString file; iter.next(&file); ) {
423 fResList.push_back(file);
424 }
425 }
426
427 struct TextSlotData {
428 GuiTextBuffer source;
429 std::string font;
430 };
431
432 std::vector<std::pair<SkString, std::array<float, 4>>> fColorSlots;
433 std::vector<std::pair<SkString, float>> fScalarSlots;
434 std::vector<std::pair<SkString, SkV2>> fVec2Slots;
435 std::vector<std::pair<SkString, TextSlotData>> fTextStringSlots;
436 std::vector<std::pair<SkString, std::string>> fImageSlots;
437
438};
439
440static void draw_stats_box(SkCanvas* canvas, const skottie::Animation::Builder::Stats& stats) {
441 static constexpr SkRect kR = { 10, 10, 280, 120 };
442 static constexpr SkScalar kTextSize = 20;
443
445 paint.setAntiAlias(true);
446 paint.setColor(0xffeeeeee);
447
449
450 canvas->drawRect(kR, paint);
451
452 paint.setColor(SK_ColorBLACK);
453
454 const auto json_size = SkStringPrintf("Json size: %zu bytes",
455 stats.fJsonSize);
456 canvas->drawString(json_size, kR.x() + 10, kR.y() + kTextSize * 1, font, paint);
457 const auto animator_count = SkStringPrintf("Animator count: %zu",
458 stats.fAnimatorCount);
459 canvas->drawString(animator_count, kR.x() + 10, kR.y() + kTextSize * 2, font, paint);
460 const auto json_parse_time = SkStringPrintf("Json parse time: %.3f ms",
461 stats.fJsonParseTimeMS);
462 canvas->drawString(json_parse_time, kR.x() + 10, kR.y() + kTextSize * 3, font, paint);
463 const auto scene_parse_time = SkStringPrintf("Scene build time: %.3f ms",
464 stats.fSceneParseTimeMS);
465 canvas->drawString(scene_parse_time, kR.x() + 10, kR.y() + kTextSize * 4, font, paint);
466 const auto total_load_time = SkStringPrintf("Total load time: %.3f ms",
467 stats.fTotalLoadTimeMS);
468 canvas->drawString(total_load_time, kR.x() + 10, kR.y() + kTextSize * 5, font, paint);
469
471 canvas->drawRect(kR, paint);
472}
473
474SkottieSlide::SkottieSlide(const SkString& name, const SkString& path)
475 : fPath(path) {
476 fName = name;
477}
478
479void SkottieSlide::init() {
480 class Logger final : public skottie::Logger {
481 public:
482 struct LogEntry {
483 SkString fMessage,
484 fJSON;
485 };
486
487 void log(skottie::Logger::Level lvl, const char message[], const char json[]) override {
488 auto& log = lvl == skottie::Logger::Level::kError ? fErrors : fWarnings;
489 log.push_back({ SkString(message), json ? SkString(json) : SkString() });
490 }
491
492 void report() const {
493 SkDebugf("Animation loaded with %zu error%s, %zu warning%s.\n",
494 fErrors.size(), fErrors.size() == 1 ? "" : "s",
495 fWarnings.size(), fWarnings.size() == 1 ? "" : "s");
496
497 const auto& show = [](const LogEntry& log, const char prefix[]) {
498 SkDebugf("%s%s", prefix, log.fMessage.c_str());
499 if (!log.fJSON.isEmpty())
500 SkDebugf(" : %s", log.fJSON.c_str());
501 SkDebugf("\n");
502 };
503
504 for (const auto& err : fErrors) show(err, " !! ");
505 for (const auto& wrn : fWarnings) show(wrn, " ?? ");
506 }
507
508 private:
509 std::vector<LogEntry> fErrors,
510 fWarnings;
511 };
512
513 auto logger = sk_make_sp<Logger>();
514
515 uint32_t flags = 0;
516 if (fPreferGlyphPaths) {
518 }
520
521 // Viewer should have already registered the codecs necessary for DataURIResourceProviderProxy
523 auto resource_provider =
524 sk_make_sp<AudioProviderProxy>(skresources::DataURIResourceProviderProxy::Make(
526 predecode),
527 predecode,
529
530 static constexpr char kInterceptPrefix[] = "__";
531 auto precomp_interceptor =
532 sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(resource_provider,
533 kInterceptPrefix);
534
535 fTransformTracker = sk_make_sp<TransformTracker>();
536 auto text_tracker = sk_make_sp<TextTracker>(fTransformTracker);
537
538 builder.setLogger(logger)
539 .setFontManager(ToolUtils::TestFontMgr())
540 .setPrecompInterceptor(std::move(precomp_interceptor))
541 .setResourceProvider(resource_provider)
542 .setPropertyObserver(text_tracker);
543
544 fAnimation = builder.makeFromFile(fPath.c_str());
545 fAnimationStats = builder.getStats();
546 fTimeBase = 0; // force a time reset
547
548 if (!fSlotManagerInterface) {
549 fSlotManagerInterface = std::make_unique<SlotManagerInterface>(builder.getSlotManager(), resource_provider);
550 }
551
552 fSlotManagerInterface->initializeSlotManagerUI();
553
554 if (fAnimation) {
555 fAnimation->seek(0);
556 fFrameTimes.resize(SkScalarCeilToInt(fAnimation->duration() * fAnimation->fps()));
557 SkDebugf("Loaded Bodymovin animation v: %s, size: [%f %f]\n",
558 fAnimation->version().c_str(),
559 fAnimation->size().width(),
560 fAnimation->size().height());
561 logger->report();
562
563 if (auto text_props = std::move(text_tracker->props()); !text_props.empty()) {
564 // Attach the editor to the first text layer, and track the rest as dependents.
565 auto editor_target = std::move(text_props[0]);
566 text_props.erase(text_props.cbegin());
567 fTextEditor = sk_make_sp<skottie_utils::TextEditor>(std::move(editor_target),
568 std::move(text_props));
569 fTextEditor->setCursorWeight(1.2f);
570 }
571 } else {
572 SkDebugf("failed to load Bodymovin animation: %s\n", fPath.c_str());
573 }
574}
575
576void SkottieSlide::load(SkScalar w, SkScalar h) {
577 fWinSize = SkSize::Make(w, h);
578 this->init();
579}
580
581void SkottieSlide::unload() {
582 fAnimation.reset();
583}
584
585void SkottieSlide::resize(SkScalar w, SkScalar h) {
586 fWinSize = { w, h };
587}
588
589void SkottieSlide::draw(SkCanvas* canvas) {
590 if (fAnimation) {
591 SkAutoCanvasRestore acr(canvas, true);
592 const auto dstR = SkRect::MakeSize(fWinSize);
593
594 {
595 const auto t0 = SkTime::GetNSecs();
596 fAnimation->render(canvas, &dstR);
597
598 // TODO: this does not capture GPU flush time!
599 const auto frame_index = static_cast<size_t>(fCurrentFrame);
600 fFrameTimes[frame_index] = static_cast<float>((SkTime::GetNSecs() - t0) * 1e-6);
601 }
602
603 double fr = 60;
604 if (fFrameRate != 0) {
605 fr = fFrameRate;
606 }
607 fTransformTracker->renderTracker(canvas, fCurrentFrame/fr, fWinSize, fAnimation->size());
608
609 if (fShowAnimationStats) {
610 draw_stats_box(canvas, fAnimationStats);
611 }
612 if (fShowAnimationInval) {
613 const auto t = SkMatrix::RectToRect(SkRect::MakeSize(fAnimation->size()), dstR,
615 SkPaint fill, stroke;
616 fill.setAntiAlias(true);
617 fill.setColor(0x40ff0000);
618 stroke.setAntiAlias(true);
619 stroke.setColor(0xffff0000);
621
622 for (const auto& r : fInvalController) {
624 t.mapRect(&bounds, r);
625 canvas->drawRect(bounds, fill);
626 canvas->drawRect(bounds, stroke);
627 }
628 }
629 if (fShowUI) {
630 this->renderUI();
631 }
632 if (fShowSlotManager) {
633 // not able to track layers with a PropertyObserver while using SM's PropertyObserver
634 fShowTrackerUI = false;
635 fSlotManagerInterface->renderUI();
636 }
637 if (fShowTrackerUI) {
638 fTransformTracker->renderUI();
639 }
640 }
641}
642
643bool SkottieSlide::animate(double nanos) {
644 if (!fTimeBase) {
645 // Reset the animation time.
646 fTimeBase = nanos;
647 }
648
649 if (fAnimation) {
650 fInvalController.reset();
651
652 const auto frame_count = fAnimation->duration() * fAnimation->fps();
653
654 if (!fDraggingProgress) {
655 // Clock-driven progress: update current frame.
656 const double t_sec = (nanos - fTimeBase) * 1e-9;
657 fCurrentFrame = std::fmod(t_sec * fAnimation->fps(), frame_count);
658 } else {
659 // Slider-driven progress: update the time origin.
660 fTimeBase = nanos - fCurrentFrame / fAnimation->fps() * 1e9;
661 }
662
663 // Sanitize and rate-lock the current frame.
664 fCurrentFrame = SkTPin<float>(fCurrentFrame, 0.0f, frame_count - 1);
665 if (fFrameRate > 0) {
666 const auto fps_scale = fFrameRate / fAnimation->fps();
667 fCurrentFrame = std::trunc(fCurrentFrame * fps_scale) / fps_scale;
668 }
669
670 fAnimation->seekFrame(fCurrentFrame, fShowAnimationInval ? &fInvalController
671 : nullptr);
672 }
673 return true;
674}
675
676bool SkottieSlide::onChar(SkUnichar c) {
677 if (fTextEditor && fTextEditor->onCharInput(c)) {
678 return true;
679 }
680
681 switch (c) {
682 case 'I':
683 fShowAnimationStats = !fShowAnimationStats;
684 return true;
685 case 'G':
686 fPreferGlyphPaths = !fPreferGlyphPaths;
687 this->load(fWinSize.width(), fWinSize.height());
688 return true;
689 case 'T':
690 fShowTrackerUI = !fShowTrackerUI;
691 return true;
692 case 'M':
693 fShowSlotManager = !fShowSlotManager;
694 return true;
695 case 'E':
696 if (fTextEditor) {
697 fTextEditor->toggleEnabled();
698 }
699 return true;
700 }
701
702 return Slide::onChar(c);
703}
704
705bool SkottieSlide::onMouse(SkScalar x, SkScalar y, skui::InputState state, skui::ModifierKey mod) {
706 if (fTextEditor && fTextEditor->onMouseInput(x, y, state, mod)) {
707 return true;
708 }
709
710 switch (state) {
712 fShowAnimationInval = !fShowAnimationInval;
713 fShowAnimationStats = !fShowAnimationStats;
714 break;
715 default:
716 break;
717 }
718
719 fShowUI = this->UIArea().contains(x, y);
720
721 return false;
722}
723
724SkRect SkottieSlide::UIArea() const {
725 static constexpr float kUIHeight = 120.0f;
726
727 return SkRect::MakeXYWH(0, fWinSize.height() - kUIHeight, fWinSize.width(), kUIHeight);
728}
729
730void SkottieSlide::renderUI() {
731 static constexpr auto kUI_opacity = 0.35f,
732 kUI_hist_height = 50.0f,
733 kUI_fps_width = 100.0f;
734
735 auto add_frame_rate_option = [this](const char* label, double rate) {
736 const auto is_selected = (fFrameRate == rate);
737 if (ImGui::Selectable(label, is_selected)) {
738 fFrameRate = rate;
739 fFrameRateLabel = label;
740 }
741 if (is_selected) {
742 ImGui::SetItemDefaultFocus();
743 }
744 };
745
746 ImGui::SetNextWindowBgAlpha(kUI_opacity);
747 if (ImGui::Begin("Skottie Controls", nullptr, ImGuiWindowFlags_NoDecoration |
748 ImGuiWindowFlags_NoResize |
749 ImGuiWindowFlags_NoMove |
750 ImGuiWindowFlags_NoSavedSettings |
751 ImGuiWindowFlags_NoFocusOnAppearing |
752 ImGuiWindowFlags_NoNav)) {
753 const auto ui_area = this->UIArea();
754 ImGui::SetWindowPos(ImVec2(ui_area.x(), ui_area.y()));
755 ImGui::SetWindowSize(ImVec2(ui_area.width(), ui_area.height()));
756
757 ImGui::PushItemWidth(-1);
758 ImGui::PlotHistogram("", fFrameTimes.data(), fFrameTimes.size(),
759 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, kUI_hist_height));
760 ImGui::SliderFloat("", &fCurrentFrame, 0, fAnimation->duration() * fAnimation->fps() - 1);
761 fDraggingProgress = ImGui::IsItemActive();
762 ImGui::PopItemWidth();
763
764 ImGui::PushItemWidth(kUI_fps_width);
765 if (ImGui::BeginCombo("FPS", fFrameRateLabel)) {
766 add_frame_rate_option("", 0.0);
767 add_frame_rate_option("Native", fAnimation->fps());
768 add_frame_rate_option( "1", 1.0);
769 add_frame_rate_option("15", 15.0);
770 add_frame_rate_option("24", 24.0);
771 add_frame_rate_option("30", 30.0);
772 add_frame_rate_option("60", 60.0);
773 ImGui::EndCombo();
774 }
775 ImGui::PopItemWidth();
776 }
777 ImGui::End();
778}
779
780#endif // SK_ENABLE_SKOTTIE
SkPath fPath
const char * fName
static constexpr float kTolerance
sk_sp< SkData > GetResourceAsData(const char *resource)
Definition Resources.cpp:42
SkString GetResourcePath(const char *resource)
Definition Resources.cpp:23
static sk_sp< SkImage > render(const SkPicture &p)
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition SkColor.h:131
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
static const char * resource_name(SkPDFResourceType type)
#define SkScalarCeilToInt(x)
Definition SkScalar.h:36
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
int32_t SkUnichar
Definition SkTypes.h:175
SI T load(const P *ptr)
static std::unique_ptr< SkAudioPlayer > Make(sk_sp< SkData >)
void drawRect(const SkRect &rect, const SkPaint &paint)
void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint &paint)
void concat(const SkMatrix &matrix)
void drawString(const char str[], SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition SkCanvas.h:1803
void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint &paint)
sk_sp< SkTypeface > matchFamilyStyle(const char familyName[], const SkFontStyle &) const
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition SkMatrix.h:157
static SkMatrix RotateDeg(SkScalar deg)
Definition SkMatrix.h:104
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition SkMatrix.h:91
@ kCenter_ScaleToFit
scales and aligns to center
Definition SkMatrix.h:139
static SkString Join(const char *rootPath, const char *relativePath)
Definition SkOSPath.cpp:14
static SkString Dirname(const char *fullPath)
Definition SkOSPath.cpp:36
void setStyle(Style style)
Definition SkPaint.cpp:105
void setColor(SkColor color)
Definition SkPaint.cpp:119
void setAntiAlias(bool aa)
Definition SkPaint.h:170
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
const char * c_str() const
Definition SkString.h:133
virtual bool onChar(SkUnichar c)
Definition Slide.h:44
void reset(T *ptr=nullptr)
Definition SkRefCnt.h:310
void seekFrame(double t, sksg::InvalidationController *ic=nullptr)
Definition Skottie.cpp:513
const SkSize & size() const
Definition Skottie.h:286
double fps() const
Definition Skottie.h:273
const SkString & version() const
Definition Skottie.h:285
double duration() const
Definition Skottie.h:268
void render(SkCanvas *canvas, const SkRect *dst=nullptr) const
Definition Skottie.cpp:482
void seek(SkScalar t, sksg::InvalidationController *ic=nullptr)
Definition Skottie.h:244
virtual void log(Level, const char message[], const char *json=nullptr)=0
virtual void onTransformProperty(const char node_name[], const LazyHandle< TransformPropertyHandle > &)
virtual void onTextProperty(const char node_name[], const LazyHandle< TextPropertyHandle > &)
static sk_sp< DataURIResourceProviderProxy > Make(sk_sp< ResourceProvider > rp, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode, sk_sp< const SkFontMgr > fontMgr=nullptr)
virtual void seek(float t)=0
static sk_sp< FileResourceProvider > Make(SkString base_dir, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode)
static sk_sp< MultiFrameImageAsset > Make(sk_sp< SkData >, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode)
sk_sp< ExternalTrackAsset > loadAudioAsset(const char[], const char[], const char[]) override
sk_sp< SkData > load(const char[], const char[]) const override
virtual sk_sp< SkData > load(const char[], const char[]) const
virtual sk_sp< ImageAsset > loadImageAsset(const char[], const char[], const char[]) const
const Paint & paint
sk_sp< SkImage > image
Definition examples.cpp:29
SkBitmap source
Definition examples.cpp:28
float SkScalar
Definition extension.cpp:12
struct MyStruct s
AtkStateType state
FlutterSemanticsFlag flags
static FlMethodResponse * show(FlTextInputPlugin *self)
const char * name
Definition fuchsia.cc:50
Win32Message message
double y
double x
Optional< SkRect > bounds
Definition SkRecords.h:189
double GetNSecs()
Definition SkTime.cpp:17
sk_sp< SkTypeface > DefaultTypeface()
sk_sp< SkFontMgr > TestFontMgr()
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
font
Font Metadata and Metrics.
dict stats
Definition malisc.py:20
InputState
Definition InputState.h:6
ModifierKey
Definition ModifierKey.h:9
Definition ref_ptr.h:256
init(device_serial, adb_binary)
Definition _adb_path.py:12
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
SkScalar w
SkScalar h
static SkRGBA4f FromColor(SkColor color)
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
static constexpr SkRect MakeSize(const SkSize &size)
Definition SkRect.h:633
static constexpr SkSize Make(SkScalar w, SkScalar h)
Definition SkSize.h:56
SkScalar width() const
Definition SkSize.h:76
SkScalar height() const
Definition SkSize.h:77