10#if defined(SK_ENABLE_SKOTTIE)
40 explicit Track(std::unique_ptr<SkAudioPlayer> player) : fPlayer(
std::move(player)) {}
43 void seek(
float t)
override {
44 if (fPlayer->isStopped() && t >=0) {
48 if (fPlayer->isPlaying()) {
53 const auto player_pos = fPlayer->time();
62 const std::unique_ptr<SkAudioPlayer> fPlayer;
73 const char[] )
override {
76 return sk_make_sp<Track>(std::move(player));
86 virtual ~Decorator() =
default;
93class SimpleMarker final :
public Decorator {
95 ~SimpleMarker()
override =
default;
97 static std::unique_ptr<Decorator>
Make() {
return std::make_unique<SimpleMarker>(); }
102 p.setAntiAlias(
true);
108 p.setStrokeWidth(1.5f);
116 TestingResourceProvider() {}
120 if (it != fResources.end()) {
129 const char [])
const override {
140 std::unordered_map<std::string, sk_sp<SkData>> fResources;
143static const struct DecoratorRec {
145 std::unique_ptr<Decorator>(*fFactory)();
154 std::vector<std::unique_ptr<skottie::TextPropertyHandle>>& props() {
160 const LazyHandle<skottie::TextPropertyHandle>& lh)
override {
161 fTextProps.push_back(lh());
164 fDelegate->onTextProperty(node_name, lh);
169 std::vector<std::unique_ptr<skottie::TextPropertyHandle>> fTextProps;
177 if (ImGui::Begin(
"Transform Tracker",
nullptr)) {
178 if (ImGui::BeginCombo(
"Transform", fTransformSelect
181 if (ImGui::Selectable(
"(none)",
true)) {
182 fTransformSelect =
nullptr;
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;
191 fDecorator = fDecoratorSelect->fFactory();
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();
214 if (!fTransformSelect) {
218 const auto tprop =
std::get<1>(*fTransformSelect)->get();
230 canvas->
concat(viewer_matrix);
233 fDecorator->render(canvas,
time,
m);
238 const LazyHandle<skottie::TransformPropertyHandle>& lh)
override {
240 fTransforms.push_back(std::make_tuple(
SkString(
name), lh()));
243 using TransformT = std::tuple<SkString, std::unique_ptr<skottie::TransformPropertyHandle>>;
245 std::vector<TransformT> fTransforms;
246 std::unique_ptr<Decorator> fDecorator;
247 const TransformT* fTransformSelect =
nullptr;
248 const DecoratorRec* fDecoratorSelect = &kDecorators[0];
252class SkottieSlide::SlotManagerInterface {
255 : fSlotManager(
std::move(slotManager))
256 , fResourceProvider(
std::move(rp))
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);
266 ImGui::Text(
"%s", cSlot.first.c_str());
267 if (ImGui::ColorEdit4(
"Color", cSlot.second.data())) {
272 ImGui::Text(
"Scalar Slots");
273 for (
size_t i = 0;
i < fScalarSlots.size();
i++) {
274 auto& oSlot = fScalarSlots.at(
i);
276 ImGui::Text(
"%s", oSlot.first.c_str());
277 if (ImGui::InputFloat(
"Scalar", &(oSlot.second))) {
282 ImGui::Text(
"Vec2 Slots");
283 for (
size_t i = 0;
i < fVec2Slots.size();
i++) {
284 auto& vSlot = fVec2Slots.at(
i);
286 ImGui::Text(
"%s", vSlot.first.c_str());
287 if (ImGui::InputFloat2(
"x, y", &(vSlot.second.x))) {
292 ImGui::Text(
"Text Slots");
293 for (
size_t i = 0;
i < fTextStringSlots.size();
i++) {
294 auto& tSlot = fTextStringSlots.at(
i);
296 ImGui::Text(
"%s", tSlot.first.c_str());
297 if (ImGui::InputText(
"Text", tSlot.second.source.data(),
298 tSlot.second.source.size())) {
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;
313 ImGui::Text(
"Image Slots");
314 for (
size_t i = 0;
i < fImageSlots.size();
i++) {
315 auto& iSlot = fImageSlots.at(
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();
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());
338 for(
const auto&
s : fScalarSlots) {
339 fSlotManager->setScalarSlot(
s.first,
s.second);
341 for(
const auto&
s : fVec2Slots) {
342 fSlotManager->setVec2Slot(
s.first, {s.second.x, s.second.y});
344 for(
const auto&
s : fTextStringSlots) {
345 auto t = fSlotManager->getTextSlot(
s.first);
346 t->fText =
SkString(
s.second.source.data());
349 fSlotManager->setTextSlot(
s.first, *t);
351 for(
const auto&
s : fImageSlots) {
352 auto image = fResourceProvider->loadImageAsset(
"images/",
s.second.c_str(),
nullptr);
353 fSlotManager->setImageSlot(
s.first,
image);
357 void initializeSlotManagerUI() {
360 if (fColorSlots.empty() && fScalarSlots.empty() && fTextStringSlots.empty()) {
361 auto slotInfos = fSlotManager->getSlotInfo();
362 for (
const auto &sid : slotInfos.fColorSlotIDs) {
365 for (
const auto &sid : slotInfos.fScalarSlotIDs) {
368 for (
const auto &sid : slotInfos.fVec2SlotIDs) {
371 for (
const auto &sid : slotInfos.fImageSlotIDs) {
374 for (
const auto &sid : slotInfos.fTextSlotIDs) {
381 static constexpr int kBufferLen = 256;
385 std::vector<SkString> fResList;
386 static constexpr std::array<const char*, 4> fTypefaceList = {
"Arial",
391 using GuiTextBuffer = std::array<char, kBufferLen>;
393 void addColorSlot(
SkString slotID) {
395 SkColor4f color4f = SkColor4f::FromColor(*c);
396 fColorSlots.push_back(std::make_pair(slotID, color4f.array()));
399 void addScalarSlot(
SkString slotID) {
400 fScalarSlots.push_back(std::make_pair(slotID, *fSlotManager->
getScalarSlot(slotID)));
404 fVec2Slots.push_back(std::make_pair(slotID, *fSlotManager->
getVec2Slot(slotID)));
408 std::array<char, kBufferLen> textSource = {
'\0'};
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));
415 void addImageSlot(
SkString slotID) {
416 fImageSlots.push_back(std::make_pair(slotID, fResList[0].
data()));
419 void prepareImageAssetList(
const char* dirname) {
423 fResList.push_back(
file);
427 struct TextSlotData {
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;
441 static constexpr SkRect kR = { 10, 10, 280, 120 };
442 static constexpr SkScalar kTextSize = 20;
445 paint.setAntiAlias(
true);
446 paint.setColor(0xffeeeeee);
458 stats.fAnimatorCount);
460 const auto json_parse_time =
SkStringPrintf(
"Json parse time: %.3f ms",
461 stats.fJsonParseTimeMS);
463 const auto scene_parse_time =
SkStringPrintf(
"Scene build time: %.3f ms",
464 stats.fSceneParseTimeMS);
466 const auto total_load_time =
SkStringPrintf(
"Total load time: %.3f ms",
467 stats.fTotalLoadTimeMS);
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");
497 const auto&
show = [](
const LogEntry&
log,
const char prefix[]) {
499 if (!
log.fJSON.isEmpty())
504 for (
const auto& err : fErrors)
show(err,
" !! ");
505 for (
const auto& wrn : fWarnings)
show(wrn,
" ?? ");
509 std::vector<LogEntry> fErrors,
513 auto logger = sk_make_sp<Logger>();
516 if (fPreferGlyphPaths) {
523 auto resource_provider =
530 static constexpr char kInterceptPrefix[] =
"__";
531 auto precomp_interceptor =
532 sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(resource_provider,
535 fTransformTracker = sk_make_sp<TransformTracker>();
536 auto text_tracker = sk_make_sp<TextTracker>(fTransformTracker);
540 .setPrecompInterceptor(std::move(precomp_interceptor))
541 .setResourceProvider(resource_provider)
542 .setPropertyObserver(text_tracker);
545 fAnimationStats =
builder.getStats();
548 if (!fSlotManagerInterface) {
549 fSlotManagerInterface = std::make_unique<SlotManagerInterface>(
builder.getSlotManager(), resource_provider);
552 fSlotManagerInterface->initializeSlotManagerUI();
557 SkDebugf(
"Loaded Bodymovin animation v: %s, size: [%f %f]\n",
563 if (
auto text_props = std::move(text_tracker->props()); !text_props.empty()) {
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);
572 SkDebugf(
"failed to load Bodymovin animation: %s\n",
fPath.c_str());
581void SkottieSlide::unload() {
596 fAnimation->
render(canvas, &dstR);
599 const auto frame_index =
static_cast<size_t>(fCurrentFrame);
604 if (fFrameRate != 0) {
607 fTransformTracker->renderTracker(canvas, fCurrentFrame/fr, fWinSize, fAnimation->
size());
609 if (fShowAnimationStats) {
610 draw_stats_box(canvas, fAnimationStats);
612 if (fShowAnimationInval) {
618 stroke.setAntiAlias(
true);
619 stroke.setColor(0xffff0000);
622 for (
const auto& r : fInvalController) {
632 if (fShowSlotManager) {
634 fShowTrackerUI =
false;
635 fSlotManagerInterface->renderUI();
637 if (fShowTrackerUI) {
638 fTransformTracker->renderUI();
643bool SkottieSlide::animate(
double nanos) {
650 fInvalController.reset();
652 const auto frame_count = fAnimation->
duration() * fAnimation->
fps();
654 if (!fDraggingProgress) {
656 const double t_sec = (nanos - fTimeBase) * 1
e-9;
657 fCurrentFrame = std::fmod(t_sec * fAnimation->
fps(), frame_count);
660 fTimeBase = nanos - fCurrentFrame / fAnimation->
fps() * 1e9;
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;
670 fAnimation->
seekFrame(fCurrentFrame, fShowAnimationInval ? &fInvalController
677 if (fTextEditor && fTextEditor->onCharInput(c)) {
683 fShowAnimationStats = !fShowAnimationStats;
686 fPreferGlyphPaths = !fPreferGlyphPaths;
687 this->
load(fWinSize.width(), fWinSize.height());
690 fShowTrackerUI = !fShowTrackerUI;
693 fShowSlotManager = !fShowSlotManager;
697 fTextEditor->toggleEnabled();
706 if (fTextEditor && fTextEditor->onMouseInput(
x,
y,
state, mod)) {
712 fShowAnimationInval = !fShowAnimationInval;
713 fShowAnimationStats = !fShowAnimationStats;
719 fShowUI = this->UIArea().contains(
x,
y);
724SkRect SkottieSlide::UIArea()
const {
725 static constexpr float kUIHeight = 120.0f;
727 return SkRect::MakeXYWH(0, fWinSize.height() - kUIHeight, fWinSize.width(), kUIHeight);
730void SkottieSlide::renderUI() {
731 static constexpr auto kUI_opacity = 0.35f,
732 kUI_hist_height = 50.0f,
733 kUI_fps_width = 100.0f;
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)) {
739 fFrameRateLabel = label;
742 ImGui::SetItemDefaultFocus();
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()));
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();
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);
775 ImGui::PopItemWidth();
static constexpr float kTolerance
sk_sp< SkData > GetResourceAsData(const char *resource)
SkString GetResourcePath(const char *resource)
constexpr SkColor SK_ColorRED
constexpr SkColor SK_ColorBLACK
constexpr SkColor SK_ColorGREEN
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static const char * resource_name(SkPDFResourceType type)
#define SkScalarCeilToInt(x)
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
static void copy(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
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)
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)
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
static SkMatrix RotateDeg(SkScalar deg)
static SkMatrix Translate(SkScalar dx, SkScalar dy)
@ kCenter_ScaleToFit
scales and aligns to center
static SkString Join(const char *rootPath, const char *relativePath)
static SkString Dirname(const char *fullPath)
void setColor(SkColor color)
void setAntiAlias(bool aa)
@ kStroke_Style
set to stroke geometry
const char * c_str() const
virtual bool onChar(SkUnichar c)
void reset(T *ptr=nullptr)
void seekFrame(double t, sksg::InvalidationController *ic=nullptr)
const SkSize & size() const
const SkString & version() const
void render(SkCanvas *canvas, const SkRect *dst=nullptr) const
void seek(SkScalar t, sksg::InvalidationController *ic=nullptr)
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 > &)
std::optional< SkV2 > getVec2Slot(const SlotID &) const
std::optional< TextPropertyValue > getTextSlot(const SlotID &) const
std::optional< float > getScalarSlot(const SlotID &) const
std::optional< SkColor > getColorSlot(const SlotID &) const
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
FlutterSemanticsFlag flags
static FlMethodResponse * show(FlTextInputPlugin *self)
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
Optional< SkRect > bounds
sk_sp< const SkImage > image
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
DEF_SWITCHES_START aot vmservice shared library name
font
Font Metadata and Metrics.
const myers::Point & get< 1 >(const myers::Segment &s)
const myers::Point & get< 0 >(const myers::Segment &s)
def render(idl_node, indent_str=' ')
SIN Vec< N, float > trunc(const Vec< N, float > &x)
SIN Vec< N, float > abs(const Vec< N, float > &x)
static double time(int loops, Benchmark *bench, Target *target)
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
static constexpr SkRect MakeSize(const SkSize &size)
static constexpr SkSize Make(SkScalar w, SkScalar h)
std::shared_ptr< const fml::Mapping > data