blob: f933658062d75441586e1ca6a138a683a509c953 [file] [log] [blame]
/*
* 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 <string>
#include <vector>
#include <emscripten.h>
#include <emscripten/bind.h>
#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<SkTypeface> 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<SkRect*>(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<void>("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<std::pair<SkString, sk_sp<SkData>>>;
static sk_sp<SkottieAssetProvider> Make(AssetVec assets, emscripten::val soundMap) {
return sk_sp<SkottieAssetProvider>(new SkottieAssetProvider(std::move(assets),
std::move(soundMap)));
}
sk_sp<skottie::ImageAsset> 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<skresources::ExternalTrackAsset> loadAudioAsset(const char[] /* path */,
const char[] /* name */,
const char id[]) override {
emscripten::val player = this->findSoundAsset(id);
if (player.as<bool>()) {
return sk_make_sp<WebTrack>(std::move(player));
}
return nullptr;
}
sk_sp<SkTypeface> loadTypeface(const char name[], const char[] /* url */) const override {
sk_sp<SkData> faceData = this->findAsset(name);
if (!faceData) {
return nullptr;
}
auto stream = std::make_unique<SkMemoryStream>(faceData);
return SkTypeface_FreeType::MakeFromStream(std::move(stream), SkFontArguments());
}
sk_sp<SkData> 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<SkData> 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<bool>() && fSoundMap.hasOwnProperty("getPlayer")) {
emscripten::val player = fSoundMap.call<emscripten::val>("getPlayer", val(name));
if (player.as<bool>() && 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<JSLogger> Make(emscripten::val logger) {
return logger.as<bool>()
&& logger.hasOwnProperty(kWrnFunc)
&& logger.hasOwnProperty(kErrFunc)
? sk_sp<JSLogger>(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<void>(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<ManagedAnimation> Make(const std::string& json,
sk_sp<skottie::ResourceProvider> rp,
std::string prop_prefix,
emscripten::val logger) {
auto mgr = std::make_unique<skottie_utils::CustomPropertyManager>(
skottie_utils::CustomPropertyManager::Mode::kCollapseProperties,
prop_prefix.c_str());
static constexpr char kInterceptPrefix[] = "__";
auto pinterceptor =
sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(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<ManagedAnimation>(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<void>("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<void>("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<void>("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<void>("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<void>("push", marker);
}
return markers;
}
JSArray copyStringArrayToJSArray(skia_private::TArray<SkString> slotIDs) const {
JSArray retVal = emscripten::val::array();
for (auto slotID : slotIDs) {
retVal.call<void>("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<float*>(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<float*>(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<std::unique_ptr<skottie::TextPropertyHandle>> 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<skottie_utils::TextEditor>(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<skottie::Animation> animation,
std::unique_ptr<skottie_utils::CustomPropertyManager> propMgr,
sk_sp<skottie::SlotManager> slotMgr,
sk_sp<skresources::ResourceProvider> rp)
: fAnimation(std::move(animation))
, fPropMgr(std::move(propMgr))
, fSlotMgr(std::move(slotMgr))
, fResourceProvider(std::move(rp))
{}
const sk_sp<skottie::Animation> fAnimation;
const std::unique_ptr<skottie_utils::CustomPropertyManager> fPropMgr;
const sk_sp<skottie::SlotManager> fSlotMgr;
const sk_sp<skresources::ResourceProvider> fResourceProvider;
sk_sp<skottie_utils::TextEditor> fTextEditor;
};
} // anonymous ns
EMSCRIPTEN_BINDINGS(Skottie) {
// Animation things (may eventually go in own library)
class_<skottie::Animation>("Animation")
.smart_ptr<sk_sp<skottie::Animation>>("sk_sp<Animation>")
.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<SkSize*>(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<const SkRect*>(fPtr);
self.render(canvas, dst);
}), allow_raw_pointers());
function("MakeAnimation", optional_override([](std::string json)->sk_sp<skottie::Animation> {
return skottie::Animation::Make(json.c_str(), json.length());
}));
constant("skottie", true);
class_<ManagedAnimation>("ManagedAnimation")
.smart_ptr<sk_sp<ManagedAnimation>>("sk_sp<ManagedAnimation>")
.function("version" , &ManagedAnimation::version)
.function("_size", optional_override([](ManagedAnimation& self,
WASMPointerF32 oPtr)->void {
SkSize* output = reinterpret_cast<SkSize*>(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<const SkRect*>(fPtr);
self.render(canvas, dst);
}), allow_raw_pointers())
.function("_seek", optional_override([](ManagedAnimation& self, SkScalar t,
WASMPointerF32 fPtr) {
SkRect* damageRect = reinterpret_cast<SkRect*>(fPtr);
damageRect[0] = self.seek(t);
}))
.function("_seekFrame", optional_override([](ManagedAnimation& self, double frame,
WASMPointerF32 fPtr) {
SkRect* damageRect = reinterpret_cast<SkRect*>(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<float*>(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<SkScalar*>(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<float*>(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<ManagedAnimation> {
const auto assetNames = reinterpret_cast<char** >(nptr);
const auto assetDatas = reinterpret_cast<uint8_t**>(dptr);
const auto assetSizes = reinterpret_cast<size_t* >(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<SkFontMgr> 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_<skui::InputState>("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_<skui::ModifierKey>("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_<skottie::Shaper::VAlign>("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_<skottie::Shaper::ResizePolicy>("ResizePolicy")
.value("None", skottie::Shaper::ResizePolicy::kNone)
.value("ScaleToFit", skottie::Shaper::ResizePolicy::kScaleToFit)
.value("DownscaleToFit", skottie::Shaper::ResizePolicy::kDownscaleToFit);
value_object<SimpleSlottableTextProperty>("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);
}