skia2/modules/canvaskit/particles_bindings.cpp
Brian Osman f2f3d52ad5 Add CanvasKit bindings for SkRuntimeEffect's uniform data
Written to use the exact same names and semantics as particles.

Cq-Include-Trybots: luci.skia.skia.primary:Test-Debian10-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit,Test-Debian10-EMCC-GCE-GPU-AVX2-wasm-Release-All-CanvasKit
Change-Id: I4031efbce06527a519f1ce8c261f79231554e1ef
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/365701
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2021-02-04 17:43:28 +00:00

181 lines
6.5 KiB
C++

/*
* 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 <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 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_<SkParticleEffect>("ParticleEffect")
.smart_ptr<sk_sp<SkParticleEffect>>("sk_sp<SkParticleEffect>")
.function("draw", &SkParticleEffect::draw, allow_raw_pointers())
.function("_uniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t {
return reinterpret_cast<uintptr_t>(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<void (float)>(&SkParticleEffect::setRate))
.function("start", select_overload<void (double, bool)>(&SkParticleEffect::start))
.function("update", select_overload<void (double)>(&SkParticleEffect::update));
value_object<ParticleUniform>("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<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)));
}
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)));
}));
constant("particles", true);
}