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 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 50 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 60 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: 66 explicit SkottieAssetProvider(AssetVec assets) : fAssets(std::move(assets)) {} 67 68 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: 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 100 void render(SkCanvas* canvas) const { fAnimation->render(canvas, nullptr); } 101 void render(SkCanvas* canvas, const SkRect& dst) const { fAnimation->render(canvas, &dst); } 102 void seek(SkScalar t) { fAnimation->seek(t); } 103 SkScalar duration() const { return fAnimation->duration(); } 104 const SkSize& size() const { return fAnimation->size(); } 105 std::string version() const { return std::string(fAnimation->version().c_str()); } 106 107 // CustomPropertyManager API 108 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 121 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 134 bool setColor(const std::string& key, JSColor c) { 135 return fPropMgr->setColor(key, static_cast<SkColor>(c)); 136 } 137 138 bool setOpacity(const std::string& key, float o) { 139 return fPropMgr->setOpacity(key, o); 140 } 141 142 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: 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 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