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