Home | History | Annotate | Download | only in canvaskit
      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