• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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