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