/* * Copyright 2019 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/codec/SkCodec.h" #include "include/core/SkCanvas.h" #include "include/core/SkFontMgr.h" #include "include/core/SkImage.h" #include "include/core/SkStream.h" #include "include/core/SkString.h" #include "include/core/SkTypes.h" #include "include/private/base/SkOnce.h" #include "modules/canvaskit/WasmCommon.h" #include "modules/skottie/include/Skottie.h" #include "modules/skottie/include/SkottieProperty.h" #include "modules/skottie/include/SlotManager.h" #include "modules/skottie/utils/SkottieUtils.h" #include "modules/skottie/utils/TextEditor.h" #include "modules/skparagraph/include/Paragraph.h" #include "modules/skresources/include/SkResources.h" #include "modules/sksg/include/SkSGInvalidationController.h" #include "modules/skshaper/utils/FactoryHelpers.h" #include "modules/skunicode/include/SkUnicode.h" #include "src/base/SkUTF.h" #include "src/ports/SkTypeface_FreeType.h" #include "tools/skui/InputState.h" #include "tools/skui/ModifierKey.h" #include #include #include #include #if defined(SK_CODEC_DECODES_GIF) #include "include/codec/SkGifDecoder.h" #endif #if defined(SK_CODEC_DECODES_JPEG) #include "include/codec/SkJpegDecoder.h" #endif #if defined(SK_CODEC_DECODES_PNG) #include "include/codec/SkPngDecoder.h" #endif #if defined(SK_CODEC_DECODES_WEBP) #include "include/codec/SkWebpDecoder.h" #endif #if !defined(CK_NO_FONTS) #include "include/ports/SkFontMgr_empty.h" #endif using namespace emscripten; namespace para = skia::textlayout; namespace { struct SimpleSlottableTextProperty { sk_sp typeface; std::string text; float textSize; float minTextSize; float maxTextSize; float strokeWidth; float lineHeight; float lineShift; float ascent; float maxLines; para::TextAlign horizAlign; skottie::Shaper::VAlign vertAlign; skottie::Shaper::ResizePolicy resize; SkUnicode::LineBreakType lineBreak; para::TextDirection direction; SkPaint::Join strokeJoin; WASMPointerF32 boundingBoxPtr; WASMPointerF32 fillColorPtr; WASMPointerF32 strokeColorPtr; operator skottie::TextPropertyValue() const { skottie::TextPropertyValue textProperty; textProperty.fTypeface = this->typeface; textProperty.fText = SkString(this->text); textProperty.fTextSize = this->textSize; textProperty.fMinTextSize = this->minTextSize; textProperty.fMaxTextSize = this->maxTextSize; textProperty.fStrokeWidth = this->strokeWidth; textProperty.fLineHeight = this->lineHeight; textProperty.fLineShift = this->lineShift; textProperty.fAscent = this->ascent; textProperty.fMaxLines = this->maxLines; switch (this->horizAlign) { case para::TextAlign::kLeft: textProperty.fHAlign = SkTextUtils::Align::kLeft_Align; break; case para::TextAlign::kCenter: textProperty.fHAlign = SkTextUtils::Align::kCenter_Align; break; case para::TextAlign::kRight: textProperty.fHAlign = SkTextUtils::Align::kRight_Align; break; default: textProperty.fHAlign = SkTextUtils::Align::kLeft_Align; break; } textProperty.fVAlign = this->vertAlign; textProperty.fResize = this->resize; if (this->lineBreak == SkUnicode::LineBreakType::kSoftLineBreak) { textProperty.fLineBreak = skottie::Shaper::LinebreakPolicy::kParagraph; } else { textProperty.fLineBreak = skottie::Shaper::LinebreakPolicy::kExplicit; } if (this->direction == para::TextDirection::kRtl) { textProperty.fDirection = skottie::Shaper::Direction::kRTL; } else { textProperty.fDirection = skottie::Shaper::Direction::kLTR; } textProperty.fStrokeJoin = this->strokeJoin; textProperty.fBox = reinterpret_cast(this->boundingBoxPtr)[0]; textProperty.fFillColor = ptrToSkColor4f(this->fillColorPtr).toSkColor(); textProperty.fStrokeColor = ptrToSkColor4f(this->strokeColorPtr).toSkColor(); return textProperty; } }; // WebTrack wraps a JS object that has a 'seek' method. // Playback logic is kept there. class WebTrack final : public skresources::ExternalTrackAsset { public: explicit WebTrack(emscripten::val player) : fPlayer(std::move(player)) {} private: void seek(float t) override { fPlayer.call("seek", val(t)); } const emscripten::val fPlayer; }; class SkottieAssetProvider : public skottie::ResourceProvider { public: ~SkottieAssetProvider() override = default; // Tried using a map, but that gave strange errors like // https://emscripten.org/docs/porting/guidelines/function_pointer_issues.html // Not entirely sure why, but perhaps the iterator in the map was // confusing enscripten. using AssetVec = std::vector>>; static sk_sp Make(AssetVec assets, emscripten::val soundMap) { return sk_sp(new SkottieAssetProvider(std::move(assets), std::move(soundMap))); } sk_sp loadImageAsset(const char[] /* path */, const char name[], const char[] /* id */) const override { // For CK/Skottie we ignore paths & IDs, and identify images based solely on name. if (auto data = this->findAsset(name)) { auto codec = DecodeImageData(data); if (!codec) { return nullptr; } return skresources::MultiFrameImageAsset::Make(std::move(codec)); } return nullptr; } sk_sp loadAudioAsset(const char[] /* path */, const char[] /* name */, const char id[]) override { emscripten::val player = this->findSoundAsset(id); if (player.as()) { return sk_make_sp(std::move(player)); } return nullptr; } sk_sp loadTypeface(const char name[], const char[] /* url */) const override { sk_sp faceData = this->findAsset(name); if (!faceData) { return nullptr; } auto stream = std::make_unique(faceData); return SkTypeface_FreeType::MakeFromStream(std::move(stream), SkFontArguments()); } sk_sp load(const char[]/*path*/, const char name[]) const override { // Ignore paths. return this->findAsset(name); } private: explicit SkottieAssetProvider(AssetVec assets, emscripten::val soundMap) : fAssets(std::move(assets)) , fSoundMap(std::move(soundMap)) {} sk_sp findAsset(const char name[]) const { for (const auto& asset : fAssets) { if (asset.first.equals(name)) { return asset.second; } } SkDebugf("Could not find %s\n", name); return nullptr; } emscripten::val findSoundAsset(const char name[]) const { if (fSoundMap.as() && fSoundMap.hasOwnProperty("getPlayer")) { emscripten::val player = fSoundMap.call("getPlayer", val(name)); if (player.as() && player.hasOwnProperty("seek")) { return player; } } return emscripten::val::null(); } const AssetVec fAssets; const emscripten::val fSoundMap; }; // Wraps a JS object with 'onError' and 'onWarning' methods. class JSLogger final : public skottie::Logger { public: static sk_sp Make(emscripten::val logger) { return logger.as() && logger.hasOwnProperty(kWrnFunc) && logger.hasOwnProperty(kErrFunc) ? sk_sp(new JSLogger(std::move(logger))) : nullptr; } private: explicit JSLogger(emscripten::val logger) : fLogger(std::move(logger)) {} void log(Level lvl, const char msg[], const char* json) override { const auto* func = lvl == Level::kError ? kErrFunc : kWrnFunc; fLogger.call(func, std::string(msg), std::string(json)); } inline static constexpr char kWrnFunc[] = "onWarning", kErrFunc[] = "onError"; const emscripten::val fLogger; }; class ManagedAnimation final : public SkRefCnt { public: static sk_sp Make(const std::string& json, sk_sp rp, std::string prop_prefix, emscripten::val logger) { auto mgr = std::make_unique( skottie_utils::CustomPropertyManager::Mode::kCollapseProperties, prop_prefix.c_str()); static constexpr char kInterceptPrefix[] = "__"; auto pinterceptor = sk_make_sp(rp, kInterceptPrefix); skottie::Animation::Builder builder; builder.setMarkerObserver(mgr->getMarkerObserver()) .setPropertyObserver(mgr->getPropertyObserver()) .setResourceProvider(rp) .setPrecompInterceptor(std::move(pinterceptor)) .setTextShapingFactory(SkShapers::BestAvailable()) .setLogger(JSLogger::Make(std::move(logger))); auto animation = builder.make(json.c_str(), json.size()); auto slotManager = builder.getSlotManager(); return animation ? sk_sp(new ManagedAnimation(std::move(animation), std::move(mgr), std::move(slotManager), std::move(rp))) : nullptr; } ~ManagedAnimation() override = default; // skottie::Animation API void render(SkCanvas* canvas, const SkRect* dst) const { fAnimation->render(canvas, dst); } // Returns a damage rect. SkRect seek(SkScalar t) { sksg::InvalidationController ic; fAnimation->seek(t, &ic); return ic.bounds(); } // Returns a damage rect. SkRect seekFrame(double t) { sksg::InvalidationController ic; fAnimation->seekFrame(t, &ic); return ic.bounds(); } double duration() const { return fAnimation->duration(); } double fps() const { return fAnimation->fps(); } const SkSize& size() const { return fAnimation->size(); } std::string version() const { return std::string(fAnimation->version().c_str()); } // CustomPropertyManager API JSArray getColorProps() const { JSArray props = emscripten::val::array(); for (const auto& cp : fPropMgr->getColorProps()) { JSObject prop = emscripten::val::object(); prop.set("key", cp); prop.set("value", fPropMgr->getColor(cp)); props.call("push", prop); } return props; } JSArray getOpacityProps() const { JSArray props = emscripten::val::array(); for (const auto& op : fPropMgr->getOpacityProps()) { JSObject prop = emscripten::val::object(); prop.set("key", op); prop.set("value", fPropMgr->getOpacity(op)); props.call("push", prop); } return props; } JSArray getTextProps() const { JSArray props = emscripten::val::array(); for (const auto& key : fPropMgr->getTextProps()) { const auto txt = fPropMgr->getText(key); JSObject txt_val = emscripten::val::object(); txt_val.set("text", txt.fText.c_str()); txt_val.set("size", txt.fTextSize); JSObject prop = emscripten::val::object(); prop.set("key", key); prop.set("value", std::move(txt_val)); props.call("push", prop); } return props; } JSArray getTransformProps() const { JSArray props = emscripten::val::array(); for (const auto& key : fPropMgr->getTransformProps()) { const auto transform = fPropMgr->getTransform(key); JSObject trans_val = emscripten::val::object(); const float anchor[] = {transform.fAnchorPoint.fX, transform.fAnchorPoint.fY}; const float position[] = {transform.fPosition.fX, transform.fPosition.fY}; const float scale[] = {transform.fScale.fX, transform.fScale.fY}; trans_val.set("anchor", MakeTypedArray(2, anchor)); trans_val.set("position", MakeTypedArray(2, position)); trans_val.set("scale", MakeTypedArray(2, scale)); trans_val.set("rotation", transform.fRotation); trans_val.set("skew", transform.fSkew); trans_val.set("skew_axis", transform.fSkewAxis); JSObject prop = emscripten::val::object(); prop.set("key", key); prop.set("value", trans_val); props.call("push", prop); } return props; } bool setColor(const std::string& key, SkColor c) { return fPropMgr->setColor(key, c); } bool setOpacity(const std::string& key, float o) { return fPropMgr->setOpacity(key, o); } bool setText(const std::string& key, std::string text, float size) { // preserve all other text fields auto t = fPropMgr->getText(key); t.fText = SkString(text); t.fTextSize = size; return fPropMgr->setText(key, t); } bool setTransform(const std::string& key, SkScalar anchorX, SkScalar anchorY, SkScalar posX, SkScalar posY, SkScalar scaleX, SkScalar scaleY, SkScalar rotation, SkScalar skew, SkScalar skewAxis) { skottie::TransformPropertyValue transform; transform.fAnchorPoint = {anchorX, anchorY}; transform.fPosition = {posX, posY}; transform.fScale = {scaleX, scaleY}; transform.fRotation = rotation; transform.fSkew = skew; transform.fSkewAxis = skewAxis; return fPropMgr->setTransform(key, transform); } JSArray getMarkers() const { JSArray markers = emscripten::val::array(); for (const auto& m : fPropMgr->markers()) { JSObject marker = emscripten::val::object(); marker.set("name", m.name); marker.set("t0" , m.t0); marker.set("t1" , m.t1); markers.call("push", marker); } return markers; } JSArray copyStringArrayToJSArray(skia_private::TArray slotIDs) const { JSArray retVal = emscripten::val::array(); for (auto slotID : slotIDs) { retVal.call("push", emscripten::val(slotID.c_str())); } return retVal; } // Slot Manager API JSObject getSlotInfo() const { JSObject slotInfoJS = emscripten::val::object(); auto slotInfo = fSlotMgr->getSlotInfo(); slotInfoJS.set("colorSlotIDs", copyStringArrayToJSArray(slotInfo.fColorSlotIDs)); slotInfoJS.set("scalarSlotIDs", copyStringArrayToJSArray(slotInfo.fScalarSlotIDs)); slotInfoJS.set("vec2SlotIDs", copyStringArrayToJSArray(slotInfo.fVec2SlotIDs)); slotInfoJS.set("imageSlotIDs", copyStringArrayToJSArray(slotInfo.fImageSlotIDs)); slotInfoJS.set("textSlotIDs", copyStringArrayToJSArray(slotInfo.fTextSlotIDs)); return slotInfoJS; } void getColorSlot(const std::string& slotID, WASMPointerF32 outPtr) { SkColor4f c4f; if (auto c = fSlotMgr->getColorSlot(SkString(slotID))) { c4f = SkColor4f::FromColor(*c); } else { c4f = {-1, -1, -1, -1}; } memcpy(reinterpret_cast(outPtr), &c4f, 4 * sizeof(float)); } emscripten::val getScalarSlot(const std::string& slotID) { if (auto s = fSlotMgr->getScalarSlot(SkString(slotID))) { return emscripten::val(*s); } return emscripten::val::null(); } void getVec2Slot(const std::string& slotID, WASMPointerF32 outPtr) { // [x, y, sentinel] SkV3 vec3; if (auto v = fSlotMgr->getVec2Slot(SkString(slotID))) { vec3 = {v->x, v->y, 1}; } else { vec3 = {0, 0, -1}; } memcpy(reinterpret_cast(outPtr), vec3.ptr(), 3 * sizeof(float)); } JSObject getTextSlot(const std::string& slotID) const { if (auto textProp = fSlotMgr->getTextSlot(SkString(slotID))){ JSObject text_val = emscripten::val::object(); text_val.set("typeface", textProp->fTypeface); text_val.set("text", emscripten::val(textProp->fText.c_str())); text_val.set("textSize", textProp->fTextSize); text_val.set("minTextSize", textProp->fMinTextSize); text_val.set("maxTextSize", textProp->fMaxTextSize); text_val.set("strokeWidth", textProp->fStrokeWidth); text_val.set("lineHeight", textProp->fLineHeight); text_val.set("lineShift", textProp->fLineShift); text_val.set("ascent", textProp->fAscent); text_val.set("maxLines", textProp->fMaxLines); switch (textProp->fHAlign) { case SkTextUtils::Align::kLeft_Align: text_val.set("horizAlign", para::TextAlign::kLeft); break; case SkTextUtils::Align::kRight_Align: text_val.set("horizAlign", para::TextAlign::kRight); break; case SkTextUtils::Align::kCenter_Align: text_val.set("horizAlign", para::TextAlign::kCenter); break; default: text_val.set("horizAlign", para::TextAlign::kLeft); break; } text_val.set("vertAlign", textProp->fVAlign); text_val.set("resize", textProp->fResize); if (textProp->fLineBreak == skottie::Shaper::LinebreakPolicy::kParagraph) { text_val.set("linebreak", SkUnicode::LineBreakType::kSoftLineBreak); } else { text_val.set("linebreak", SkUnicode::LineBreakType::kHardLineBreak); } if (textProp->fDirection == skottie::Shaper::Direction::kLTR) { text_val.set("direction", para::TextDirection::kLtr); } else { text_val.set("direction", para::TextDirection::kRtl); } text_val.set("strokeJoin", textProp->fStrokeJoin); text_val.set("fillColor", MakeTypedArray(4, SkColor4f::FromColor(textProp->fFillColor) .vec())); text_val.set("strokeColor", MakeTypedArray(4, SkColor4f::FromColor(textProp->fStrokeColor) .vec())); const float box[] = {textProp->fBox.fLeft, textProp->fBox.fTop, textProp->fBox.fRight, textProp->fBox.fBottom}; text_val.set("boundingBox", MakeTypedArray(4, box)); return text_val; } return emscripten::val::null(); } bool setImageSlot(const std::string& slotID, const std::string& assetName) { // look for resource in preloaded SkottieAssetProvider return fSlotMgr->setImageSlot(SkString(slotID), fResourceProvider->loadImageAsset(nullptr, assetName.data(), nullptr)); } bool setColorSlot(const std::string& slotID, SkColor c) { return fSlotMgr->setColorSlot(SkString(slotID), c); } bool setScalarSlot(const std::string& slotID, float s) { return fSlotMgr->setScalarSlot(SkString(slotID), s); } bool setVec2Slot(const std::string& slotID, SkV2 v) { return fSlotMgr->setVec2Slot(SkString(slotID), v); } bool attachEditor(const std::string& layerID, size_t layerIndex) { if (fTextEditor) { fTextEditor->setEnabled(false); fTextEditor = nullptr; } if (layerID.empty()) { return true; } auto txt_handle = fPropMgr->getTextHandle(layerID, layerIndex); if (!txt_handle) { return false; } std::vector> deps; for (size_t i = 0; ; ++i) { if (i == layerIndex) { continue; } auto dep_handle = fPropMgr->getTextHandle(layerID, i); if (!dep_handle) { break; } deps.push_back(std::move(dep_handle)); } fTextEditor = sk_make_sp(std::move(txt_handle), std::move(deps)); return true; } void enableEditor(bool enable) { if (fTextEditor) { fTextEditor->setEnabled(enable); } } bool dispatchEditorKey(const std::string& key) { // Map some useful keys to the current (odd) text editor bindings. // TODO: Add support for custom bindings in the editor. auto key2char = [](const std::string& key) -> SkUnichar { // Special keys. if (key == "ArrowLeft") return '['; if (key == "ArrowRight") return ']'; if (key == "Backspace") return '\\'; const char* str = key.c_str(); const char* end = str + key.size(); const SkUnichar uch = SkUTF::NextUTF8(&str, end); // Pass through single code points, ignore everything else. return str == end ? uch : -1; }; if (fTextEditor) { const auto uch = key2char(key); if (uch != -1) { return fTextEditor->onCharInput(uch); } } return false; } bool dispatchEditorPointer(float x, float y, skui::InputState state, skui::ModifierKey mod) { return fTextEditor ? fTextEditor->onMouseInput(x, y, state, mod) : false; } void setEditorCursorWeight(float w) { if (fTextEditor) { fTextEditor->setCursorWeight(w); } } bool setTextSlot(const std::string& slotID, SimpleSlottableTextProperty t) { return fSlotMgr->setTextSlot(SkString(slotID), t); } private: ManagedAnimation(sk_sp animation, std::unique_ptr propMgr, sk_sp slotMgr, sk_sp rp) : fAnimation(std::move(animation)) , fPropMgr(std::move(propMgr)) , fSlotMgr(std::move(slotMgr)) , fResourceProvider(std::move(rp)) {} const sk_sp fAnimation; const std::unique_ptr fPropMgr; const sk_sp fSlotMgr; const sk_sp fResourceProvider; sk_sp fTextEditor; }; } // anonymous ns EMSCRIPTEN_BINDINGS(Skottie) { // Animation things (may eventually go in own library) class_("Animation") .smart_ptr>("sk_sp") .function("version", optional_override([](skottie::Animation& self)->std::string { return std::string(self.version().c_str()); })) .function("_size", optional_override([](skottie::Animation& self, WASMPointerF32 oPtr)->void { SkSize* output = reinterpret_cast(oPtr); *output = self.size(); })) .function("duration", &skottie::Animation::duration) .function("fps" , &skottie::Animation::fps) .function("seek", optional_override([](skottie::Animation& self, SkScalar t)->void { self.seek(t); })) .function("seekFrame", optional_override([](skottie::Animation& self, double t)->void { self.seekFrame(t); })) .function("_render", optional_override([](skottie::Animation& self, SkCanvas* canvas, WASMPointerF32 fPtr)->void { const SkRect* dst = reinterpret_cast(fPtr); self.render(canvas, dst); }), allow_raw_pointers()); function("MakeAnimation", optional_override([](std::string json)->sk_sp { return skottie::Animation::Make(json.c_str(), json.length()); })); constant("skottie", true); class_("ManagedAnimation") .smart_ptr>("sk_sp") .function("version" , &ManagedAnimation::version) .function("_size", optional_override([](ManagedAnimation& self, WASMPointerF32 oPtr)->void { SkSize* output = reinterpret_cast(oPtr); *output = self.size(); })) .function("duration" , &ManagedAnimation::duration) .function("fps" , &ManagedAnimation::fps) .function("_render", optional_override([](ManagedAnimation& self, SkCanvas* canvas, WASMPointerF32 fPtr)->void { const SkRect* dst = reinterpret_cast(fPtr); self.render(canvas, dst); }), allow_raw_pointers()) .function("_seek", optional_override([](ManagedAnimation& self, SkScalar t, WASMPointerF32 fPtr) { SkRect* damageRect = reinterpret_cast(fPtr); damageRect[0] = self.seek(t); })) .function("_seekFrame", optional_override([](ManagedAnimation& self, double frame, WASMPointerF32 fPtr) { SkRect* damageRect = reinterpret_cast(fPtr); damageRect[0] = self.seekFrame(frame); })) .function("seekFrame" , &ManagedAnimation::seekFrame) .function("_setColor" , optional_override([](ManagedAnimation& self, const std::string& key, WASMPointerF32 cPtr) { float* fourFloats = reinterpret_cast(cPtr); SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] }; return self.setColor(key, color.toSkColor()); })) .function("_setTransform" , optional_override([](ManagedAnimation& self, const std::string& key, WASMPointerF32 transformData) { // transform value info is passed in as an array of 9 scalars in the following order: // anchor xy, position xy, scalexy, rotation, skew, skew axis auto transform = reinterpret_cast(transformData); return self.setTransform(key, transform[0], transform[1], transform[2], transform[3], transform[4], transform[5], transform[6], transform[7], transform[8]); })) .function("getMarkers" , &ManagedAnimation::getMarkers) .function("getColorProps" , &ManagedAnimation::getColorProps) .function("getOpacityProps" , &ManagedAnimation::getOpacityProps) .function("setOpacity" , &ManagedAnimation::setOpacity) .function("getTextProps" , &ManagedAnimation::getTextProps) .function("setText" , &ManagedAnimation::setText) .function("getTransformProps", &ManagedAnimation::getTransformProps) .function("getSlotInfo" , &ManagedAnimation::getSlotInfo) .function("_getColorSlot" , &ManagedAnimation::getColorSlot) .function("_setColorSlot" , optional_override([](ManagedAnimation& self, const std::string& key, WASMPointerF32 cPtr) { SkColor4f color = ptrToSkColor4f(cPtr); return self.setColorSlot(key, color.toSkColor()); })) .function("_getVec2Slot" , &ManagedAnimation::getVec2Slot) .function("_setVec2Slot" , optional_override([](ManagedAnimation& self, const std::string& key, WASMPointerF32 vPtr) { float* twoFloats = reinterpret_cast(vPtr); SkV2 vec2 = {twoFloats[0], twoFloats[1]}; return self.setVec2Slot(key, vec2); })) .function("getScalarSlot" , &ManagedAnimation::getScalarSlot) .function("setScalarSlot" , &ManagedAnimation::setScalarSlot) .function("attachEditor" , &ManagedAnimation::attachEditor) .function("enableEditor" , &ManagedAnimation::enableEditor) .function("dispatchEditorKey" , &ManagedAnimation::dispatchEditorKey) .function("dispatchEditorPointer", &ManagedAnimation::dispatchEditorPointer) .function("setEditorCursorWeight", &ManagedAnimation::setEditorCursorWeight) .function("getTextSlot" , &ManagedAnimation::getTextSlot) .function("_setTextSlot" , &ManagedAnimation::setTextSlot) .function("setImageSlot" , &ManagedAnimation::setImageSlot); function("_MakeManagedAnimation", optional_override([](std::string json, size_t assetCount, WASMPointerU32 nptr, WASMPointerU32 dptr, WASMPointerU32 sptr, std::string prop_prefix, emscripten::val soundMap, emscripten::val logger) ->sk_sp { const auto assetNames = reinterpret_cast(nptr); const auto assetDatas = reinterpret_cast(dptr); const auto assetSizes = reinterpret_cast(sptr); SkottieAssetProvider::AssetVec assets; assets.reserve(assetCount); for (size_t i = 0; i < assetCount; i++) { auto name = SkString(assetNames[i]); auto bytes = SkData::MakeFromMalloc(assetDatas[i], assetSizes[i]); assets.push_back(std::make_pair(std::move(name), std::move(bytes))); } // DataURIResourceProviderProxy needs codecs registered to try to process Base64 encoded // images. static SkOnce once; once([] { #if defined(SK_CODEC_DECODES_PNG) SkCodecs::Register(SkPngDecoder::Decoder()); #endif #if defined(SK_CODEC_DECODES_JPEG) SkCodecs::Register(SkJpegDecoder::Decoder()); #endif #if defined(SK_CODEC_DECODES_GIF) SkCodecs::Register(SkGifDecoder::Decoder()); #endif #if defined(SK_CODEC_DECODES_WEBP) SkCodecs::Register(SkWebpDecoder::Decoder()); #endif }); sk_sp fontmgr; #if !defined(CK_NO_FONTS) fontmgr = SkFontMgr_New_Custom_Empty(); #endif return ManagedAnimation::Make(json, skresources::DataURIResourceProviderProxy::Make( SkottieAssetProvider::Make(std::move(assets), std::move(soundMap)), skresources::ImageDecodeStrategy::kPreDecode, std::move(fontmgr)), prop_prefix, std::move(logger)); })); enum_("InputState") .value("Down", skui::InputState::kDown) .value("Up", skui::InputState::kUp) .value("Move", skui::InputState::kMove) .value("Right", skui::InputState::kRight) .value("Left", skui::InputState::kLeft); enum_("ModifierKey") .value("None", skui::ModifierKey::kNone) .value("Shift", skui::ModifierKey::kShift) .value("Control", skui::ModifierKey::kControl) .value("Option", skui::ModifierKey::kOption) .value("Command", skui::ModifierKey::kCommand) .value("FirstPress", skui::ModifierKey::kFirstPress); enum_("VerticalTextAlign") .value("Top", skottie::Shaper::VAlign::kTop) .value("TopBaseline", skottie::Shaper::VAlign::kTopBaseline) .value("VisualTop", skottie::Shaper::VAlign::kVisualTop) .value("VisualCenter", skottie::Shaper::VAlign::kVisualCenter) .value("VisualBottom", skottie::Shaper::VAlign::kVisualBottom); enum_("ResizePolicy") .value("None", skottie::Shaper::ResizePolicy::kNone) .value("ScaleToFit", skottie::Shaper::ResizePolicy::kScaleToFit) .value("DownscaleToFit", skottie::Shaper::ResizePolicy::kDownscaleToFit); value_object("SlottableTextProperty") .field("typeface", &SimpleSlottableTextProperty::typeface) .field("text", &SimpleSlottableTextProperty::text) .field("textSize", &SimpleSlottableTextProperty::textSize) .field("minTextSize", &SimpleSlottableTextProperty::minTextSize) .field("maxTextSize", &SimpleSlottableTextProperty::maxTextSize) .field("strokeWidth", &SimpleSlottableTextProperty::strokeWidth) .field("lineHeight", &SimpleSlottableTextProperty::lineHeight) .field("lineShift", &SimpleSlottableTextProperty::lineShift) .field("ascent", &SimpleSlottableTextProperty::ascent) .field("maxLines", &SimpleSlottableTextProperty::maxLines) .field("horizAlign", &SimpleSlottableTextProperty::horizAlign) .field("vertAlign", &SimpleSlottableTextProperty::vertAlign) .field("strokeJoin", &SimpleSlottableTextProperty::strokeJoin) .field("direction", &SimpleSlottableTextProperty::direction) .field("linebreak", &SimpleSlottableTextProperty::lineBreak) .field("resize", &SimpleSlottableTextProperty::resize) .field("_fillColorPtr", &SimpleSlottableTextProperty::fillColorPtr) .field("_strokeColorPtr", &SimpleSlottableTextProperty::strokeColorPtr) .field("_boundingBoxPtr", &SimpleSlottableTextProperty::boundingBoxPtr); constant("managed_skottie", true); }