text: /*
   * Copyright 2019 Google LLC
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "include/core/SkCanvas.h"
  #include "include/core/SkTypes.h"
  #include "include/utils/SkRandom.h"
  #include "modules/particles/include/SkParticleEffect.h"
  #include "modules/particles/include/SkParticleSerialization.h"
  #include "modules/skresources/include/SkResources.h"
  #include "src/sksl/SkSLByteCode.h"
  
  #include <string>
  
  #include "modules/canvaskit/WasmCommon.h"
  
  #include <emscripten.h>
  #include <emscripten/bind.h>
  
  using namespace emscripten;
  
  namespace {
  
  class ParticleAssetProvider : public skresources::ResourceProvider {
  public:
      ~ParticleAssetProvider() override = default;
  
      // Tried using a map, but that gave strange errors like
      // https://emscripten.org/docs/porting/guidelines/function_pointer_issues.html
      // Not entirely sure why, but perhaps the iterator in the map was
      // confusing enscripten.
      using AssetVec = std::vector<std::pair<SkString, sk_sp<SkData>>>;
  
      static sk_sp<ParticleAssetProvider> Make(AssetVec assets) {
          if (assets.empty()) {
              return nullptr;
          }
  
          return sk_sp<ParticleAssetProvider>(new ParticleAssetProvider(std::move(assets)));
      }
  
      sk_sp<skresources::ImageAsset> loadImageAsset(const char[] /* path */,
                                                    const char name[],
                                                    const char[] /* id */) const override {
          // For CK we ignore paths & IDs, and identify images based solely on name.
          if (auto data = this->findAsset(name)) {
              return skresources::MultiFrameImageAsset::Make(std::move(data));
          }
  
          return nullptr;
      }
  
      sk_sp<SkData> loadFont(const char name[], const char[] /* url */) const override {
          // Same as images paths, we ignore font URLs.
          return this->findAsset(name);
      }
  
  private:
      explicit ParticleAssetProvider(AssetVec assets) : fAssets(std::move(assets)) {}
  
      sk_sp<SkData> findAsset(const char name[]) const {
          for (const auto& asset : fAssets) {
              if (asset.first.equals(name)) {
                  return asset.second;
              }
          }
  
          SkDebugf("Could not find %s\n", name);
          return nullptr;
      }
  
      const AssetVec fAssets;
  };
  
  }
  
  struct SimpleUniform {
      int columns;
      int rows;
      int slot; // the index into the uniforms array that this uniform begins.
  };
  
  SimpleUniform fromUniform(SkSL::ByteCode::Uniform u) {
      SimpleUniform su;
      su.columns = u.fColumns;
      su.rows = u.fRows;
      su.slot = u.fSlot;
      return su;
  }
  
  EMSCRIPTEN_BINDINGS(Particles) {
      class_<SkParticleEffect>("SkParticleEffect")
          .smart_ptr<sk_sp<SkParticleEffect>>("sk_sp<SkParticleEffect>")
          .function("draw", &SkParticleEffect::draw, allow_raw_pointers())
          .function("_effectUniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t {
              return reinterpret_cast<uintptr_t>(self.effectUniforms());
          }))
          .function("_particleUniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t {
              return reinterpret_cast<uintptr_t>(self.particleUniforms());
          }))
          .function("getEffectUniformCount", optional_override([](SkParticleEffect& self)->int {
              auto ec = self.effectCode();
              if (!ec) {
                  return -1;
              }
              return ec->getUniformCount();
          }))
          .function("getEffectUniformFloatCount", optional_override([](SkParticleEffect& self)->int {
              auto ec = self.effectCode();
              if (!ec) {
                  return -1;
              }
              return ec->getUniformSlotCount();
          }))
          .function("getEffectUniformName", optional_override([](SkParticleEffect& self, int i)->JSString {
              auto ec = self.effectCode();
              if (!ec) {
                  return emscripten::val::null();
              }
              return emscripten::val(ec->getUniform(i).fName.c_str());
          }))
          .function("getEffectUniform", optional_override([](SkParticleEffect& self, int i)->SimpleUniform {
              SimpleUniform su;
              auto ec = self.effectCode();
              if (!ec) {
                  return su;
              }
              su = fromUniform(ec->getUniform(i));
              return su;
          }))
          .function("getParticleUniformCount", optional_override([](SkParticleEffect& self)->int {
              auto ec = self.particleCode();
              if (!ec) {
                  return -1;
              }
              return ec->getUniformCount();
          }))
          .function("getParticleUniformFloatCount", optional_override([](SkParticleEffect& self)->int {
              auto ec = self.particleCode();
              if (!ec) {
                  return -1;
              }
              return ec->getUniformSlotCount();
          }))
          .function("getParticleUniformName", optional_override([](SkParticleEffect& self, int i)->JSString {
              auto ec = self.particleCode();
              if (!ec) {
                  return emscripten::val::null();
              }
              return emscripten::val(ec->getUniform(i).fName.c_str());
          }))
          .function("getParticleUniform", optional_override([](SkParticleEffect& self, int i)->SimpleUniform {
              SimpleUniform su;
              auto ec = self.particleCode();
              if (!ec) {
                  return su;
              }
              su = fromUniform(ec->getUniform(i));
              return su;
          }))
          .function("setPosition", select_overload<void (SkPoint)>(&SkParticleEffect::setPosition))
          .function("setRate", select_overload<void (float)>(&SkParticleEffect::setRate))
          .function("start", select_overload<void (double, bool)>(&SkParticleEffect::start))
          .function("update", select_overload<void (double)>(&SkParticleEffect::update));
  
      value_object<SimpleUniform>("SimpleUniform")
          .field("columns", &SimpleUniform::columns)
          .field("rows",    &SimpleUniform::rows)
          .field("slot",    &SimpleUniform::slot);
  
      function("_MakeParticles", optional_override([](std::string json,
                                                     size_t assetCount,
                                                     uintptr_t /* char**    */ nptr,
                                                     uintptr_t /* uint8_t** */ dptr,
                                                     uintptr_t /* size_t*   */ sptr)
                                                  ->sk_sp<SkParticleEffect> {
          // See the comment in canvaskit_bindings.cpp about the use of uintptr_t
          static bool didInit = false;
          if (!didInit) {
              SkParticleEffect::RegisterParticleTypes();
              didInit = true;
          }
  
          const auto assetNames = reinterpret_cast<char**   >(nptr);
          const auto assetDatas = reinterpret_cast<uint8_t**>(dptr);
          const auto assetSizes = reinterpret_cast<size_t*  >(sptr);
  
          ParticleAssetProvider::AssetVec assets;
          assets.reserve(assetCount);
  
          for (size_t i = 0; i < assetCount; i++) {
              auto name  = SkString(assetNames[i]);
              auto bytes = SkData::MakeFromMalloc(assetDatas[i], assetSizes[i]);
              assets.push_back(std::make_pair(std::move(name), std::move(bytes)));
          }
  
          SkRandom r;
          sk_sp<SkParticleEffectParams> params(new SkParticleEffectParams());
          skjson::DOM dom(json.c_str(), json.length());
          SkFromJsonVisitor fromJson(dom.root());
          params->visitFields(&fromJson);
          params->prepare(skresources::DataURIResourceProviderProxy::Make(
                              ParticleAssetProvider::Make(std::move(assets))).get());
          return sk_sp<SkParticleEffect>(new SkParticleEffect(std::move(params), r));
      }));
      constant("particles", true);
  
  }
  