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/SkCanvas.h"
9 #include "include/core/SkColorFilter.h"
10 #include "include/core/SkColorPriv.h"
11 #include "include/core/SkContourMeasure.h"
12 #include "include/core/SkGraphics.h"
13 #include "include/core/SkPath.h"
14 #include "include/core/SkRegion.h"
15 #include "include/core/SkShader.h"
16 #include "include/core/SkStream.h"
17 #include "include/core/SkTime.h"
18 #include "include/core/SkTypeface.h"
19 #include "include/core/SkVertices.h"
20 #include "include/effects/SkGradientShader.h"
21 #include "include/effects/SkOpPathEffect.h"
22 #include "include/private/SkTDArray.h"
23 #include "include/utils/SkRandom.h"
24 #include "samplecode/DecodeFile.h"
25 #include "samplecode/Sample.h"
26 #include "src/core/SkGeometry.h"
27 #include "src/core/SkOSFile.h"
28 #include "src/utils/SkUTF.h"
29 #include "tools/Resources.h"
30 #include "tools/timer/TimeUtils.h"
31
32 namespace {
make_shader0(SkIPoint * size)33 static sk_sp<SkShader> make_shader0(SkIPoint* size) {
34 SkBitmap bm;
35 decode_file(GetResourceAsData("images/dog.jpg"), &bm);
36 *size = SkIPoint{bm.width(), bm.height()};
37 return bm.makeShader(SkSamplingOptions(SkFilterMode::kLinear));
38 }
39
make_shader1(const SkIPoint & size)40 static sk_sp<SkShader> make_shader1(const SkIPoint& size) {
41 SkPoint pts[] = { { 0, 0, },
42 { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
43 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
44 return SkGradientShader::MakeLinear(pts, colors, nullptr,
45 SK_ARRAY_COUNT(colors), SkTileMode::kMirror);
46 }
47
48 class Patch {
49 public:
Patch()50 Patch() { sk_bzero(fPts, sizeof(fPts)); }
~Patch()51 ~Patch() {}
52
setPatch(const SkPoint pts[12])53 void setPatch(const SkPoint pts[12]) {
54 memcpy(fPts, pts, 12 * sizeof(SkPoint));
55 fPts[12] = pts[0]; // the last shall be first
56 }
setBounds(int w,int h)57 void setBounds(int w, int h) { fW = w; fH = h; }
58
59 void draw(SkCanvas*, const SkPaint&, int segsU, int segsV,
60 bool doTextures, bool doColors);
61
62 private:
63 SkPoint fPts[13];
64 int fW, fH;
65 };
66
eval_patch_edge(const SkPoint cubic[],SkPoint samples[],int segs)67 static void eval_patch_edge(const SkPoint cubic[], SkPoint samples[], int segs) {
68 SkScalar t = 0;
69 SkScalar dt = SK_Scalar1 / segs;
70
71 samples[0] = cubic[0];
72 for (int i = 1; i < segs; i++) {
73 t += dt;
74 SkEvalCubicAt(cubic, t, &samples[i], nullptr, nullptr);
75 }
76 }
77
eval_sheet(const SkPoint edge[],int nu,int nv,int iu,int iv,SkPoint * pt)78 static void eval_sheet(const SkPoint edge[], int nu, int nv, int iu, int iv,
79 SkPoint* pt) {
80 const int TL = 0;
81 const int TR = nu;
82 const int BR = TR + nv;
83 const int BL = BR + nu;
84
85 SkScalar u = SkIntToScalar(iu) / nu;
86 SkScalar v = SkIntToScalar(iv) / nv;
87
88 SkScalar uv = u * v;
89 SkScalar Uv = (1 - u) * v;
90 SkScalar uV = u * (1 - v);
91 SkScalar UV = (1 - u) * (1 - v);
92
93 SkScalar x0 = UV * edge[TL].fX + uV * edge[TR].fX + Uv * edge[BL].fX + uv * edge[BR].fX;
94 SkScalar y0 = UV * edge[TL].fY + uV * edge[TR].fY + Uv * edge[BL].fY + uv * edge[BR].fY;
95
96 SkScalar x = (1 - v) * edge[TL+iu].fX + u * edge[TR+iv].fX +
97 v * edge[BR+nu-iu].fX + (1 - u) * edge[BL+nv-iv].fX - x0;
98 SkScalar y = (1 - v) * edge[TL+iu].fY + u * edge[TR+iv].fY +
99 v * edge[BR+nu-iu].fY + (1 - u) * edge[BL+nv-iv].fY - y0;
100 pt->set(x, y);
101 }
102
make_color(SkScalar s,SkScalar t)103 static SkColor make_color(SkScalar s, SkScalar t) {
104 return SkColorSetARGB(0xFF, SkUnitScalarClampToByte(s), SkUnitScalarClampToByte(t), 0);
105 }
106
draw(SkCanvas * canvas,const SkPaint & paint,int nu,int nv,bool doTextures,bool doColors)107 void Patch::draw(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
108 bool doTextures, bool doColors) {
109 if (nu < 1 || nv < 1) {
110 return;
111 }
112
113 int i, npts = (nu + nv) * 2;
114 SkAutoSTMalloc<16, SkPoint> storage(npts + 1);
115 SkPoint* edge0 = storage.get();
116 SkPoint* edge1 = edge0 + nu;
117 SkPoint* edge2 = edge1 + nv;
118 SkPoint* edge3 = edge2 + nu;
119
120 // evaluate the edge points
121 eval_patch_edge(fPts + 0, edge0, nu);
122 eval_patch_edge(fPts + 3, edge1, nv);
123 eval_patch_edge(fPts + 6, edge2, nu);
124 eval_patch_edge(fPts + 9, edge3, nv);
125 edge3[nv] = edge0[0]; // the last shall be first
126
127 for (i = 0; i < npts; i++) {
128 // canvas->drawLine(edge0[i].fX, edge0[i].fY, edge0[i+1].fX, edge0[i+1].fY, paint);
129 }
130
131 int row, vertCount = (nu + 1) * (nv + 1);
132 SkAutoTMalloc<SkPoint> vertStorage(vertCount);
133 SkPoint* verts = vertStorage.get();
134
135 // first row
136 memcpy(verts, edge0, (nu + 1) * sizeof(SkPoint));
137 // rows
138 SkPoint* r = verts;
139 for (row = 1; row < nv; row++) {
140 r += nu + 1;
141 r[0] = edge3[nv - row];
142 for (int col = 1; col < nu; col++) {
143 eval_sheet(edge0, nu, nv, col, row, &r[col]);
144 }
145 r[nu] = edge1[row];
146 }
147 // last row
148 SkPoint* last = verts + nv * (nu + 1);
149 for (i = 0; i <= nu; i++) {
150 last[i] = edge2[nu - i];
151 }
152
153 // canvas->drawPoints(verts, vertCount, paint);
154
155 int stripCount = (nu + 1) * 2;
156 SkAutoTMalloc<SkPoint> stripStorage(stripCount * 2);
157 SkAutoTMalloc<SkColor> colorStorage(stripCount);
158 SkPoint* strip = stripStorage.get();
159 SkPoint* tex = strip + stripCount;
160 SkColor* colors = colorStorage.get();
161 SkScalar t = 0;
162 const SkScalar ds = SK_Scalar1 * fW / nu;
163 const SkScalar dt = SK_Scalar1 * fH / nv;
164 r = verts;
165 for (row = 0; row < nv; row++) {
166 SkPoint* upper = r;
167 SkPoint* lower = r + nu + 1;
168 r = lower;
169 SkScalar s = 0;
170 for (i = 0; i <= nu; i++) {
171 strip[i*2 + 0] = *upper++;
172 strip[i*2 + 1] = *lower++;
173 tex[i*2 + 0].set(s, t);
174 tex[i*2 + 1].set(s, t + dt);
175 colors[i*2 + 0] = make_color(s/fW, t/fH);
176 colors[i*2 + 1] = make_color(s/fW, (t + dt)/fH);
177 s += ds;
178 }
179 t += dt;
180 canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleStrip_VertexMode, stripCount,
181 strip, doTextures ? tex : nullptr,
182 doColors ? colors : nullptr),
183 SkBlendMode::kModulate, paint);
184 }
185 }
186
drawpatches(SkCanvas * canvas,const SkPaint & paint,int nu,int nv,Patch * patch)187 static void drawpatches(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
188 Patch* patch) {
189 SkAutoCanvasRestore ar(canvas, true);
190
191 patch->draw(canvas, paint, nu, nv, false, false);
192 canvas->translate(SkIntToScalar(180), 0);
193 patch->draw(canvas, paint, nu, nv, true, false);
194 canvas->translate(SkIntToScalar(180), 0);
195 patch->draw(canvas, paint, nu, nv, false, true);
196 canvas->translate(SkIntToScalar(180), 0);
197 patch->draw(canvas, paint, nu, nv, true, true);
198 }
199
200 static constexpr SkScalar DX = 20;
201 static constexpr SkScalar DY = 0;
202 static constexpr SkScalar kS = 50;
203 static constexpr SkScalar kT = 40;
204
205 struct PatchView : public Sample {
206 sk_sp<SkShader> fShader0;
207 sk_sp<SkShader> fShader1;
208 SkScalar fAngle = 0;
209 SkIPoint fSize0 = {0, 0},
210 fSize1 = {0, 0};
211 SkPoint fPts[12] = {
212 {kS * 0, kT * 1},
213 {kS * 1, kT * 1},
214 {kS * 2, kT * 1},
215 {kS * 3, kT * 1},
216 {kS * 3, kT * 2},
217 {kS * 3, kT * 3},
218 {kS * 3, kT * 4},
219 {kS * 2, kT * 4},
220 {kS * 1, kT * 4},
221 {kS * 0, kT * 4},
222 {kS * 0, kT * 3},
223 {kS * 0, kT * 2},
224 };
225
onOnceBeforeDraw__anon4596c1490111::PatchView226 void onOnceBeforeDraw() override {
227 fShader0 = make_shader0(&fSize0);
228 fSize1 = fSize0;
229 if (fSize0.fX == 0 || fSize0.fY == 0) {
230 fSize1.set(2, 2);
231 }
232 fShader1 = make_shader1(fSize1);
233 this->setBGColor(SK_ColorGRAY);
234 }
235
name__anon4596c1490111::PatchView236 SkString name() override { return SkString("Patch"); }
237
onDrawContent__anon4596c1490111::PatchView238 void onDrawContent(SkCanvas* canvas) override {
239 const int nu = 10;
240 const int nv = 10;
241
242 SkPaint paint;
243 paint.setDither(true);
244
245 canvas->translate(DX, DY);
246
247 Patch patch;
248
249 paint.setShader(fShader0);
250 if (fSize0.fX == 0) {
251 fSize0.fX = 1;
252 }
253 if (fSize0.fY == 0) {
254 fSize0.fY = 1;
255 }
256 patch.setBounds(fSize0.fX, fSize0.fY);
257
258 patch.setPatch(fPts);
259 drawpatches(canvas, paint, nu, nv, &patch);
260
261 paint.setShader(nullptr);
262 paint.setAntiAlias(true);
263 paint.setStrokeWidth(SkIntToScalar(5));
264 canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(fPts), fPts, paint);
265
266 canvas->translate(0, SkIntToScalar(300));
267
268 paint.setAntiAlias(false);
269 paint.setShader(fShader1);
270 {
271 SkMatrix m;
272 m.setSkew(1, 0);
273 paint.setShader(paint.getShader()->makeWithLocalMatrix(m));
274 }
275 {
276 SkMatrix m;
277 m.setRotate(fAngle);
278 paint.setShader(paint.getShader()->makeWithLocalMatrix(m));
279 }
280 patch.setBounds(fSize1.fX, fSize1.fY);
281 drawpatches(canvas, paint, nu, nv, &patch);
282 }
283
onAnimate__anon4596c1490111::PatchView284 bool onAnimate(double nanos) override {
285 fAngle = TimeUtils::Scaled(1e-9 * nanos, 60, 360);
286 return true;
287 }
288
289 class PtClick : public Click {
290 public:
291 int fIndex;
PtClick(int index)292 PtClick(int index) : fIndex(index) {}
293 };
294
hittest__anon4596c1490111::PatchView295 static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
296 return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5);
297 }
298
onFindClickHandler__anon4596c1490111::PatchView299 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
300 x -= DX;
301 y -= DY;
302 for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
303 if (hittest(fPts[i], x, y)) {
304 return new PtClick((int)i);
305 }
306 }
307 return nullptr;
308 }
309
onClick__anon4596c1490111::PatchView310 bool onClick(Click* click) override {
311 fPts[((PtClick*)click)->fIndex].set(click->fCurr.fX - DX, click->fCurr.fY - DY);
312 return true;
313 }
314
315 private:
316 using INHERITED = Sample;
317 };
318 } // namespace
319 DEF_SAMPLE( return new PatchView(); )
320
321 //////////////////////////////////////////////////////////////////////////////
322
323 namespace {
make_verts(const SkPath & path,SkScalar width)324 static sk_sp<SkVertices> make_verts(const SkPath& path, SkScalar width) {
325 auto meas = SkContourMeasureIter(path, false).next();
326 if (!meas) {
327 return nullptr;
328 }
329
330 const SkPoint src[2] = {
331 { 0, -width/2 }, { 0, width/2 },
332 };
333 SkTDArray<SkPoint> pts;
334
335 const SkScalar step = 2;
336 for (SkScalar distance = 0; distance < meas->length(); distance += step) {
337 SkMatrix mx;
338 if (!meas->getMatrix(distance, &mx)) {
339 continue;
340 }
341 SkPoint* dst = pts.append(2);
342 mx.mapPoints(dst, src, 2);
343 }
344
345 int vertCount = pts.count();
346 int indexCount = 0; // no texture
347 unsigned flags = SkVertices::kHasColors_BuilderFlag;
348 SkVertices::Builder builder(SkVertices::kTriangleStrip_VertexMode,
349 vertCount, indexCount, flags);
350 memcpy(builder.positions(), pts.begin(), vertCount * sizeof(SkPoint));
351 SkRandom rand;
352 for (int i = 0; i < vertCount; ++i) {
353 builder.colors()[i] = rand.nextU() | 0xFF000000;
354 }
355 SkDebugf("vert count = %d\n", vertCount);
356
357 return builder.detach();
358 }
359
360 class PseudoInkView : public Sample {
361 enum { N = 100 };
362 SkPath fPath;
363 sk_sp<SkVertices> fVertices[N];
364 SkPaint fSkeletonP, fStrokeP, fVertsP;
365 bool fDirty = true;
366
367 public:
PseudoInkView()368 PseudoInkView() {
369 fSkeletonP.setStyle(SkPaint::kStroke_Style);
370 fSkeletonP.setAntiAlias(true);
371
372 fStrokeP.setStyle(SkPaint::kStroke_Style);
373 fStrokeP.setStrokeWidth(30);
374 fStrokeP.setColor(0x44888888);
375 }
376
377 protected:
name()378 SkString name() override { return SkString("PseudoInk"); }
379
onAnimate(double nanos)380 bool onAnimate(double nanos) override { return true; }
381
onDrawContent(SkCanvas * canvas)382 void onDrawContent(SkCanvas* canvas) override {
383 if (fDirty) {
384 for (int i = 0; i < N; ++i) {
385 fVertices[i] = make_verts(fPath, 30);
386 }
387 fDirty = false;
388 }
389 for (int i = 0; i < N; ++i) {
390 canvas->drawVertices(fVertices[i], SkBlendMode::kSrc, fVertsP);
391 canvas->translate(1, 1);
392 }
393 // canvas->drawPath(fPath, fStrokeP);
394 // canvas->drawPath(fPath, fSkeletonP);
395 }
396
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)397 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
398 Click* click = new Click();
399 fPath.reset();
400 fPath.moveTo(x, y);
401 return click;
402 }
403
onClick(Click * click)404 bool onClick(Click* click) override {
405 switch (click->fState) {
406 case skui::InputState::kMove:
407 fPath.lineTo(click->fCurr);
408 fDirty = true;
409 break;
410 default:
411 break;
412 }
413 return true;
414 }
415
416 private:
417 using INHERITED = Sample;
418 };
419 } // namespace
420 DEF_SAMPLE( return new PseudoInkView(); )
421
422 namespace {
423 // Show stroking options using patheffects (and pathops)
424 // and why strokeandfill is a hacks
425 class ManyStrokesView : public Sample {
426 SkPath fPath;
427 sk_sp<SkPathEffect> fPE[6];
428
429 public:
ManyStrokesView()430 ManyStrokesView() {
431 fPE[0] = SkStrokePathEffect::Make(20, SkPaint::kRound_Join, SkPaint::kRound_Cap);
432
433 auto p0 = SkStrokePathEffect::Make(25, SkPaint::kRound_Join, SkPaint::kRound_Cap);
434 auto p1 = SkStrokePathEffect::Make(20, SkPaint::kRound_Join, SkPaint::kRound_Cap);
435 fPE[1] = SkMergePathEffect::Make(p0, p1, SkPathOp::kDifference_SkPathOp);
436
437 fPE[2] = SkMergePathEffect::Make(nullptr, p1, SkPathOp::kDifference_SkPathOp);
438 fPE[3] = SkMergePathEffect::Make(nullptr, p1, SkPathOp::kUnion_SkPathOp);
439 fPE[4] = SkMergePathEffect::Make(p0, nullptr, SkPathOp::kDifference_SkPathOp);
440 fPE[5] = SkMergePathEffect::Make(p0, nullptr, SkPathOp::kIntersect_SkPathOp);
441 }
442
443 protected:
name()444 SkString name() override { return SkString("ManyStrokes"); }
445
onAnimate(double nanos)446 bool onAnimate(double nanos) override { return true; }
447
dodraw(SkCanvas * canvas,sk_sp<SkPathEffect> pe,SkScalar x,SkScalar y,const SkPaint * ptr=nullptr)448 void dodraw(SkCanvas* canvas, sk_sp<SkPathEffect> pe, SkScalar x, SkScalar y,
449 const SkPaint* ptr = nullptr) {
450 SkPaint paint;
451 paint.setAntiAlias(true);
452 paint.setPathEffect(pe);
453 canvas->save();
454 canvas->translate(x, y);
455 canvas->drawPath(fPath, ptr ? *ptr : paint);
456
457 paint.setPathEffect(nullptr);
458 paint.setStyle(SkPaint::kStroke_Style);
459 paint.setColor(SK_ColorGREEN);
460 canvas->drawPath(fPath, paint);
461
462 canvas->restore();
463 }
464
onDrawContent(SkCanvas * canvas)465 void onDrawContent(SkCanvas* canvas) override {
466 SkPaint p;
467 p.setColor(0);
468 this->dodraw(canvas, nullptr, 0, 0, &p);
469
470 this->dodraw(canvas, fPE[0], 300, 0);
471 this->dodraw(canvas, fPE[1], 0, 300);
472 this->dodraw(canvas, fPE[2], 300, 300);
473 this->dodraw(canvas, fPE[3], 600, 300);
474 this->dodraw(canvas, fPE[4], 900, 0);
475 this->dodraw(canvas, fPE[5], 900, 300);
476
477 p.setColor(SK_ColorBLACK);
478 p.setStyle(SkPaint::kStrokeAndFill_Style);
479 p.setStrokeJoin(SkPaint::kRound_Join);
480 p.setStrokeCap(SkPaint::kRound_Cap);
481 p.setStrokeWidth(20);
482 this->dodraw(canvas, nullptr, 600, 0, &p);
483 }
484
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)485 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
486 Click* click = new Click();
487 fPath.reset();
488 fPath.moveTo(x, y);
489 return click;
490 }
491
onClick(Click * click)492 bool onClick(Click* click) override {
493 switch (click->fState) {
494 case skui::InputState::kMove:
495 fPath.lineTo(click->fCurr);
496 break;
497 default:
498 break;
499 }
500 return true;
501 }
502
503 private:
504 using INHERITED = Sample;
505 };
506 } // namespace
507 DEF_SAMPLE( return new ManyStrokesView(); )
508