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