blob: 01619f861b2b89f84b1742b5f4a9d0c83ef81247 [file] [log] [blame]
Brian Osmand927bd22019-12-18 11:23:12 -05001/*
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 "tools/viewer/SkSLSlide.h"
9
Brian Osman173e1532019-12-18 15:44:27 -050010#include "include/effects/SkGradientShader.h"
Brian Osman87e3bef2020-01-27 16:21:34 -050011#include "include/effects/SkPerlinNoiseShader.h"
Brian Osman173e1532019-12-18 15:44:27 -050012#include "src/core/SkEnumerate.h"
Brian Osmand927bd22019-12-18 11:23:12 -050013#include "tools/Resources.h"
14#include "tools/viewer/ImGuiLayer.h"
15
Brian Osman107c6662019-12-30 15:02:30 -050016#include <algorithm>
Brian Osmand927bd22019-12-18 11:23:12 -050017#include "imgui.h"
18
19using namespace sk_app;
20
21///////////////////////////////////////////////////////////////////////////////
22
23static int InputTextCallback(ImGuiInputTextCallbackData* data) {
24 if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
25 SkString* s = (SkString*)data->UserData;
26 SkASSERT(data->Buf == s->writable_str());
27 SkString tmp(data->Buf, data->BufTextLen);
28 s->swap(tmp);
29 data->Buf = s->writable_str();
30 }
31 return 0;
32}
33
34SkSLSlide::SkSLSlide() {
35 // Register types for serialization
36 fName = "SkSL";
37
38 fSkSL =
39
Brian Osman87e3bef2020-01-27 16:21:34 -050040 "in fragmentProcessor fp;\n"
Brian Osmand927bd22019-12-18 11:23:12 -050041 "\n"
42 "void main(float x, float y, inout half4 color) {\n"
Brian Osman87e3bef2020-01-27 16:21:34 -050043 " color = sample(fp, float2(x, y));\n"
Brian Osmand927bd22019-12-18 11:23:12 -050044 "}\n";
Brian Osman173e1532019-12-18 15:44:27 -050045}
46
47void SkSLSlide::load(SkScalar winWidth, SkScalar winHeight) {
48 SkPoint points[] = { { 0, 0 }, { 256, 0 } };
49 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
50
51 sk_sp<SkShader> shader;
52
53 shader = SkGradientShader::MakeLinear(points, colors, nullptr, 2, SkTileMode::kClamp);
54 fShaders.push_back(std::make_pair("Linear Gradient", shader));
55
56 shader = SkGradientShader::MakeRadial({ 128, 128 }, 128, colors, nullptr, 2,
57 SkTileMode::kClamp);
58 fShaders.push_back(std::make_pair("Radial Gradient", shader));
59
60 shader = SkGradientShader::MakeSweep(128, 128, colors, nullptr, 2);
61 fShaders.push_back(std::make_pair("Sweep Gradient", shader));
62
63 shader = GetResourceAsImage("images/mandrill_256.png")->makeShader();
64 fShaders.push_back(std::make_pair("Mandrill", shader));
Brian Osmand927bd22019-12-18 11:23:12 -050065
Brian Osman87e3bef2020-01-27 16:21:34 -050066 shader = SkPerlinNoiseShader::MakeImprovedNoise(0.025f, 0.025f, 3, 0.0f);
67 fShaders.push_back(std::make_pair("Perlin Noise", shader));
68
Brian Osmand927bd22019-12-18 11:23:12 -050069 this->rebuild();
70}
71
Brian Osman173e1532019-12-18 15:44:27 -050072void SkSLSlide::unload() {
73 fEffect.reset();
74 fInputs.reset();
75 fChildren.reset();
76 fShaders.reset();
77}
78
Brian Osmand927bd22019-12-18 11:23:12 -050079bool SkSLSlide::rebuild() {
Brian Osman088913a2019-12-19 15:44:56 -050080 auto [effect, errorText] = SkRuntimeEffect::Make(fSkSL);
81 if (!effect) {
Brian Osmand927bd22019-12-18 11:23:12 -050082 return false;
83 }
84
85 size_t oldSize = fEffect ? fEffect->inputSize() : 0;
86 fInputs.realloc(effect->inputSize());
87 if (effect->inputSize() > oldSize) {
88 memset(fInputs.get() + oldSize, 0, effect->inputSize() - oldSize);
89 }
Brian Osmanf72dedd2020-01-08 13:19:58 -050090 fChildren.resize_back(effect->children().count());
Brian Osman173e1532019-12-18 15:44:27 -050091 for (auto& c : fChildren) {
92 if (!c) {
93 c = fShaders[0].second;
94 }
95 }
96
Brian Osmand927bd22019-12-18 11:23:12 -050097 fEffect = effect;
98 return true;
99}
100
101void SkSLSlide::draw(SkCanvas* canvas) {
102 canvas->clear(SK_ColorWHITE);
103
104 ImGui::Begin("SkSL", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar);
105
106 // Edit box for shader code
107 ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
108 ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 30);
109 if (ImGui::InputTextMultiline("Code", fSkSL.writable_str(), fSkSL.size() + 1,
110 boxSize, flags, InputTextCallback, &fSkSL)) {
111 this->rebuild();
112 }
113
Brian Osman173e1532019-12-18 15:44:27 -0500114 if (!fEffect) {
115 ImGui::End();
116 return;
117 }
118
Brian Osmanf72dedd2020-01-08 13:19:58 -0500119 for (const auto& v : fEffect->inputs()) {
Brian Osmand927bd22019-12-18 11:23:12 -0500120 switch (v.fType) {
121 case SkRuntimeEffect::Variable::Type::kBool:
122 ImGui::Checkbox(v.fName.c_str(), (bool*)(fInputs.get() + v.fOffset));
123 break;
124 case SkRuntimeEffect::Variable::Type::kInt:
125 ImGui::DragInt(v.fName.c_str(), (int*)(fInputs.get() + v.fOffset));
126 break;
127 case SkRuntimeEffect::Variable::Type::kFloat:
128 case SkRuntimeEffect::Variable::Type::kFloat2:
129 case SkRuntimeEffect::Variable::Type::kFloat3:
130 case SkRuntimeEffect::Variable::Type::kFloat4: {
131 int rows = ((int)v.fType - (int)SkRuntimeEffect::Variable::Type::kFloat) + 1;
132 float* f = (float*)(fInputs.get() + v.fOffset);
133 for (int c = 0; c < v.fCount; ++c, f += rows) {
134 SkString name = v.isArray() ? SkStringPrintf("%s[%d]", v.fName.c_str(), c)
135 : v.fName;
136 ImGui::PushID(c);
137 ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
138 ImGui::PopID();
139 }
140 break;
141 }
142 case SkRuntimeEffect::Variable::Type::kFloat2x2:
143 case SkRuntimeEffect::Variable::Type::kFloat3x3:
144 case SkRuntimeEffect::Variable::Type::kFloat4x4: {
145 int rows = ((int)v.fType - (int)SkRuntimeEffect::Variable::Type::kFloat2x2) + 2;
146 int cols = rows;
147 float* f = (float*)(fInputs.get() + v.fOffset);
148 for (int e = 0; e < v.fCount; ++e) {
149 for (int c = 0; c < cols; ++c, f += rows) {
150 SkString name = v.isArray()
151 ? SkStringPrintf("%s[%d][%d]", v.fName.c_str(), e, c)
152 : SkStringPrintf("%s[%d]", v.fName.c_str(), c);
153 ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
154 }
155 }
156 break;
157 }
158 }
159 }
160
Brian Osmanf72dedd2020-01-08 13:19:58 -0500161 for (const auto [i, name] : SkMakeEnumerate(fEffect->children())) {
Brian Osman173e1532019-12-18 15:44:27 -0500162 auto curShader = std::find_if(fShaders.begin(), fShaders.end(),
163 [tgt = fChildren[i]](auto p) { return p.second == tgt; });
164 SkASSERT(curShader!= fShaders.end());
165
166 if (ImGui::BeginCombo(name.c_str(), curShader->first)) {
167 for (const auto& namedShader : fShaders) {
168 if (ImGui::Selectable(namedShader.first, curShader->second == namedShader.second)) {
169 fChildren[i] = namedShader.second;
170 }
171 }
172 ImGui::EndCombo();
173 }
174 }
175
Brian Osmand927bd22019-12-18 11:23:12 -0500176 ImGui::End();
177
Brian Osman173e1532019-12-18 15:44:27 -0500178 auto inputs = SkData::MakeWithoutCopy(fInputs.get(), fEffect->inputSize());
Brian Osman93de1622019-12-26 08:43:05 -0500179 auto shader = fEffect->makeShader(std::move(inputs), fChildren.data(), fChildren.count(),
180 nullptr, false);
181
Brian Osmand927bd22019-12-18 11:23:12 -0500182 SkPaint p;
183 p.setShader(std::move(shader));
184 canvas->drawRect({ 0, 0, 256, 256 }, p);
185}