• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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();
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__anon1ba6a13e0111::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__anon1ba6a13e0111::PatchView236     SkString name() override { return SkString("Patch"); }
237 
onDrawContent__anon1ba6a13e0111::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         paint.setFilterQuality(kLow_SkFilterQuality);
245 
246         canvas->translate(DX, DY);
247 
248         Patch   patch;
249 
250         paint.setShader(fShader0);
251         if (fSize0.fX == 0) {
252             fSize0.fX = 1;
253         }
254         if (fSize0.fY == 0) {
255             fSize0.fY = 1;
256         }
257         patch.setBounds(fSize0.fX, fSize0.fY);
258 
259         patch.setPatch(fPts);
260         drawpatches(canvas, paint, nu, nv, &patch);
261 
262         paint.setShader(nullptr);
263         paint.setAntiAlias(true);
264         paint.setStrokeWidth(SkIntToScalar(5));
265         canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(fPts), fPts, paint);
266 
267         canvas->translate(0, SkIntToScalar(300));
268 
269         paint.setAntiAlias(false);
270         paint.setShader(fShader1);
271         {
272             SkMatrix m;
273             m.setSkew(1, 0);
274             paint.setShader(paint.getShader()->makeWithLocalMatrix(m));
275         }
276         {
277             SkMatrix m;
278             m.setRotate(fAngle);
279             paint.setShader(paint.getShader()->makeWithLocalMatrix(m));
280         }
281         patch.setBounds(fSize1.fX, fSize1.fY);
282         drawpatches(canvas, paint, nu, nv, &patch);
283     }
284 
onAnimate__anon1ba6a13e0111::PatchView285     bool onAnimate(double nanos) override {
286         fAngle = TimeUtils::Scaled(1e-9 * nanos, 60, 360);
287         return true;
288     }
289 
290     class PtClick : public Click {
291     public:
292         int fIndex;
PtClick(int index)293         PtClick(int index) : fIndex(index) {}
294     };
295 
hittest__anon1ba6a13e0111::PatchView296     static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
297         return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5);
298     }
299 
onFindClickHandler__anon1ba6a13e0111::PatchView300     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, ModifierKey modi) override {
301         x -= DX;
302         y -= DY;
303         for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
304             if (hittest(fPts[i], x, y)) {
305                 return new PtClick((int)i);
306             }
307         }
308         return nullptr;
309     }
310 
onClick__anon1ba6a13e0111::PatchView311     bool onClick(Click* click) override {
312         fPts[((PtClick*)click)->fIndex].set(click->fCurr.fX - DX, click->fCurr.fY - DY);
313         return true;
314     }
315 
316 private:
317     typedef Sample INHERITED;
318 };
319 }  // namespace
320 DEF_SAMPLE( return new PatchView(); )
321 
322 //////////////////////////////////////////////////////////////////////////////
323 
324 namespace {
make_verts(const SkPath & path,SkScalar width)325 static sk_sp<SkVertices> make_verts(const SkPath& path, SkScalar width) {
326     auto meas = SkContourMeasureIter(path, false).next();
327     if (!meas) {
328         return nullptr;
329     }
330 
331     const SkPoint src[2] = {
332         { 0, -width/2 }, { 0, width/2 },
333     };
334     SkTDArray<SkPoint> pts;
335 
336     const SkScalar step = 2;
337     for (SkScalar distance = 0; distance < meas->length(); distance += step) {
338         SkMatrix mx;
339         if (!meas->getMatrix(distance, &mx)) {
340             continue;
341         }
342         SkPoint* dst = pts.append(2);
343         mx.mapPoints(dst, src, 2);
344     }
345 
346     int vertCount = pts.count();
347     int indexCount = 0; // no texture
348     unsigned flags = SkVertices::kHasColors_BuilderFlag |
349                      SkVertices::kIsNonVolatile_BuilderFlag;
350     SkVertices::Builder builder(SkVertices::kTriangleStrip_VertexMode,
351                                 vertCount, indexCount, flags);
352     memcpy(builder.positions(), pts.begin(), vertCount * sizeof(SkPoint));
353     SkRandom rand;
354     for (int i = 0; i < vertCount; ++i) {
355         builder.colors()[i] = rand.nextU() | 0xFF000000;
356     }
357     SkDebugf("vert count = %d\n", vertCount);
358 
359     return builder.detach();
360 }
361 
362 class PseudoInkView : public Sample {
363     enum { N = 100 };
364     SkPath            fPath;
365     sk_sp<SkVertices> fVertices[N];
366     SkPaint           fSkeletonP, fStrokeP, fVertsP;
367     bool              fDirty = true;
368 
369 public:
PseudoInkView()370     PseudoInkView() {
371         fSkeletonP.setStyle(SkPaint::kStroke_Style);
372         fSkeletonP.setAntiAlias(true);
373 
374         fStrokeP.setStyle(SkPaint::kStroke_Style);
375         fStrokeP.setStrokeWidth(30);
376         fStrokeP.setColor(0x44888888);
377     }
378 
379 protected:
name()380     SkString name() override { return SkString("PseudoInk"); }
381 
onAnimate(double nanos)382     bool onAnimate(double nanos) override { return true; }
383 
onDrawContent(SkCanvas * canvas)384     void onDrawContent(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 
onFindClickHandler(SkScalar x,SkScalar y,ModifierKey modi)399     Click* onFindClickHandler(SkScalar x, SkScalar y, ModifierKey modi) override {
400         Click* click = new Click();
401         fPath.reset();
402         fPath.moveTo(x, y);
403         return click;
404     }
405 
onClick(Click * click)406     bool onClick(Click* click) override {
407         switch (click->fState) {
408             case InputState::kMove:
409                 fPath.lineTo(click->fCurr);
410                 fDirty = true;
411                 break;
412             default:
413                 break;
414         }
415         return true;
416     }
417 
418 private:
419     typedef Sample INHERITED;
420 };
421 }  // namespace
422 DEF_SAMPLE( return new PseudoInkView(); )
423 
424 namespace {
425 // Show stroking options using patheffects (and pathops)
426 // and why strokeandfill is a hacks
427 class ManyStrokesView : public Sample {
428     SkPath              fPath;
429     sk_sp<SkPathEffect> fPE[6];
430 
431 public:
ManyStrokesView()432     ManyStrokesView() {
433         fPE[0] = SkStrokePathEffect::Make(20, SkPaint::kRound_Join, SkPaint::kRound_Cap);
434 
435         auto p0 = SkStrokePathEffect::Make(25, SkPaint::kRound_Join, SkPaint::kRound_Cap);
436         auto p1 = SkStrokePathEffect::Make(20, SkPaint::kRound_Join, SkPaint::kRound_Cap);
437         fPE[1] = SkMergePathEffect::Make(p0, p1, SkPathOp::kDifference_SkPathOp);
438 
439         fPE[2] = SkMergePathEffect::Make(nullptr, p1, SkPathOp::kDifference_SkPathOp);
440         fPE[3] = SkMergePathEffect::Make(nullptr, p1, SkPathOp::kUnion_SkPathOp);
441         fPE[4] = SkMergePathEffect::Make(p0, nullptr, SkPathOp::kDifference_SkPathOp);
442         fPE[5] = SkMergePathEffect::Make(p0, nullptr, SkPathOp::kIntersect_SkPathOp);
443     }
444 
445 protected:
name()446     SkString name() override { return SkString("ManyStrokes"); }
447 
onAnimate(double nanos)448     bool onAnimate(double nanos) override { return true; }
449 
dodraw(SkCanvas * canvas,sk_sp<SkPathEffect> pe,SkScalar x,SkScalar y,const SkPaint * ptr=nullptr)450     void dodraw(SkCanvas* canvas, sk_sp<SkPathEffect> pe, SkScalar x, SkScalar y,
451                 const SkPaint* ptr = nullptr) {
452         SkPaint paint;
453         paint.setAntiAlias(true);
454         paint.setPathEffect(pe);
455         canvas->save();
456         canvas->translate(x, y);
457         canvas->drawPath(fPath, ptr ? *ptr : paint);
458 
459         paint.setPathEffect(nullptr);
460         paint.setStyle(SkPaint::kStroke_Style);
461         paint.setColor(SK_ColorGREEN);
462         canvas->drawPath(fPath, paint);
463 
464         canvas->restore();
465     }
466 
onDrawContent(SkCanvas * canvas)467     void onDrawContent(SkCanvas* canvas) override {
468         SkPaint p;
469         p.setColor(0);
470         this->dodraw(canvas, nullptr, 0, 0, &p);
471 
472         this->dodraw(canvas, fPE[0], 300, 0);
473         this->dodraw(canvas, fPE[1], 0, 300);
474         this->dodraw(canvas, fPE[2], 300, 300);
475         this->dodraw(canvas, fPE[3], 600, 300);
476         this->dodraw(canvas, fPE[4], 900, 0);
477         this->dodraw(canvas, fPE[5], 900, 300);
478 
479         p.setColor(SK_ColorBLACK);
480         p.setStyle(SkPaint::kStrokeAndFill_Style);
481         p.setStrokeJoin(SkPaint::kRound_Join);
482         p.setStrokeCap(SkPaint::kRound_Cap);
483         p.setStrokeWidth(20);
484         this->dodraw(canvas, nullptr, 600, 0, &p);
485     }
486 
onFindClickHandler(SkScalar x,SkScalar y,ModifierKey modi)487     Click* onFindClickHandler(SkScalar x, SkScalar y, ModifierKey modi) override {
488         Click* click = new Click();
489         fPath.reset();
490         fPath.moveTo(x, y);
491         return click;
492     }
493 
onClick(Click * click)494     bool onClick(Click* click) override {
495         switch (click->fState) {
496             case InputState::kMove:
497                 fPath.lineTo(click->fCurr);
498                 break;
499             default:
500                 break;
501         }
502         return true;
503     }
504 
505 private:
506     typedef Sample INHERITED;
507 };
508 }  // namespace
509 DEF_SAMPLE( return new ManyStrokesView(); )
510