• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 #include <string>
16 #include <vector>
17 
18 #include <emscripten.h>
19 #include <emscripten/bind.h>
20 #include "modules/canvaskit/WasmAliases.h"
21 
22 #if SK_INCLUDE_MANAGED_SKOTTIE
23 #include "modules/skottie/include/SkottieProperty.h"
24 #include "modules/skottie/utils/SkottieUtils.h"
25 #include "modules/skresources/include/SkResources.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 skresources::MultiFrameImageAsset::Make(std::move(data));
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<skottie::ResourceProvider> rp)86     static sk_sp<ManagedAnimation> Make(const std::string& json,
87                                         sk_sp<skottie::ResourceProvider> rp) {
88         auto mgr = std::make_unique<skottie_utils::CustomPropertyManager>();
89         auto animation = skottie::Animation::Builder()
90                             .setMarkerObserver(mgr->getMarkerObserver())
91                             .setPropertyObserver(mgr->getPropertyObserver())
92                             .setResourceProvider(rp)
93                             .make(json.c_str(), json.size());
94 
95         return animation
96             ? sk_sp<ManagedAnimation>(new ManagedAnimation(std::move(animation), std::move(mgr)))
97             : nullptr;
98     }
99 
100     ~ManagedAnimation() override = default;
101 
102     // skottie::Animation API
render(SkCanvas * canvas) const103     void render(SkCanvas* canvas) const { fAnimation->render(canvas, nullptr); }
render(SkCanvas * canvas,const SkRect & dst) const104     void render(SkCanvas* canvas, const SkRect& dst) const { fAnimation->render(canvas, &dst); }
105     // Returns a damage rect.
seek(SkScalar t)106     SkRect seek(SkScalar t) {
107         sksg::InvalidationController ic;
108         fAnimation->seek(t, &ic);
109         return ic.bounds();
110     }
111     // Returns a damage rect.
seekFrame(double t)112     SkRect seekFrame(double t) {
113         sksg::InvalidationController ic;
114         fAnimation->seekFrame(t, &ic);
115         return ic.bounds();
116     }
duration() const117     double duration() const { return fAnimation->duration(); }
fps() const118     double fps() const { return fAnimation->fps(); }
size() const119     const SkSize& size() const { return fAnimation->size(); }
version() const120     std::string version() const { return std::string(fAnimation->version().c_str()); }
121 
122     // CustomPropertyManager API
getColorProps() const123     JSArray getColorProps() const {
124         JSArray props = emscripten::val::array();
125 
126         for (const auto& cp : fPropMgr->getColorProps()) {
127             JSObject prop = emscripten::val::object();
128             prop.set("key", cp);
129             prop.set("value", fPropMgr->getColor(cp));
130             props.call<void>("push", prop);
131         }
132 
133         return props;
134     }
135 
getOpacityProps() const136     JSArray getOpacityProps() const {
137         JSArray props = emscripten::val::array();
138 
139         for (const auto& op : fPropMgr->getOpacityProps()) {
140             JSObject prop = emscripten::val::object();
141             prop.set("key", op);
142             prop.set("value", fPropMgr->getOpacity(op));
143             props.call<void>("push", prop);
144         }
145 
146         return props;
147     }
148 
setColor(const std::string & key,SkColor c)149     bool setColor(const std::string& key, SkColor c) {
150         return fPropMgr->setColor(key, c);
151     }
152 
setOpacity(const std::string & key,float o)153     bool setOpacity(const std::string& key, float o) {
154         return fPropMgr->setOpacity(key, o);
155     }
156 
getMarkers() const157     JSArray getMarkers() const {
158         JSArray markers = emscripten::val::array();
159         for (const auto& m : fPropMgr->markers()) {
160             JSObject marker = emscripten::val::object();
161             marker.set("name", m.name);
162             marker.set("t0"  , m.t0);
163             marker.set("t1"  , m.t1);
164             markers.call<void>("push", marker);
165         }
166         return markers;
167     }
168 
169 private:
ManagedAnimation(sk_sp<skottie::Animation> animation,std::unique_ptr<skottie_utils::CustomPropertyManager> propMgr)170     ManagedAnimation(sk_sp<skottie::Animation> animation,
171                      std::unique_ptr<skottie_utils::CustomPropertyManager> propMgr)
172         : fAnimation(std::move(animation))
173         , fPropMgr(std::move(propMgr)) {}
174 
175     sk_sp<skottie::Animation>                             fAnimation;
176     std::unique_ptr<skottie_utils::CustomPropertyManager> fPropMgr;
177 };
178 
179 } // anonymous ns
180 #endif // SK_INCLUDE_MANAGED_SKOTTIE
181 
EMSCRIPTEN_BINDINGS(Skottie)182 EMSCRIPTEN_BINDINGS(Skottie) {
183     // Animation things (may eventually go in own library)
184     class_<skottie::Animation>("Animation")
185         .smart_ptr<sk_sp<skottie::Animation>>("sk_sp<Animation>")
186         .function("version", optional_override([](skottie::Animation& self)->std::string {
187             return std::string(self.version().c_str());
188         }))
189         .function("size"    , &skottie::Animation::size)
190         .function("duration", &skottie::Animation::duration)
191         .function("fps"     , &skottie::Animation::fps)
192         .function("seek", optional_override([](skottie::Animation& self, SkScalar t)->void {
193             self.seek(t);
194         }))
195         .function("seekFrame", optional_override([](skottie::Animation& self, double t)->void {
196             self.seekFrame(t);
197         }))
198         .function("render", optional_override([](skottie::Animation& self, SkCanvas* canvas)->void {
199             self.render(canvas, nullptr);
200         }), allow_raw_pointers())
201         .function("render", optional_override([](skottie::Animation& self, SkCanvas* canvas,
202                                                  const SkRect r)->void {
203             self.render(canvas, &r);
204         }), allow_raw_pointers());
205 
206     function("MakeAnimation", optional_override([](std::string json)->sk_sp<skottie::Animation> {
207         return skottie::Animation::Make(json.c_str(), json.length());
208     }));
209     constant("skottie", true);
210 
211 #if SK_INCLUDE_MANAGED_SKOTTIE
212     class_<ManagedAnimation>("ManagedAnimation")
213         .smart_ptr<sk_sp<ManagedAnimation>>("sk_sp<ManagedAnimation>")
214         .function("version"   , &ManagedAnimation::version)
215         .function("size"      , &ManagedAnimation::size)
216         .function("duration"  , &ManagedAnimation::duration)
217         .function("fps"       , &ManagedAnimation::fps)
218         .function("seek"      , &ManagedAnimation::seek)
219         .function("seekFrame" , &ManagedAnimation::seekFrame)
220         .function("render"    , select_overload<void(SkCanvas*) const>(&ManagedAnimation::render), allow_raw_pointers())
221         .function("render"    , select_overload<void(SkCanvas*, const SkRect&) const>
222                                     (&ManagedAnimation::render), allow_raw_pointers())
223         .function("setColor"  , &ManagedAnimation::setColor)
224         .function("setOpacity", &ManagedAnimation::setOpacity)
225         .function("getMarkers", &ManagedAnimation::getMarkers)
226         .function("getColorProps"  , &ManagedAnimation::getColorProps)
227         .function("getOpacityProps", &ManagedAnimation::getOpacityProps);
228 
229     function("_MakeManagedAnimation", optional_override([](std::string json,
230                                                            size_t assetCount,
231                                                            uintptr_t /* char**     */ nptr,
232                                                            uintptr_t /* uint8_t**  */ dptr,
233                                                            uintptr_t /* size_t*    */ sptr)
234                                                         ->sk_sp<ManagedAnimation> {
235         // See the comment in canvaskit_bindings.cpp about the use of uintptr_t
236         const auto assetNames = reinterpret_cast<char**   >(nptr);
237         const auto assetDatas = reinterpret_cast<uint8_t**>(dptr);
238         const auto assetSizes = reinterpret_cast<size_t*  >(sptr);
239 
240         SkottieAssetProvider::AssetVec assets;
241         assets.reserve(assetCount);
242 
243         for (size_t i = 0; i < assetCount; i++) {
244             auto name  = SkString(assetNames[i]);
245             auto bytes = SkData::MakeFromMalloc(assetDatas[i], assetSizes[i]);
246             assets.push_back(std::make_pair(std::move(name), std::move(bytes)));
247         }
248 
249         return ManagedAnimation::Make(json,
250                  skresources::DataURIResourceProviderProxy::Make(
251                     SkottieAssetProvider::Make(std::move(assets))));
252     }));
253     constant("managed_skottie", true);
254 #endif // SK_INCLUDE_MANAGED_SKOTTIE
255 }
256