/* * 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 #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 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") .smart_ptr>("sk_sp") .function("draw", &SkParticleEffect::draw, allow_raw_pointers()) .function("_effectUniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t { return reinterpret_cast(self.effectUniforms()); })) .function("_particleUniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t { return reinterpret_cast(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(&SkParticleEffect::setPosition)) .function("setRate", select_overload(&SkParticleEffect::setRate)) .function("start", select_overload(&SkParticleEffect::start)) .function("update", select_overload(&SkParticleEffect::update)); value_object("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 { // 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); }