blob: 75657a18833ea51edbbd3e11b506100101fe04ff [file] [log] [blame]
caryclark64022c12016-05-27 05:13:26 -07001/*
2 * Copyright 2015 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 "SampleCode.h"
Florin Malitaab244f02017-05-03 19:16:58 +00009#include "SkBitmap.h"
caryclark64022c12016-05-27 05:13:26 -070010#include "SkCanvas.h"
11#include "SkGeometry.h"
12#include "SkIntersections.h"
13#include "SkOpEdgeBuilder.h"
14// #include "SkPathOpsSimplifyAA.h"
15// #include "SkPathStroker.h"
Cary Clarkdf429f32017-11-08 11:44:31 -050016#include "SkPointPriv.h"
Brian Osman8ceee432017-12-01 10:52:28 -050017#include "SkString.h"
caryclark64022c12016-05-27 05:13:26 -070018#include "SkView.h"
19
20#if 0
21void SkStrokeSegment::dump() const {
22 SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}", fPts[0].fX, fPts[0].fY, fPts[1].fX, fPts[1].fY);
23 if (SkPath::kQuad_Verb == fVerb) {
24 SkDebugf(", {%1.9g,%1.9g}", fPts[2].fX, fPts[2].fY);
25 }
26 SkDebugf("}}");
27#ifdef SK_DEBUG
28 SkDebugf(" id=%d", fDebugID);
29#endif
30 SkDebugf("\n");
31}
32
33void SkStrokeSegment::dumpAll() const {
34 const SkStrokeSegment* segment = this;
35 while (segment) {
36 segment->dump();
37 segment = segment->fNext;
38 }
39}
40
41void SkStrokeTriple::dump() const {
42 SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}", fPts[0].fX, fPts[0].fY, fPts[1].fX, fPts[1].fY);
43 if (SkPath::kQuad_Verb <= fVerb) {
44 SkDebugf(", {%1.9g,%1.9g}", fPts[2].fX, fPts[2].fY);
45 }
46 if (SkPath::kCubic_Verb == fVerb) {
47 SkDebugf(", {%1.9g,%1.9g}", fPts[3].fX, fPts[3].fY);
48 } else if (SkPath::kConic_Verb == fVerb) {
49 SkDebugf(", %1.9g", weight());
50 }
51 SkDebugf("}}");
52#ifdef SK_DEBUG
53 SkDebugf(" triple id=%d", fDebugID);
54#endif
55 SkDebugf("\ninner:\n");
56 fInner->dumpAll();
57 SkDebugf("outer:\n");
58 fOuter->dumpAll();
59 SkDebugf("join:\n");
60 fJoin->dumpAll();
61}
62
63void SkStrokeTriple::dumpAll() const {
64 const SkStrokeTriple* triple = this;
65 while (triple) {
66 triple->dump();
67 triple = triple->fNext;
68 }
69}
70
71void SkStrokeContour::dump() const {
72#ifdef SK_DEBUG
73 SkDebugf("id=%d ", fDebugID);
74#endif
75 SkDebugf("head:\n");
76 fHead->dumpAll();
77 SkDebugf("head cap:\n");
78 fHeadCap->dumpAll();
79 SkDebugf("tail cap:\n");
80 fTailCap->dumpAll();
81}
82
83void SkStrokeContour::dumpAll() const {
84 const SkStrokeContour* contour = this;
85 while (contour) {
86 contour->dump();
87 contour = contour->fNext;
88 }
89}
90#endif
91
92SkScalar gCurveDistance = 10;
93
94#if 0 // unused
95static SkPath::Verb get_path_verb(int index, const SkPath& path) {
96 if (index < 0) {
97 return SkPath::kMove_Verb;
98 }
99 SkPoint pts[4];
100 SkPath::Verb verb;
101 SkPath::Iter iter(path, true);
102 int counter = -1;
103 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
104 if (++counter < index) {
105 continue;
106 }
107 return verb;
108 }
109 SkASSERT(0);
110 return SkPath::kMove_Verb;
111}
112#endif
113
114static SkScalar get_path_weight(int index, const SkPath& path) {
115 SkPoint pts[4];
116 SkPath::Verb verb;
117 SkPath::Iter iter(path, true);
118 int counter = -1;
119 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
120 if (++counter < index) {
121 continue;
122 }
123 return verb == SkPath::kConic_Verb ? iter.conicWeight() : 1;
124 }
125 SkASSERT(0);
126 return 0;
127}
128
129static void set_path_pt(int index, const SkPoint& pt, SkPath* path) {
130 SkPath result;
131 SkPoint pts[4];
132 SkPath::Verb verb;
133 SkPath::RawIter iter(*path);
134 int startIndex = 0;
135 int endIndex = 0;
136 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
137 switch (verb) {
138 case SkPath::kMove_Verb:
139 endIndex += 1;
140 break;
141 case SkPath::kLine_Verb:
142 endIndex += 1;
143 break;
144 case SkPath::kQuad_Verb:
145 case SkPath::kConic_Verb:
146 endIndex += 2;
147 break;
148 case SkPath::kCubic_Verb:
149 endIndex += 3;
150 break;
151 case SkPath::kClose_Verb:
152 break;
153 case SkPath::kDone_Verb:
154 break;
155 default:
156 SkASSERT(0);
157 }
158 if (startIndex <= index && index < endIndex) {
159 pts[index - startIndex] = pt;
160 index = -1;
161 }
162 switch (verb) {
163 case SkPath::kMove_Verb:
164 result.moveTo(pts[0]);
165 break;
166 case SkPath::kLine_Verb:
167 result.lineTo(pts[1]);
168 startIndex += 1;
169 break;
170 case SkPath::kQuad_Verb:
171 result.quadTo(pts[1], pts[2]);
172 startIndex += 2;
173 break;
174 case SkPath::kConic_Verb:
175 result.conicTo(pts[1], pts[2], iter.conicWeight());
176 startIndex += 2;
177 break;
178 case SkPath::kCubic_Verb:
179 result.cubicTo(pts[1], pts[2], pts[3]);
180 startIndex += 3;
181 break;
182 case SkPath::kClose_Verb:
183 result.close();
184 startIndex += 1;
185 break;
186 case SkPath::kDone_Verb:
187 break;
188 default:
189 SkASSERT(0);
190 }
191 }
192#if 0
193 SkDebugf("\n\noriginal\n");
194 path->dump();
195 SkDebugf("\nedited\n");
196 result.dump();
197#endif
198 *path = result;
199}
200
201static void add_path_segment(int index, SkPath* path) {
202 SkPath result;
203 SkPoint pts[4];
204 SkPoint firstPt = { 0, 0 }; // init to avoid warning
205 SkPoint lastPt = { 0, 0 }; // init to avoid warning
206 SkPath::Verb verb;
207 SkPath::RawIter iter(*path);
208 int counter = -1;
209 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
210 SkScalar weight SK_INIT_TO_AVOID_WARNING;
211 if (++counter == index) {
212 switch (verb) {
213 case SkPath::kLine_Verb:
214 result.lineTo((pts[0].fX + pts[1].fX) / 2, (pts[0].fY + pts[1].fY) / 2);
215 break;
216 case SkPath::kQuad_Verb: {
217 SkPoint chop[5];
218 SkChopQuadAtHalf(pts, chop);
219 result.quadTo(chop[1], chop[2]);
220 pts[1] = chop[3];
221 } break;
222 case SkPath::kConic_Verb: {
223 SkConic chop[2];
224 SkConic conic;
225 conic.set(pts, iter.conicWeight());
caryclark414c4292016-09-26 11:03:54 -0700226 if (!conic.chopAt(0.5f, chop)) {
227 return;
228 }
caryclark64022c12016-05-27 05:13:26 -0700229 result.conicTo(chop[0].fPts[1], chop[0].fPts[2], chop[0].fW);
230 pts[1] = chop[1].fPts[1];
231 weight = chop[1].fW;
232 } break;
233 case SkPath::kCubic_Verb: {
234 SkPoint chop[7];
235 SkChopCubicAtHalf(pts, chop);
236 result.cubicTo(chop[1], chop[2], chop[3]);
237 pts[1] = chop[4];
238 pts[2] = chop[5];
239 } break;
240 case SkPath::kClose_Verb: {
241 result.lineTo((lastPt.fX + firstPt.fX) / 2, (lastPt.fY + firstPt.fY) / 2);
242 } break;
243 default:
244 SkASSERT(0);
245 }
246 } else if (verb == SkPath::kConic_Verb) {
247 weight = iter.conicWeight();
248 }
249 switch (verb) {
250 case SkPath::kMove_Verb:
251 result.moveTo(firstPt = pts[0]);
252 break;
253 case SkPath::kLine_Verb:
254 result.lineTo(lastPt = pts[1]);
255 break;
256 case SkPath::kQuad_Verb:
257 result.quadTo(pts[1], lastPt = pts[2]);
258 break;
259 case SkPath::kConic_Verb:
260 result.conicTo(pts[1], lastPt = pts[2], weight);
261 break;
262 case SkPath::kCubic_Verb:
263 result.cubicTo(pts[1], pts[2], lastPt = pts[3]);
264 break;
265 case SkPath::kClose_Verb:
266 result.close();
267 break;
268 case SkPath::kDone_Verb:
269 break;
270 default:
271 SkASSERT(0);
272 }
273 }
274 *path = result;
275}
276
277static void delete_path_segment(int index, SkPath* path) {
278 SkPath result;
279 SkPoint pts[4];
280 SkPath::Verb verb;
281 SkPath::RawIter iter(*path);
282 int counter = -1;
283 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
284 if (++counter == index) {
285 continue;
286 }
287 switch (verb) {
288 case SkPath::kMove_Verb:
289 result.moveTo(pts[0]);
290 break;
291 case SkPath::kLine_Verb:
292 result.lineTo(pts[1]);
293 break;
294 case SkPath::kQuad_Verb:
295 result.quadTo(pts[1], pts[2]);
296 break;
297 case SkPath::kConic_Verb:
298 result.conicTo(pts[1], pts[2], iter.conicWeight());
299 break;
300 case SkPath::kCubic_Verb:
301 result.cubicTo(pts[1], pts[2], pts[3]);
302 break;
303 case SkPath::kClose_Verb:
304 result.close();
305 break;
306 case SkPath::kDone_Verb:
307 break;
308 default:
309 SkASSERT(0);
310 }
311 }
312 *path = result;
313}
314
315static void set_path_weight(int index, SkScalar w, SkPath* path) {
316 SkPath result;
317 SkPoint pts[4];
318 SkPath::Verb verb;
319 SkPath::Iter iter(*path, true);
320 int counter = -1;
321 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
322 ++counter;
323 switch (verb) {
324 case SkPath::kMove_Verb:
325 result.moveTo(pts[0]);
326 break;
327 case SkPath::kLine_Verb:
328 result.lineTo(pts[1]);
329 break;
330 case SkPath::kQuad_Verb:
331 result.quadTo(pts[1], pts[2]);
332 break;
333 case SkPath::kConic_Verb:
334 result.conicTo(pts[1], pts[2], counter == index ? w : iter.conicWeight());
335 break;
336 case SkPath::kCubic_Verb:
337 result.cubicTo(pts[1], pts[2], pts[3]);
338 break;
339 case SkPath::kClose_Verb:
340 result.close();
341 break;
342 case SkPath::kDone_Verb:
343 break;
344 default:
345 SkASSERT(0);
346 }
347 }
348 *path = result;
349}
350
351static void set_path_verb(int index, SkPath::Verb v, SkPath* path, SkScalar w) {
352 SkASSERT(SkPath::kLine_Verb <= v && v <= SkPath::kCubic_Verb);
353 SkPath result;
354 SkPoint pts[4];
355 SkPath::Verb verb;
356 SkPath::Iter iter(*path, true);
357 int counter = -1;
358 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
359 SkScalar weight = verb == SkPath::kConic_Verb ? iter.conicWeight() : 1;
360 if (++counter == index && v != verb) {
361 SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
362 switch (verb) {
363 case SkPath::kLine_Verb:
364 switch (v) {
365 case SkPath::kConic_Verb:
366 weight = w;
367 case SkPath::kQuad_Verb:
368 pts[2] = pts[1];
369 pts[1].fX = (pts[0].fX + pts[2].fX) / 2;
370 pts[1].fY = (pts[0].fY + pts[2].fY) / 2;
371 break;
372 case SkPath::kCubic_Verb:
373 pts[3] = pts[1];
374 pts[1].fX = (pts[0].fX * 2 + pts[3].fX) / 3;
375 pts[1].fY = (pts[0].fY * 2 + pts[3].fY) / 3;
376 pts[2].fX = (pts[0].fX + pts[3].fX * 2) / 3;
377 pts[2].fY = (pts[0].fY + pts[3].fY * 2) / 3;
378 break;
379 default:
380 SkASSERT(0);
381 break;
382 }
383 break;
384 case SkPath::kQuad_Verb:
385 case SkPath::kConic_Verb:
386 switch (v) {
387 case SkPath::kLine_Verb:
388 pts[1] = pts[2];
389 break;
390 case SkPath::kConic_Verb:
391 weight = w;
392 case SkPath::kQuad_Verb:
393 break;
394 case SkPath::kCubic_Verb: {
395 SkDQuad dQuad;
396 dQuad.set(pts);
397 SkDCubic dCubic = dQuad.debugToCubic();
398 pts[3] = pts[2];
399 pts[1] = dCubic[1].asSkPoint();
400 pts[2] = dCubic[2].asSkPoint();
401 } break;
402 default:
403 SkASSERT(0);
404 break;
405 }
406 break;
407 case SkPath::kCubic_Verb:
408 switch (v) {
409 case SkPath::kLine_Verb:
410 pts[1] = pts[3];
411 break;
412 case SkPath::kConic_Verb:
413 weight = w;
414 case SkPath::kQuad_Verb: {
415 SkDCubic dCubic;
416 dCubic.set(pts);
417 SkDQuad dQuad = dCubic.toQuad();
418 pts[1] = dQuad[1].asSkPoint();
419 pts[2] = pts[3];
420 } break;
421 default:
422 SkASSERT(0);
423 break;
424 }
425 break;
426 default:
427 SkASSERT(0);
428 break;
429 }
430 verb = v;
431 }
432 switch (verb) {
433 case SkPath::kMove_Verb:
434 result.moveTo(pts[0]);
435 break;
436 case SkPath::kLine_Verb:
437 result.lineTo(pts[1]);
438 break;
439 case SkPath::kQuad_Verb:
440 result.quadTo(pts[1], pts[2]);
441 break;
442 case SkPath::kConic_Verb:
443 result.conicTo(pts[1], pts[2], weight);
444 break;
445 case SkPath::kCubic_Verb:
446 result.cubicTo(pts[1], pts[2], pts[3]);
447 break;
448 case SkPath::kClose_Verb:
449 result.close();
450 break;
451 default:
452 SkASSERT(0);
453 break;
454 }
455 }
456 *path = result;
457}
458
459static void add_to_map(SkScalar coverage, int x, int y, uint8_t* distanceMap, int w, int h) {
460 int byteCoverage = (int) (coverage * 256);
461 if (byteCoverage < 0) {
462 byteCoverage = 0;
463 } else if (byteCoverage > 255) {
464 byteCoverage = 255;
465 }
466 SkASSERT(x < w);
467 SkASSERT(y < h);
468 distanceMap[y * w + x] = SkTMax(distanceMap[y * w + x], (uint8_t) byteCoverage);
469}
470
471static void filter_coverage(const uint8_t* map, int len, uint8_t min, uint8_t max,
472 uint8_t* filter) {
473 for (int index = 0; index < len; ++index) {
474 uint8_t in = map[index];
475 filter[index] = in < min ? 0 : max < in ? 0 : in;
476 }
477}
478
479static void construct_path(SkPath& path) {
480 path.reset();
481 path.moveTo(442, 101.5f);
482 path.quadTo(413.5f, 691, 772, 514);
483 path.lineTo(346, 721.5f);
484 path.lineTo(154, 209);
485 path.lineTo(442, 101.5f);
486 path.close();
487}
488
489struct ButtonPaints {
490 static const int kMaxStateCount = 3;
491 SkPaint fDisabled;
492 SkPaint fStates[kMaxStateCount];
493 SkPaint fLabel;
494
495 ButtonPaints() {
496 fStates[0].setAntiAlias(true);
497 fStates[0].setStyle(SkPaint::kStroke_Style);
498 fStates[0].setColor(0xFF3F0000);
499 fStates[1] = fStates[0];
500 fStates[1].setStrokeWidth(3);
501 fStates[2] = fStates[1];
502 fStates[2].setColor(0xFFcf0000);
503 fLabel.setAntiAlias(true);
504 fLabel.setTextSize(25.0f);
505 fLabel.setTextAlign(SkPaint::kCenter_Align);
506 fLabel.setStyle(SkPaint::kFill_Style);
507 }
508};
509
510struct Button {
511 SkRect fBounds;
512 int fStateCount;
513 int fState;
514 char fLabel;
515 bool fVisible;
516
517 Button(char label) {
518 fStateCount = 2;
519 fState = 0;
520 fLabel = label;
521 fVisible = false;
522 }
523
524 Button(char label, int stateCount) {
525 SkASSERT(stateCount <= ButtonPaints::kMaxStateCount);
526 fStateCount = stateCount;
527 fState = 0;
528 fLabel = label;
529 fVisible = false;
530 }
531
532 bool contains(const SkRect& rect) {
533 return fVisible && fBounds.contains(rect);
534 }
535
536 bool enabled() {
537 return SkToBool(fState);
538 }
539
540 void draw(SkCanvas* canvas, const ButtonPaints& paints) {
541 if (!fVisible) {
542 return;
543 }
544 canvas->drawRect(fBounds, paints.fStates[fState]);
545 canvas->drawText(&fLabel, 1, fBounds.centerX(), fBounds.fBottom - 5, paints.fLabel);
546 }
547
548 void toggle() {
549 if (++fState == fStateCount) {
550 fState = 0;
551 }
552 }
553
554 void setEnabled(bool enabled) {
555 fState = (int) enabled;
556 }
557};
558
559struct ControlPaints {
560 SkPaint fOutline;
561 SkPaint fIndicator;
562 SkPaint fFill;
563 SkPaint fLabel;
564 SkPaint fValue;
565
566 ControlPaints() {
567 fOutline.setAntiAlias(true);
568 fOutline.setStyle(SkPaint::kStroke_Style);
569 fIndicator = fOutline;
570 fIndicator.setColor(SK_ColorRED);
571 fFill.setAntiAlias(true);
572 fFill.setColor(0x7fff0000);
573 fLabel.setAntiAlias(true);
574 fLabel.setTextSize(13.0f);
575 fValue.setAntiAlias(true);
576 fValue.setTextSize(11.0f);
577 }
578};
579
580struct UniControl {
581 SkString fName;
582 SkRect fBounds;
583 SkScalar fMin;
584 SkScalar fMax;
585 SkScalar fValLo;
586 SkScalar fYLo;
587 bool fVisible;
588
589 UniControl(const char* name, SkScalar min, SkScalar max) {
590 fName = name;
591 fValLo = fMin = min;
592 fMax = max;
593 fVisible = false;
594
595 }
596
597 virtual ~UniControl() {}
598
599 bool contains(const SkRect& rect) {
600 return fVisible && fBounds.contains(rect);
601 }
602
603 virtual void draw(SkCanvas* canvas, const ControlPaints& paints) {
604 if (!fVisible) {
605 return;
606 }
607 canvas->drawRect(fBounds, paints.fOutline);
608 fYLo = fBounds.fTop + (fValLo - fMin) * fBounds.height() / (fMax - fMin);
609 canvas->drawLine(fBounds.fLeft - 5, fYLo, fBounds.fRight + 5, fYLo, paints.fIndicator);
610 SkString label;
611 label.printf("%0.3g", fValLo);
Cary Clark2a475ea2017-04-28 15:35:12 -0400612 canvas->drawString(label, fBounds.fLeft + 5, fYLo - 5, paints.fValue);
613 canvas->drawString(fName, fBounds.fLeft, fBounds.bottom() + 11,
caryclark64022c12016-05-27 05:13:26 -0700614 paints.fLabel);
615 }
616};
617
618struct BiControl : public UniControl {
619 SkScalar fValHi;
620
Ben Wagner63fd7602017-10-09 15:45:33 -0400621 BiControl(const char* name, SkScalar min, SkScalar max)
caryclark64022c12016-05-27 05:13:26 -0700622 : UniControl(name, min, max)
623 , fValHi(fMax) {
624 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400625
caryclark64022c12016-05-27 05:13:26 -0700626 virtual ~BiControl() {}
627
628 virtual void draw(SkCanvas* canvas, const ControlPaints& paints) {
629 UniControl::draw(canvas, paints);
630 if (!fVisible || fValHi == fValLo) {
631 return;
632 }
633 SkScalar yPos = fBounds.fTop + (fValHi - fMin) * fBounds.height() / (fMax - fMin);
634 canvas->drawLine(fBounds.fLeft - 5, yPos, fBounds.fRight + 5, yPos, paints.fIndicator);
635 SkString label;
636 label.printf("%0.3g", fValHi);
637 if (yPos < fYLo + 10) {
638 yPos = fYLo + 10;
639 }
Cary Clark2a475ea2017-04-28 15:35:12 -0400640 canvas->drawString(label, fBounds.fLeft + 5, yPos - 5, paints.fValue);
caryclark64022c12016-05-27 05:13:26 -0700641 SkRect fill = { fBounds.fLeft, fYLo, fBounds.fRight, yPos };
642 canvas->drawRect(fill, paints.fFill);
643 }
644};
645
646
647class MyClick : public SampleView::Click {
648public:
649 enum ClickType {
650 kInvalidType = -1,
651 kPtType,
652 kVerbType,
653 kControlType,
654 kPathType,
655 } fType;
656
657 enum ControlType {
658 kInvalidControl = -1,
659 kFirstControl,
660 kFilterControl = kFirstControl,
661 kResControl,
662 kWeightControl,
663 kWidthControl,
664 kLastControl = kWidthControl,
665 kFirstButton,
666 kCubicButton = kFirstButton,
667 kConicButton,
668 kQuadButton,
669 kLineButton,
670 kLastVerbButton = kLineButton,
671 kAddButton,
672 kDeleteButton,
673 kInOutButton,
674 kFillButton,
675 kSkeletonButton,
676 kFilterButton,
677 kBisectButton,
678 kJoinButton,
679 kLastButton = kJoinButton,
680 kPathMove,
681 } fControl;
682
683 SkPath::Verb fVerb;
684 SkScalar fWeight;
685
686 MyClick(SkView* target, ClickType type, ControlType control)
687 : Click(target)
Ben Wagner63fd7602017-10-09 15:45:33 -0400688 , fType(type)
caryclark64022c12016-05-27 05:13:26 -0700689 , fControl(control)
690 , fVerb((SkPath::Verb) -1)
691 , fWeight(1) {
692 }
693
694 MyClick(SkView* target, ClickType type, int index)
695 : Click(target)
Ben Wagner63fd7602017-10-09 15:45:33 -0400696 , fType(type)
caryclark64022c12016-05-27 05:13:26 -0700697 , fControl((ControlType) index)
698 , fVerb((SkPath::Verb) -1)
699 , fWeight(1) {
700 }
701
702 MyClick(SkView* target, ClickType type, int index, SkPath::Verb verb, SkScalar weight)
703 : Click(target)
Ben Wagner63fd7602017-10-09 15:45:33 -0400704 , fType(type)
caryclark64022c12016-05-27 05:13:26 -0700705 , fControl((ControlType) index)
706 , fVerb(verb)
707 , fWeight(weight) {
708 }
709
710 bool isButton() {
711 return kFirstButton <= fControl && fControl <= kLastButton;
712 }
713
714 int ptHit() const {
715 SkASSERT(fType == kPtType);
716 return (int) fControl;
717 }
718
719 int verbHit() const {
720 SkASSERT(fType == kVerbType);
721 return (int) fControl;
722 }
723};
724
725enum {
726 kControlCount = MyClick::kLastControl - MyClick::kFirstControl + 1,
727};
728
729static struct ControlPair {
730 UniControl* fControl;
731 MyClick::ControlType fControlType;
732} kControlList[kControlCount];
733
734enum {
735 kButtonCount = MyClick::kLastButton - MyClick::kFirstButton + 1,
736 kVerbCount = MyClick::kLastVerbButton - MyClick::kFirstButton + 1,
737};
738
739static struct ButtonPair {
740 Button* fButton;
741 MyClick::ControlType fButtonType;
742} kButtonList[kButtonCount];
743
744static void enable_verb_button(MyClick::ControlType type) {
745 for (int index = 0; index < kButtonCount; ++index) {
746 MyClick::ControlType testType = kButtonList[index].fButtonType;
747 if (MyClick::kFirstButton <= testType && testType <= MyClick::kLastVerbButton) {
748 Button* button = kButtonList[index].fButton;
749 button->setEnabled(testType == type);
750 }
751 }
752}
753
754struct Stroke;
755
756struct Active {
757 Active* fNext;
758 Stroke* fParent;
759 SkScalar fStart;
760 SkScalar fEnd;
761
762 void reset() {
Ben Wagnera93a14a2017-08-28 10:34:05 -0400763 fNext = nullptr;
caryclark64022c12016-05-27 05:13:26 -0700764 fStart = 0;
765 fEnd = 1;
766 }
767};
768
769struct Stroke {
770 SkPath fPath;
771 Active fActive;
772 bool fInner;
773
774 void reset() {
775 fPath.reset();
776 fActive.reset();
777 }
778};
779
780struct PathUndo {
781 SkPath fPath;
782 PathUndo* fNext;
783};
784
785class AAGeometryView : public SampleView {
786 SkPaint fActivePaint;
787 SkPaint fComplexPaint;
788 SkPaint fCoveragePaint;
789 SkPaint fLegendLeftPaint;
790 SkPaint fLegendRightPaint;
791 SkPaint fPointPaint;
792 SkPaint fSkeletonPaint;
793 SkPaint fLightSkeletonPaint;
794 SkPath fPath;
795 ControlPaints fControlPaints;
796 UniControl fResControl;
797 UniControl fWeightControl;
798 UniControl fWidthControl;
799 BiControl fFilterControl;
800 ButtonPaints fButtonPaints;
801 Button fCubicButton;
802 Button fConicButton;
803 Button fQuadButton;
804 Button fLineButton;
805 Button fAddButton;
806 Button fDeleteButton;
807 Button fFillButton;
808 Button fSkeletonButton;
809 Button fFilterButton;
810 Button fBisectButton;
811 Button fJoinButton;
812 Button fInOutButton;
813 SkTArray<Stroke> fStrokes;
814 PathUndo* fUndo;
815 int fActivePt;
816 int fActiveVerb;
817 bool fHandlePathMove;
818 bool fShowLegend;
819 bool fHideAll;
Chris Dalton08d1a252017-10-20 11:46:47 -0600820 const int kHitToleranace = 25;
caryclark64022c12016-05-27 05:13:26 -0700821
822public:
823
Ben Wagner63fd7602017-10-09 15:45:33 -0400824 AAGeometryView()
caryclark64022c12016-05-27 05:13:26 -0700825 : fResControl("error", 0, 10)
826 , fWeightControl("weight", 0, 5)
827 , fWidthControl("width", FLT_EPSILON, 100)
828 , fFilterControl("filter", 0, 255)
829 , fCubicButton('C')
830 , fConicButton('K')
831 , fQuadButton('Q')
832 , fLineButton('L')
833 , fAddButton('+')
834 , fDeleteButton('x')
835 , fFillButton('p')
836 , fSkeletonButton('s')
837 , fFilterButton('f', 3)
838 , fBisectButton('b')
839 , fJoinButton('j')
840 , fInOutButton('|')
Ben Wagnera93a14a2017-08-28 10:34:05 -0400841 , fUndo(nullptr)
caryclark64022c12016-05-27 05:13:26 -0700842 , fActivePt(-1)
843 , fActiveVerb(-1)
844 , fHandlePathMove(true)
845 , fShowLegend(false)
846 , fHideAll(false)
847 {
848 fCoveragePaint.setAntiAlias(true);
849 fCoveragePaint.setColor(SK_ColorBLUE);
850 SkPaint strokePaint;
851 strokePaint.setAntiAlias(true);
852 strokePaint.setStyle(SkPaint::kStroke_Style);
853 fPointPaint = strokePaint;
854 fPointPaint.setColor(0x99ee3300);
855 fSkeletonPaint = strokePaint;
856 fSkeletonPaint.setColor(SK_ColorRED);
857 fLightSkeletonPaint = fSkeletonPaint;
858 fLightSkeletonPaint.setColor(0xFFFF7f7f);
859 fActivePaint = strokePaint;
860 fActivePaint.setColor(0x99ee3300);
861 fActivePaint.setStrokeWidth(5);
862 fComplexPaint = fActivePaint;
863 fComplexPaint.setColor(SK_ColorBLUE);
864 fLegendLeftPaint.setAntiAlias(true);
865 fLegendLeftPaint.setTextSize(13);
866 fLegendRightPaint = fLegendLeftPaint;
867 fLegendRightPaint.setTextAlign(SkPaint::kRight_Align);
868 construct_path(fPath);
869 fFillButton.fVisible = fSkeletonButton.fVisible = fFilterButton.fVisible
870 = fBisectButton.fVisible = fJoinButton.fVisible = fInOutButton.fVisible = true;
871 fSkeletonButton.setEnabled(true);
872 fInOutButton.setEnabled(true);
873 fJoinButton.setEnabled(true);
874 fFilterControl.fValLo = 120;
875 fFilterControl.fValHi = 141;
876 fFilterControl.fVisible = fFilterButton.fState == 2;
877 fResControl.fValLo = 5;
878 fResControl.fVisible = true;
879 fWidthControl.fValLo = 50;
880 fWidthControl.fVisible = true;
881 init_controlList();
882 init_buttonList();
883 }
884
885 bool constructPath() {
886 construct_path(fPath);
caryclark64022c12016-05-27 05:13:26 -0700887 return true;
888 }
889
890 void savePath(Click::State state) {
891 if (state != Click::kDown_State) {
892 return;
893 }
894 if (fUndo && fUndo->fPath == fPath) {
895 return;
896 }
897 PathUndo* undo = new PathUndo;
898 undo->fPath = fPath;
899 undo->fNext = fUndo;
900 fUndo = undo;
901 }
902
903 bool undo() {
904 if (!fUndo) {
905 return false;
906 }
907 fPath = fUndo->fPath;
908 validatePath();
909 PathUndo* next = fUndo->fNext;
910 delete fUndo;
911 fUndo = next;
caryclark64022c12016-05-27 05:13:26 -0700912 return true;
913 }
914
915 void validatePath() {
916 PathUndo* undo = fUndo;
917 int match = 0;
918 while (undo) {
919 match += fPath == undo->fPath;
920 undo = undo->fNext;
921 }
922 }
923
924 void set_controlList(int index, UniControl* control, MyClick::ControlType type) {
925 kControlList[index].fControl = control;
926 kControlList[index].fControlType = type;
927 }
928
929 #define SET_CONTROL(Name) set_controlList(index++, &f##Name##Control, \
930 MyClick::k##Name##Control)
931
932 bool hideAll() {
933 fHideAll ^= true;
caryclark64022c12016-05-27 05:13:26 -0700934 return true;
935 }
936
937 void init_controlList() {
938 int index = 0;
939 SET_CONTROL(Width);
940 SET_CONTROL(Res);
941 SET_CONTROL(Filter);
942 SET_CONTROL(Weight);
Brian Osman16adfa32016-10-18 14:42:44 -0400943 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400944
caryclark64022c12016-05-27 05:13:26 -0700945 #undef SET_CONTROL
946
947 void set_buttonList(int index, Button* button, MyClick::ControlType type) {
948 kButtonList[index].fButton = button;
949 kButtonList[index].fButtonType = type;
950 }
951
952 #define SET_BUTTON(Name) set_buttonList(index++, &f##Name##Button, \
953 MyClick::k##Name##Button)
954
955 void init_buttonList() {
956 int index = 0;
957 SET_BUTTON(Fill);
958 SET_BUTTON(Skeleton);
959 SET_BUTTON(Filter);
960 SET_BUTTON(Bisect);
961 SET_BUTTON(Join);
962 SET_BUTTON(InOut);
963 SET_BUTTON(Cubic);
964 SET_BUTTON(Conic);
965 SET_BUTTON(Quad);
966 SET_BUTTON(Line);
967 SET_BUTTON(Add);
968 SET_BUTTON(Delete);
969 }
970
971 #undef SET_BUTTON
972
973 // overrides from SkEventSink
974 bool onQuery(SkEvent* evt) override;
Ben Wagner63fd7602017-10-09 15:45:33 -0400975
caryclark64022c12016-05-27 05:13:26 -0700976 void onSizeChange() override {
977 setControlButtonsPos();
978 this->INHERITED::onSizeChange();
979 }
980
981 bool pathDump() {
982 fPath.dump();
983 return true;
984 }
985
986 bool scaleDown() {
987 SkMatrix matrix;
988 SkRect bounds = fPath.getBounds();
989 matrix.setScale(1.f / 1.5f, 1.f / 1.5f, bounds.centerX(), bounds.centerY());
990 fPath.transform(matrix);
991 validatePath();
caryclark64022c12016-05-27 05:13:26 -0700992 return true;
993 }
994
995 bool scaleToFit() {
996 SkMatrix matrix;
997 SkRect bounds = fPath.getBounds();
998 SkScalar scale = SkTMin(this->width() / bounds.width(), this->height() / bounds.height())
999 * 0.8f;
1000 matrix.setScale(scale, scale, bounds.centerX(), bounds.centerY());
1001 fPath.transform(matrix);
1002 bounds = fPath.getBounds();
1003 SkScalar offsetX = (this->width() - bounds.width()) / 2 - bounds.fLeft;
1004 SkScalar offsetY = (this->height() - bounds.height()) / 2 - bounds.fTop;
1005 fPath.offset(offsetX, offsetY);
1006 validatePath();
caryclark64022c12016-05-27 05:13:26 -07001007 return true;
1008 }
1009
1010 bool scaleUp() {
1011 SkMatrix matrix;
1012 SkRect bounds = fPath.getBounds();
1013 matrix.setScale(1.5f, 1.5f, bounds.centerX(), bounds.centerY());
1014 fPath.transform(matrix);
1015 validatePath();
caryclark64022c12016-05-27 05:13:26 -07001016 return true;
1017 }
1018
1019 void setControlButtonsPos() {
1020 SkScalar widthOffset = this->width() - 100;
1021 for (int index = 0; index < kControlCount; ++index) {
1022 if (kControlList[index].fControl->fVisible) {
1023 kControlList[index].fControl->fBounds.setXYWH(widthOffset, 30, 30, 400);
1024 widthOffset -= 50;
1025 }
1026 }
1027 SkScalar buttonOffset = 0;
1028 for (int index = 0; index < kButtonCount; ++index) {
1029 kButtonList[index].fButton->fBounds.setXYWH(this->width() - 50,
1030 buttonOffset += 50, 30, 30);
1031 }
1032 }
1033
1034 bool showLegend() {
1035 fShowLegend ^= true;
caryclark64022c12016-05-27 05:13:26 -07001036 return true;
1037 }
1038
1039 void draw_bisect(SkCanvas* canvas, const SkVector& lastVector, const SkVector& vector,
1040 const SkPoint& pt) {
1041 SkVector lastV = lastVector;
1042 SkScalar lastLen = lastVector.length();
1043 SkVector nextV = vector;
1044 SkScalar nextLen = vector.length();
1045 if (lastLen < nextLen) {
1046 lastV.setLength(nextLen);
1047 } else {
1048 nextV.setLength(lastLen);
1049 }
1050
1051 SkVector bisect = { (lastV.fX + nextV.fX) / 2, (lastV.fY + nextV.fY) / 2 };
1052 bisect.setLength(fWidthControl.fValLo * 2);
1053 if (fBisectButton.enabled()) {
Hal Canary23e474c2017-05-15 13:35:35 -04001054 canvas->drawLine(pt, pt + bisect, fSkeletonPaint);
caryclark64022c12016-05-27 05:13:26 -07001055 }
1056 lastV.setLength(fWidthControl.fValLo);
1057 if (fBisectButton.enabled()) {
Hal Canary23e474c2017-05-15 13:35:35 -04001058 canvas->drawLine(pt, {pt.fX - lastV.fY, pt.fY + lastV.fX}, fSkeletonPaint);
caryclark64022c12016-05-27 05:13:26 -07001059 }
1060 nextV.setLength(fWidthControl.fValLo);
1061 if (fBisectButton.enabled()) {
Hal Canary23e474c2017-05-15 13:35:35 -04001062 canvas->drawLine(pt, {pt.fX + nextV.fY, pt.fY - nextV.fX}, fSkeletonPaint);
caryclark64022c12016-05-27 05:13:26 -07001063 }
1064 if (fJoinButton.enabled()) {
1065 SkScalar r = fWidthControl.fValLo;
1066 SkRect oval = { pt.fX - r, pt.fY - r, pt.fX + r, pt.fY + r};
1067 SkScalar startAngle = SkScalarATan2(lastV.fX, -lastV.fY) * 180.f / SK_ScalarPI;
1068 SkScalar endAngle = SkScalarATan2(-nextV.fX, nextV.fY) * 180.f / SK_ScalarPI;
1069 if (endAngle > startAngle) {
1070 canvas->drawArc(oval, startAngle, endAngle - startAngle, false, fSkeletonPaint);
1071 } else {
1072 canvas->drawArc(oval, startAngle, 360 - (startAngle - endAngle), false,
1073 fSkeletonPaint);
1074 }
1075 }
1076 }
1077
1078 void draw_bisects(SkCanvas* canvas, bool activeOnly) {
1079 SkVector firstVector, lastVector, nextLast, vector;
1080 SkPoint pts[4];
1081 SkPoint firstPt = { 0, 0 }; // init to avoid warning;
1082 SkPath::Verb verb;
1083 SkPath::Iter iter(fPath, true);
1084 bool foundFirst = false;
1085 int counter = -1;
1086 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1087 ++counter;
1088 if (activeOnly && counter != fActiveVerb && counter - 1 != fActiveVerb
1089 && counter + 1 != fActiveVerb
1090 && (fActiveVerb != 1 || counter != fPath.countVerbs())) {
1091 continue;
1092 }
1093 switch (verb) {
1094 case SkPath::kLine_Verb:
1095 nextLast = pts[0] - pts[1];
1096 vector = pts[1] - pts[0];
1097 break;
1098 case SkPath::kQuad_Verb: {
1099 nextLast = pts[1] - pts[2];
1100 if (SkScalarNearlyZero(nextLast.length())) {
1101 nextLast = pts[0] - pts[2];
1102 }
1103 vector = pts[1] - pts[0];
1104 if (SkScalarNearlyZero(vector.length())) {
1105 vector = pts[2] - pts[0];
1106 }
1107 if (!fBisectButton.enabled()) {
1108 break;
1109 }
1110 SkScalar t = SkFindQuadMaxCurvature(pts);
1111 if (0 < t && t < 1) {
1112 SkPoint maxPt = SkEvalQuadAt(pts, t);
1113 SkVector tangent = SkEvalQuadTangentAt(pts, t);
1114 tangent.setLength(fWidthControl.fValLo * 2);
Hal Canary23e474c2017-05-15 13:35:35 -04001115 canvas->drawLine(maxPt, {maxPt.fX + tangent.fY, maxPt.fY - tangent.fX},
1116 fSkeletonPaint);
caryclark64022c12016-05-27 05:13:26 -07001117 }
1118 } break;
1119 case SkPath::kConic_Verb:
1120 nextLast = pts[1] - pts[2];
1121 if (SkScalarNearlyZero(nextLast.length())) {
1122 nextLast = pts[0] - pts[2];
1123 }
1124 vector = pts[1] - pts[0];
1125 if (SkScalarNearlyZero(vector.length())) {
1126 vector = pts[2] - pts[0];
1127 }
1128 if (!fBisectButton.enabled()) {
1129 break;
1130 }
1131 // FIXME : need max curvature or equivalent here
1132 break;
1133 case SkPath::kCubic_Verb: {
1134 nextLast = pts[2] - pts[3];
1135 if (SkScalarNearlyZero(nextLast.length())) {
1136 nextLast = pts[1] - pts[3];
1137 if (SkScalarNearlyZero(nextLast.length())) {
1138 nextLast = pts[0] - pts[3];
1139 }
1140 }
1141 vector = pts[0] - pts[1];
1142 if (SkScalarNearlyZero(vector.length())) {
1143 vector = pts[0] - pts[2];
1144 if (SkScalarNearlyZero(vector.length())) {
1145 vector = pts[0] - pts[3];
1146 }
1147 }
1148 if (!fBisectButton.enabled()) {
1149 break;
1150 }
1151 SkScalar tMax[2];
1152 int tMaxCount = SkFindCubicMaxCurvature(pts, tMax);
1153 for (int tIndex = 0; tIndex < tMaxCount; ++tIndex) {
1154 if (0 >= tMax[tIndex] || tMax[tIndex] >= 1) {
1155 continue;
1156 }
1157 SkPoint maxPt;
1158 SkVector tangent;
Ben Wagnera93a14a2017-08-28 10:34:05 -04001159 SkEvalCubicAt(pts, tMax[tIndex], &maxPt, &tangent, nullptr);
caryclark64022c12016-05-27 05:13:26 -07001160 tangent.setLength(fWidthControl.fValLo * 2);
Hal Canary23e474c2017-05-15 13:35:35 -04001161 canvas->drawLine(maxPt, {maxPt.fX + tangent.fY, maxPt.fY - tangent.fX},
1162 fSkeletonPaint);
caryclark64022c12016-05-27 05:13:26 -07001163 }
1164 } break;
1165 case SkPath::kClose_Verb:
1166 if (foundFirst) {
1167 draw_bisect(canvas, lastVector, firstVector, firstPt);
1168 foundFirst = false;
1169 }
1170 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001171 default:
caryclark64022c12016-05-27 05:13:26 -07001172 break;
1173 }
1174 if (SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb) {
1175 if (!foundFirst) {
1176 firstPt = pts[0];
1177 firstVector = vector;
1178 foundFirst = true;
1179 } else {
1180 draw_bisect(canvas, lastVector, vector, pts[0]);
1181 }
1182 lastVector = nextLast;
1183 }
1184 }
1185 }
1186
1187 void draw_legend(SkCanvas* canvas);
1188
1189 void draw_segment(SkCanvas* canvas) {
1190 SkPoint pts[4];
1191 SkPath::Verb verb;
1192 SkPath::Iter iter(fPath, true);
1193 int counter = -1;
1194 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1195 if (++counter < fActiveVerb) {
1196 continue;
1197 }
1198 switch (verb) {
1199 case SkPath::kLine_Verb:
1200 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, fActivePaint);
1201 draw_points(canvas, pts, 2);
1202 break;
1203 case SkPath::kQuad_Verb: {
1204 SkPath qPath;
1205 qPath.moveTo(pts[0]);
1206 qPath.quadTo(pts[1], pts[2]);
1207 canvas->drawPath(qPath, fActivePaint);
1208 draw_points(canvas, pts, 3);
1209 } break;
1210 case SkPath::kConic_Verb: {
1211 SkPath conicPath;
1212 conicPath.moveTo(pts[0]);
1213 conicPath.conicTo(pts[1], pts[2], iter.conicWeight());
1214 canvas->drawPath(conicPath, fActivePaint);
1215 draw_points(canvas, pts, 3);
1216 } break;
1217 case SkPath::kCubic_Verb: {
Cary Clark7eb01e02016-12-08 14:36:32 -05001218 SkScalar loopT[3];
1219 int complex = SkDCubic::ComplexBreak(pts, loopT);
caryclark64022c12016-05-27 05:13:26 -07001220 SkPath cPath;
1221 cPath.moveTo(pts[0]);
1222 cPath.cubicTo(pts[1], pts[2], pts[3]);
1223 canvas->drawPath(cPath, complex ? fComplexPaint : fActivePaint);
1224 draw_points(canvas, pts, 4);
1225 } break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001226 default:
caryclark64022c12016-05-27 05:13:26 -07001227 break;
1228 }
1229 return;
1230 }
1231 }
1232
1233 void draw_points(SkCanvas* canvas, SkPoint* points, int count) {
1234 for (int index = 0; index < count; ++index) {
1235 canvas->drawCircle(points[index].fX, points[index].fY, 10, fPointPaint);
1236 }
1237 }
1238
1239 int hittest_verb(SkPoint pt, SkPath::Verb* verbPtr, SkScalar* weight) {
1240 SkIntersections i;
1241 SkDLine hHit = {{{pt.fX - kHitToleranace, pt.fY }, {pt.fX + kHitToleranace, pt.fY}}};
1242 SkDLine vHit = {{{pt.fX, pt.fY - kHitToleranace }, {pt.fX, pt.fY + kHitToleranace}}};
1243 SkPoint pts[4];
1244 SkPath::Verb verb;
1245 SkPath::Iter iter(fPath, true);
1246 int counter = -1;
1247 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1248 ++counter;
1249 switch (verb) {
1250 case SkPath::kLine_Verb: {
1251 SkDLine line;
1252 line.set(pts);
1253 if (i.intersect(line, hHit) || i.intersect(line, vHit)) {
1254 *verbPtr = verb;
1255 *weight = 1;
1256 return counter;
1257 }
1258 } break;
1259 case SkPath::kQuad_Verb: {
1260 SkDQuad quad;
1261 quad.set(pts);
1262 if (i.intersect(quad, hHit) || i.intersect(quad, vHit)) {
1263 *verbPtr = verb;
1264 *weight = 1;
1265 return counter;
1266 }
1267 } break;
1268 case SkPath::kConic_Verb: {
1269 SkDConic conic;
1270 SkScalar w = iter.conicWeight();
1271 conic.set(pts, w);
1272 if (i.intersect(conic, hHit) || i.intersect(conic, vHit)) {
1273 *verbPtr = verb;
1274 *weight = w;
1275 return counter;
1276 }
1277 } break;
1278 case SkPath::kCubic_Verb: {
1279 SkDCubic cubic;
1280 cubic.set(pts);
1281 if (i.intersect(cubic, hHit) || i.intersect(cubic, vHit)) {
1282 *verbPtr = verb;
1283 *weight = 1;
1284 return counter;
1285 }
1286 } break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001287 default:
caryclark64022c12016-05-27 05:13:26 -07001288 break;
1289 }
1290 }
1291 return -1;
1292 }
1293
1294 SkScalar pt_to_line(SkPoint s, SkPoint e, int x, int y) {
1295 SkScalar radius = fWidthControl.fValLo;
1296 SkVector adjOpp = e - s;
Cary Clarkdf429f32017-11-08 11:44:31 -05001297 SkScalar lenSq = SkPointPriv::LengthSqd(adjOpp);
caryclark64022c12016-05-27 05:13:26 -07001298 SkPoint rotated = {
1299 (y - s.fY) * adjOpp.fY + (x - s.fX) * adjOpp.fX,
1300 (y - s.fY) * adjOpp.fX - (x - s.fX) * adjOpp.fY,
1301 };
1302 if (rotated.fX < 0 || rotated.fX > lenSq) {
1303 return -radius;
1304 }
1305 rotated.fY /= SkScalarSqrt(lenSq);
1306 return SkTMax(-radius, SkTMin(radius, rotated.fY));
1307 }
1308
1309 // given a line, compute the interior and exterior gradient coverage
1310 bool coverage(SkPoint s, SkPoint e, uint8_t* distanceMap, int w, int h) {
1311 SkScalar radius = fWidthControl.fValLo;
1312 int minX = SkTMax(0, (int) (SkTMin(s.fX, e.fX) - radius));
1313 int minY = SkTMax(0, (int) (SkTMin(s.fY, e.fY) - radius));
1314 int maxX = SkTMin(w, (int) (SkTMax(s.fX, e.fX) + radius) + 1);
1315 int maxY = SkTMin(h, (int) (SkTMax(s.fY, e.fY) + radius) + 1);
1316 for (int y = minY; y < maxY; ++y) {
1317 for (int x = minX; x < maxX; ++x) {
1318 SkScalar ptToLineDist = pt_to_line(s, e, x, y);
1319 if (ptToLineDist > -radius && ptToLineDist < radius) {
1320 SkScalar coverage = ptToLineDist / radius;
1321 add_to_map(1 - SkScalarAbs(coverage), x, y, distanceMap, w, h);
1322 }
1323 SkVector ptToS = { x - s.fX, y - s.fY };
1324 SkScalar dist = ptToS.length();
1325 if (dist < radius) {
1326 SkScalar coverage = dist / radius;
1327 add_to_map(1 - SkScalarAbs(coverage), x, y, distanceMap, w, h);
1328 }
1329 SkVector ptToE = { x - e.fX, y - e.fY };
1330 dist = ptToE.length();
1331 if (dist < radius) {
1332 SkScalar coverage = dist / radius;
1333 add_to_map(1 - SkScalarAbs(coverage), x, y, distanceMap, w, h);
1334 }
1335 }
1336 }
1337 return true;
1338 }
1339
1340 void quad_coverage(SkPoint pts[3], uint8_t* distanceMap, int w, int h) {
1341 SkScalar dist = pts[0].Distance(pts[0], pts[2]);
1342 if (dist < gCurveDistance) {
1343 (void) coverage(pts[0], pts[2], distanceMap, w, h);
1344 return;
1345 }
1346 SkPoint split[5];
1347 SkChopQuadAt(pts, split, 0.5f);
1348 quad_coverage(&split[0], distanceMap, w, h);
1349 quad_coverage(&split[2], distanceMap, w, h);
1350 }
1351
1352 void conic_coverage(SkPoint pts[3], SkScalar weight, uint8_t* distanceMap, int w, int h) {
1353 SkScalar dist = pts[0].Distance(pts[0], pts[2]);
1354 if (dist < gCurveDistance) {
1355 (void) coverage(pts[0], pts[2], distanceMap, w, h);
1356 return;
1357 }
1358 SkConic split[2];
1359 SkConic conic;
1360 conic.set(pts, weight);
caryclark414c4292016-09-26 11:03:54 -07001361 if (conic.chopAt(0.5f, split)) {
1362 conic_coverage(split[0].fPts, split[0].fW, distanceMap, w, h);
1363 conic_coverage(split[1].fPts, split[1].fW, distanceMap, w, h);
1364 }
caryclark64022c12016-05-27 05:13:26 -07001365 }
1366
1367 void cubic_coverage(SkPoint pts[4], uint8_t* distanceMap, int w, int h) {
1368 SkScalar dist = pts[0].Distance(pts[0], pts[3]);
1369 if (dist < gCurveDistance) {
1370 (void) coverage(pts[0], pts[3], distanceMap, w, h);
1371 return;
1372 }
1373 SkPoint split[7];
1374 SkChopCubicAt(pts, split, 0.5f);
1375 cubic_coverage(&split[0], distanceMap, w, h);
1376 cubic_coverage(&split[3], distanceMap, w, h);
1377 }
1378
1379 void path_coverage(const SkPath& path, uint8_t* distanceMap, int w, int h) {
1380 memset(distanceMap, 0, sizeof(distanceMap[0]) * w * h);
1381 SkPoint pts[4];
1382 SkPath::Verb verb;
1383 SkPath::Iter iter(path, true);
1384 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1385 switch (verb) {
1386 case SkPath::kLine_Verb:
1387 (void) coverage(pts[0], pts[1], distanceMap, w, h);
1388 break;
1389 case SkPath::kQuad_Verb:
1390 quad_coverage(pts, distanceMap, w, h);
1391 break;
1392 case SkPath::kConic_Verb:
1393 conic_coverage(pts, iter.conicWeight(), distanceMap, w, h);
1394 break;
1395 case SkPath::kCubic_Verb:
1396 cubic_coverage(pts, distanceMap, w, h);
1397 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001398 default:
caryclark64022c12016-05-27 05:13:26 -07001399 break;
1400 }
1401 }
1402 }
1403
1404 static uint8_t* set_up_dist_map(const SkImageInfo& imageInfo, SkBitmap* distMap) {
1405 distMap->setInfo(imageInfo);
1406 distMap->setIsVolatile(true);
1407 SkAssertResult(distMap->tryAllocPixels());
1408 SkASSERT((int) distMap->rowBytes() == imageInfo.width());
1409 return distMap->getAddr8(0, 0);
1410 }
1411
1412 void path_stroke(int index, SkPath* inner, SkPath* outer) {
1413 #if 0
1414 SkPathStroker stroker(fPath, fWidthControl.fValLo, 0,
1415 SkPaint::kRound_Cap, SkPaint::kRound_Join, fResControl.fValLo);
1416 SkPoint pts[4], firstPt, lastPt;
1417 SkPath::Verb verb;
1418 SkPath::Iter iter(fPath, true);
1419 int counter = -1;
1420 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1421 ++counter;
1422 switch (verb) {
1423 case SkPath::kMove_Verb:
1424 firstPt = pts[0];
1425 break;
1426 case SkPath::kLine_Verb:
1427 if (counter == index) {
1428 stroker.moveTo(pts[0]);
1429 stroker.lineTo(pts[1]);
1430 goto done;
1431 }
1432 lastPt = pts[1];
1433 break;
1434 case SkPath::kQuad_Verb:
1435 if (counter == index) {
1436 stroker.moveTo(pts[0]);
1437 stroker.quadTo(pts[1], pts[2]);
1438 goto done;
1439 }
1440 lastPt = pts[2];
1441 break;
1442 case SkPath::kConic_Verb:
1443 if (counter == index) {
1444 stroker.moveTo(pts[0]);
1445 stroker.conicTo(pts[1], pts[2], iter.conicWeight());
1446 goto done;
1447 }
1448 lastPt = pts[2];
1449 break;
1450 case SkPath::kCubic_Verb:
1451 if (counter == index) {
1452 stroker.moveTo(pts[0]);
1453 stroker.cubicTo(pts[1], pts[2], pts[3]);
1454 goto done;
1455 }
1456 lastPt = pts[3];
1457 break;
1458 case SkPath::kClose_Verb:
1459 if (counter == index) {
1460 stroker.moveTo(lastPt);
1461 stroker.lineTo(firstPt);
1462 goto done;
1463 }
1464 break;
1465 case SkPath::kDone_Verb:
1466 break;
1467 default:
1468 SkASSERT(0);
1469 }
1470 }
1471 done:
1472 *inner = stroker.fInner;
1473 *outer = stroker.fOuter;
1474#endif
1475 }
1476
1477 void draw_stroke(SkCanvas* canvas, int active) {
1478 SkPath inner, outer;
1479 path_stroke(active, &inner, &outer);
1480 canvas->drawPath(inner, fSkeletonPaint);
1481 canvas->drawPath(outer, fSkeletonPaint);
1482 }
1483
1484 void gather_strokes() {
1485 fStrokes.reset();
1486 for (int index = 0; index < fPath.countVerbs(); ++index) {
1487 Stroke& inner = fStrokes.push_back();
1488 inner.reset();
1489 inner.fInner = true;
1490 Stroke& outer = fStrokes.push_back();
1491 outer.reset();
1492 outer.fInner = false;
1493 path_stroke(index, &inner.fPath, &outer.fPath);
1494 }
1495 }
1496
1497 void trim_strokes() {
1498 // eliminate self-itersecting loops
1499 // trim outside edges
1500 gather_strokes();
1501 for (int index = 0; index < fStrokes.count(); ++index) {
1502 SkPath& outPath = fStrokes[index].fPath;
1503 for (int inner = 0; inner < fStrokes.count(); ++inner) {
1504 if (index == inner) {
1505 continue;
1506 }
1507 SkPath& inPath = fStrokes[inner].fPath;
1508 if (!outPath.getBounds().intersects(inPath.getBounds())) {
1509 continue;
1510 }
Ben Wagner63fd7602017-10-09 15:45:33 -04001511
caryclark64022c12016-05-27 05:13:26 -07001512 }
1513 }
1514 }
1515
1516 void onDrawContent(SkCanvas* canvas) override {
1517#if 0
1518 SkDEBUGCODE(SkDebugStrokeGlobals debugGlobals);
1519 SkOpAA aaResult(fPath, fWidthControl.fValLo, fResControl.fValLo
1520 SkDEBUGPARAMS(&debugGlobals));
1521#endif
1522 SkPath strokePath;
1523// aaResult.simplify(&strokePath);
1524 canvas->drawPath(strokePath, fSkeletonPaint);
1525 SkRect bounds = fPath.getBounds();
1526 SkScalar radius = fWidthControl.fValLo;
1527 int w = (int) (bounds.fRight + radius + 1);
1528 int h = (int) (bounds.fBottom + radius + 1);
1529 SkImageInfo imageInfo = SkImageInfo::MakeA8(w, h);
1530 SkBitmap distMap;
1531 uint8_t* distanceMap = set_up_dist_map(imageInfo, &distMap);
1532 path_coverage(fPath, distanceMap, w, h);
1533 if (fFillButton.enabled()) {
1534 canvas->drawPath(fPath, fCoveragePaint);
1535 }
1536 if (fFilterButton.fState == 2
1537 && (0 < fFilterControl.fValLo || fFilterControl.fValHi < 255)) {
1538 SkBitmap filteredMap;
1539 uint8_t* filtered = set_up_dist_map(imageInfo, &filteredMap);
1540 filter_coverage(distanceMap, sizeof(uint8_t) * w * h, (uint8_t) fFilterControl.fValLo,
1541 (uint8_t) fFilterControl.fValHi, filtered);
1542 canvas->drawBitmap(filteredMap, 0, 0, &fCoveragePaint);
1543 } else if (fFilterButton.enabled()) {
1544 canvas->drawBitmap(distMap, 0, 0, &fCoveragePaint);
1545 }
1546 if (fSkeletonButton.enabled()) {
1547 canvas->drawPath(fPath, fActiveVerb >= 0 ? fLightSkeletonPaint : fSkeletonPaint);
1548 }
1549 if (fActiveVerb >= 0) {
1550 draw_segment(canvas);
1551 }
1552 if (fBisectButton.enabled() || fJoinButton.enabled()) {
1553 draw_bisects(canvas, fActiveVerb >= 0);
1554 }
1555 if (fInOutButton.enabled()) {
1556 if (fActiveVerb >= 0) {
1557 draw_stroke(canvas, fActiveVerb);
1558 } else {
1559 for (int index = 0; index < fPath.countVerbs(); ++index) {
1560 draw_stroke(canvas, index);
1561 }
1562 }
1563 }
1564 if (fHideAll) {
1565 return;
1566 }
1567 for (int index = 0; index < kControlCount; ++index) {
1568 kControlList[index].fControl->draw(canvas, fControlPaints);
1569 }
1570 for (int index = 0; index < kButtonCount; ++index) {
1571 kButtonList[index].fButton->draw(canvas, fButtonPaints);
1572 }
1573 if (fShowLegend) {
1574 draw_legend(canvas);
1575 }
1576
1577#if 0
1578 SkPaint paint;
1579 paint.setARGB(255, 34, 31, 31);
1580 paint.setAntiAlias(true);
1581
1582 SkPath path;
1583 path.moveTo(18,439);
1584 path.lineTo(414,439);
1585 path.lineTo(414,702);
1586 path.lineTo(18,702);
1587 path.lineTo(18,439);
1588
1589 path.moveTo(19,701);
1590 path.lineTo(413,701);
1591 path.lineTo(413,440);
1592 path.lineTo(19,440);
1593 path.lineTo(19,701);
1594 path.close();
1595 canvas->drawPath(path, paint);
1596
1597 canvas->scale(1.0f, -1.0f);
1598 canvas->translate(0.0f, -800.0f);
1599 canvas->drawPath(path, paint);
1600#endif
1601
1602 }
1603
1604 int hittest_pt(SkPoint pt) {
1605 for (int index = 0; index < fPath.countPoints(); ++index) {
1606 if (SkPoint::Distance(fPath.getPoint(index), pt) <= kHitToleranace * 2) {
1607 return index;
1608 }
1609 }
1610 return -1;
1611 }
1612
1613 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
1614 SkPoint pt = {x, y};
1615 int ptHit = hittest_pt(pt);
1616 if (ptHit >= 0) {
1617 return new MyClick(this, MyClick::kPtType, ptHit);
1618 }
1619 SkPath::Verb verb;
1620 SkScalar weight;
1621 int verbHit = hittest_verb(pt, &verb, &weight);
1622 if (verbHit >= 0) {
1623 return new MyClick(this, MyClick::kVerbType, verbHit, verb, weight);
1624 }
1625 if (!fHideAll) {
1626 const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1);
1627 for (int index = 0; index < kControlCount; ++index) {
1628 if (kControlList[index].fControl->contains(rectPt)) {
1629 return new MyClick(this, MyClick::kControlType,
1630 kControlList[index].fControlType);
1631 }
1632 }
1633 for (int index = 0; index < kButtonCount; ++index) {
1634 if (kButtonList[index].fButton->contains(rectPt)) {
1635 return new MyClick(this, MyClick::kControlType, kButtonList[index].fButtonType);
1636 }
1637 }
1638 }
1639 fLineButton.fVisible = fQuadButton.fVisible = fConicButton.fVisible
1640 = fCubicButton.fVisible = fWeightControl.fVisible = fAddButton.fVisible
1641 = fDeleteButton.fVisible = false;
1642 fActiveVerb = -1;
1643 fActivePt = -1;
1644 if (fHandlePathMove) {
1645 return new MyClick(this, MyClick::kPathType, MyClick::kPathMove);
1646 }
1647 return this->INHERITED::onFindClickHandler(x, y, modi);
1648 }
1649
1650 static SkScalar MapScreenYtoValue(int y, const UniControl& control) {
1651 return SkTMin(1.f, SkTMax(0.f,
1652 SkIntToScalar(y) - control.fBounds.fTop) / control.fBounds.height())
1653 * (control.fMax - control.fMin) + control.fMin;
1654 }
1655
1656 bool onClick(Click* click) override {
1657 MyClick* myClick = (MyClick*) click;
1658 switch (myClick->fType) {
1659 case MyClick::kPtType: {
1660 savePath(click->fState);
1661 fActivePt = myClick->ptHit();
1662 SkPoint pt = fPath.getPoint((int) myClick->fControl);
1663 pt.offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX),
1664 SkIntToScalar(click->fICurr.fY - click->fIPrev.fY));
1665 set_path_pt(fActivePt, pt, &fPath);
1666 validatePath();
caryclark64022c12016-05-27 05:13:26 -07001667 return true;
1668 }
1669 case MyClick::kPathType:
1670 savePath(click->fState);
1671 fPath.offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX),
1672 SkIntToScalar(click->fICurr.fY - click->fIPrev.fY));
1673 validatePath();
caryclark64022c12016-05-27 05:13:26 -07001674 return true;
1675 case MyClick::kVerbType: {
1676 fActiveVerb = myClick->verbHit();
1677 fLineButton.fVisible = fQuadButton.fVisible = fConicButton.fVisible
1678 = fCubicButton.fVisible = fAddButton.fVisible = fDeleteButton.fVisible
1679 = true;
1680 fLineButton.setEnabled(myClick->fVerb == SkPath::kLine_Verb);
1681 fQuadButton.setEnabled(myClick->fVerb == SkPath::kQuad_Verb);
1682 fConicButton.setEnabled(myClick->fVerb == SkPath::kConic_Verb);
1683 fCubicButton.setEnabled(myClick->fVerb == SkPath::kCubic_Verb);
1684 fWeightControl.fValLo = myClick->fWeight;
1685 fWeightControl.fVisible = myClick->fVerb == SkPath::kConic_Verb;
1686 } break;
1687 case MyClick::kControlType: {
1688 if (click->fState != Click::kDown_State && myClick->isButton()) {
1689 return true;
1690 }
1691 switch (myClick->fControl) {
1692 case MyClick::kFilterControl: {
1693 SkScalar val = MapScreenYtoValue(click->fICurr.fY, fFilterControl);
1694 if (val - fFilterControl.fValLo < fFilterControl.fValHi - val) {
1695 fFilterControl.fValLo = SkTMax(0.f, val);
1696 } else {
1697 fFilterControl.fValHi = SkTMin(255.f, val);
1698 }
1699 } break;
1700 case MyClick::kResControl:
1701 fResControl.fValLo = MapScreenYtoValue(click->fICurr.fY, fResControl);
1702 break;
1703 case MyClick::kWeightControl: {
1704 savePath(click->fState);
1705 SkScalar w = MapScreenYtoValue(click->fICurr.fY, fWeightControl);
1706 set_path_weight(fActiveVerb, w, &fPath);
1707 validatePath();
1708 fWeightControl.fValLo = w;
1709 } break;
1710 case MyClick::kWidthControl:
1711 fWidthControl.fValLo = MapScreenYtoValue(click->fICurr.fY, fWidthControl);
1712 break;
1713 case MyClick::kLineButton:
1714 savePath(click->fState);
1715 enable_verb_button(myClick->fControl);
1716 fWeightControl.fVisible = false;
1717 set_path_verb(fActiveVerb, SkPath::kLine_Verb, &fPath, 1);
1718 validatePath();
1719 break;
1720 case MyClick::kQuadButton:
1721 savePath(click->fState);
1722 enable_verb_button(myClick->fControl);
1723 fWeightControl.fVisible = false;
1724 set_path_verb(fActiveVerb, SkPath::kQuad_Verb, &fPath, 1);
1725 validatePath();
1726 break;
1727 case MyClick::kConicButton: {
1728 savePath(click->fState);
1729 enable_verb_button(myClick->fControl);
1730 fWeightControl.fVisible = true;
1731 const SkScalar defaultConicWeight = 1.f / SkScalarSqrt(2);
1732 set_path_verb(fActiveVerb, SkPath::kConic_Verb, &fPath, defaultConicWeight);
1733 validatePath();
1734 fWeightControl.fValLo = get_path_weight(fActiveVerb, fPath);
1735 } break;
1736 case MyClick::kCubicButton:
1737 savePath(click->fState);
1738 enable_verb_button(myClick->fControl);
1739 fWeightControl.fVisible = false;
1740 set_path_verb(fActiveVerb, SkPath::kCubic_Verb, &fPath, 1);
1741 validatePath();
1742 break;
1743 case MyClick::kAddButton:
1744 savePath(click->fState);
1745 add_path_segment(fActiveVerb, &fPath);
1746 validatePath();
1747 if (fWeightControl.fVisible) {
1748 fWeightControl.fValLo = get_path_weight(fActiveVerb, fPath);
1749 }
1750 break;
1751 case MyClick::kDeleteButton:
1752 savePath(click->fState);
1753 delete_path_segment(fActiveVerb, &fPath);
1754 validatePath();
1755 break;
1756 case MyClick::kFillButton:
1757 fFillButton.toggle();
1758 break;
1759 case MyClick::kSkeletonButton:
1760 fSkeletonButton.toggle();
1761 break;
1762 case MyClick::kFilterButton:
1763 fFilterButton.toggle();
1764 fFilterControl.fVisible = fFilterButton.fState == 2;
1765 break;
1766 case MyClick::kBisectButton:
1767 fBisectButton.toggle();
1768 break;
1769 case MyClick::kJoinButton:
1770 fJoinButton.toggle();
1771 break;
1772 case MyClick::kInOutButton:
1773 fInOutButton.toggle();
1774 break;
1775 default:
1776 SkASSERT(0);
1777 break;
1778 }
1779 } break;
1780 default:
1781 SkASSERT(0);
1782 break;
1783 }
1784 setControlButtonsPos();
caryclark64022c12016-05-27 05:13:26 -07001785 return true;
1786 }
1787
1788private:
1789 typedef SampleView INHERITED;
1790};
1791
1792static struct KeyCommand {
1793 char fKey;
1794 char fAlternate;
1795 const char* fDescriptionL;
1796 const char* fDescriptionR;
1797 bool (AAGeometryView::*fFunction)();
1798} kKeyCommandList[] = {
Ben Wagner63fd7602017-10-09 15:45:33 -04001799 { ' ', 0, "space", "center path", &AAGeometryView::scaleToFit },
1800 { '-', 0, "-", "zoom out", &AAGeometryView::scaleDown },
1801 { '+', '=', "+/=", "zoom in", &AAGeometryView::scaleUp },
Chris Dalton08d1a252017-10-20 11:46:47 -06001802 { 'D', 0, "D", "dump to console", &AAGeometryView::pathDump },
1803 { 'H', 0, "H", "hide controls", &AAGeometryView::hideAll },
1804 { 'R', 0, "R", "reset path", &AAGeometryView::constructPath },
1805 { 'Z', 0, "Z", "undo", &AAGeometryView::undo },
caryclark64022c12016-05-27 05:13:26 -07001806 { '?', 0, "?", "show legend", &AAGeometryView::showLegend },
1807};
1808
1809const int kKeyCommandCount = (int) SK_ARRAY_COUNT(kKeyCommandList);
1810
1811void AAGeometryView::draw_legend(SkCanvas* canvas) {
1812 SkScalar bottomOffset = this->height() - 10;
1813 for (int index = kKeyCommandCount - 1; index >= 0; --index) {
1814 bottomOffset -= 15;
Cary Clark2a475ea2017-04-28 15:35:12 -04001815 canvas->drawString(kKeyCommandList[index].fDescriptionL,
1816 this->width() - 160, bottomOffset,
caryclark64022c12016-05-27 05:13:26 -07001817 fLegendLeftPaint);
Cary Clark2a475ea2017-04-28 15:35:12 -04001818 canvas->drawString(kKeyCommandList[index].fDescriptionR,
1819 this->width() - 20, bottomOffset,
caryclark64022c12016-05-27 05:13:26 -07001820 fLegendRightPaint);
1821 }
1822}
1823
1824// overrides from SkEventSink
1825bool AAGeometryView::onQuery(SkEvent* evt) {
1826 if (SampleCode::TitleQ(*evt)) {
1827 SampleCode::TitleR(evt, "AAGeometry");
1828 return true;
1829 }
1830 SkUnichar uni;
1831 if (false) {
1832 return this->INHERITED::onQuery(evt);
1833 }
1834 if (SampleCode::CharQ(*evt, &uni)) {
1835 for (int index = 0; index < kButtonCount; ++index) {
1836 Button* button = kButtonList[index].fButton;
1837 if (button->fVisible && uni == button->fLabel) {
1838 MyClick click(this, MyClick::kControlType, kButtonList[index].fButtonType);
1839 click.fState = Click::kDown_State;
1840 (void) this->onClick(&click);
1841 return true;
1842 }
1843 }
1844 for (int index = 0; index < kKeyCommandCount; ++index) {
1845 KeyCommand& keyCommand = kKeyCommandList[index];
1846 if (uni == keyCommand.fKey || uni == keyCommand.fAlternate) {
1847 return (this->*keyCommand.fFunction)();
1848 }
1849 }
1850 if (('A' <= uni && uni <= 'Z') || ('a' <= uni && uni <= 'z')) {
1851 for (int index = 0; index < kButtonCount; ++index) {
1852 Button* button = kButtonList[index].fButton;
1853 if (button->fVisible && (uni & ~0x20) == (button->fLabel & ~0x20)) {
1854 MyClick click(this, MyClick::kControlType, kButtonList[index].fButtonType);
1855 click.fState = Click::kDown_State;
1856 (void) this->onClick(&click);
1857 return true;
1858 }
1859 }
1860 }
1861 }
1862 return this->INHERITED::onQuery(evt);
1863}
Ben Wagner63fd7602017-10-09 15:45:33 -04001864
caryclark64022c12016-05-27 05:13:26 -07001865DEF_SAMPLE( return new AAGeometryView; )