1 /*
2 * Copyright 2019 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkCanvas.h"
9 #include "include/core/SkImage.h"
10 #include "include/core/SkString.h"
11 #include "include/core/SkTypes.h"
12 #include "modules/skottie/include/Skottie.h"
13 #include "modules/sksg/include/SkSGInvalidationController.h"
14 #include "src/core/SkMakeUnique.h"
15
16 #include <string>
17 #include <vector>
18
19 #include <emscripten.h>
20 #include <emscripten/bind.h>
21 #include "modules/canvaskit/WasmAliases.h"
22
23 #if SK_INCLUDE_MANAGED_SKOTTIE
24 #include "modules/skottie/include/SkottieProperty.h"
25 #include "modules/skottie/utils/SkottieUtils.h"
26 #endif // SK_INCLUDE_MANAGED_SKOTTIE
27
28 using namespace emscripten;
29
30 #if SK_INCLUDE_MANAGED_SKOTTIE
31 namespace {
32
33 class SkottieAssetProvider : public skottie::ResourceProvider {
34 public:
35 ~SkottieAssetProvider() override = default;
36
37 // Tried using a map, but that gave strange errors like
38 // https://emscripten.org/docs/porting/guidelines/function_pointer_issues.html
39 // Not entirely sure why, but perhaps the iterator in the map was
40 // confusing enscripten.
41 using AssetVec = std::vector<std::pair<SkString, sk_sp<SkData>>>;
42
Make(AssetVec assets)43 static sk_sp<SkottieAssetProvider> Make(AssetVec assets) {
44 if (assets.empty()) {
45 return nullptr;
46 }
47
48 return sk_sp<SkottieAssetProvider>(new SkottieAssetProvider(std::move(assets)));
49 }
50
loadImageAsset(const char[],const char name[],const char[]) const51 sk_sp<skottie::ImageAsset> loadImageAsset(const char[] /* path */,
52 const char name[],
53 const char[] /* id */) const override {
54 // For CK/Skottie we ignore paths & IDs, and identify images based solely on name.
55 if (auto data = this->findAsset(name)) {
56 return skottie_utils::MultiFrameImageAsset::Make(std::move(data), true /* predecode */);
57 }
58
59 return nullptr;
60 }
61
loadFont(const char name[],const char[]) const62 sk_sp<SkData> loadFont(const char name[], const char[] /* url */) const override {
63 // Same as images paths, we ignore font URLs.
64 return this->findAsset(name);
65 }
66
67 private:
SkottieAssetProvider(AssetVec assets)68 explicit SkottieAssetProvider(AssetVec assets) : fAssets(std::move(assets)) {}
69
findAsset(const char name[]) const70 sk_sp<SkData> findAsset(const char name[]) const {
71 for (const auto& asset : fAssets) {
72 if (asset.first.equals(name)) {
73 return asset.second;
74 }
75 }
76
77 SkDebugf("Could not find %s\n", name);
78 return nullptr;
79 }
80
81 const AssetVec fAssets;
82 };
83
84 class ManagedAnimation final : public SkRefCnt {
85 public:
Make(const std::string & json,sk_sp<SkottieAssetProvider> ap)86 static sk_sp<ManagedAnimation> Make(const std::string& json, sk_sp<SkottieAssetProvider> ap) {
87 auto mgr = skstd::make_unique<skottie_utils::CustomPropertyManager>();
88 auto animation = skottie::Animation::Builder()
89 .setMarkerObserver(mgr->getMarkerObserver())
90 .setPropertyObserver(mgr->getPropertyObserver())
91 .setResourceProvider(ap)
92 .make(json.c_str(), json.size());
93
94 return animation
95 ? sk_sp<ManagedAnimation>(new ManagedAnimation(std::move(animation), std::move(mgr)))
96 : nullptr;
97 }
98
99 ~ManagedAnimation() override = default;
100
101 // skottie::Animation API
render(SkCanvas * canvas) const102 void render(SkCanvas* canvas) const { fAnimation->render(canvas, nullptr); }
render(SkCanvas * canvas,const SkRect & dst) const103 void render(SkCanvas* canvas, const SkRect& dst) const { fAnimation->render(canvas, &dst); }
104 // Returns a damage rect.
seek(SkScalar t)105 SkRect seek(SkScalar t) {
106 sksg::InvalidationController ic;
107 fAnimation->seek(t, &ic);
108 return ic.bounds();
109 }
duration() const110 SkScalar duration() const { return fAnimation->duration(); }
size() const111 const SkSize& size() const { return fAnimation->size(); }
version() const112 std::string version() const { return std::string(fAnimation->version().c_str()); }
113
114 // CustomPropertyManager API
getColorProps() const115 JSArray getColorProps() const {
116 JSArray props = emscripten::val::array();
117
118 for (const auto& cp : fPropMgr->getColorProps()) {
119 JSObject prop = emscripten::val::object();
120 prop.set("key", cp);
121 prop.set("value", fPropMgr->getColor(cp));
122 props.call<void>("push", prop);
123 }
124
125 return props;
126 }
127
getOpacityProps() const128 JSArray getOpacityProps() const {
129 JSArray props = emscripten::val::array();
130
131 for (const auto& op : fPropMgr->getOpacityProps()) {
132 JSObject prop = emscripten::val::object();
133 prop.set("key", op);
134 prop.set("value", fPropMgr->getOpacity(op));
135 props.call<void>("push", prop);
136 }
137
138 return props;
139 }
140
setColor(const std::string & key,SkColor c)141 bool setColor(const std::string& key, SkColor c) {
142 return fPropMgr->setColor(key, c);
143 }
144
setOpacity(const std::string & key,float o)145 bool setOpacity(const std::string& key, float o) {
146 return fPropMgr->setOpacity(key, o);
147 }
148
getMarkers() const149 JSArray getMarkers() const {
150 JSArray markers = emscripten::val::array();
151 for (const auto& m : fPropMgr->markers()) {
152 JSObject marker = emscripten::val::object();
153 marker.set("name", m.name);
154 marker.set("t0" , m.t0);
155 marker.set("t1" , m.t1);
156 markers.call<void>("push", marker);
157 }
158 return markers;
159 }
160
161 private:
ManagedAnimation(sk_sp<skottie::Animation> animation,std::unique_ptr<skottie_utils::CustomPropertyManager> propMgr)162 ManagedAnimation(sk_sp<skottie::Animation> animation,
163 std::unique_ptr<skottie_utils::CustomPropertyManager> propMgr)
164 : fAnimation(std::move(animation))
165 , fPropMgr(std::move(propMgr)) {}
166
167 sk_sp<skottie::Animation> fAnimation;
168 std::unique_ptr<skottie_utils::CustomPropertyManager> fPropMgr;
169 };
170
171 } // anonymous ns
172 #endif // SK_INCLUDE_MANAGED_SKOTTIE
173
EMSCRIPTEN_BINDINGS(Skottie)174 EMSCRIPTEN_BINDINGS(Skottie) {
175 // Animation things (may eventually go in own library)
176 class_<skottie::Animation>("Animation")
177 .smart_ptr<sk_sp<skottie::Animation>>("sk_sp<Animation>")
178 .function("version", optional_override([](skottie::Animation& self)->std::string {
179 return std::string(self.version().c_str());
180 }))
181 .function("size", &skottie::Animation::size)
182 .function("duration", &skottie::Animation::duration)
183 .function("seek", optional_override([](skottie::Animation& self, SkScalar t)->void {
184 self.seek(t);
185 }))
186 .function("render", optional_override([](skottie::Animation& self, SkCanvas* canvas)->void {
187 self.render(canvas, nullptr);
188 }), allow_raw_pointers())
189 .function("render", optional_override([](skottie::Animation& self, SkCanvas* canvas,
190 const SkRect r)->void {
191 self.render(canvas, &r);
192 }), allow_raw_pointers());
193
194 function("MakeAnimation", optional_override([](std::string json)->sk_sp<skottie::Animation> {
195 return skottie::Animation::Make(json.c_str(), json.length());
196 }));
197 constant("skottie", true);
198
199 #if SK_INCLUDE_MANAGED_SKOTTIE
200 class_<ManagedAnimation>("ManagedAnimation")
201 .smart_ptr<sk_sp<ManagedAnimation>>("sk_sp<ManagedAnimation>")
202 .function("version" , &ManagedAnimation::version)
203 .function("size" , &ManagedAnimation::size)
204 .function("duration" , &ManagedAnimation::duration)
205 .function("seek" , &ManagedAnimation::seek)
206 .function("render" , select_overload<void(SkCanvas*) const>(&ManagedAnimation::render), allow_raw_pointers())
207 .function("render" , select_overload<void(SkCanvas*, const SkRect&) const>
208 (&ManagedAnimation::render), allow_raw_pointers())
209 .function("setColor" , &ManagedAnimation::setColor)
210 .function("setOpacity", &ManagedAnimation::setOpacity)
211 .function("getMarkers", &ManagedAnimation::getMarkers)
212 .function("getColorProps" , &ManagedAnimation::getColorProps)
213 .function("getOpacityProps", &ManagedAnimation::getOpacityProps);
214
215 function("_MakeManagedAnimation", optional_override([](std::string json,
216 size_t assetCount,
217 uintptr_t /* char** */ nptr,
218 uintptr_t /* uint8_t** */ dptr,
219 uintptr_t /* size_t* */ sptr)
220 ->sk_sp<ManagedAnimation> {
221 const auto assetNames = reinterpret_cast<char** >(nptr);
222 const auto assetDatas = reinterpret_cast<uint8_t**>(dptr);
223 const auto assetSizes = reinterpret_cast<size_t* >(sptr);
224
225 SkottieAssetProvider::AssetVec assets;
226 assets.reserve(assetCount);
227
228 for (size_t i = 0; i < assetCount; i++) {
229 auto name = SkString(assetNames[i]);
230 auto bytes = SkData::MakeFromMalloc(assetDatas[i], assetSizes[i]);
231 assets.push_back(std::make_pair(std::move(name), std::move(bytes)));
232 }
233
234 return ManagedAnimation::Make(json, SkottieAssetProvider::Make(std::move(assets)));
235 }));
236 constant("managed_skottie", true);
237 #endif // SK_INCLUDE_MANAGED_SKOTTIE
238 }
239