xref: /aosp_15_r20/external/skia/tools/viewer/PatchSlide.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 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/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColorFilter.h"
11 #include "include/core/SkColorPriv.h"
12 #include "include/core/SkContourMeasure.h"
13 #include "include/core/SkGraphics.h"
14 #include "include/core/SkPath.h"
15 #include "include/core/SkPathEffect.h"
16 #include "include/core/SkRegion.h"
17 #include "include/core/SkShader.h"
18 #include "include/core/SkStream.h"
19 #include "include/core/SkTypeface.h"
20 #include "include/core/SkVertices.h"
21 #include "include/effects/SkGradientShader.h"
22 #include "include/private/base/SkTDArray.h"
23 #include "src/base/SkRandom.h"
24 #include "src/base/SkTime.h"
25 #include "src/base/SkUTF.h"
26 #include "src/core/SkGeometry.h"
27 #include "src/core/SkOSFile.h"
28 #include "tools/DecodeUtils.h"
29 #include "tools/Resources.h"
30 #include "tools/timer/TimeUtils.h"
31 #include "tools/viewer/ClickHandlerSlide.h"
32 #include "tools/viewer/Slide.h"
33 
34 namespace {
make_shader0(SkIPoint * size)35 static sk_sp<SkShader> make_shader0(SkIPoint* size) {
36     SkBitmap bm;
37     SkASSERT_RELEASE(ToolUtils::GetResourceAsBitmap("images/dog.jpg", &bm));
38     *size = SkIPoint{bm.width(), bm.height()};
39     return bm.makeShader(SkSamplingOptions(SkFilterMode::kLinear));
40 }
41 
make_shader1(const SkIPoint & size)42 static sk_sp<SkShader> make_shader1(const SkIPoint& size) {
43     SkPoint pts[] = { { 0, 0, },
44                       { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
45     SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
46     return SkGradientShader::MakeLinear(pts, colors, nullptr,
47                     std::size(colors), SkTileMode::kMirror);
48 }
49 
50 class Patch {
51 public:
Patch()52     Patch() { sk_bzero(fPts, sizeof(fPts)); }
~Patch()53     ~Patch() {}
54 
setPatch(const SkPoint pts[12])55     void setPatch(const SkPoint pts[12]) {
56         memcpy(fPts, pts, 12 * sizeof(SkPoint));
57         fPts[12] = pts[0];  // the last shall be first
58     }
setBounds(int w,int h)59     void setBounds(int w, int h) { fW = w; fH = h; }
60 
61     void draw(SkCanvas*, const SkPaint&, int segsU, int segsV,
62               bool doTextures, bool doColors);
63 
64 private:
65     SkPoint fPts[13];
66     int     fW, fH;
67 };
68 
eval_patch_edge(const SkPoint cubic[],SkPoint samples[],int segs)69 static void eval_patch_edge(const SkPoint cubic[], SkPoint samples[], int segs) {
70     SkScalar t = 0;
71     SkScalar dt = SK_Scalar1 / segs;
72 
73     samples[0] = cubic[0];
74     for (int i = 1; i < segs; i++) {
75         t += dt;
76         SkEvalCubicAt(cubic, t, &samples[i], nullptr, nullptr);
77     }
78 }
79 
eval_sheet(const SkPoint edge[],int nu,int nv,int iu,int iv,SkPoint * pt)80 static void eval_sheet(const SkPoint edge[], int nu, int nv, int iu, int iv,
81                        SkPoint* pt) {
82     const int TL = 0;
83     const int TR = nu;
84     const int BR = TR + nv;
85     const int BL = BR + nu;
86 
87     SkScalar u = SkIntToScalar(iu) / nu;
88     SkScalar v = SkIntToScalar(iv) / nv;
89 
90     SkScalar uv = u * v;
91     SkScalar Uv = (1 - u) * v;
92     SkScalar uV = u * (1 - v);
93     SkScalar UV = (1 - u) * (1 - v);
94 
95     SkScalar x0 = UV * edge[TL].fX + uV * edge[TR].fX + Uv * edge[BL].fX + uv * edge[BR].fX;
96     SkScalar y0 = UV * edge[TL].fY + uV * edge[TR].fY + Uv * edge[BL].fY + uv * edge[BR].fY;
97 
98     SkScalar x = (1 - v) * edge[TL+iu].fX + u * edge[TR+iv].fX +
99                  v * edge[BR+nu-iu].fX + (1 - u) * edge[BL+nv-iv].fX - x0;
100     SkScalar y = (1 - v) * edge[TL+iu].fY + u * edge[TR+iv].fY +
101                  v * edge[BR+nu-iu].fY + (1 - u) * edge[BL+nv-iv].fY - y0;
102     pt->set(x, y);
103 }
104 
make_color(SkScalar s,SkScalar t)105 static SkColor make_color(SkScalar s, SkScalar t) {
106     return SkColorSetARGB(0xFF, SkUnitScalarClampToByte(s), SkUnitScalarClampToByte(t), 0);
107 }
108 
draw(SkCanvas * canvas,const SkPaint & paint,int nu,int nv,bool doTextures,bool doColors)109 void Patch::draw(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
110                  bool doTextures, bool doColors) {
111     if (nu < 1 || nv < 1) {
112         return;
113     }
114 
115     int i, npts = (nu + nv) * 2;
116     std::unique_ptr<SkPoint[]> storage(new SkPoint[npts + 1]);
117     SkPoint* edge0 = storage.get();
118     SkPoint* edge1 = edge0 + nu;
119     SkPoint* edge2 = edge1 + nv;
120     SkPoint* edge3 = edge2 + nu;
121 
122     // evaluate the edge points
123     eval_patch_edge(fPts + 0, edge0, nu);
124     eval_patch_edge(fPts + 3, edge1, nv);
125     eval_patch_edge(fPts + 6, edge2, nu);
126     eval_patch_edge(fPts + 9, edge3, nv);
127     edge3[nv] = edge0[0];   // the last shall be first
128 
129     for (i = 0; i < npts; i++) {
130 //        canvas->drawLine(edge0[i].fX, edge0[i].fY, edge0[i+1].fX, edge0[i+1].fY, paint);
131     }
132 
133     int row, vertCount = (nu + 1) * (nv + 1);
134     std::unique_ptr<SkPoint[]>  vertStorage(new SkPoint[vertCount]);
135     SkPoint* verts = vertStorage.get();
136 
137     // first row
138     memcpy(verts, edge0, (nu + 1) * sizeof(SkPoint));
139     // rows
140     SkPoint* r = verts;
141     for (row = 1; row < nv; row++) {
142         r += nu + 1;
143         r[0] = edge3[nv - row];
144         for (int col = 1; col < nu; col++) {
145             eval_sheet(edge0, nu, nv, col, row, &r[col]);
146         }
147         r[nu] = edge1[row];
148     }
149     // last row
150     SkPoint* last = verts + nv * (nu + 1);
151     for (i = 0; i <= nu; i++) {
152         last[i] = edge2[nu - i];
153     }
154 
155 //    canvas->drawPoints(verts, vertCount, paint);
156 
157     int stripCount = (nu + 1) * 2;
158     std::unique_ptr<SkPoint[]>  stripStorage(new SkPoint[stripCount * 2]);
159     std::unique_ptr<SkColor[]>  colorStorage(new SkColor[stripCount]);
160     SkPoint* strip = stripStorage.get();
161     SkPoint* tex = strip + stripCount;
162     SkColor* colors = colorStorage.get();
163     SkScalar t = 0;
164     const SkScalar ds = SK_Scalar1 * fW / nu;
165     const SkScalar dt = SK_Scalar1 * fH / nv;
166     r = verts;
167     for (row = 0; row < nv; row++) {
168         SkPoint* upper = r;
169         SkPoint* lower = r + nu + 1;
170         r = lower;
171         SkScalar s = 0;
172         for (i = 0; i <= nu; i++)  {
173             strip[i*2 + 0] = *upper++;
174             strip[i*2 + 1] = *lower++;
175             tex[i*2 + 0].set(s, t);
176             tex[i*2 + 1].set(s, t + dt);
177             colors[i*2 + 0] = make_color(s/fW, t/fH);
178             colors[i*2 + 1] = make_color(s/fW, (t + dt)/fH);
179             s += ds;
180         }
181         t += dt;
182         canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleStrip_VertexMode, stripCount,
183                                                   strip, doTextures ? tex : nullptr,
184                                                   doColors ? colors : nullptr),
185                              SkBlendMode::kModulate, paint);
186     }
187 }
188 
drawpatches(SkCanvas * canvas,const SkPaint & paint,int nu,int nv,Patch * patch)189 static void drawpatches(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
190                         Patch* patch) {
191     SkAutoCanvasRestore ar(canvas, true);
192 
193     patch->draw(canvas, paint, nu, nv, false, false);
194     canvas->translate(SkIntToScalar(180), 0);
195     patch->draw(canvas, paint, nu, nv, true, false);
196     canvas->translate(SkIntToScalar(180), 0);
197     patch->draw(canvas, paint, nu, nv, false, true);
198     canvas->translate(SkIntToScalar(180), 0);
199     patch->draw(canvas, paint, nu, nv, true, true);
200 }
201 
202 static constexpr SkScalar DX = 20;
203 static constexpr SkScalar DY = 0;
204 static constexpr SkScalar kS = 50;
205 static constexpr SkScalar kT = 40;
206 
207 struct PatchSlide : public ClickHandlerSlide {
208     sk_sp<SkShader> fShader0;
209     sk_sp<SkShader> fShader1;
210     SkScalar fAngle = 0;
211     SkIPoint fSize0 = {0, 0},
212              fSize1 = {0, 0};
213     SkPoint  fPts[12] = {
214         {kS * 0, kT * 1},
215         {kS * 1, kT * 1},
216         {kS * 2, kT * 1},
217         {kS * 3, kT * 1},
218         {kS * 3, kT * 2},
219         {kS * 3, kT * 3},
220         {kS * 3, kT * 4},
221         {kS * 2, kT * 4},
222         {kS * 1, kT * 4},
223         {kS * 0, kT * 4},
224         {kS * 0, kT * 3},
225         {kS * 0, kT * 2},
226     };
227 
228 public:
PatchSlide__anonb6045d310111::PatchSlide229     PatchSlide() { fName = "Patch"; }
230 
load__anonb6045d310111::PatchSlide231     void load(SkScalar w, SkScalar h) override {
232         fShader0 = make_shader0(&fSize0);
233         fSize1 = fSize0;
234         if (fSize0.fX == 0 || fSize0.fY == 0) {
235             fSize1.set(2, 2);
236         }
237         fShader1 = make_shader1(fSize1);
238     }
239 
240 
draw__anonb6045d310111::PatchSlide241     void draw(SkCanvas* canvas) override {
242         canvas->clear(SK_ColorGRAY);
243 
244         const int nu = 10;
245         const int nv = 10;
246 
247         SkPaint paint;
248         paint.setDither(true);
249 
250         canvas->translate(DX, DY);
251 
252         Patch   patch;
253 
254         paint.setShader(fShader0);
255         if (fSize0.fX == 0) {
256             fSize0.fX = 1;
257         }
258         if (fSize0.fY == 0) {
259             fSize0.fY = 1;
260         }
261         patch.setBounds(fSize0.fX, fSize0.fY);
262 
263         patch.setPatch(fPts);
264         drawpatches(canvas, paint, nu, nv, &patch);
265 
266         paint.setShader(nullptr);
267         paint.setAntiAlias(true);
268         paint.setStrokeWidth(SkIntToScalar(5));
269         canvas->drawPoints(SkCanvas::kPoints_PointMode, std::size(fPts), fPts, paint);
270 
271         canvas->translate(0, SkIntToScalar(300));
272 
273         paint.setAntiAlias(false);
274         paint.setShader(fShader1);
275         {
276             SkMatrix m;
277             m.setSkew(1, 0);
278             paint.setShader(paint.getShader()->makeWithLocalMatrix(m));
279         }
280         {
281             SkMatrix m;
282             m.setRotate(fAngle);
283             paint.setShader(paint.getShader()->makeWithLocalMatrix(m));
284         }
285         patch.setBounds(fSize1.fX, fSize1.fY);
286         drawpatches(canvas, paint, nu, nv, &patch);
287     }
288 
animate__anonb6045d310111::PatchSlide289     bool animate(double nanos) override {
290         fAngle = TimeUtils::Scaled(1e-9 * nanos, 60, 360);
291         return true;
292     }
293 
294 protected:
295     class PtClick : public Click {
296     public:
297         int fIndex;
PtClick(int index)298         PtClick(int index) : fIndex(index) {}
299     };
300 
onFindClickHandler__anonb6045d310111::PatchSlide301     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
302         x -= DX;
303         y -= DY;
304         for (size_t i = 0; i < std::size(fPts); i++) {
305             if (hittest(fPts[i], x, y)) {
306                 return new PtClick((int)i);
307             }
308         }
309         return nullptr;
310     }
311 
onClick__anonb6045d310111::PatchSlide312     bool onClick(Click* click) override {
313         fPts[((PtClick*)click)->fIndex].set(click->fCurr.fX - DX, click->fCurr.fY - DY);
314         return true;
315     }
316 
317 private:
hittest__anonb6045d310111::PatchSlide318     static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
319         return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5);
320     }
321 };
322 }  // namespace
323 DEF_SLIDE( return new PatchSlide(); )
324 
325 //////////////////////////////////////////////////////////////////////////////
326 
327 namespace {
make_verts(const SkPath & path,SkScalar width)328 static sk_sp<SkVertices> make_verts(const SkPath& path, SkScalar width) {
329     auto meas = SkContourMeasureIter(path, false).next();
330     if (!meas) {
331         return nullptr;
332     }
333 
334     const SkPoint src[2] = {
335         { 0, -width/2 }, { 0, width/2 },
336     };
337     SkTDArray<SkPoint> pts;
338 
339     const SkScalar step = 2;
340     for (SkScalar distance = 0; distance < meas->length(); distance += step) {
341         SkMatrix mx;
342         if (!meas->getMatrix(distance, &mx)) {
343             continue;
344         }
345         SkPoint* dst = pts.append(2);
346         mx.mapPoints(dst, src, 2);
347     }
348 
349     int vertCount = pts.size();
350     int indexCount = 0; // no texture
351     unsigned flags = SkVertices::kHasColors_BuilderFlag;
352     SkVertices::Builder builder(SkVertices::kTriangleStrip_VertexMode,
353                                 vertCount, indexCount, flags);
354     memcpy(builder.positions(), pts.begin(), vertCount * sizeof(SkPoint));
355     SkRandom rand;
356     for (int i = 0; i < vertCount; ++i) {
357         builder.colors()[i] = rand.nextU() | 0xFF000000;
358     }
359     SkDebugf("vert count = %d\n", vertCount);
360 
361     return builder.detach();
362 }
363 
364 class PseudoInkSlide : public ClickHandlerSlide {
365     enum { N = 100 };
366     SkPath            fPath;
367     sk_sp<SkVertices> fVertices[N];
368     SkPaint           fSkeletonP, fStrokeP, fVertsP;
369     bool              fDirty = true;
370 
371 public:
PseudoInkSlide()372     PseudoInkSlide() {
373         fSkeletonP.setStyle(SkPaint::kStroke_Style);
374         fSkeletonP.setAntiAlias(true);
375 
376         fStrokeP.setStyle(SkPaint::kStroke_Style);
377         fStrokeP.setStrokeWidth(30);
378         fStrokeP.setColor(0x44888888);
379         fName = "PseudoInk";
380     }
381 
animate(double nanos)382     bool animate(double nanos) override { return true; }
383 
draw(SkCanvas * canvas)384     void draw(SkCanvas* canvas) override {
385         if (fDirty) {
386             for (int i = 0; i < N; ++i) {
387                 fVertices[i] = make_verts(fPath, 30);
388             }
389             fDirty = false;
390         }
391         for (int i = 0; i < N; ++i) {
392             canvas->drawVertices(fVertices[i], SkBlendMode::kSrc, fVertsP);
393             canvas->translate(1, 1);
394         }
395 //        canvas->drawPath(fPath, fStrokeP);
396  //       canvas->drawPath(fPath, fSkeletonP);
397     }
398 
399 protected:
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)400     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
401         Click* click = new Click();
402         fPath.reset();
403         fPath.moveTo(x, y);
404         return click;
405     }
406 
onClick(Click * click)407     bool onClick(Click* click) override {
408         switch (click->fState) {
409             case skui::InputState::kMove:
410                 fPath.lineTo(click->fCurr);
411                 fDirty = true;
412                 break;
413             default:
414                 break;
415         }
416         return true;
417     }
418 };
419 }  // namespace
420 DEF_SLIDE( return new PseudoInkSlide(); )
421