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/SkSLByteCode.h"
15
16 #include <string>
17
18 #include "modules/canvaskit/WasmAliases.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 SimpleUniform {
81 int columns;
82 int rows;
83 int slot; // the index into the uniforms array that this uniform begins.
84 };
85
fromUniform(SkSL::ByteCode::Uniform u)86 SimpleUniform fromUniform(SkSL::ByteCode::Uniform u) {
87 SimpleUniform 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>("SkParticleEffect")
96 .smart_ptr<sk_sp<SkParticleEffect>>("sk_sp<SkParticleEffect>")
97 .function("draw", &SkParticleEffect::draw, allow_raw_pointers())
98 .function("_effectUniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t {
99 return reinterpret_cast<uintptr_t>(self.effectUniforms());
100 }))
101 .function("_particleUniformPtr", optional_override([](SkParticleEffect& self)->uintptr_t {
102 return reinterpret_cast<uintptr_t>(self.particleUniforms());
103 }))
104 .function("getEffectUniformCount", optional_override([](SkParticleEffect& self)->int {
105 auto ec = self.effectCode();
106 if (!ec) {
107 return -1;
108 }
109 return ec->getUniformCount();
110 }))
111 .function("getEffectUniformFloatCount", optional_override([](SkParticleEffect& self)->int {
112 auto ec = self.effectCode();
113 if (!ec) {
114 return -1;
115 }
116 return ec->getUniformSlotCount();
117 }))
118 .function("getEffectUniformName", optional_override([](SkParticleEffect& self, int i)->JSString {
119 auto ec = self.effectCode();
120 if (!ec) {
121 return emscripten::val::null();
122 }
123 return emscripten::val(ec->getUniform(i).fName.c_str());
124 }))
125 .function("getEffectUniform", optional_override([](SkParticleEffect& self, int i)->SimpleUniform {
126 SimpleUniform su;
127 auto ec = self.effectCode();
128 if (!ec) {
129 return su;
130 }
131 su = fromUniform(ec->getUniform(i));
132 return su;
133 }))
134 .function("getParticleUniformCount", optional_override([](SkParticleEffect& self)->int {
135 auto ec = self.particleCode();
136 if (!ec) {
137 return -1;
138 }
139 return ec->getUniformCount();
140 }))
141 .function("getParticleUniformFloatCount", optional_override([](SkParticleEffect& self)->int {
142 auto ec = self.particleCode();
143 if (!ec) {
144 return -1;
145 }
146 return ec->getUniformSlotCount();
147 }))
148 .function("getParticleUniformName", optional_override([](SkParticleEffect& self, int i)->JSString {
149 auto ec = self.particleCode();
150 if (!ec) {
151 return emscripten::val::null();
152 }
153 return emscripten::val(ec->getUniform(i).fName.c_str());
154 }))
155 .function("getParticleUniform", optional_override([](SkParticleEffect& self, int i)->SimpleUniform {
156 SimpleUniform su;
157 auto ec = self.particleCode();
158 if (!ec) {
159 return su;
160 }
161 su = fromUniform(ec->getUniform(i));
162 return su;
163 }))
164 .function("setPosition", select_overload<void (SkPoint)>(&SkParticleEffect::setPosition))
165 .function("setRate", select_overload<void (float)>(&SkParticleEffect::setRate))
166 .function("start", select_overload<void (double, bool)>(&SkParticleEffect::start))
167 .function("update", select_overload<void (double)>(&SkParticleEffect::update));
168
169 value_object<SimpleUniform>("SimpleUniform")
170 .field("columns", &SimpleUniform::columns)
171 .field("rows", &SimpleUniform::rows)
172 .field("slot", &SimpleUniform::slot);
173
174 function("_MakeParticles", optional_override([](std::string json,
175 size_t assetCount,
176 uintptr_t /* char** */ nptr,
177 uintptr_t /* uint8_t** */ dptr,
178 uintptr_t /* size_t* */ sptr)
179 ->sk_sp<SkParticleEffect> {
180 // See the comment in canvaskit_bindings.cpp about the use of uintptr_t
181 static bool didInit = false;
182 if (!didInit) {
183 SkParticleEffect::RegisterParticleTypes();
184 didInit = true;
185 }
186
187 const auto assetNames = reinterpret_cast<char** >(nptr);
188 const auto assetDatas = reinterpret_cast<uint8_t**>(dptr);
189 const auto assetSizes = reinterpret_cast<size_t* >(sptr);
190
191 ParticleAssetProvider::AssetVec assets;
192 assets.reserve(assetCount);
193
194 for (size_t i = 0; i < assetCount; i++) {
195 auto name = SkString(assetNames[i]);
196 auto bytes = SkData::MakeFromMalloc(assetDatas[i], assetSizes[i]);
197 assets.push_back(std::make_pair(std::move(name), std::move(bytes)));
198 }
199
200 SkRandom r;
201 sk_sp<SkParticleEffectParams> params(new SkParticleEffectParams());
202 skjson::DOM dom(json.c_str(), json.length());
203 SkFromJsonVisitor fromJson(dom.root());
204 params->visitFields(&fromJson);
205 params->prepare(skresources::DataURIResourceProviderProxy::Make(
206 ParticleAssetProvider::Make(std::move(assets))).get());
207 return sk_sp<SkParticleEffect>(new SkParticleEffect(std::move(params), r));
208 }));
209 constant("particles", true);
210
211 }
212