/* * 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/SkSLVMGenerator.h" #include #include "modules/canvaskit/WasmCommon.h" #include #include 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>>; static sk_sp Make(AssetVec assets) { if (assets.empty()) { return nullptr; } return sk_sp(new ParticleAssetProvider(std::move(assets))); } sk_sp 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 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 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 ParticleUniform { int columns; int rows; int slot; // the index into the uniforms array that this uniform begins. }; ParticleUniform fromUniform(const SkSL::UniformInfo::Uniform& u) { ParticleUniform su; su.columns = u.fColumns; su.rows = u.fRows; su.slot = u.fSlot; return su; } EMSCRIPTEN_BINDINGS(Particles) { class_("ParticleEffect") .smart_ptr>("sk_sp") .function("draw", &SkParticleEffect::draw, allow_raw_pointers()) .function("_uniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t { return reinterpret_cast(self.uniformData()); })) .function("getUniformCount", optional_override([](SkParticleEffect& self)->int { auto info = self.uniformInfo(); if (!info) { return -1; } return info->fUniforms.size(); })) .function("getUniformFloatCount", optional_override([](SkParticleEffect& self)->int { auto info = self.uniformInfo(); if (!info) { return -1; } return info->fUniformSlotCount; })) .function("getUniformName", optional_override([](SkParticleEffect& self, int i)->JSString { auto info = self.uniformInfo(); if (!info) { return emscripten::val::null(); } return emscripten::val(info->fUniforms[i].fName.c_str()); })) .function("getUniform", optional_override([](SkParticleEffect& self, int i)->ParticleUniform { ParticleUniform su; auto info = self.uniformInfo(); if (!info) { return su; } su = fromUniform(info->fUniforms[i]); return su; })) .function("_setPosition", optional_override([](SkParticleEffect& self, SkScalar x, SkScalar y)->void { self.setPosition({x, y}); })) .function("setRate", select_overload(&SkParticleEffect::setRate)) .function("start", select_overload(&SkParticleEffect::start)) .function("update", select_overload(&SkParticleEffect::update)); value_object("ParticleUniform") .field("columns", &ParticleUniform::columns) .field("rows", &ParticleUniform::rows) .field("slot", &ParticleUniform::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 { // 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(nptr); const auto assetDatas = reinterpret_cast(dptr); const auto assetSizes = reinterpret_cast(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))); } sk_sp 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(new SkParticleEffect(std::move(params))); })); constant("particles", true); }