3f45e4b322
Currently we use a hard-coded name prefix ('$') and the properties are "namespaced" (the tree hierarchy is part of the key name). This doesn't work well for most clients looking to test property manipulation, as they would rather handle similarly named props in bulk. E.g. instead of precomp1.layer1.Group1.COLOR_01 precomp1.layer1.Group1.COLOR_02 precomp1.layer2.Group1.COLOR_01 precomp1.layer2.Group2.COLOR_02 the UI should simply present COLOR_01 COLOR_02 To support this, introduce a new operation mode for CustomPropertyManager (kCollapseProperties), and keep the old behavior around as kNamespacedProperties. Also drop filtering for markers as anyone interested in markers would want to see all of them. Plumb these options all the way into CK (to be added to the player later). Change-Id: I57ec78c669f3870939d48fbfc492b97f63ea600d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/312301 Reviewed-by: Kevin Lubick <kjlubick@google.com> Commit-Queue: Florin Malita <fmalita@google.com>
274 lines
11 KiB
C++
274 lines
11 KiB
C++
/*
|
|
* 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/core/SkCanvas.h"
|
|
#include "include/core/SkImage.h"
|
|
#include "include/core/SkString.h"
|
|
#include "include/core/SkTypes.h"
|
|
#include "modules/skottie/include/Skottie.h"
|
|
#include "modules/sksg/include/SkSGInvalidationController.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <emscripten.h>
|
|
#include <emscripten/bind.h>
|
|
#include "modules/canvaskit/WasmCommon.h"
|
|
|
|
#if SK_INCLUDE_MANAGED_SKOTTIE
|
|
#include "modules/skottie/include/SkottieProperty.h"
|
|
#include "modules/skottie/utils/SkottieUtils.h"
|
|
#include "modules/skresources/include/SkResources.h"
|
|
#endif // SK_INCLUDE_MANAGED_SKOTTIE
|
|
|
|
using namespace emscripten;
|
|
|
|
#if SK_INCLUDE_MANAGED_SKOTTIE
|
|
namespace {
|
|
|
|
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) {
|
|
if (assets.empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return sk_sp<SkottieAssetProvider>(new SkottieAssetProvider(std::move(assets)));
|
|
}
|
|
|
|
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)) {
|
|
return skresources::MultiFrameImageAsset::Make(std::move(data));
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
sk_sp<SkData> loadFont(const char name[], const char[] /* url */) const override {
|
|
// Same as images paths, we ignore font URLs.
|
|
return this->findAsset(name);
|
|
}
|
|
|
|
sk_sp<SkData> load(const char[]/*path*/, const char name[]) const override {
|
|
// Ignore paths.
|
|
return this->findAsset(name);
|
|
}
|
|
|
|
private:
|
|
explicit SkottieAssetProvider(AssetVec assets) : fAssets(std::move(assets)) {}
|
|
|
|
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;
|
|
}
|
|
|
|
const AssetVec fAssets;
|
|
};
|
|
|
|
class ManagedAnimation final : public SkRefCnt {
|
|
public:
|
|
static sk_sp<ManagedAnimation> Make(const std::string& json,
|
|
sk_sp<skottie::ResourceProvider> rp,
|
|
std::string prop_prefix) {
|
|
auto mgr = std::make_unique<skottie_utils::CustomPropertyManager>(
|
|
skottie_utils::CustomPropertyManager::Mode::kCollapseProperties,
|
|
prop_prefix.empty() ? nullptr : prop_prefix.c_str());
|
|
static constexpr char kInterceptPrefix[] = "__";
|
|
auto pinterceptor =
|
|
sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(rp, kInterceptPrefix);
|
|
auto animation = skottie::Animation::Builder()
|
|
.setMarkerObserver(mgr->getMarkerObserver())
|
|
.setPropertyObserver(mgr->getPropertyObserver())
|
|
.setResourceProvider(std::move(rp))
|
|
.setPrecompInterceptor(std::move(pinterceptor))
|
|
.make(json.c_str(), json.size());
|
|
|
|
return animation
|
|
? sk_sp<ManagedAnimation>(new ManagedAnimation(std::move(animation), std::move(mgr)))
|
|
: nullptr;
|
|
}
|
|
|
|
~ManagedAnimation() override = default;
|
|
|
|
// skottie::Animation API
|
|
void render(SkCanvas* canvas) const { fAnimation->render(canvas, nullptr); }
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
private:
|
|
ManagedAnimation(sk_sp<skottie::Animation> animation,
|
|
std::unique_ptr<skottie_utils::CustomPropertyManager> propMgr)
|
|
: fAnimation(std::move(animation))
|
|
, fPropMgr(std::move(propMgr)) {}
|
|
|
|
sk_sp<skottie::Animation> fAnimation;
|
|
std::unique_ptr<skottie_utils::CustomPropertyManager> fPropMgr;
|
|
};
|
|
|
|
} // anonymous ns
|
|
#endif // SK_INCLUDE_MANAGED_SKOTTIE
|
|
|
|
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" , &skottie::Animation::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)->void {
|
|
self.render(canvas, nullptr);
|
|
}), allow_raw_pointers())
|
|
.function("render", optional_override([](skottie::Animation& self, SkCanvas* canvas,
|
|
const SkRect r)->void {
|
|
self.render(canvas, &r);
|
|
}), 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);
|
|
|
|
#if SK_INCLUDE_MANAGED_SKOTTIE
|
|
class_<ManagedAnimation>("ManagedAnimation")
|
|
.smart_ptr<sk_sp<ManagedAnimation>>("sk_sp<ManagedAnimation>")
|
|
.function("version" , &ManagedAnimation::version)
|
|
.function("size" , &ManagedAnimation::size)
|
|
.function("duration" , &ManagedAnimation::duration)
|
|
.function("fps" , &ManagedAnimation::fps)
|
|
.function("seek" , &ManagedAnimation::seek)
|
|
.function("seekFrame" , &ManagedAnimation::seekFrame)
|
|
.function("render" , select_overload<void(SkCanvas*) const>(&ManagedAnimation::render), allow_raw_pointers())
|
|
.function("render" , select_overload<void(SkCanvas*, const SkRect&) const>
|
|
(&ManagedAnimation::render), allow_raw_pointers())
|
|
.function("_setColor" , optional_override([](ManagedAnimation& self, const std::string& key, uintptr_t /* float* */ cPtr) {
|
|
float* fourFloats = reinterpret_cast<float*>(cPtr);
|
|
SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] };
|
|
self.setColor(key, color.toSkColor());
|
|
}))
|
|
.function("setOpacity", &ManagedAnimation::setOpacity)
|
|
.function("getMarkers", &ManagedAnimation::getMarkers)
|
|
.function("getColorProps" , &ManagedAnimation::getColorProps)
|
|
.function("getOpacityProps", &ManagedAnimation::getOpacityProps);
|
|
|
|
function("_MakeManagedAnimation", optional_override([](std::string json,
|
|
size_t assetCount,
|
|
uintptr_t /* char** */ nptr,
|
|
uintptr_t /* uint8_t** */ dptr,
|
|
uintptr_t /* size_t* */ sptr,
|
|
std::string prop_prefix)
|
|
->sk_sp<ManagedAnimation> {
|
|
// See the comment in canvaskit_bindings.cpp about the use of uintptr_t
|
|
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)));
|
|
}
|
|
|
|
return ManagedAnimation::Make(json,
|
|
skresources::DataURIResourceProviderProxy::Make(
|
|
SkottieAssetProvider::Make(std::move(assets))),
|
|
prop_prefix);
|
|
}));
|
|
constant("managed_skottie", true);
|
|
#endif // SK_INCLUDE_MANAGED_SKOTTIE
|
|
}
|