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