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/SkSLSlide.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
11cb93a386Sopenharmony_ci#include "include/core/SkFont.h"
12cb93a386Sopenharmony_ci#include "include/effects/SkGradientShader.h"
13cb93a386Sopenharmony_ci#include "include/effects/SkPerlinNoiseShader.h"
14cb93a386Sopenharmony_ci#include "src/core/SkEnumerate.h"
15cb93a386Sopenharmony_ci#include "tools/Resources.h"
16cb93a386Sopenharmony_ci#include "tools/viewer/Viewer.h"
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_ci#include <algorithm>
19cb93a386Sopenharmony_ci#include <cstdio>
20cb93a386Sopenharmony_ci#include "imgui.h"
21cb93a386Sopenharmony_ci
22cb93a386Sopenharmony_ciusing namespace sk_app;
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_cistatic int InputTextCallback(ImGuiInputTextCallbackData* data) {
27cb93a386Sopenharmony_ci    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
28cb93a386Sopenharmony_ci        SkString* s = (SkString*)data->UserData;
29cb93a386Sopenharmony_ci        SkASSERT(data->Buf == s->writable_str());
30cb93a386Sopenharmony_ci        SkString tmp(data->Buf, data->BufTextLen);
31cb93a386Sopenharmony_ci        s->swap(tmp);
32cb93a386Sopenharmony_ci        data->Buf = s->writable_str();
33cb93a386Sopenharmony_ci    }
34cb93a386Sopenharmony_ci    return 0;
35cb93a386Sopenharmony_ci}
36cb93a386Sopenharmony_ci
37cb93a386Sopenharmony_ciSkSLSlide::SkSLSlide() {
38cb93a386Sopenharmony_ci    // Register types for serialization
39cb93a386Sopenharmony_ci    fName = "SkSL";
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci    fSkSL =
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_ci        "uniform shader child;\n"
44cb93a386Sopenharmony_ci        "\n"
45cb93a386Sopenharmony_ci        "half4 main(float2 p) {\n"
46cb93a386Sopenharmony_ci        "    return child.eval(p);\n"
47cb93a386Sopenharmony_ci        "}\n";
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_ci    fCodeIsDirty = true;
50cb93a386Sopenharmony_ci}
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_civoid SkSLSlide::load(SkScalar winWidth, SkScalar winHeight) {
53cb93a386Sopenharmony_ci    SkPoint points[] = { { 0, 0 }, { 256, 0 } };
54cb93a386Sopenharmony_ci    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci    sk_sp<SkShader> shader;
57cb93a386Sopenharmony_ci
58cb93a386Sopenharmony_ci    fShaders.push_back(std::make_pair("Null", nullptr));
59cb93a386Sopenharmony_ci
60cb93a386Sopenharmony_ci    shader = SkGradientShader::MakeLinear(points, colors, nullptr, 2, SkTileMode::kClamp);
61cb93a386Sopenharmony_ci    fShaders.push_back(std::make_pair("Linear Gradient", shader));
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_ci    shader = SkGradientShader::MakeRadial({ 256, 256 }, 256, colors, nullptr, 2,
64cb93a386Sopenharmony_ci                                          SkTileMode::kClamp);
65cb93a386Sopenharmony_ci    fShaders.push_back(std::make_pair("Radial Gradient", shader));
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci    shader = SkGradientShader::MakeSweep(256, 256, colors, nullptr, 2);
68cb93a386Sopenharmony_ci    fShaders.push_back(std::make_pair("Sweep Gradient", shader));
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci    shader = GetResourceAsImage("images/mandrill_256.png")->makeShader(SkSamplingOptions());
71cb93a386Sopenharmony_ci    fShaders.push_back(std::make_pair("Mandrill", shader));
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_ci    fResolution = { winWidth, winHeight, 1.0f };
74cb93a386Sopenharmony_ci}
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_civoid SkSLSlide::unload() {
77cb93a386Sopenharmony_ci    fEffect.reset();
78cb93a386Sopenharmony_ci    fInputs.reset();
79cb93a386Sopenharmony_ci    fChildren.reset();
80cb93a386Sopenharmony_ci    fShaders.reset();
81cb93a386Sopenharmony_ci}
82cb93a386Sopenharmony_ci
83cb93a386Sopenharmony_cibool SkSLSlide::rebuild() {
84cb93a386Sopenharmony_ci    // Some of the standard shadertoy inputs:
85cb93a386Sopenharmony_ci    SkString sksl("uniform float3 iResolution;\n"
86cb93a386Sopenharmony_ci                  "uniform float  iTime;\n"
87cb93a386Sopenharmony_ci                  "uniform float4 iMouse;\n");
88cb93a386Sopenharmony_ci    sksl.append(fSkSL);
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci    // It shouldn't happen, but it's possible to assert in the compiler, especially mid-edit.
91cb93a386Sopenharmony_ci    // To guard against losing your work, write out the shader to a backup file, then remove it
92cb93a386Sopenharmony_ci    // when we compile successfully.
93cb93a386Sopenharmony_ci    constexpr char kBackupFile[] = "sksl.bak";
94cb93a386Sopenharmony_ci    FILE* backup = fopen(kBackupFile, "w");
95cb93a386Sopenharmony_ci    if (backup) {
96cb93a386Sopenharmony_ci        fwrite(fSkSL.c_str(), 1, fSkSL.size(), backup);
97cb93a386Sopenharmony_ci        fclose(backup);
98cb93a386Sopenharmony_ci    }
99cb93a386Sopenharmony_ci    auto [effect, errorText] = SkRuntimeEffect::MakeForShader(sksl);
100cb93a386Sopenharmony_ci    if (backup) {
101cb93a386Sopenharmony_ci        std::remove(kBackupFile);
102cb93a386Sopenharmony_ci    }
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_ci    if (!effect) {
105cb93a386Sopenharmony_ci        Viewer::ShaderErrorHandler()->compileError(sksl.c_str(), errorText.c_str());
106cb93a386Sopenharmony_ci        return false;
107cb93a386Sopenharmony_ci    }
108cb93a386Sopenharmony_ci
109cb93a386Sopenharmony_ci    size_t oldSize = fEffect ? fEffect->uniformSize() : 0;
110cb93a386Sopenharmony_ci    fInputs.realloc(effect->uniformSize());
111cb93a386Sopenharmony_ci    if (effect->uniformSize() > oldSize) {
112cb93a386Sopenharmony_ci        memset(fInputs.get() + oldSize, 0, effect->uniformSize() - oldSize);
113cb93a386Sopenharmony_ci    }
114cb93a386Sopenharmony_ci    fChildren.resize_back(effect->children().size());
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci    fEffect = effect;
117cb93a386Sopenharmony_ci    fCodeIsDirty = false;
118cb93a386Sopenharmony_ci    return true;
119cb93a386Sopenharmony_ci}
120cb93a386Sopenharmony_ci
121cb93a386Sopenharmony_civoid SkSLSlide::draw(SkCanvas* canvas) {
122cb93a386Sopenharmony_ci    canvas->clear(SK_ColorWHITE);
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci    ImGui::Begin("SkSL", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar);
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ci    // Edit box for shader code
127cb93a386Sopenharmony_ci    ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
128cb93a386Sopenharmony_ci    ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 30);
129cb93a386Sopenharmony_ci    if (ImGui::InputTextMultiline("Code", fSkSL.writable_str(), fSkSL.size() + 1, boxSize, flags,
130cb93a386Sopenharmony_ci                                  InputTextCallback, &fSkSL)) {
131cb93a386Sopenharmony_ci        fCodeIsDirty = true;
132cb93a386Sopenharmony_ci    }
133cb93a386Sopenharmony_ci
134cb93a386Sopenharmony_ci    if (fCodeIsDirty || !fEffect) {
135cb93a386Sopenharmony_ci        this->rebuild();
136cb93a386Sopenharmony_ci    }
137cb93a386Sopenharmony_ci
138cb93a386Sopenharmony_ci    if (!fEffect) {
139cb93a386Sopenharmony_ci        ImGui::End();
140cb93a386Sopenharmony_ci        return;
141cb93a386Sopenharmony_ci    }
142cb93a386Sopenharmony_ci
143cb93a386Sopenharmony_ci    // Update fMousePos
144cb93a386Sopenharmony_ci    ImVec2 mousePos = ImGui::GetMousePos();
145cb93a386Sopenharmony_ci    if (ImGui::IsMouseDown(0)) {
146cb93a386Sopenharmony_ci        fMousePos.x = mousePos.x;
147cb93a386Sopenharmony_ci        fMousePos.y = mousePos.y;
148cb93a386Sopenharmony_ci    }
149cb93a386Sopenharmony_ci    if (ImGui::IsMouseClicked(0)) {
150cb93a386Sopenharmony_ci        fMousePos.z = mousePos.x;
151cb93a386Sopenharmony_ci        fMousePos.w = mousePos.y;
152cb93a386Sopenharmony_ci    }
153cb93a386Sopenharmony_ci    fMousePos.z = abs(fMousePos.z) * (ImGui::IsMouseDown(0)    ? 1 : -1);
154cb93a386Sopenharmony_ci    fMousePos.w = abs(fMousePos.w) * (ImGui::IsMouseClicked(0) ? 1 : -1);
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ci    for (const auto& v : fEffect->uniforms()) {
157cb93a386Sopenharmony_ci        char* data = fInputs.get() + v.offset;
158cb93a386Sopenharmony_ci        if (v.name.equals("iResolution")) {
159cb93a386Sopenharmony_ci            memcpy(data, &fResolution, sizeof(fResolution));
160cb93a386Sopenharmony_ci            continue;
161cb93a386Sopenharmony_ci        }
162cb93a386Sopenharmony_ci        if (v.name.equals("iTime")) {
163cb93a386Sopenharmony_ci            memcpy(data, &fSeconds, sizeof(fSeconds));
164cb93a386Sopenharmony_ci            continue;
165cb93a386Sopenharmony_ci        }
166cb93a386Sopenharmony_ci        if (v.name.equals("iMouse")) {
167cb93a386Sopenharmony_ci            memcpy(data, &fMousePos, sizeof(fMousePos));
168cb93a386Sopenharmony_ci            continue;
169cb93a386Sopenharmony_ci        }
170cb93a386Sopenharmony_ci        switch (v.type) {
171cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kFloat:
172cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kFloat2:
173cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kFloat3:
174cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kFloat4: {
175cb93a386Sopenharmony_ci                int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kFloat) + 1;
176cb93a386Sopenharmony_ci                float* f = reinterpret_cast<float*>(data);
177cb93a386Sopenharmony_ci                for (int c = 0; c < v.count; ++c, f += rows) {
178cb93a386Sopenharmony_ci                    SkString name = v.isArray() ? SkStringPrintf("%s[%d]", v.name.c_str(), c)
179cb93a386Sopenharmony_ci                                                : v.name;
180cb93a386Sopenharmony_ci                    ImGui::PushID(c);
181cb93a386Sopenharmony_ci                    ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
182cb93a386Sopenharmony_ci                    ImGui::PopID();
183cb93a386Sopenharmony_ci                }
184cb93a386Sopenharmony_ci                break;
185cb93a386Sopenharmony_ci            }
186cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kFloat2x2:
187cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kFloat3x3:
188cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kFloat4x4: {
189cb93a386Sopenharmony_ci                int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kFloat2x2) + 2;
190cb93a386Sopenharmony_ci                int cols = rows;
191cb93a386Sopenharmony_ci                float* f = reinterpret_cast<float*>(data);
192cb93a386Sopenharmony_ci                for (int e = 0; e < v.count; ++e) {
193cb93a386Sopenharmony_ci                    for (int c = 0; c < cols; ++c, f += rows) {
194cb93a386Sopenharmony_ci                        SkString name = v.isArray()
195cb93a386Sopenharmony_ci                            ? SkStringPrintf("%s[%d][%d]", v.name.c_str(), e, c)
196cb93a386Sopenharmony_ci                            : SkStringPrintf("%s[%d]", v.name.c_str(), c);
197cb93a386Sopenharmony_ci                        ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
198cb93a386Sopenharmony_ci                    }
199cb93a386Sopenharmony_ci                }
200cb93a386Sopenharmony_ci                break;
201cb93a386Sopenharmony_ci            }
202cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kInt:
203cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kInt2:
204cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kInt3:
205cb93a386Sopenharmony_ci            case SkRuntimeEffect::Uniform::Type::kInt4: {
206cb93a386Sopenharmony_ci                int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kInt) + 1;
207cb93a386Sopenharmony_ci                int* i = reinterpret_cast<int*>(data);
208cb93a386Sopenharmony_ci                for (int c = 0; c < v.count; ++c, i += rows) {
209cb93a386Sopenharmony_ci                    SkString name = v.isArray() ? SkStringPrintf("%s[%d]", v.name.c_str(), c)
210cb93a386Sopenharmony_ci                                                : v.name;
211cb93a386Sopenharmony_ci                    ImGui::PushID(c);
212cb93a386Sopenharmony_ci                    ImGui::DragScalarN(name.c_str(), ImGuiDataType_S32, i, rows, 1.0f);
213cb93a386Sopenharmony_ci                    ImGui::PopID();
214cb93a386Sopenharmony_ci                }
215cb93a386Sopenharmony_ci                break;
216cb93a386Sopenharmony_ci            }
217cb93a386Sopenharmony_ci        }
218cb93a386Sopenharmony_ci    }
219cb93a386Sopenharmony_ci
220cb93a386Sopenharmony_ci    for (const auto& c : fEffect->children()) {
221cb93a386Sopenharmony_ci        auto curShader =
222cb93a386Sopenharmony_ci                std::find_if(fShaders.begin(), fShaders.end(), [tgt = fChildren[c.index]](auto p) {
223cb93a386Sopenharmony_ci                    return p.second == tgt;
224cb93a386Sopenharmony_ci                });
225cb93a386Sopenharmony_ci        SkASSERT(curShader != fShaders.end());
226cb93a386Sopenharmony_ci
227cb93a386Sopenharmony_ci        if (ImGui::BeginCombo(c.name.c_str(), curShader->first)) {
228cb93a386Sopenharmony_ci            for (const auto& namedShader : fShaders) {
229cb93a386Sopenharmony_ci                if (ImGui::Selectable(namedShader.first, curShader->second == namedShader.second)) {
230cb93a386Sopenharmony_ci                    fChildren[c.index] = namedShader.second;
231cb93a386Sopenharmony_ci                }
232cb93a386Sopenharmony_ci            }
233cb93a386Sopenharmony_ci            ImGui::EndCombo();
234cb93a386Sopenharmony_ci        }
235cb93a386Sopenharmony_ci    }
236cb93a386Sopenharmony_ci
237cb93a386Sopenharmony_ci    static SkColor4f gPaintColor { 1.0f, 1.0f, 1.0f , 1.0f };
238cb93a386Sopenharmony_ci    ImGui::ColorEdit4("Paint Color", gPaintColor.vec());
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_ci    ImGui::RadioButton("Fill",      &fGeometry, kFill);      ImGui::SameLine();
241cb93a386Sopenharmony_ci    ImGui::RadioButton("Circle",    &fGeometry, kCircle);    ImGui::SameLine();
242cb93a386Sopenharmony_ci    ImGui::RadioButton("RoundRect", &fGeometry, kRoundRect); ImGui::SameLine();
243cb93a386Sopenharmony_ci    ImGui::RadioButton("Capsule",   &fGeometry, kCapsule);   ImGui::SameLine();
244cb93a386Sopenharmony_ci    ImGui::RadioButton("Text",      &fGeometry, kText);
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci    ImGui::End();
247cb93a386Sopenharmony_ci
248cb93a386Sopenharmony_ci    auto inputs = SkData::MakeWithoutCopy(fInputs.get(), fEffect->uniformSize());
249cb93a386Sopenharmony_ci    auto shader = fEffect->makeShader(std::move(inputs), fChildren.data(), fChildren.count(),
250cb93a386Sopenharmony_ci                                      nullptr, false);
251cb93a386Sopenharmony_ci
252cb93a386Sopenharmony_ci    SkPaint p;
253cb93a386Sopenharmony_ci    p.setColor4f(gPaintColor);
254cb93a386Sopenharmony_ci    p.setShader(std::move(shader));
255cb93a386Sopenharmony_ci
256cb93a386Sopenharmony_ci    switch (fGeometry) {
257cb93a386Sopenharmony_ci        case kFill:
258cb93a386Sopenharmony_ci            canvas->drawPaint(p);
259cb93a386Sopenharmony_ci            break;
260cb93a386Sopenharmony_ci        case kCircle:
261cb93a386Sopenharmony_ci            canvas->drawCircle({ 256, 256 }, 256, p);
262cb93a386Sopenharmony_ci            break;
263cb93a386Sopenharmony_ci        case kRoundRect:
264cb93a386Sopenharmony_ci            canvas->drawRoundRect({ 0, 0, 512, 512 }, 64, 64, p);
265cb93a386Sopenharmony_ci            break;
266cb93a386Sopenharmony_ci        case kCapsule:
267cb93a386Sopenharmony_ci            canvas->drawRoundRect({ 0, 224, 512, 288 }, 32, 32, p);
268cb93a386Sopenharmony_ci            break;
269cb93a386Sopenharmony_ci        case kText: {
270cb93a386Sopenharmony_ci            SkFont font;
271cb93a386Sopenharmony_ci            font.setSize(SkIntToScalar(96));
272cb93a386Sopenharmony_ci            canvas->drawSimpleText("Hello World", strlen("Hello World"), SkTextEncoding::kUTF8, 0,
273cb93a386Sopenharmony_ci                                   256, font, p);
274cb93a386Sopenharmony_ci        } break;
275cb93a386Sopenharmony_ci        default: break;
276cb93a386Sopenharmony_ci    }
277cb93a386Sopenharmony_ci}
278cb93a386Sopenharmony_ci
279cb93a386Sopenharmony_cibool SkSLSlide::animate(double nanos) {
280cb93a386Sopenharmony_ci    fSeconds = static_cast<float>(nanos * 1E-9);
281cb93a386Sopenharmony_ci    return true;
282cb93a386Sopenharmony_ci}
283