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 "include/core/SkCanvas.h"
9 #include "include/core/SkTypes.h"
10 #include "include/utils/SkRandom.h"
11 #include "modules/particles/include/SkParticleEffect.h"
12 #include "modules/particles/include/SkParticleSerialization.h"
13 #include "modules/skresources/include/SkResources.h"
14 #include "src/sksl/codegen/SkSLVMCodeGenerator.h"
15
16 #include <string>
17
18 #include "modules/canvaskit/WasmCommon.h"
19
20 #include <emscripten.h>
21 #include <emscripten/bind.h>
22
23 using namespace emscripten;
24
25 namespace {
26
27 class ParticleAssetProvider : public skresources::ResourceProvider {
28 public:
29 ~ParticleAssetProvider() override = default;
30
31 // Tried using a map, but that gave strange errors like
32 // https://emscripten.org/docs/porting/guidelines/function_pointer_issues.html
33 // Not entirely sure why, but perhaps the iterator in the map was
34 // confusing enscripten.
35 using AssetVec = std::vector<std::pair<SkString, sk_sp<SkData>>>;
36
Make(AssetVec assets)37 static sk_sp<ParticleAssetProvider> Make(AssetVec assets) {
38 if (assets.empty()) {
39 return nullptr;
40 }
41
42 return sk_sp<ParticleAssetProvider>(new ParticleAssetProvider(std::move(assets)));
43 }
44
loadImageAsset(const char[],const char name[],const char[]) const45 sk_sp<skresources::ImageAsset> loadImageAsset(const char[] /* path */,
46 const char name[],
47 const char[] /* id */) const override {
48 // For CK we ignore paths & IDs, and identify images based solely on name.
49 if (auto data = this->findAsset(name)) {
50 return skresources::MultiFrameImageAsset::Make(std::move(data));
51 }
52
53 return nullptr;
54 }
55
loadFont(const char name[],const char[]) const56 sk_sp<SkData> loadFont(const char name[], const char[] /* url */) const override {
57 // Same as images paths, we ignore font URLs.
58 return this->findAsset(name);
59 }
60
61 private:
ParticleAssetProvider(AssetVec assets)62 explicit ParticleAssetProvider(AssetVec assets) : fAssets(std::move(assets)) {}
63
findAsset(const char name[]) const64 sk_sp<SkData> findAsset(const char name[]) const {
65 for (const auto& asset : fAssets) {
66 if (asset.first.equals(name)) {
67 return asset.second;
68 }
69 }
70
71 SkDebugf("Could not find %s\n", name);
72 return nullptr;
73 }
74
75 const AssetVec fAssets;
76 };
77
78 }
79
80 struct ParticleUniform {
81 int columns;
82 int rows;
83 int slot; // the index into the uniforms array that this uniform begins.
84 };
85
fromUniform(const SkSL::UniformInfo::Uniform & u)86 ParticleUniform fromUniform(const SkSL::UniformInfo::Uniform& u) {
87 ParticleUniform su;
88 su.columns = u.fColumns;
89 su.rows = u.fRows;
90 su.slot = u.fSlot;
91 return su;
92 }
93
EMSCRIPTEN_BINDINGS(Particles)94 EMSCRIPTEN_BINDINGS(Particles) {
95 class_<SkParticleEffect>("ParticleEffect")
96 .smart_ptr<sk_sp<SkParticleEffect>>("sk_sp<SkParticleEffect>")
97 .function("draw", &SkParticleEffect::draw, allow_raw_pointers())
98 .function("_uniformPtr", optional_override([](SkParticleEffect& self)->WASMPointerF32 {
99 return reinterpret_cast<WASMPointerF32>(self.uniformData());
100 }))
101 .function("getUniformCount", optional_override([](SkParticleEffect& self)->int {
102 auto info = self.uniformInfo();
103 if (!info) {
104 return -1;
105 }
106 return info->fUniforms.size();
107 }))
108 .function("getUniformFloatCount", optional_override([](SkParticleEffect& self)->int {
109 auto info = self.uniformInfo();
110 if (!info) {
111 return -1;
112 }
113 return info->fUniformSlotCount;
114 }))
115 .function("getUniformName", optional_override([](SkParticleEffect& self, int i)->JSString {
116 auto info = self.uniformInfo();
117 if (!info) {
118 return emscripten::val::null();
119 }
120 return emscripten::val(info->fUniforms[i].fName.c_str());
121 }))
122 .function("getUniform", optional_override([](SkParticleEffect& self, int i)->ParticleUniform {
123 ParticleUniform su;
124 auto info = self.uniformInfo();
125 if (!info) {
126 return su;
127 }
128 su = fromUniform(info->fUniforms[i]);
129 return su;
130 }))
131 .function("_setPosition", optional_override([](SkParticleEffect& self,
132 SkScalar x, SkScalar y)->void {
133 self.setPosition({x, y});
134 }))
135 .function("setRate", select_overload<void (float)>(&SkParticleEffect::setRate))
136 .function("start", select_overload<void (double, bool)>(&SkParticleEffect::start))
137 .function("update", select_overload<void (double)>(&SkParticleEffect::update));
138
139 value_object<ParticleUniform>("ParticleUniform")
140 .field("columns", &ParticleUniform::columns)
141 .field("rows", &ParticleUniform::rows)
142 .field("slot", &ParticleUniform::slot);
143
144 function("_MakeParticles", optional_override([](std::string json,
145 size_t assetCount,
146 WASMPointerU32 nptr,
147 WASMPointerU32 dptr,
148 WASMPointerU32 sptr)
149 ->sk_sp<SkParticleEffect> {
150 static bool didInit = false;
151 if (!didInit) {
152 SkParticleEffect::RegisterParticleTypes();
153 didInit = true;
154 }
155
156 const auto assetNames = reinterpret_cast<char** >(nptr);
157 const auto assetDatas = reinterpret_cast<uint8_t**>(dptr);
158 const auto assetSizes = reinterpret_cast<size_t* >(sptr);
159
160 ParticleAssetProvider::AssetVec assets;
161 assets.reserve(assetCount);
162
163 for (size_t i = 0; i < assetCount; i++) {
164 auto name = SkString(assetNames[i]);
165 auto bytes = SkData::MakeFromMalloc(assetDatas[i], assetSizes[i]);
166 assets.push_back(std::make_pair(std::move(name), std::move(bytes)));
167 }
168
169 sk_sp<SkParticleEffectParams> params(new SkParticleEffectParams());
170 skjson::DOM dom(json.c_str(), json.length());
171 SkFromJsonVisitor fromJson(dom.root());
172 params->visitFields(&fromJson);
173 params->prepare(skresources::DataURIResourceProviderProxy::Make(
174 ParticleAssetProvider::Make(std::move(assets))).get());
175 return sk_sp<SkParticleEffect>(new SkParticleEffect(std::move(params)));
176 }));
177 constant("particles", true);
178
179 }
180