• 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 "Sample.h"
9 #include "SkAnimTimer.h"
10 #include "SkBitmap.h"
11 #include "SkCanvas.h"
12 #include "SkGradientShader.h"
13 #include "SkGraphics.h"
14 #include "SkFont.h"
15 #include "SkPath.h"
16 #include "SkRegion.h"
17 #include "SkShader.h"
18 #include "SkUTF.h"
19 #include "SkColorPriv.h"
20 #include "SkColorFilter.h"
21 #include "SkParsePath.h"
22 #include "SkTime.h"
23 #include "SkTypeface.h"
24 
25 #include "SkGeometry.h"
26 
27 #include <stdlib.h>
28 
29 // http://code.google.com/p/skia/issues/detail?id=32
test_cubic()30 static void test_cubic() {
31     SkPoint src[4] = {
32         { 556.25000f, 523.03003f },
33         { 556.23999f, 522.96002f },
34         { 556.21997f, 522.89001f },
35         { 556.21997f, 522.82001f }
36     };
37     SkPoint dst[11];
38     dst[10].set(42, -42);   // one past the end, that we don't clobber these
39     SkScalar tval[] = { 0.33333334f, 0.99999994f };
40 
41     SkChopCubicAt(src, dst, tval, 2);
42 
43 #if 0
44     for (int i = 0; i < 11; i++) {
45         SkDebugf("--- %d [%g %g]\n", i, dst[i].fX, dst[i].fY);
46     }
47 #endif
48 }
49 
test_cubic2()50 static void test_cubic2() {
51     const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z";
52     SkPath path;
53     SkParsePath::FromSVGString(str, &path);
54 
55     {
56         SkRect r = path.getBounds();
57         SkIRect ir;
58         r.round(&ir);
59         SkDebugf("[%g %g %g %g] [%x %x %x %x]\n",
60                 SkScalarToDouble(r.fLeft), SkScalarToDouble(r.fTop),
61                 SkScalarToDouble(r.fRight), SkScalarToDouble(r.fBottom),
62                 ir.fLeft, ir.fTop, ir.fRight, ir.fBottom);
63     }
64 
65     SkBitmap bitmap;
66     bitmap.allocN32Pixels(300, 200);
67 
68     SkCanvas canvas(bitmap);
69     SkPaint paint;
70     paint.setAntiAlias(true);
71     canvas.drawPath(path, paint);
72 }
73 
74 class PathView : public Sample {
75     SkScalar fPrevSecs;
76 public:
77     SkScalar fDStroke, fStroke, fMinStroke, fMaxStroke;
78     SkPath fPath[6];
79     bool fShowHairline;
80     bool fOnce;
81 
PathView()82     PathView() {
83         fPrevSecs = 0;
84         fOnce = false;
85     }
86 
init()87     void init() {
88         if (fOnce) {
89             return;
90         }
91         fOnce = true;
92 
93         test_cubic();
94         test_cubic2();
95 
96         fShowHairline = false;
97 
98         fDStroke = 1;
99         fStroke = 10;
100         fMinStroke = 10;
101         fMaxStroke = 180;
102 
103         const SkScalar V = 85;
104 
105         fPath[0].moveTo(40, 70);
106         fPath[0].lineTo(70, 70 + SK_ScalarHalf);
107         fPath[0].lineTo(110, 70);
108 
109         fPath[1].moveTo(40, 70);
110         fPath[1].lineTo(70, 70 - SK_ScalarHalf);
111         fPath[1].lineTo(110, 70);
112 
113         fPath[2].moveTo(V, V);
114         fPath[2].lineTo(50, V);
115         fPath[2].lineTo(50, 50);
116 
117         fPath[3].moveTo(50, 50);
118         fPath[3].lineTo(50, V);
119         fPath[3].lineTo(V, V);
120 
121         fPath[4].moveTo(50, 50);
122         fPath[4].lineTo(50, V);
123         fPath[4].lineTo(52, 50);
124 
125         fPath[5].moveTo(52, 50);
126         fPath[5].lineTo(50, V);
127         fPath[5].lineTo(50, 50);
128 
129         this->setBGColor(0xFFDDDDDD);
130     }
131 
132 protected:
onQuery(Sample::Event * evt)133     bool onQuery(Sample::Event* evt) override {
134         if (Sample::TitleQ(*evt)) {
135             Sample::TitleR(evt, "Paths");
136             return true;
137         }
138         return this->INHERITED::onQuery(evt);
139     }
140 
drawPath(SkCanvas * canvas,const SkPath & path,SkPaint::Join j)141     void drawPath(SkCanvas* canvas, const SkPath& path, SkPaint::Join j) {
142         SkPaint paint;
143 
144         paint.setAntiAlias(true);
145         paint.setStyle(SkPaint::kStroke_Style);
146         paint.setStrokeJoin(j);
147         paint.setStrokeWidth(fStroke);
148 
149         if (fShowHairline) {
150             SkPath  fill;
151 
152             paint.getFillPath(path, &fill);
153             paint.setStrokeWidth(0);
154             canvas->drawPath(fill, paint);
155         } else {
156             canvas->drawPath(path, paint);
157         }
158 
159         paint.setColor(SK_ColorRED);
160         paint.setStrokeWidth(0);
161         canvas->drawPath(path, paint);
162     }
163 
onDrawContent(SkCanvas * canvas)164     void onDrawContent(SkCanvas* canvas) override {
165         this->init();
166         canvas->translate(50, 50);
167 
168         static const SkPaint::Join gJoins[] = {
169             SkPaint::kBevel_Join,
170             SkPaint::kMiter_Join,
171             SkPaint::kRound_Join
172         };
173 
174         for (size_t i = 0; i < SK_ARRAY_COUNT(gJoins); i++) {
175             canvas->save();
176             for (size_t j = 0; j < SK_ARRAY_COUNT(fPath); j++) {
177                 this->drawPath(canvas, fPath[j], gJoins[i]);
178                 canvas->translate(200, 0);
179             }
180             canvas->restore();
181 
182             canvas->translate(0, 200);
183         }
184     }
185 
onAnimate(const SkAnimTimer & timer)186     bool onAnimate(const SkAnimTimer& timer) override {
187         SkScalar currSecs = timer.scaled(100);
188         SkScalar delta = currSecs - fPrevSecs;
189         fPrevSecs = currSecs;
190 
191         fStroke += fDStroke * delta;
192         if (fStroke > fMaxStroke || fStroke < fMinStroke) {
193             fDStroke = -fDStroke;
194         }
195         return true;
196     }
197 
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)198     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
199         fShowHairline = !fShowHairline;
200         return this->INHERITED::onFindClickHandler(x, y, modi);
201     }
202 
203 private:
204     typedef Sample INHERITED;
205 };
206 DEF_SAMPLE( return new PathView; )
207 
208 //////////////////////////////////////////////////////////////////////////////
209 
210 #include "SkCornerPathEffect.h"
211 #include "SkRandom.h"
212 
213 class ArcToView : public Sample {
214     bool fDoFrame, fDoCorner, fDoConic;
215     SkPaint fPtsPaint, fSkeletonPaint, fCornerPaint;
216 public:
217     enum {
218         N = 4
219     };
220     SkPoint fPts[N];
221 
ArcToView()222     ArcToView()
223         : fDoFrame(false), fDoCorner(false), fDoConic(false)
224     {
225         SkRandom rand;
226         for (int i = 0; i < N; ++i) {
227             fPts[i].fX = 20 + rand.nextUScalar1() * 640;
228             fPts[i].fY = 20 + rand.nextUScalar1() * 480;
229         }
230 
231         const SkScalar rad = 50;
232 
233         fPtsPaint.setAntiAlias(true);
234         fPtsPaint.setStrokeWidth(15);
235         fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
236 
237         fCornerPaint.setAntiAlias(true);
238         fCornerPaint.setStyle(SkPaint::kStroke_Style);
239         fCornerPaint.setStrokeWidth(13);
240         fCornerPaint.setColor(SK_ColorGREEN);
241         fCornerPaint.setPathEffect(SkCornerPathEffect::Make(rad*2));
242 
243         fSkeletonPaint.setAntiAlias(true);
244         fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
245         fSkeletonPaint.setColor(SK_ColorRED);
246     }
247 
toggle(bool & value)248     void toggle(bool& value) {
249         value = !value;
250     }
251 
252 protected:
onQuery(Sample::Event * evt)253     bool onQuery(Sample::Event* evt) override {
254         if (Sample::TitleQ(*evt)) {
255             Sample::TitleR(evt, "ArcTo");
256             return true;
257         }
258         SkUnichar uni;
259         if (Sample::CharQ(*evt, &uni)) {
260             switch (uni) {
261                 case '1': this->toggle(fDoFrame); return true;
262                 case '2': this->toggle(fDoCorner); return true;
263                 case '3': this->toggle(fDoConic); return true;
264                 default: break;
265             }
266         }
267         return this->INHERITED::onQuery(evt);
268     }
269 
makePath(SkPath * path)270     void makePath(SkPath* path) {
271         path->moveTo(fPts[0]);
272         for (int i = 1; i < N; ++i) {
273             path->lineTo(fPts[i]);
274         }
275         if (!fDoFrame) {
276             path->close();
277         }
278     }
279 
onDrawContent(SkCanvas * canvas)280     void onDrawContent(SkCanvas* canvas) override {
281         canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
282 
283         SkPath path;
284         this->makePath(&path);
285 
286         if (fDoCorner) {
287             canvas->drawPath(path, fCornerPaint);
288         }
289 
290         canvas->drawPath(path, fSkeletonPaint);
291     }
292 
onClick(Click * click)293     bool onClick(Click* click) override {
294         int32_t index;
295         if (click->fMeta.findS32("index", &index)) {
296             SkASSERT((unsigned)index < N);
297             fPts[index] = click->fCurr;
298             return true;
299         }
300         return false;
301     }
302 
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)303     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
304         const SkScalar tol = 4;
305         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
306         for (int i = 0; i < N; ++i) {
307             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
308                 Click* click = new Click(this);
309                 click->fMeta.setS32("index", i);
310                 return click;
311             }
312         }
313         return this->INHERITED::onFindClickHandler(x, y, modi);
314     }
315 
316 private:
317     typedef Sample INHERITED;
318 };
319 DEF_SAMPLE( return new ArcToView; )
320 
321 /////////////
322 
323 class FatStroke : public Sample {
324     bool fClosed, fShowStroke, fShowHidden, fShowSkeleton;
325     int  fJoinType, fCapType;
326     float fWidth = 30;
327     SkPaint fPtsPaint, fHiddenPaint, fSkeletonPaint, fStrokePaint;
328 public:
329     enum {
330         N = 4
331     };
332     SkPoint fPts[N];
333 
FatStroke()334     FatStroke() : fClosed(false), fShowStroke(true), fShowHidden(false), fShowSkeleton(true),
335                   fJoinType(0), fCapType(0)
336     {
337         SkRandom rand;
338         for (int i = 0; i < N; ++i) {
339             fPts[i].fX = 20 + rand.nextUScalar1() * 640;
340             fPts[i].fY = 20 + rand.nextUScalar1() * 480;
341         }
342 
343         fPtsPaint.setAntiAlias(true);
344         fPtsPaint.setStrokeWidth(10);
345         fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
346 
347         fHiddenPaint.setAntiAlias(true);
348         fHiddenPaint.setStyle(SkPaint::kStroke_Style);
349         fHiddenPaint.setColor(0xFF0000FF);
350 
351         fStrokePaint.setAntiAlias(true);
352         fStrokePaint.setStyle(SkPaint::kStroke_Style);
353         fStrokePaint.setStrokeWidth(50);
354         fStrokePaint.setColor(0x8000FF00);
355 
356         fSkeletonPaint.setAntiAlias(true);
357         fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
358         fSkeletonPaint.setColor(SK_ColorRED);
359     }
360 
toggle(bool & value)361     void toggle(bool& value) {
362         value = !value;
363     }
364 
toggle3(int & value)365     void toggle3(int& value) {
366         value = (value + 1) % 3;
367     }
368 
369 protected:
onQuery(Sample::Event * evt)370     bool onQuery(Sample::Event* evt) override {
371         if (Sample::TitleQ(*evt)) {
372             Sample::TitleR(evt, "FatStroke");
373             return true;
374         }
375         SkUnichar uni;
376         if (Sample::CharQ(*evt, &uni)) {
377             switch (uni) {
378                 case '1': this->toggle(fShowSkeleton); return true;
379                 case '2': this->toggle(fShowStroke); return true;
380                 case '3': this->toggle(fShowHidden); return true;
381                 case '4': this->toggle3(fJoinType); return true;
382                 case '5': this->toggle3(fCapType); return true;
383                 case '6': this->toggle(fClosed); return true;
384                 case '-': fWidth -= 5; return true;
385                 case '=': fWidth += 5; return true;
386                 default: break;
387             }
388         }
389         return this->INHERITED::onQuery(evt);
390     }
391 
makePath(SkPath * path)392     void makePath(SkPath* path) {
393         path->moveTo(fPts[0]);
394         for (int i = 1; i < N; ++i) {
395             path->lineTo(fPts[i]);
396         }
397         if (fClosed) {
398             path->close();
399         }
400     }
401 
onDrawContent(SkCanvas * canvas)402     void onDrawContent(SkCanvas* canvas) override {
403         canvas->drawColor(0xFFEEEEEE);
404 
405         SkPath path;
406         this->makePath(&path);
407 
408         fStrokePaint.setStrokeWidth(fWidth);
409         fStrokePaint.setStrokeJoin((SkPaint::Join)fJoinType);
410         fStrokePaint.setStrokeCap((SkPaint::Cap)fCapType);
411 
412         if (fShowStroke) {
413             canvas->drawPath(path, fStrokePaint);
414         }
415         if (fShowHidden) {
416             SkPath hidden;
417             fStrokePaint.getFillPath(path, &hidden);
418             canvas->drawPath(hidden, fHiddenPaint);
419         }
420         if (fShowSkeleton) {
421             canvas->drawPath(path, fSkeletonPaint);
422         }
423         canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
424     }
425 
onClick(Click * click)426     bool onClick(Click* click) override {
427         int32_t index;
428         if (click->fMeta.findS32("index", &index)) {
429             SkASSERT((unsigned)index < N);
430             fPts[index] = click->fCurr;
431             return true;
432         }
433         return false;
434     }
435 
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)436     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
437         const SkScalar tol = 4;
438         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
439         for (int i = 0; i < N; ++i) {
440             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
441                 Click* click = new Click(this);
442                 click->fMeta.setS32("index", i);
443                 return click;
444             }
445         }
446         return this->INHERITED::onFindClickHandler(x, y, modi);
447     }
448 
449 private:
450     typedef Sample INHERITED;
451 };
DEF_SAMPLE(return new FatStroke;)452 DEF_SAMPLE( return new FatStroke; )
453 
454 static int compute_parallel_to_base(const SkPoint pts[4], SkScalar t[2]) {
455     // F = At^3 + Bt^2 + Ct + D
456     SkVector A = pts[3] - pts[0] + (pts[1] - pts[2]) * 3.0f;
457     SkVector B = (pts[0] - pts[1] - pts[1] + pts[2]) * 3.0f;
458     SkVector C = (pts[1] - pts[0]) * 3.0f;
459     SkVector DA = pts[3] - pts[0];
460 
461     // F' = 3At^2 + 2Bt + C
462     SkScalar a = 3 * A.cross(DA);
463     SkScalar b = 2 * B.cross(DA);
464     SkScalar c = C.cross(DA);
465 
466     int n = SkFindUnitQuadRoots(a, b, c, t);
467     SkString str;
468     for (int i = 0; i < n; ++i) {
469         str.appendf(" %g", t[i]);
470     }
471     SkDebugf("roots %s\n", str.c_str());
472     return n;
473 }
474 
475 class CubicCurve : public Sample {
476 public:
477     enum {
478         N = 4
479     };
480     SkPoint fPts[N];
481 
CubicCurve()482     CubicCurve() {
483         SkRandom rand;
484         for (int i = 0; i < N; ++i) {
485             fPts[i].fX = 20 + rand.nextUScalar1() * 640;
486             fPts[i].fY = 20 + rand.nextUScalar1() * 480;
487         }
488     }
489 
490 protected:
onQuery(Sample::Event * evt)491     bool onQuery(Sample::Event* evt) override {
492         if (Sample::TitleQ(*evt)) {
493             Sample::TitleR(evt, "CubicCurve");
494             return true;
495         }
496         return this->INHERITED::onQuery(evt);
497     }
498 
onDrawContent(SkCanvas * canvas)499     void onDrawContent(SkCanvas* canvas) override {
500         SkPaint paint;
501         paint.setAntiAlias(true);
502 
503         {
504             SkPath path;
505             path.moveTo(fPts[0]);
506             path.cubicTo(fPts[1], fPts[2], fPts[3]);
507             paint.setStyle(SkPaint::kStroke_Style);
508             canvas->drawPath(path, paint);
509         }
510 
511         {
512             paint.setColor(SK_ColorRED);
513             SkScalar t[2];
514             int n = compute_parallel_to_base(fPts, t);
515             SkPoint loc;
516             SkVector tan;
517             for (int i = 0; i < n; ++i) {
518                 SkEvalCubicAt(fPts, t[i], &loc, &tan, nullptr);
519                 tan.setLength(30);
520                 canvas->drawLine(loc - tan, loc + tan, paint);
521             }
522             paint.setStrokeWidth(0.5f);
523             canvas->drawLine(fPts[0], fPts[3], paint);
524 
525             paint.setColor(SK_ColorBLUE);
526             paint.setStrokeWidth(6);
527             SkEvalCubicAt(fPts, 0.5f, &loc, nullptr, nullptr);
528             canvas->drawPoint(loc, paint);
529 
530             paint.setColor(0xFF008800);
531             SkEvalCubicAt(fPts, 1.0f/3, &loc, nullptr, nullptr);
532             canvas->drawPoint(loc, paint);
533             SkEvalCubicAt(fPts, 2.0f/3, &loc, nullptr, nullptr);
534             canvas->drawPoint(loc, paint);
535 
536        //     n = SkFindCubicInflections(fPts, t);
537        //     printf("inflections %d %g %g\n", n, t[0], t[1]);
538         }
539 
540         {
541             paint.setStyle(SkPaint::kFill_Style);
542             paint.setColor(SK_ColorRED);
543             for (SkPoint p : fPts) {
544                 canvas->drawCircle(p.fX, p.fY, 8, paint);
545             }
546         }
547     }
548 
onClick(Click * click)549     bool onClick(Click* click) override {
550         int32_t index;
551         if (click->fMeta.findS32("index", &index)) {
552             SkASSERT((unsigned)index < N);
553             fPts[index] = click->fCurr;
554             return true;
555         }
556         return false;
557     }
558 
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)559     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
560         const SkScalar tol = 8;
561         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
562         for (int i = 0; i < N; ++i) {
563             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
564                 Click* click = new Click(this);
565                 click->fMeta.setS32("index", i);
566                 return click;
567             }
568         }
569         return this->INHERITED::onFindClickHandler(x, y, modi);
570     }
571 
572 private:
573     typedef Sample INHERITED;
574 };
DEF_SAMPLE(return new CubicCurve;)575 DEF_SAMPLE( return new CubicCurve; )
576 
577 static SkPoint lerp(SkPoint a, SkPoint b, float t) {
578     return a * (1 - t) + b * t;
579 }
580 
find_max_deviation_cubic(const SkPoint src[4],SkScalar ts[2])581 static int find_max_deviation_cubic(const SkPoint src[4], SkScalar ts[2]) {
582     // deviation = F' x (d - a) == 0, solve for t(s)
583     // F = At^3 + Bt^2 + Ct + D
584     // F' = 3At^2 + 2Bt + C
585     // Z = d - a
586     // F' x Z = 3(A x Z)t^2 + 2(B x Z)t + (C x Z)
587     //
588     SkVector A = src[3] + (src[1] - src[2]) * 3 - src[0];
589     SkVector B = (src[2] - src[1] - src[1] + src[0]) * 3;
590     SkVector C = (src[1] - src[0]) * 3;
591     SkVector Z = src[3] - src[0];
592     // now forumlate the quadratic coefficients we need to solve for t : F' x Z
593     return SkFindUnitQuadRoots(3 * A.cross(Z), 2 * B.cross(Z), C.cross(Z), ts);
594 }
595 
596 class CubicCurve2 : public Sample {
597 public:
598     enum {
599         N = 7
600     };
601     SkPoint fPts[N];
602     SkPoint* fQuad = fPts + 4;
603     SkScalar fT = 0.5f;
604     bool fShowSub = false;
605     bool fShowFlatness = false;
606     SkScalar fScale = 0.75;
607 
CubicCurve2()608     CubicCurve2() {
609         fPts[0] = { 90, 300 };
610         fPts[1] = { 30, 60 };
611         fPts[2] = { 250, 30 };
612         fPts[3] = { 350, 200 };
613 
614         fQuad[0] = fPts[0] + SkVector{ 300, 0};
615         fQuad[1] = fPts[1] + SkVector{ 300, 0};
616         fQuad[2] = fPts[2] + SkVector{ 300, 0};
617     }
618 
619 protected:
onQuery(Sample::Event * evt)620     bool onQuery(Sample::Event* evt) override {
621         if (Sample::TitleQ(*evt)) {
622             Sample::TitleR(evt, "CubicCurve2");
623             return true;
624         }
625         SkUnichar uni;
626         if (Sample::CharQ(*evt, &uni)) {
627             switch (uni) {
628                 case 's': fShowSub = !fShowSub; break;
629                 case 'f': fShowFlatness = !fShowFlatness; break;
630                 case '-': fT -= 1.0f / 32; break;
631                 case '=': fT += 1.0f / 32; break;
632                 default: goto DONE;
633             }
634             fT = std::min(1.0f, std::max(0.0f, fT));
635             return true;
636         }
637         DONE:
638         return this->INHERITED::onQuery(evt);
639     }
640 
showFrame(SkCanvas * canvas,const SkPoint pts[],int count,const SkPaint & p)641     void showFrame(SkCanvas* canvas, const SkPoint pts[], int count, const SkPaint& p) {
642         SkPaint paint(p);
643         SkPoint storage[3 + 2 + 1];
644         SkPoint* tmp = storage;
645         const SkPoint* prev = pts;
646         int n = count;
647         for (int n = count; n > 0; --n) {
648             for (int i = 0; i < n; ++i) {
649                 canvas->drawLine(prev[i], prev[i+1], paint);
650                 tmp[i] = lerp(prev[i], prev[i+1], fT);
651             }
652             prev = tmp;
653             tmp += n;
654         }
655 
656         paint.setColor(SK_ColorBLUE);
657         paint.setStyle(SkPaint::kFill_Style);
658         n = tmp - storage;
659         for (int i = 0; i < n; ++i) {
660             canvas->drawCircle(storage[i].fX, storage[i].fY, 4, paint);
661         }
662     }
663 
showFlattness(SkCanvas * canvas)664     void showFlattness(SkCanvas* canvas) {
665         SkPaint paint;
666         paint.setStyle(SkPaint::kStroke_Style);
667         paint.setAntiAlias(true);
668 
669         SkPaint paint2(paint);
670         paint2.setColor(0xFF008800);
671 
672         paint.setColor(0xFF888888);
673         canvas->drawLine(fPts[0], fPts[3], paint);
674         canvas->drawLine(fQuad[0], fQuad[2], paint);
675 
676         paint.setColor(0xFF0000FF);
677         SkPoint pts[2];
678         pts[0] = (fQuad[0] + fQuad[1] + fQuad[1] + fQuad[2])*0.25;
679         pts[1] = (fQuad[0] + fQuad[2]) * 0.5;
680         canvas->drawLine(pts[0], pts[1], paint);
681 
682         // cubic
683 
684         SkVector v0 = (fPts[0] - fPts[1] - fPts[1] + fPts[2]) * fScale;
685         SkVector v1 = (fPts[1] - fPts[2] - fPts[2] + fPts[3]) * fScale;
686         SkVector v = (v0 + v1) * 0.5f;
687 
688         SkPoint anchor;
689         SkScalar ts[2];
690         int n = find_max_deviation_cubic(fPts, ts);
691         if (n > 0) {
692             SkEvalCubicAt(fPts, ts[0], &anchor, nullptr, nullptr);
693             canvas->drawLine(anchor, anchor + v, paint2);
694             canvas->drawLine(anchor, anchor + v0, paint);
695             if (n == 2) {
696                 SkEvalCubicAt(fPts, ts[1], &anchor, nullptr, nullptr);
697                 canvas->drawLine(anchor, anchor + v, paint2);
698             }
699             canvas->drawLine(anchor, anchor + v1, paint);
700         }
701         // not sure we can get here
702     }
703 
onDrawContent(SkCanvas * canvas)704     void onDrawContent(SkCanvas* canvas) override {
705         SkPaint paint;
706         paint.setAntiAlias(true);
707 
708         {
709             paint.setStyle(SkPaint::kStroke_Style);
710             SkPath path;
711             path.moveTo(fPts[0]);
712             path.cubicTo(fPts[1], fPts[2], fPts[3]);
713             path.moveTo(fQuad[0]);
714             path.quadTo(fQuad[1], fQuad[2]);
715             canvas->drawPath(path, paint);
716         }
717 
718         if (fShowSub) {
719             paint.setColor(SK_ColorRED);
720             paint.setStrokeWidth(1.7f);
721             this->showFrame(canvas, fPts, 3, paint);
722             this->showFrame(canvas, fQuad, 2, paint);
723 
724             paint.setColor(SK_ColorBLACK);
725             paint.setStyle(SkPaint::kFill_Style);
726             SkFont font(nullptr, 20);
727             canvas->drawString(SkStringPrintf("t = %g", fT), 20, 20, font, paint);
728         }
729 
730         if (fShowFlatness) {
731             this->showFlattness(canvas);
732         }
733 
734         paint.setStyle(SkPaint::kFill_Style);
735         paint.setColor(SK_ColorRED);
736         for (SkPoint p : fPts) {
737             canvas->drawCircle(p.fX, p.fY, 7, paint);
738         }
739 
740         {
741             SkScalar ts[2];
742             int n = SkFindCubicInflections(fPts, ts);
743             for (int i = 0; i < n; ++i) {
744                 SkPoint p;
745                 SkEvalCubicAt(fPts, ts[i], &p, nullptr, nullptr);
746                 canvas->drawCircle(p.fX, p.fY, 3, paint);
747             }
748         }
749 
750     }
751 
onClick(Click * click)752     bool onClick(Click* click) override {
753         int32_t index;
754         if (click->fMeta.findS32("index", &index)) {
755             SkASSERT((unsigned)index < N);
756             fPts[index] = click->fCurr;
757             return true;
758         }
759         return false;
760     }
761 
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)762     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
763         const SkScalar tol = 8;
764         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
765         for (int i = 0; i < N; ++i) {
766             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
767                 Click* click = new Click(this);
768                 click->fMeta.setS32("index", i);
769                 return click;
770             }
771         }
772         return this->INHERITED::onFindClickHandler(x, y, modi);
773     }
774 
775 private:
776     typedef Sample INHERITED;
777 };
778 DEF_SAMPLE( return new CubicCurve2; )
779 
780