34#include <emscripten.h>
35#include <emscripten/bind.h>
37#if defined(SK_CODEC_DECODES_GIF)
40#if defined(SK_CODEC_DECODES_JPEG)
43#if defined(SK_CODEC_DECODES_PNG)
46#if defined(SK_CODEC_DECODES_WEBP)
50#if !defined(CK_NO_FONTS)
58struct SimpleSlottableTextProperty {
96 switch (this->horizAlign) {
101 textProperty.
fHAlign = SkTextUtils::Align::kCenter_Align;
111 textProperty.
fVAlign = this->vertAlign;
112 textProperty.
fResize = this->resize;
120 if (this->direction == para::TextDirection::kRtl) {
128 textProperty.
fBox =
reinterpret_cast<SkRect*
>(this->boundingBoxPtr)[0];
140 explicit WebTrack(emscripten::val player) : fPlayer(
std::move(player)) {}
143 void seek(
float t)
override {
144 fPlayer.call<
void>(
"seek", val(t));
147 const emscripten::val fPlayer;
152 ~SkottieAssetProvider()
override =
default;
158 using AssetVec = std::vector<std::pair<SkString, sk_sp<SkData>>>;
162 std::move(soundMap)));
167 const char[] )
const override {
169 if (
auto data = this->findAsset(
name)) {
182 const char id[])
override {
183 emscripten::val player = this->findSoundAsset(
id);
184 if (player.as<
bool>()) {
185 return sk_make_sp<WebTrack>(std::move(player));
196 auto stream = std::make_unique<SkMemoryStream>(faceData);
202 return this->findAsset(
name);
206 explicit SkottieAssetProvider(AssetVec assets, emscripten::val soundMap)
207 : fAssets(
std::move(assets))
208 , fSoundMap(
std::move(soundMap)) {}
211 for (
const auto& asset : fAssets) {
212 if (asset.first.equals(
name)) {
221 emscripten::val findSoundAsset(
const char name[])
const {
222 if (fSoundMap.as<
bool>() && fSoundMap.hasOwnProperty(
"getPlayer")) {
223 emscripten::val player = fSoundMap.call<emscripten::val>(
"getPlayer", val(
name));
224 if (player.as<
bool>() && player.hasOwnProperty(
"seek")) {
228 return emscripten::val::null();
231 const AssetVec fAssets;
232 const emscripten::val fSoundMap;
240 &&
logger.hasOwnProperty(kWrnFunc)
241 &&
logger.hasOwnProperty(kErrFunc)
247 explicit JSLogger(emscripten::val
logger) : fLogger(
std::move(
logger)) {}
249 void log(Level lvl,
const char msg[],
const char* json)
override {
250 const auto* func = lvl ==
Level::kError ? kErrFunc : kWrnFunc;
251 fLogger.call<
void>(func, std::string(msg), std::string(json));
254 inline static constexpr char kWrnFunc[] =
"onWarning",
255 kErrFunc[] =
"onError";
257 const emscripten::val fLogger;
260class ManagedAnimation final :
public SkRefCnt {
264 std::string prop_prefix,
266 auto mgr = std::make_unique<skottie_utils::CustomPropertyManager>(
268 prop_prefix.c_str());
269 static constexpr char kInterceptPrefix[] =
"__";
271 sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(rp, kInterceptPrefix);
273 builder.setMarkerObserver(mgr->getMarkerObserver())
274 .setPropertyObserver(mgr->getPropertyObserver())
275 .setResourceProvider(rp)
276 .setPrecompInterceptor(std::move(pinterceptor))
279 auto animation =
builder.make(json.c_str(), json.size());
280 auto slotManager =
builder.getSlotManager();
284 std::move(slotManager), std::move(rp)))
288 ~ManagedAnimation()
override =
default;
295 fAnimation->
seek(t, &ic);
299 SkRect seekFrame(
double t) {
305 double fps()
const {
return fAnimation->
fps(); }
310 JSArray getColorProps()
const {
311 JSArray props = emscripten::val::array();
313 for (
const auto& cp : fPropMgr->getColorProps()) {
316 prop.set(
"value", fPropMgr->getColor(cp));
317 props.call<
void>(
"push",
prop);
323 JSArray getOpacityProps()
const {
324 JSArray props = emscripten::val::array();
326 for (
const auto& op : fPropMgr->getOpacityProps()) {
329 prop.set(
"value", fPropMgr->getOpacity(op));
330 props.call<
void>(
"push",
prop);
337 JSArray props = emscripten::val::array();
339 for (
const auto&
key : fPropMgr->getTextProps()) {
340 const auto txt = fPropMgr->getText(
key);
341 JSObject txt_val = emscripten::val::object();
342 txt_val.set(
"text",
txt.fText.c_str());
343 txt_val.set(
"size",
txt.fTextSize);
347 prop.set(
"value", std::move(txt_val));
349 props.call<
void>(
"push",
prop);
355 JSArray getTransformProps()
const {
356 JSArray props = emscripten::val::array();
358 for (
const auto&
key : fPropMgr->getTransformProps()) {
360 JSObject trans_val = emscripten::val::object();
367 trans_val.set(
"rotation",
transform.fRotation);
369 trans_val.set(
"skew_axis",
transform.fSkewAxis);
373 prop.set(
"value", trans_val);
374 props.call<
void>(
"push",
prop);
380 bool setColor(
const std::string&
key,
SkColor c) {
381 return fPropMgr->setColor(
key, c);
384 bool setOpacity(
const std::string&
key,
float o) {
385 return fPropMgr->setOpacity(
key, o);
388 bool setText(
const std::string&
key, std::string
text,
float size) {
390 auto t = fPropMgr->getText(
key);
395 return fPropMgr->setText(
key, t);
403 transform.fAnchorPoint = {anchorX, anchorY};
413 JSArray markers = emscripten::val::array();
414 for (
const auto&
m : fPropMgr->markers()) {
419 markers.call<
void>(
"push",
marker);
425 JSArray retVal = emscripten::val::array();
426 for (
auto slotID : slotIDs) {
427 retVal.call<
void>(
"push", emscripten::val(slotID.c_str()));
434 JSObject slotInfoJS = emscripten::val::object();
435 auto slotInfo = fSlotMgr->getSlotInfo();
437 slotInfoJS.set(
"colorSlotIDs", copyStringArrayToJSArray(slotInfo.fColorSlotIDs));
438 slotInfoJS.set(
"scalarSlotIDs", copyStringArrayToJSArray(slotInfo.fScalarSlotIDs));
439 slotInfoJS.set(
"vec2SlotIDs", copyStringArrayToJSArray(slotInfo.fVec2SlotIDs));
440 slotInfoJS.set(
"imageSlotIDs", copyStringArrayToJSArray(slotInfo.fImageSlotIDs));
441 slotInfoJS.set(
"textSlotIDs", copyStringArrayToJSArray(slotInfo.fTextSlotIDs));
446 void getColorSlot(
const std::string& slotID,
WASMPointerF32 outPtr) {
448 if (
auto c = fSlotMgr->getColorSlot(
SkString(slotID))) {
449 c4f = SkColor4f::FromColor(*c);
451 c4f = {-1, -1, -1, -1};
453 memcpy(
reinterpret_cast<float*
>(outPtr), &c4f, 4 *
sizeof(
float));
456 emscripten::val getScalarSlot(
const std::string& slotID) {
457 if (
auto s = fSlotMgr->getScalarSlot(
SkString(slotID))) {
458 return emscripten::val(*
s);
460 return emscripten::val::null();
463 void getVec2Slot(
const std::string& slotID,
WASMPointerF32 outPtr) {
466 if (
auto v = fSlotMgr->getVec2Slot(
SkString(slotID))) {
467 vec3 = {v->
x, v->y, 1};
471 memcpy(
reinterpret_cast<float*
>(outPtr), vec3.
ptr(), 3 *
sizeof(
float));
474 JSObject getTextSlot(
const std::string& slotID)
const {
475 if (
auto textProp = fSlotMgr->getTextSlot(
SkString(slotID))){
476 JSObject text_val = emscripten::val::object();
478 text_val.set(
"typeface", textProp->fTypeface);
479 text_val.set(
"text", emscripten::val(textProp->fText.c_str()));
480 text_val.set(
"textSize", textProp->fTextSize);
481 text_val.set(
"minTextSize", textProp->fMinTextSize);
482 text_val.set(
"maxTextSize", textProp->fMaxTextSize);
483 text_val.set(
"strokeWidth", textProp->fStrokeWidth);
484 text_val.set(
"lineHeight", textProp->fLineHeight);
485 text_val.set(
"lineShift", textProp->fLineShift);
486 text_val.set(
"ascent", textProp->fAscent);
487 text_val.set(
"maxLines", textProp->fMaxLines);
489 switch (textProp->fHAlign) {
496 case SkTextUtils::Align::kCenter_Align:
504 text_val.set(
"vertAlign", textProp->fVAlign);
505 text_val.set(
"resize", textProp->fResize);
514 text_val.set(
"direction", para::TextDirection::kLtr);
516 text_val.set(
"direction", para::TextDirection::kRtl);
518 text_val.set(
"strokeJoin", textProp->fStrokeJoin);
520 text_val.set(
"fillColor",
MakeTypedArray(4, SkColor4f::FromColor(textProp->fFillColor)
523 text_val.set(
"strokeColor",
MakeTypedArray(4, SkColor4f::FromColor(textProp->fStrokeColor)
526 const float box[] = {textProp->fBox.fLeft, textProp->fBox.fTop,
527 textProp->fBox.fRight, textProp->fBox.fBottom};
531 return emscripten::val::null();
534 bool setImageSlot(
const std::string& slotID,
const std::string& assetName) {
536 return fSlotMgr->setImageSlot(
SkString(slotID), fResourceProvider->loadImageAsset(
nullptr,
541 bool setColorSlot(
const std::string& slotID,
SkColor c) {
542 return fSlotMgr->setColorSlot(
SkString(slotID), c);
545 bool setScalarSlot(
const std::string& slotID,
float s) {
546 return fSlotMgr->setScalarSlot(
SkString(slotID),
s);
549 bool setVec2Slot(
const std::string& slotID,
SkV2 v) {
550 return fSlotMgr->setVec2Slot(
SkString(slotID), v);
553 bool attachEditor(
const std::string& layerID,
size_t layerIndex) {
555 fTextEditor->setEnabled(
false);
556 fTextEditor =
nullptr;
559 if (layerID.empty()) {
563 auto txt_handle = fPropMgr->getTextHandle(layerID, layerIndex);
568 std::vector<std::unique_ptr<skottie::TextPropertyHandle>> deps;
569 for (
size_t i = 0; ; ++
i) {
570 if (
i == layerIndex) {
574 auto dep_handle = fPropMgr->getTextHandle(layerID,
i);
578 deps.push_back(std::move(dep_handle));
581 fTextEditor = sk_make_sp<skottie_utils::TextEditor>(std::move(txt_handle),
586 void enableEditor(
bool enable) {
588 fTextEditor->setEnabled(enable);
592 bool dispatchEditorKey(
const std::string&
key) {
595 auto key2char = [](
const std::string&
key) ->
SkUnichar {
597 if (
key ==
"ArrowLeft")
return '[';
598 if (
key ==
"ArrowRight")
return ']';
599 if (
key ==
"Backspace")
return '\\';
601 const char* str =
key.c_str();
602 const char*
end = str +
key.size();
606 return str ==
end ? uch : -1;
610 const auto uch = key2char(
key);
612 return fTextEditor->onCharInput(uch);
621 ? fTextEditor->onMouseInput(
x,
y,
state, mod)
625 void setEditorCursorWeight(
float w) {
627 fTextEditor->setCursorWeight(
w);
631 bool setTextSlot(
const std::string& slotID, SimpleSlottableTextProperty t) {
632 return fSlotMgr->setTextSlot(
SkString(slotID), t);
637 std::unique_ptr<skottie_utils::CustomPropertyManager> propMgr,
640 : fAnimation(
std::move(animation))
641 , fPropMgr(
std::move(propMgr))
642 , fSlotMgr(
std::move(slotMgr))
643 , fResourceProvider(
std::move(rp))
647 const std::unique_ptr<skottie_utils::CustomPropertyManager> fPropMgr;
658 class_<skottie::Animation>(
"Animation")
661 return std::string(
self.version().c_str());
680 }), allow_raw_pointers());
685 constant(
"skottie",
true);
687 class_<ManagedAnimation>(
"ManagedAnimation")
690 .function(
"_size", optional_override([](ManagedAnimation&
self,
696 .function(
"fps" , &ManagedAnimation::fps)
697 .function(
"_render", optional_override([](ManagedAnimation&
self,
SkCanvas* canvas,
701 }), allow_raw_pointers())
705 damageRect[0] =
self.seek(t);
707 .function(
"_seekFrame", optional_override([](ManagedAnimation&
self,
double frame,
712 .function(
"seekFrame" , &ManagedAnimation::seekFrame)
713 .function(
"_setColor" , optional_override([](ManagedAnimation&
self,
const std::string&
key,
WASMPointerF32 cPtr) {
714 float* fourFloats =
reinterpret_cast<float*
>(cPtr);
715 SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] };
718 .function(
"_setTransform" , optional_override([](ManagedAnimation&
self,
719 const std::string&
key,
727 .function(
"getMarkers" , &ManagedAnimation::getMarkers)
728 .function(
"getColorProps" , &ManagedAnimation::getColorProps)
729 .function(
"getOpacityProps" , &ManagedAnimation::getOpacityProps)
730 .function(
"setOpacity" , &ManagedAnimation::setOpacity)
731 .function(
"getTextProps" , &ManagedAnimation::getTextProps)
732 .function(
"setText" , &ManagedAnimation::setText)
733 .function(
"getTransformProps", &ManagedAnimation::getTransformProps)
734 .function(
"getSlotInfo" , &ManagedAnimation::getSlotInfo)
735 .function(
"_getColorSlot" , &ManagedAnimation::getColorSlot)
736 .function(
"_setColorSlot" , optional_override([](ManagedAnimation&
self,
const std::string&
key,
WASMPointerF32 cPtr) {
740 .function(
"_getVec2Slot" , &ManagedAnimation::getVec2Slot)
741 .function(
"_setVec2Slot" , optional_override([](ManagedAnimation&
self,
const std::string&
key,
WASMPointerF32 vPtr) {
742 float* twoFloats =
reinterpret_cast<float*
>(vPtr);
743 SkV2 vec2 = {twoFloats[0], twoFloats[1]};
744 return self.setVec2Slot(
key, vec2);
746 .function(
"getScalarSlot" , &ManagedAnimation::getScalarSlot)
747 .function(
"setScalarSlot" , &ManagedAnimation::setScalarSlot)
748 .function(
"attachEditor" , &ManagedAnimation::attachEditor)
749 .function(
"enableEditor" , &ManagedAnimation::enableEditor)
750 .function(
"dispatchEditorKey" , &ManagedAnimation::dispatchEditorKey)
751 .function(
"dispatchEditorPointer", &ManagedAnimation::dispatchEditorPointer)
752 .function(
"setEditorCursorWeight", &ManagedAnimation::setEditorCursorWeight)
753 .function(
"getTextSlot" , &ManagedAnimation::getTextSlot)
754 .function(
"_setTextSlot" , &ManagedAnimation::setTextSlot)
755 .function(
"setImageSlot" , &ManagedAnimation::setImageSlot);
757 function(
"_MakeManagedAnimation", optional_override([](std::string json,
762 std::string prop_prefix,
763 emscripten::val soundMap,
766 const auto assetNames =
reinterpret_cast<char**
>(nptr);
767 const auto assetDatas =
reinterpret_cast<uint8_t**
>(dptr);
768 const auto assetSizes =
reinterpret_cast<size_t*
>(sptr);
770 SkottieAssetProvider::AssetVec assets;
771 assets.reserve(assetCount);
773 for (
size_t i = 0;
i < assetCount;
i++) {
776 assets.push_back(std::make_pair(std::move(
name), std::move(bytes)));
783#if defined(SK_CODEC_DECODES_PNG)
786#if defined(SK_CODEC_DECODES_JPEG)
789#if defined(SK_CODEC_DECODES_GIF)
792#if defined(SK_CODEC_DECODES_WEBP)
798#if !defined(CK_NO_FONTS)
805 std::move(soundMap)),
808 prop_prefix, std::move(
logger));
811 enum_<skui::InputState>(
"InputState")
818 enum_<skui::ModifierKey>(
"ModifierKey")
826 enum_<skottie::Shaper::VAlign>(
"VerticalTextAlign")
833 enum_<skottie::Shaper::ResizePolicy>(
"ResizePolicy")
838 value_object<SimpleSlottableTextProperty>(
"SlottableTextProperty")
839 .field(
"typeface", &SimpleSlottableTextProperty::typeface)
841 .field(
"textSize", &SimpleSlottableTextProperty::textSize)
842 .field(
"minTextSize", &SimpleSlottableTextProperty::minTextSize)
843 .field(
"maxTextSize", &SimpleSlottableTextProperty::maxTextSize)
845 .field(
"lineHeight", &SimpleSlottableTextProperty::lineHeight)
846 .field(
"lineShift", &SimpleSlottableTextProperty::lineShift)
848 .field(
"maxLines", &SimpleSlottableTextProperty::maxLines)
849 .field(
"horizAlign", &SimpleSlottableTextProperty::horizAlign)
850 .field(
"vertAlign", &SimpleSlottableTextProperty::vertAlign)
851 .field(
"strokeJoin", &SimpleSlottableTextProperty::strokeJoin)
852 .field(
"direction", &SimpleSlottableTextProperty::direction)
853 .field(
"linebreak", &SimpleSlottableTextProperty::lineBreak)
854 .field(
"resize", &SimpleSlottableTextProperty::resize)
855 .field(
"_fillColorPtr", &SimpleSlottableTextProperty::fillColorPtr)
856 .field(
"_strokeColorPtr", &SimpleSlottableTextProperty::strokeColorPtr)
857 .field(
"_boundingBoxPtr", &SimpleSlottableTextProperty::boundingBoxPtr);
859 constant(
"managed_skottie",
true);
static const int strokeWidth
static const char marker[]
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
SK_API sk_sp< SkFontMgr > SkFontMgr_New_Custom_Empty()
TypedArray MakeTypedArray(int count, const T src[])
SkColor4f ptrToSkColor4f(WASMPointerF32 cPtr)
std::unique_ptr< SkCodec > DecodeImageData(sk_sp< SkData > data)
static sk_sp< SkData > MakeFromMalloc(const void *data, size_t length)
const char * c_str() const
static sk_sp< SkTypeface > MakeFromStream(std::unique_ptr< SkStreamAsset >, const SkFontArguments &)
void seekFrame(double t, sksg::InvalidationController *ic=nullptr)
const SkSize & size() const
const SkString & version() const
static sk_sp< Animation > Make(const char *data, size_t length)
void render(SkCanvas *canvas, const SkRect *dst=nullptr) const
void seek(SkScalar t, sksg::InvalidationController *ic=nullptr)
static sk_sp< DataURIResourceProviderProxy > Make(sk_sp< ResourceProvider > rp, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode, sk_sp< const SkFontMgr > fontMgr=nullptr)
static sk_sp< MultiFrameImageAsset > Make(sk_sp< SkData >, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode)
const SkRect & bounds() const
Dart_NativeFunction function
void SK_API Register(Decoder d)
constexpr SkCodecs::Decoder Decoder()
constexpr SkCodecs::Decoder Decoder()
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
constexpr SkCodecs::Decoder Decoder()
sk_sp< Factory > BestAvailable()
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
constexpr SkCodecs::Decoder Decoder()
DEF_SWITCHES_START aot vmservice shared library name
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
def render(idl_node, indent_str=' ')
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
static SkScalar prop(SkScalar radius, SkScalar newSize, SkScalar oldSize)
EMSCRIPTEN_BINDINGS(Skottie)
const float * ptr() const
sk_sp< SkTypeface > fTypeface
SkPaint::Join fStrokeJoin
Shaper::Direction fDirection
Shaper::LinebreakPolicy fLineBreak
Shaper::ResizePolicy fResize
SkTextUtils::Align fHAlign
std::shared_ptr< const fml::Mapping > data