xref: /aosp_15_r20/external/skia/tools/viewer/MeshSlide.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2023 Google Inc.
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/SkBlendMode.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkCubicMap.h"
12 #include "include/core/SkImage.h"
13 #include "include/core/SkPoint.h"
14 #include "include/core/SkSamplingOptions.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkShader.h"
17 #include "include/core/SkTileMode.h"
18 #include "include/core/SkVertices.h"
19 #include "include/effects/SkGradientShader.h"
20 #include "include/private/base/SkFloatingPoint.h"
21 #include "tools/DecodeUtils.h"
22 #include "tools/Resources.h"
23 #include "tools/timer/TimeUtils.h"
24 #include "tools/viewer/Slide.h"
25 
26 #include <cmath>
27 #include <cstddef>
28 #include <cstdint>
29 #include <iterator>
30 #include <vector>
31 
32 #include "imgui.h"
33 
34 namespace {
35 
lerp(float a,float b,float t)36 float lerp(float a, float b, float t) {
37     return a + (b - a)*t;
38 }
39 
make_image_shader(const char * resource)40 sk_sp<SkShader> make_image_shader(const char* resource) {
41     sk_sp<SkImage> img = ToolUtils::GetResourceAsImage(resource);
42 
43     // Normalize to 1x1 for UV sampling.
44     const auto lm = SkMatrix::Scale(1.0f/img->width(), 1.0f/img->height());
45 
46     return img->makeShader(SkTileMode::kDecal,
47                            SkTileMode::kDecal,
48                            SkSamplingOptions(SkFilterMode::kLinear),
49                            &lm);
50 }
51 
52 static constexpr struct ShaderFactory {
53     const char*     fName;
54     sk_sp<SkShader> (*fBuild)();
55 } gShaderFactories[] = {
56     {
57         "Img (Mandrill)",
__anon58b8984e0202() 58         []() ->sk_sp<SkShader> { return make_image_shader("images/mandrill_512.png"); }
59     },
60     {
61         "Img (Baby Tux)",
__anon58b8984e0302() 62         []() ->sk_sp<SkShader> { return make_image_shader("images/baby_tux.png"); }
63     },
64     {
65         "Img (Brickwork)",
__anon58b8984e0402() 66         []() ->sk_sp<SkShader> { return make_image_shader("images/brickwork-texture.jpg"); }
67     },
68     {
69         "Radial Gradient",
__anon58b8984e0502() 70         []() ->sk_sp<SkShader> {
71             static constexpr SkColor gColors[] = {
72                 SK_ColorGREEN, SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN
73             };
74             return SkGradientShader::MakeRadial({0.5f, 0.5f}, 0.5f, gColors, nullptr,
75                                                 std::size(gColors), SkTileMode::kRepeat);
76         }
77     },
78     {
79         "Colors",
__anon58b8984e0602() 80         []() ->sk_sp<SkShader> { return nullptr; }
81     },
82 };
83 
84 static constexpr struct VertexAnimator {
85     const char* fName;
86     void (*fAanimate)(const std::vector<SkPoint>& uv, float t, std::vector<SkPoint>& out);
87 } gVertexAnimators[] = {
88     {
89         "Cylinderator",
90         // Simulate a cylinder rolling sideways across the 1x1 uv space.
__anon58b8984e0702(const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) 91         [](const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) {
92             static constexpr float kCylRadius = .2f;
93 
94             const auto cyl_pos = t;
95 
96             for (size_t i = 0; i < uvs.size(); ++i) {
97                 const auto& uv = uvs[i];
98 
99                 if (uv.fX <= cyl_pos) {
100                     out[i] = uv;
101                     continue;
102                 }
103 
104                 const auto arc_len = uv.fX - cyl_pos,
105                            arc_ang = arc_len/kCylRadius;
106 
107                 out[i] = SkPoint{
108                     cyl_pos + std::sin(arc_ang)*kCylRadius,
109                     uv.fY,
110                 };
111             }
112         },
113     },
114     {
115         "Squircillator",
116         // Pull all vertices towards center, proportionally, such that the outer square edge
117         // is mapped to a circle for t == 1.
__anon58b8984e0802(const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) 118         [](const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) {
119             for (size_t i = 0; i < uvs.size(); ++i) {
120                 // remap to [-.5,.5]
121                 const auto uv = (uvs[i] - SkPoint{0.5,0.5});
122 
123                 // Distance from center to outer edge for the line pasing through uv.
124                 const auto d = uv.length()*0.5f/std::max(std::abs(uv.fX), std::abs(uv.fY));
125                 // Scale needed to pull the outer edge to the r=0.5 circle at t == 1.
126                 const auto s = lerp(1, (0.5f / d), t);
127 
128                 out[i] = uv*s + SkPoint{0.5, 0.5};
129             }
130         },
131     },
132     {
133         "Twirlinator",
134         // Rotate vertices proportional to their distance to center.
__anon58b8984e0902(const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) 135         [](const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) {
136             static constexpr float kMaxRotate = SK_FloatPI*4;
137 
138             for (size_t i = 0; i < uvs.size(); ++i) {
139                 // remap to [-.5,.5]
140                 const auto uv = (uvs[i] - SkPoint{0.5,0.5});
141                 const auto angle = kMaxRotate * t * uv.length();
142 
143                 out[i] = SkMatrix::RotateRad(angle).mapPoint(uv) + SkPoint{0.5, 0.5};
144             }
145         },
146     },
147     {
148         "Wigglynator",
__anon58b8984e0a02(const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) 149         [](const std::vector<SkPoint>& uvs, float t, std::vector<SkPoint>& out) {
150             const float radius = t*0.2f/(std::sqrt(uvs.size()) - 1);
151             for (size_t i = 0; i < uvs.size(); ++i) {
152                 const float phase = i*SK_FloatPI*0.31f,
153                             angle = phase + t*SK_FloatPI*2;
154                 out[i] = uvs[i] + SkVector{
155                     radius*std::cos(angle),
156                     radius*std::sin(angle),
157                 };
158             }
159         },
160     },
161     {
162         "None",
__anon58b8984e0b02(const std::vector<SkPoint>& uvs, float, std::vector<SkPoint>& out) 163         [](const std::vector<SkPoint>& uvs, float, std::vector<SkPoint>& out) { out = uvs; },
164     },
165 };
166 
167 class MeshSlide final : public Slide {
168 public:
MeshSlide()169     MeshSlide() : fTimeMapper({0.5f, 0}, {0.5f, 1}) { fName = "Mesh"; }
170 
load(SkScalar w,SkScalar h)171     void load(SkScalar w, SkScalar h) override {
172         fSize = {w, h};
173 
174         this->initMesh(256);
175         this->initShader(gShaderFactories[0]);
176     }
177 
resize(SkScalar w,SkScalar h)178     void resize(SkScalar w, SkScalar h) override { fSize = {w, h}; }
179 
draw(SkCanvas * canvas)180     void draw(SkCanvas* canvas) override {
181         SkAutoCanvasRestore acr(canvas, true);
182 
183         SkPaint p;
184         p.setAntiAlias(true);
185         p.setColor(SK_ColorWHITE);
186 
187         static constexpr float kMeshFraction = 0.85f;
188         const float mesh_size = std::min(fSize.fWidth, fSize.fHeight)*kMeshFraction;
189 
190         canvas->translate((fSize.fWidth  - mesh_size) * 0.5f,
191                           (fSize.fHeight - mesh_size) * 0.5f);
192         canvas->scale(mesh_size, mesh_size);
193 
194         auto verts = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
195                                           fVertices.size(),
196                                           fVertices.data(),
197                                           fShader ? fUVs.data() : nullptr,
198                                           fShader ? nullptr : fColors.data(),
199                                           fIndices.size(),
200                                           fIndices.data());
201         p.setShader(fShader);
202         canvas->drawVertices(verts, SkBlendMode::kModulate, p);
203 
204         if (fShowMesh) {
205             p.setShader(nullptr);
206             p.setColor(SK_ColorBLUE);
207             p.setStroke(true);
208             p.setStrokeWidth(0.5f/mesh_size);
209 
210             SkASSERT(fIndices.size() % 6 == 0);
211             for (auto i = fIndices.cbegin(); i < fIndices.cend(); i += 6) {
212                 canvas->drawLine(fVertices[i[0]], fVertices[i[1]], p);
213                 canvas->drawLine(fVertices[i[1]], fVertices[i[2]], p);
214                 canvas->drawLine(fVertices[i[2]], fVertices[i[0]], p);
215                 canvas->drawLine(fVertices[i[3]], fVertices[i[4]], p);
216                 canvas->drawLine(fVertices[i[4]], fVertices[i[5]], p);
217                 canvas->drawLine(fVertices[i[5]], fVertices[i[3]], p);
218             }
219 
220             p.setStrokeCap(SkPaint::kRound_Cap);
221             p.setStrokeWidth(5/mesh_size);
222             canvas->drawPoints(SkCanvas::kPoints_PointMode, fVertices.size(), fVertices.data(), p);
223         }
224 
225         this->drawControls();
226     }
227 
animate(double nanos)228     bool animate(double nanos) override {
229         if (!fTimeBase) {
230             fTimeBase = nanos;
231         }
232 
233         // Oscillating between 0..1
234         const float t =
235                 std::abs((std::fmod((nanos - fTimeBase)*0.000000001*fAnimationSpeed, 2) - 1));
236 
237         // Add some easing
238         fCurrentAnimator->fAanimate(fUVs, fTimeMapper.computeYFromX(t), fVertices);
239 
240         return true;
241     }
242 
243 private:
initMesh(size_t vertex_count)244     void initMesh(size_t vertex_count) {
245         // Generate an NxN mesh.  For simplicity, we keep the vertices in normalized space
246         // (1x1 same as UVs), and scale the mesh up when rendering.
247         const auto n = static_cast<size_t>(std::sqrt(vertex_count));
248         SkASSERT(n > 0);
249         SkASSERT(n == std::sqrt(vertex_count));
250 
251         fVertices.resize(vertex_count);
252         fUVs.resize(vertex_count);
253         fColors.resize(vertex_count);
254         for (size_t i = 0; i < vertex_count; ++i) {
255             fVertices[i] = fUVs[i] = {
256                 static_cast<float>(i % n) / (n - 1),
257                 static_cast<float>(i / n) / (n - 1),
258             };
259             fColors[i] = SkColorSetRGB(!!(i%2)*255,
260                                        !!(i%3)*255,
261                                        !!((i+1)%3)*255);
262         }
263 
264         // Trivial triangle tessellation pattern:
265         //
266         // *---*---*
267         // |  /|\  |
268         // | / | \ |
269         // |/  |  \|
270         // *---*---*
271         // |\  |  /|
272         // | \ | / |
273         // |  \|/  |
274         // *---*---*
275         const size_t triangle_count = 2*(n - 1)*(n - 1),
276                         index_count = 3*triangle_count;
277 
278         fIndices.clear();
279         fIndices.reserve(index_count);
280         for (size_t i = 0; i < n - 1; ++i) {
281             for (size_t j = 0; j < n - 1; ++j) {
282                 const auto row_0_idx = j*n + i,
283                            row_1_idx = row_0_idx + n,
284                            off_0 = (i + j) % 2,
285                            off_1 = 1 - off_0;
286 
287                 fIndices.push_back(row_0_idx +     0);
288                 fIndices.push_back(row_0_idx +     1);
289                 fIndices.push_back(row_1_idx + off_0);
290 
291                 fIndices.push_back(row_0_idx + off_1);
292                 fIndices.push_back(row_1_idx +     1);
293                 fIndices.push_back(row_1_idx +     0);
294             }
295         }
296 
297         SkASSERT(fIndices.size() == index_count);
298     }
299 
initShader(const ShaderFactory & fact)300     void initShader(const ShaderFactory& fact) {
301         fShader = fact.fBuild();
302         fCurrentShaderFactory = &fact;
303     }
304 
drawControls()305     void drawControls() {
306         ImGui::Begin("Mesh Options");
307 
308         if (ImGui::BeginCombo("Texture", fCurrentShaderFactory->fName)) {
309             for (const auto& fact : gShaderFactories) {
310                 const auto is_selected = (fCurrentShaderFactory->fName == fact.fName);
311                 if (ImGui::Selectable(fact.fName) && !is_selected) {
312                     this->initShader(fact);
313                 }
314                 if (is_selected) {
315                     ImGui::SetItemDefaultFocus();
316                 }
317             }
318             ImGui::EndCombo();
319         }
320 
321         if (ImGui::BeginCombo("Animator", fCurrentAnimator->fName)) {
322             for (const auto& anim : gVertexAnimators) {
323                 const auto is_selected = (fCurrentAnimator->fName == anim.fName);
324                 if (ImGui::Selectable(anim.fName) && !is_selected) {
325                     fCurrentAnimator = &anim;
326                     fTimeBase = 0;
327                 }
328                 if (is_selected) {
329                     ImGui::SetItemDefaultFocus();
330                 }
331             }
332             ImGui::EndCombo();
333         }
334 
335         static constexpr struct {
336             const char* fLabel;
337             size_t      fCount;
338         } gSizeInfo[] = {
339             {     "4x4",    16 },
340             {     "8x8",    64 },
341             {   "16x16",   256 },
342             {   "32x32",  1024 },
343             {   "64x64",  4096 },
344             { "128x128", 16384 },
345         };
346         ImGui::SliderInt("Mesh Size",
347                          &fMeshSizeSelector,
348                          0, std::size(gSizeInfo) - 1,
349                          gSizeInfo[fMeshSizeSelector].fLabel);
350         if (fVertices.size() != gSizeInfo[fMeshSizeSelector].fCount) {
351             this->initMesh(gSizeInfo[fMeshSizeSelector].fCount);
352         }
353 
354         ImGui::SliderFloat("Speed", &fAnimationSpeed, 0.25, 4, "%.2f");
355 
356         ImGui::Checkbox("Show mesh", &fShowMesh);
357 
358         ImGui::End();
359     }
360 
361     SkSize                  fSize;
362     sk_sp<SkShader>         fShader;
363     std::vector<SkPoint>    fVertices,
364                             fUVs;
365     std::vector<SkColor>    fColors;
366     std::vector<uint16_t>   fIndices;
367 
368     double                  fTimeBase = 0;
369     const SkCubicMap        fTimeMapper;
370 
371     // UI stuff
372     const ShaderFactory*    fCurrentShaderFactory = &gShaderFactories[0];
373     const VertexAnimator*   fCurrentAnimator      = &gVertexAnimators[0];
374     int                     fMeshSizeSelector     = 2;
375     float                   fAnimationSpeed       = 1.f;
376     bool                    fShowMesh             = false;
377 };
378 
379 }  // anonymous namespace
380 
381 DEF_SLIDE(return new MeshSlide{};)
382