1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci* Copyright 2019 Google LLC
3cb93a386Sopenharmony_ci*
4cb93a386Sopenharmony_ci* Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci* found in the LICENSE file.
6cb93a386Sopenharmony_ci*/
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "tools/viewer/ParticlesSlide.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
11cb93a386Sopenharmony_ci#include "modules/particles/include/SkParticleEffect.h"
12cb93a386Sopenharmony_ci#include "modules/particles/include/SkParticleSerialization.h"
13cb93a386Sopenharmony_ci#include "modules/particles/include/SkReflected.h"
14cb93a386Sopenharmony_ci#include "modules/skresources/include/SkResources.h"
15cb93a386Sopenharmony_ci#include "src/core/SkOSFile.h"
16cb93a386Sopenharmony_ci#include "src/sksl/codegen/SkSLVMCodeGenerator.h"
17cb93a386Sopenharmony_ci#include "src/utils/SkOSPath.h"
18cb93a386Sopenharmony_ci#include "tools/Resources.h"
19cb93a386Sopenharmony_ci#include "tools/ToolUtils.h"
20cb93a386Sopenharmony_ci#include "tools/viewer/ImGuiLayer.h"
21cb93a386Sopenharmony_ci
22cb93a386Sopenharmony_ci#include "imgui.h"
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_ci#include <string>
25cb93a386Sopenharmony_ci#include <unordered_map>
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_ciusing namespace sk_app;
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ciclass TestingResourceProvider : public skresources::ResourceProvider {
30cb93a386Sopenharmony_cipublic:
31cb93a386Sopenharmony_ci    TestingResourceProvider() {}
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci    sk_sp<SkData> load(const char resource_path[], const char resource_name[]) const override {
34cb93a386Sopenharmony_ci        auto it = fResources.find(resource_name);
35cb93a386Sopenharmony_ci        if (it != fResources.end()) {
36cb93a386Sopenharmony_ci            return it->second;
37cb93a386Sopenharmony_ci        } else {
38cb93a386Sopenharmony_ci            return GetResourceAsData(SkOSPath::Join(resource_path, resource_name).c_str());
39cb93a386Sopenharmony_ci        }
40cb93a386Sopenharmony_ci    }
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_ci    sk_sp<skresources::ImageAsset> loadImageAsset(const char resource_path[],
43cb93a386Sopenharmony_ci                                                  const char resource_name[],
44cb93a386Sopenharmony_ci                                                  const char /*resource_id*/[]) const override {
45cb93a386Sopenharmony_ci        auto data = this->load(resource_path, resource_name);
46cb93a386Sopenharmony_ci        return skresources::MultiFrameImageAsset::Make(data);
47cb93a386Sopenharmony_ci    }
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_ci    void addPath(const char resource_name[], const SkPath& path) {
50cb93a386Sopenharmony_ci        fResources[resource_name] = path.serialize();
51cb93a386Sopenharmony_ci    }
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_ciprivate:
54cb93a386Sopenharmony_ci    std::unordered_map<std::string, sk_sp<SkData>> fResources;
55cb93a386Sopenharmony_ci};
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
58cb93a386Sopenharmony_ci
59cb93a386Sopenharmony_cistatic int InputTextCallback(ImGuiInputTextCallbackData* data) {
60cb93a386Sopenharmony_ci    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
61cb93a386Sopenharmony_ci        SkString* s = (SkString*)data->UserData;
62cb93a386Sopenharmony_ci        SkASSERT(data->Buf == s->writable_str());
63cb93a386Sopenharmony_ci        SkString tmp(data->Buf, data->BufTextLen);
64cb93a386Sopenharmony_ci        s->swap(tmp);
65cb93a386Sopenharmony_ci        data->Buf = s->writable_str();
66cb93a386Sopenharmony_ci    }
67cb93a386Sopenharmony_ci    return 0;
68cb93a386Sopenharmony_ci}
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_cistatic int count_lines(const SkString& s) {
71cb93a386Sopenharmony_ci    int lines = 1;
72cb93a386Sopenharmony_ci    for (size_t i = 0; i < s.size(); ++i) {
73cb93a386Sopenharmony_ci        if (s[i] == '\n') {
74cb93a386Sopenharmony_ci            ++lines;
75cb93a386Sopenharmony_ci        }
76cb93a386Sopenharmony_ci    }
77cb93a386Sopenharmony_ci    return lines;
78cb93a386Sopenharmony_ci}
79cb93a386Sopenharmony_ci
80cb93a386Sopenharmony_ciclass SkGuiVisitor : public SkFieldVisitor {
81cb93a386Sopenharmony_cipublic:
82cb93a386Sopenharmony_ci    SkGuiVisitor() {
83cb93a386Sopenharmony_ci        fTreeStack.push_back(true);
84cb93a386Sopenharmony_ci    }
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_ci    void visit(const char* name, float& f) override {
87cb93a386Sopenharmony_ci        fDirty = (fTreeStack.back() && ImGui::DragFloat(item(name), &f)) || fDirty;
88cb93a386Sopenharmony_ci    }
89cb93a386Sopenharmony_ci    void visit(const char* name, int& i) override {
90cb93a386Sopenharmony_ci        fDirty = (fTreeStack.back() && ImGui::DragInt(item(name), &i)) || fDirty;
91cb93a386Sopenharmony_ci    }
92cb93a386Sopenharmony_ci    void visit(const char* name, bool& b) override {
93cb93a386Sopenharmony_ci        fDirty = (fTreeStack.back() && ImGui::Checkbox(item(name), &b)) || fDirty;
94cb93a386Sopenharmony_ci    }
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ci    void visit(const char* name, SkString& s) override {
97cb93a386Sopenharmony_ci        if (fTreeStack.back()) {
98cb93a386Sopenharmony_ci            int lines = count_lines(s);
99cb93a386Sopenharmony_ci            ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
100cb93a386Sopenharmony_ci            if (lines > 1) {
101cb93a386Sopenharmony_ci                ImGui::LabelText("##Label", "%s", name);
102cb93a386Sopenharmony_ci                ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * (lines + 1));
103cb93a386Sopenharmony_ci                fDirty = ImGui::InputTextMultiline(item(name), s.writable_str(), s.size() + 1,
104cb93a386Sopenharmony_ci                                                   boxSize, flags, InputTextCallback, &s)
105cb93a386Sopenharmony_ci                      || fDirty;
106cb93a386Sopenharmony_ci            } else {
107cb93a386Sopenharmony_ci                fDirty = ImGui::InputText(item(name), s.writable_str(), s.size() + 1, flags,
108cb93a386Sopenharmony_ci                                          InputTextCallback, &s)
109cb93a386Sopenharmony_ci                      || fDirty;
110cb93a386Sopenharmony_ci            }
111cb93a386Sopenharmony_ci        }
112cb93a386Sopenharmony_ci    }
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    void visit(sk_sp<SkReflected>& e, const SkReflected::Type* baseType) override {
115cb93a386Sopenharmony_ci        if (fTreeStack.back()) {
116cb93a386Sopenharmony_ci            const SkReflected::Type* curType = e ? e->getType() : nullptr;
117cb93a386Sopenharmony_ci            if (ImGui::BeginCombo("Type", curType ? curType->fName : "Null")) {
118cb93a386Sopenharmony_ci                auto visitType = [baseType, curType, &e, this](const SkReflected::Type* t) {
119cb93a386Sopenharmony_ci                    if (t->fFactory && (t == baseType || t->isDerivedFrom(baseType)) &&
120cb93a386Sopenharmony_ci                        ImGui::Selectable(t->fName, curType == t)) {
121cb93a386Sopenharmony_ci                        e = t->fFactory();
122cb93a386Sopenharmony_ci                        fDirty = true;
123cb93a386Sopenharmony_ci                    }
124cb93a386Sopenharmony_ci                };
125cb93a386Sopenharmony_ci                SkReflected::VisitTypes(visitType);
126cb93a386Sopenharmony_ci                ImGui::EndCombo();
127cb93a386Sopenharmony_ci            }
128cb93a386Sopenharmony_ci        }
129cb93a386Sopenharmony_ci    }
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_ci    void enterObject(const char* name) override {
132cb93a386Sopenharmony_ci        if (fTreeStack.back()) {
133cb93a386Sopenharmony_ci            fTreeStack.push_back(ImGui::TreeNodeEx(item(name),
134cb93a386Sopenharmony_ci                                                   ImGuiTreeNodeFlags_AllowItemOverlap));
135cb93a386Sopenharmony_ci        } else {
136cb93a386Sopenharmony_ci            fTreeStack.push_back(false);
137cb93a386Sopenharmony_ci        }
138cb93a386Sopenharmony_ci    }
139cb93a386Sopenharmony_ci    void exitObject() override {
140cb93a386Sopenharmony_ci        if (fTreeStack.back()) {
141cb93a386Sopenharmony_ci            ImGui::TreePop();
142cb93a386Sopenharmony_ci        }
143cb93a386Sopenharmony_ci        fTreeStack.pop_back();
144cb93a386Sopenharmony_ci    }
145cb93a386Sopenharmony_ci
146cb93a386Sopenharmony_ci    int enterArray(const char* name, int oldCount) override {
147cb93a386Sopenharmony_ci        this->enterObject(item(name));
148cb93a386Sopenharmony_ci        fArrayCounterStack.push_back(0);
149cb93a386Sopenharmony_ci        fArrayEditStack.push_back();
150cb93a386Sopenharmony_ci
151cb93a386Sopenharmony_ci        int count = oldCount;
152cb93a386Sopenharmony_ci        if (fTreeStack.back()) {
153cb93a386Sopenharmony_ci            ImGui::SameLine();
154cb93a386Sopenharmony_ci            if (ImGui::Button("+")) {
155cb93a386Sopenharmony_ci                ++count;
156cb93a386Sopenharmony_ci                fDirty = true;
157cb93a386Sopenharmony_ci            }
158cb93a386Sopenharmony_ci        }
159cb93a386Sopenharmony_ci        return count;
160cb93a386Sopenharmony_ci    }
161cb93a386Sopenharmony_ci    ArrayEdit exitArray() override {
162cb93a386Sopenharmony_ci        fArrayCounterStack.pop_back();
163cb93a386Sopenharmony_ci        auto edit = fArrayEditStack.back();
164cb93a386Sopenharmony_ci        fArrayEditStack.pop_back();
165cb93a386Sopenharmony_ci        this->exitObject();
166cb93a386Sopenharmony_ci        return edit;
167cb93a386Sopenharmony_ci    }
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci    bool fDirty = false;
170cb93a386Sopenharmony_ci
171cb93a386Sopenharmony_ciprivate:
172cb93a386Sopenharmony_ci    const char* item(const char* name) {
173cb93a386Sopenharmony_ci        if (name) {
174cb93a386Sopenharmony_ci            return name;
175cb93a386Sopenharmony_ci        }
176cb93a386Sopenharmony_ci
177cb93a386Sopenharmony_ci        // We're in an array. Add extra controls and a dynamic label.
178cb93a386Sopenharmony_ci        int index = fArrayCounterStack.back()++;
179cb93a386Sopenharmony_ci        ArrayEdit& edit(fArrayEditStack.back());
180cb93a386Sopenharmony_ci        fScratchLabel = SkStringPrintf("[%d]", index);
181cb93a386Sopenharmony_ci
182cb93a386Sopenharmony_ci        ImGui::PushID(index);
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ci        if (ImGui::Button("X")) {
185cb93a386Sopenharmony_ci            edit.fVerb = ArrayEdit::Verb::kRemove;
186cb93a386Sopenharmony_ci            edit.fIndex = index;
187cb93a386Sopenharmony_ci            fDirty = true;
188cb93a386Sopenharmony_ci        }
189cb93a386Sopenharmony_ci        ImGui::SameLine();
190cb93a386Sopenharmony_ci
191cb93a386Sopenharmony_ci        ImGui::PopID();
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_ci        return fScratchLabel.c_str();
194cb93a386Sopenharmony_ci    }
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_ci    SkSTArray<16, bool, true> fTreeStack;
197cb93a386Sopenharmony_ci    SkSTArray<16, int, true>  fArrayCounterStack;
198cb93a386Sopenharmony_ci    SkSTArray<16, ArrayEdit, true> fArrayEditStack;
199cb93a386Sopenharmony_ci    SkString fScratchLabel;
200cb93a386Sopenharmony_ci};
201cb93a386Sopenharmony_ci
202cb93a386Sopenharmony_ciParticlesSlide::ParticlesSlide() {
203cb93a386Sopenharmony_ci    // Register types for serialization
204cb93a386Sopenharmony_ci    SkParticleEffect::RegisterParticleTypes();
205cb93a386Sopenharmony_ci    fName = "Particles";
206cb93a386Sopenharmony_ci    auto provider = sk_make_sp<TestingResourceProvider>();
207cb93a386Sopenharmony_ci    SkPath star = ToolUtils::make_star({ 0, 0, 100, 100 }, 5);
208cb93a386Sopenharmony_ci    star.close();
209cb93a386Sopenharmony_ci    provider->addPath("star", star);
210cb93a386Sopenharmony_ci    fResourceProvider = provider;
211cb93a386Sopenharmony_ci}
212cb93a386Sopenharmony_ci
213cb93a386Sopenharmony_civoid ParticlesSlide::loadEffects(const char* dirname) {
214cb93a386Sopenharmony_ci    fLoaded.reset();
215cb93a386Sopenharmony_ci    fRunning.reset();
216cb93a386Sopenharmony_ci    SkOSFile::Iter iter(dirname, ".json");
217cb93a386Sopenharmony_ci    for (SkString file; iter.next(&file); ) {
218cb93a386Sopenharmony_ci        LoadedEffect effect;
219cb93a386Sopenharmony_ci        effect.fName = SkOSPath::Join(dirname, file.c_str());
220cb93a386Sopenharmony_ci        effect.fParams.reset(new SkParticleEffectParams());
221cb93a386Sopenharmony_ci        if (auto fileData = SkData::MakeFromFileName(effect.fName.c_str())) {
222cb93a386Sopenharmony_ci            skjson::DOM dom(static_cast<const char*>(fileData->data()), fileData->size());
223cb93a386Sopenharmony_ci            SkFromJsonVisitor fromJson(dom.root());
224cb93a386Sopenharmony_ci            effect.fParams->visitFields(&fromJson);
225cb93a386Sopenharmony_ci            effect.fParams->prepare(fResourceProvider.get());
226cb93a386Sopenharmony_ci            fLoaded.push_back(effect);
227cb93a386Sopenharmony_ci        }
228cb93a386Sopenharmony_ci    }
229cb93a386Sopenharmony_ci    std::sort(fLoaded.begin(), fLoaded.end(), [](const LoadedEffect& a, const LoadedEffect& b) {
230cb93a386Sopenharmony_ci        return strcmp(a.fName.c_str(), b.fName.c_str()) < 0;
231cb93a386Sopenharmony_ci    });
232cb93a386Sopenharmony_ci}
233cb93a386Sopenharmony_ci
234cb93a386Sopenharmony_civoid ParticlesSlide::load(SkScalar winWidth, SkScalar winHeight) {
235cb93a386Sopenharmony_ci    this->loadEffects(GetResourcePath("particles").c_str());
236cb93a386Sopenharmony_ci}
237cb93a386Sopenharmony_ci
238cb93a386Sopenharmony_civoid ParticlesSlide::draw(SkCanvas* canvas) {
239cb93a386Sopenharmony_ci    canvas->clear(SK_ColorGRAY);
240cb93a386Sopenharmony_ci
241cb93a386Sopenharmony_ci    // Window to show all loaded effects, and allow playing them
242cb93a386Sopenharmony_ci    if (ImGui::Begin("Library", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
243cb93a386Sopenharmony_ci        static bool looped = true;
244cb93a386Sopenharmony_ci        ImGui::Checkbox("Looped", &looped);
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci        static SkString dirname = GetResourcePath("particles");
247cb93a386Sopenharmony_ci        ImGuiInputTextFlags textFlags = ImGuiInputTextFlags_CallbackResize;
248cb93a386Sopenharmony_ci        ImGui::InputText("Directory", dirname.writable_str(), dirname.size() + 1, textFlags,
249cb93a386Sopenharmony_ci                         InputTextCallback, &dirname);
250cb93a386Sopenharmony_ci
251cb93a386Sopenharmony_ci        if (ImGui::Button("New")) {
252cb93a386Sopenharmony_ci            LoadedEffect effect;
253cb93a386Sopenharmony_ci            effect.fName = SkOSPath::Join(dirname.c_str(), "new.json");
254cb93a386Sopenharmony_ci            effect.fParams.reset(new SkParticleEffectParams());
255cb93a386Sopenharmony_ci            fLoaded.push_back(effect);
256cb93a386Sopenharmony_ci        }
257cb93a386Sopenharmony_ci        ImGui::SameLine();
258cb93a386Sopenharmony_ci
259cb93a386Sopenharmony_ci        if (ImGui::Button("Load")) {
260cb93a386Sopenharmony_ci            this->loadEffects(dirname.c_str());
261cb93a386Sopenharmony_ci        }
262cb93a386Sopenharmony_ci        ImGui::SameLine();
263cb93a386Sopenharmony_ci
264cb93a386Sopenharmony_ci        if (ImGui::Button("Save")) {
265cb93a386Sopenharmony_ci            for (const auto& effect : fLoaded) {
266cb93a386Sopenharmony_ci                SkFILEWStream fileStream(effect.fName.c_str());
267cb93a386Sopenharmony_ci                if (fileStream.isValid()) {
268cb93a386Sopenharmony_ci                    SkJSONWriter writer(&fileStream, SkJSONWriter::Mode::kPretty);
269cb93a386Sopenharmony_ci                    SkToJsonVisitor toJson(writer);
270cb93a386Sopenharmony_ci                    writer.beginObject();
271cb93a386Sopenharmony_ci                    effect.fParams->visitFields(&toJson);
272cb93a386Sopenharmony_ci                    writer.endObject();
273cb93a386Sopenharmony_ci                    writer.flush();
274cb93a386Sopenharmony_ci                    fileStream.flush();
275cb93a386Sopenharmony_ci                } else {
276cb93a386Sopenharmony_ci                    SkDebugf("Failed to open %s\n", effect.fName.c_str());
277cb93a386Sopenharmony_ci                }
278cb93a386Sopenharmony_ci            }
279cb93a386Sopenharmony_ci        }
280cb93a386Sopenharmony_ci
281cb93a386Sopenharmony_ci        SkGuiVisitor gui;
282cb93a386Sopenharmony_ci        for (int i = 0; i < fLoaded.count(); ++i) {
283cb93a386Sopenharmony_ci            ImGui::PushID(i);
284cb93a386Sopenharmony_ci            if (fAnimated && ImGui::Button("Play")) {
285cb93a386Sopenharmony_ci                sk_sp<SkParticleEffect> effect(new SkParticleEffect(fLoaded[i].fParams));
286cb93a386Sopenharmony_ci                effect->start(fAnimationTime, looped, { 0, 0 }, { 0, -1 }, 1, { 0, 0 }, 0,
287cb93a386Sopenharmony_ci                              { 1, 1, 1, 1 }, 0, fRandom.nextF());
288cb93a386Sopenharmony_ci                fRunning.push_back({ fLoaded[i].fName, effect, false });
289cb93a386Sopenharmony_ci            }
290cb93a386Sopenharmony_ci            ImGui::SameLine();
291cb93a386Sopenharmony_ci
292cb93a386Sopenharmony_ci            ImGui::InputText("##Name", fLoaded[i].fName.writable_str(), fLoaded[i].fName.size() + 1,
293cb93a386Sopenharmony_ci                             textFlags, InputTextCallback, &fLoaded[i].fName);
294cb93a386Sopenharmony_ci
295cb93a386Sopenharmony_ci            if (ImGui::TreeNode("##Details")) {
296cb93a386Sopenharmony_ci                fLoaded[i].fParams->visitFields(&gui);
297cb93a386Sopenharmony_ci                ImGui::TreePop();
298cb93a386Sopenharmony_ci                if (gui.fDirty) {
299cb93a386Sopenharmony_ci                    fLoaded[i].fParams->prepare(fResourceProvider.get());
300cb93a386Sopenharmony_ci                    gui.fDirty = false;
301cb93a386Sopenharmony_ci                }
302cb93a386Sopenharmony_ci            }
303cb93a386Sopenharmony_ci            ImGui::PopID();
304cb93a386Sopenharmony_ci        }
305cb93a386Sopenharmony_ci    }
306cb93a386Sopenharmony_ci    ImGui::End();
307cb93a386Sopenharmony_ci
308cb93a386Sopenharmony_ci    // Most effects are centered around the origin, so we shift the canvas...
309cb93a386Sopenharmony_ci    constexpr SkVector kTranslation = { 250.0f, 250.0f };
310cb93a386Sopenharmony_ci    const SkPoint mousePos = fMousePos - kTranslation;
311cb93a386Sopenharmony_ci
312cb93a386Sopenharmony_ci    // Another window to show all the running effects
313cb93a386Sopenharmony_ci    if (ImGui::Begin("Running")) {
314cb93a386Sopenharmony_ci        for (int i = 0; i < fRunning.count(); ++i) {
315cb93a386Sopenharmony_ci            SkParticleEffect* effect = fRunning[i].fEffect.get();
316cb93a386Sopenharmony_ci            ImGui::PushID(effect);
317cb93a386Sopenharmony_ci
318cb93a386Sopenharmony_ci            ImGui::Checkbox("##Track", &fRunning[i].fTrackMouse);
319cb93a386Sopenharmony_ci            ImGui::SameLine();
320cb93a386Sopenharmony_ci            bool remove = ImGui::Button("X") || !effect->isAlive();
321cb93a386Sopenharmony_ci            ImGui::SameLine();
322cb93a386Sopenharmony_ci            ImGui::Text("%5d %s", effect->getCount(), fRunning[i].fName.c_str());
323cb93a386Sopenharmony_ci            if (fRunning[i].fTrackMouse) {
324cb93a386Sopenharmony_ci                effect->setPosition(mousePos);
325cb93a386Sopenharmony_ci            }
326cb93a386Sopenharmony_ci
327cb93a386Sopenharmony_ci            auto uniformsGui = [mousePos](const SkSL::UniformInfo* info, float* data) {
328cb93a386Sopenharmony_ci                if (!info || !data) {
329cb93a386Sopenharmony_ci                    return;
330cb93a386Sopenharmony_ci                }
331cb93a386Sopenharmony_ci                for (size_t i = 0; i < info->fUniforms.size(); ++i) {
332cb93a386Sopenharmony_ci                    const auto& uni = info->fUniforms[i];
333cb93a386Sopenharmony_ci                    float* vals = data + uni.fSlot;
334cb93a386Sopenharmony_ci
335cb93a386Sopenharmony_ci                    // Skip over builtin uniforms, to reduce clutter
336cb93a386Sopenharmony_ci                    if (uni.fName == "dt" || uni.fName.starts_with("effect.")) {
337cb93a386Sopenharmony_ci                        continue;
338cb93a386Sopenharmony_ci                    }
339cb93a386Sopenharmony_ci
340cb93a386Sopenharmony_ci                    // Special case for 'uniform float2 mouse_pos' - an example of likely app logic
341cb93a386Sopenharmony_ci                    if (uni.fName == "mouse_pos" &&
342cb93a386Sopenharmony_ci                        uni.fKind == SkSL::Type::NumberKind::kFloat &&
343cb93a386Sopenharmony_ci                        uni.fRows == 2 && uni.fColumns == 1) {
344cb93a386Sopenharmony_ci                        vals[0] = mousePos.fX;
345cb93a386Sopenharmony_ci                        vals[1] = mousePos.fY;
346cb93a386Sopenharmony_ci                        continue;
347cb93a386Sopenharmony_ci                    }
348cb93a386Sopenharmony_ci
349cb93a386Sopenharmony_ci                    if (uni.fKind == SkSL::Type::NumberKind::kBoolean) {
350cb93a386Sopenharmony_ci                        for (int c = 0; c < uni.fColumns; ++c, vals += uni.fRows) {
351cb93a386Sopenharmony_ci                            for (int r = 0; r < uni.fRows; ++r, ++vals) {
352cb93a386Sopenharmony_ci                                ImGui::PushID(c*uni.fRows + r);
353cb93a386Sopenharmony_ci                                if (r > 0) {
354cb93a386Sopenharmony_ci                                    ImGui::SameLine();
355cb93a386Sopenharmony_ci                                }
356cb93a386Sopenharmony_ci                                ImGui::CheckboxFlags(r == uni.fRows - 1 ? uni.fName.c_str()
357cb93a386Sopenharmony_ci                                                                        : "##Hidden",
358cb93a386Sopenharmony_ci                                                     (unsigned int*)vals, ~0);
359cb93a386Sopenharmony_ci                                ImGui::PopID();
360cb93a386Sopenharmony_ci                            }
361cb93a386Sopenharmony_ci                        }
362cb93a386Sopenharmony_ci                        continue;
363cb93a386Sopenharmony_ci                    }
364cb93a386Sopenharmony_ci
365cb93a386Sopenharmony_ci                    ImGuiDataType dataType = ImGuiDataType_COUNT;
366cb93a386Sopenharmony_ci                    using NumberKind = SkSL::Type::NumberKind;
367cb93a386Sopenharmony_ci                    switch (uni.fKind) {
368cb93a386Sopenharmony_ci                        case NumberKind::kSigned:   dataType = ImGuiDataType_S32;   break;
369cb93a386Sopenharmony_ci                        case NumberKind::kUnsigned: dataType = ImGuiDataType_U32;   break;
370cb93a386Sopenharmony_ci                        case NumberKind::kFloat:    dataType = ImGuiDataType_Float; break;
371cb93a386Sopenharmony_ci                        default:                                                    break;
372cb93a386Sopenharmony_ci                    }
373cb93a386Sopenharmony_ci                    SkASSERT(dataType != ImGuiDataType_COUNT);
374cb93a386Sopenharmony_ci                    for (int c = 0; c < uni.fColumns; ++c, vals += uni.fRows) {
375cb93a386Sopenharmony_ci                        ImGui::PushID(c);
376cb93a386Sopenharmony_ci                        ImGui::DragScalarN(uni.fName.c_str(), dataType, vals, uni.fRows, 1.0f);
377cb93a386Sopenharmony_ci                        ImGui::PopID();
378cb93a386Sopenharmony_ci                    }
379cb93a386Sopenharmony_ci                }
380cb93a386Sopenharmony_ci            };
381cb93a386Sopenharmony_ci            uniformsGui(effect->uniformInfo(), effect->uniformData());
382cb93a386Sopenharmony_ci            if (remove) {
383cb93a386Sopenharmony_ci                fRunning.removeShuffle(i);
384cb93a386Sopenharmony_ci            }
385cb93a386Sopenharmony_ci            ImGui::PopID();
386cb93a386Sopenharmony_ci        }
387cb93a386Sopenharmony_ci    }
388cb93a386Sopenharmony_ci    ImGui::End();
389cb93a386Sopenharmony_ci
390cb93a386Sopenharmony_ci    canvas->save();
391cb93a386Sopenharmony_ci    canvas->translate(kTranslation.fX, kTranslation.fY);
392cb93a386Sopenharmony_ci    for (const auto& effect : fRunning) {
393cb93a386Sopenharmony_ci        effect.fEffect->draw(canvas);
394cb93a386Sopenharmony_ci    }
395cb93a386Sopenharmony_ci    canvas->restore();
396cb93a386Sopenharmony_ci}
397cb93a386Sopenharmony_ci
398cb93a386Sopenharmony_cibool ParticlesSlide::animate(double nanos) {
399cb93a386Sopenharmony_ci    fAnimated = true;
400cb93a386Sopenharmony_ci    fAnimationTime = 1e-9 * nanos;
401cb93a386Sopenharmony_ci    for (const auto& effect : fRunning) {
402cb93a386Sopenharmony_ci        effect.fEffect->update(fAnimationTime);
403cb93a386Sopenharmony_ci    }
404cb93a386Sopenharmony_ci    return true;
405cb93a386Sopenharmony_ci}
406cb93a386Sopenharmony_ci
407cb93a386Sopenharmony_cibool ParticlesSlide::onMouse(SkScalar x, SkScalar y, skui::InputState state, skui::ModifierKey modifiers) {
408cb93a386Sopenharmony_ci    fMousePos.set(x, y);
409cb93a386Sopenharmony_ci    return false;
410cb93a386Sopenharmony_ci}
411