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