• 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 "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