774aeba9d0
Some animations use humongous images which thrash Skia's resource caches at raster time. To avoid that, we can 1) force decoding upfront, when the animation is loaded 2) clamp the image size to something reasonable (2K^2) Add a flag to support this operation mode, and enable for Skottie/WASM. Change-Id: Id2f127a5200b812b87e588904393d82c80452aa1 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206179 Commit-Queue: Florin Malita <fmalita@chromium.org> Reviewed-by: Kevin Lubick <kjlubick@google.com>
230 lines
8.5 KiB
C++
230 lines
8.5 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 "SkCanvas.h"
|
|
#include "SkImage.h"
|
|
#include "SkMakeUnique.h"
|
|
#include "SkTypes.h"
|
|
#include "SkString.h"
|
|
#include "Skottie.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <emscripten.h>
|
|
#include <emscripten/bind.h>
|
|
#include "WasmAliases.h"
|
|
|
|
#if SK_INCLUDE_MANAGED_SKOTTIE
|
|
#include "SkottieProperty.h"
|
|
#include "SkottieUtils.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 override {
|
|
// For CK/Skottie we ignore paths and identify images based solely on name.
|
|
if (auto data = this->findAsset(name)) {
|
|
return skottie_utils::MultiFrameImageAsset::Make(std::move(data), true /* predecode */);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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<SkottieAssetProvider> ap) {
|
|
auto mgr = skstd::make_unique<skottie_utils::CustomPropertyManager>();
|
|
auto animation = skottie::Animation::Builder()
|
|
.setMarkerObserver(mgr->getMarkerObserver())
|
|
.setPropertyObserver(mgr->getPropertyObserver())
|
|
.setResourceProvider(ap)
|
|
.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); }
|
|
void seek(SkScalar t) { fAnimation->seek(t); }
|
|
SkScalar duration() const { return fAnimation->duration(); }
|
|
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("seek", &skottie::Animation::seek)
|
|
.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("seek" , &ManagedAnimation::seek)
|
|
.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" , &ManagedAnimation::setColor)
|
|
.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)
|
|
->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)));
|
|
}
|
|
|
|
return ManagedAnimation::Make(json, SkottieAssetProvider::Make(std::move(assets)));
|
|
}));
|
|
constant("managed_skottie", true);
|
|
#endif // SK_INCLUDE_MANAGED_SKOTTIE
|
|
}
|