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