blob: 7d873032e49dc53bdec8c8f73a22bb8c8408b717 [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"
9#include "SkCanvas.h"
10#include "SkGeometry.h"
11#include "SkIntersections.h"
12#include "SkOpEdgeBuilder.h"
13// #include "SkPathOpsSimplifyAA.h"
14// #include "SkPathStroker.h"
15#include "SkView.h"
16
17#if 0
18void SkStrokeSegment::dump() const {
19 SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}", fPts[0].fX, fPts[0].fY, fPts[1].fX, fPts[1].fY);
20 if (SkPath::kQuad_Verb == fVerb) {
21 SkDebugf(", {%1.9g,%1.9g}", fPts[2].fX, fPts[2].fY);
22 }
23 SkDebugf("}}");
24#ifdef SK_DEBUG
25 SkDebugf(" id=%d", fDebugID);
26#endif
27 SkDebugf("\n");
28}
29
30void SkStrokeSegment::dumpAll() const {
31 const SkStrokeSegment* segment = this;
32 while (segment) {
33 segment->dump();
34 segment = segment->fNext;
35 }
36}
37
38void SkStrokeTriple::dump() const {
39 SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}", fPts[0].fX, fPts[0].fY, fPts[1].fX, fPts[1].fY);
40 if (SkPath::kQuad_Verb <= fVerb) {
41 SkDebugf(", {%1.9g,%1.9g}", fPts[2].fX, fPts[2].fY);
42 }
43 if (SkPath::kCubic_Verb == fVerb) {
44 SkDebugf(", {%1.9g,%1.9g}", fPts[3].fX, fPts[3].fY);
45 } else if (SkPath::kConic_Verb == fVerb) {
46 SkDebugf(", %1.9g", weight());
47 }
48 SkDebugf("}}");
49#ifdef SK_DEBUG
50 SkDebugf(" triple id=%d", fDebugID);
51#endif
52 SkDebugf("\ninner:\n");
53 fInner->dumpAll();
54 SkDebugf("outer:\n");
55 fOuter->dumpAll();
56 SkDebugf("join:\n");
57 fJoin->dumpAll();
58}
59
60void SkStrokeTriple::dumpAll() const {
61 const SkStrokeTriple* triple = this;
62 while (triple) {
63 triple->dump();
64 triple = triple->fNext;
65 }
66}
67
68void SkStrokeContour::dump() const {
69#ifdef SK_DEBUG
70 SkDebugf("id=%d ", fDebugID);
71#endif
72 SkDebugf("head:\n");
73 fHead->dumpAll();
74 SkDebugf("head cap:\n");
75 fHeadCap->dumpAll();
76 SkDebugf("tail cap:\n");
77 fTailCap->dumpAll();
78}
79
80void SkStrokeContour::dumpAll() const {
81 const SkStrokeContour* contour = this;
82 while (contour) {
83 contour->dump();
84 contour = contour->fNext;
85 }
86}
87#endif
88
89SkScalar gCurveDistance = 10;
90
91#if 0 // unused
92static SkPath::Verb get_path_verb(int index, const SkPath& path) {
93 if (index < 0) {
94 return SkPath::kMove_Verb;
95 }
96 SkPoint pts[4];
97 SkPath::Verb verb;
98 SkPath::Iter iter(path, true);
99 int counter = -1;
100 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
101 if (++counter < index) {
102 continue;
103 }
104 return verb;
105 }
106 SkASSERT(0);
107 return SkPath::kMove_Verb;
108}
109#endif
110
111static SkScalar get_path_weight(int index, const SkPath& path) {
112 SkPoint pts[4];
113 SkPath::Verb verb;
114 SkPath::Iter iter(path, true);
115 int counter = -1;
116 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
117 if (++counter < index) {
118 continue;
119 }
120 return verb == SkPath::kConic_Verb ? iter.conicWeight() : 1;
121 }
122 SkASSERT(0);
123 return 0;
124}
125
126static void set_path_pt(int index, const SkPoint& pt, SkPath* path) {
127 SkPath result;
128 SkPoint pts[4];
129 SkPath::Verb verb;
130 SkPath::RawIter iter(*path);
131 int startIndex = 0;
132 int endIndex = 0;
133 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
134 switch (verb) {
135 case SkPath::kMove_Verb:
136 endIndex += 1;
137 break;
138 case SkPath::kLine_Verb:
139 endIndex += 1;
140 break;
141 case SkPath::kQuad_Verb:
142 case SkPath::kConic_Verb:
143 endIndex += 2;
144 break;
145 case SkPath::kCubic_Verb:
146 endIndex += 3;
147 break;
148 case SkPath::kClose_Verb:
149 break;
150 case SkPath::kDone_Verb:
151 break;
152 default:
153 SkASSERT(0);
154 }
155 if (startIndex <= index && index < endIndex) {
156 pts[index - startIndex] = pt;
157 index = -1;
158 }
159 switch (verb) {
160 case SkPath::kMove_Verb:
161 result.moveTo(pts[0]);
162 break;
163 case SkPath::kLine_Verb:
164 result.lineTo(pts[1]);
165 startIndex += 1;
166 break;
167 case SkPath::kQuad_Verb:
168 result.quadTo(pts[1], pts[2]);
169 startIndex += 2;
170 break;
171 case SkPath::kConic_Verb:
172 result.conicTo(pts[1], pts[2], iter.conicWeight());
173 startIndex += 2;
174 break;
175 case SkPath::kCubic_Verb:
176 result.cubicTo(pts[1], pts[2], pts[3]);
177 startIndex += 3;
178 break;
179 case SkPath::kClose_Verb:
180 result.close();
181 startIndex += 1;
182 break;
183 case SkPath::kDone_Verb:
184 break;
185 default:
186 SkASSERT(0);
187 }
188 }
189#if 0
190 SkDebugf("\n\noriginal\n");
191 path->dump();
192 SkDebugf("\nedited\n");
193 result.dump();
194#endif
195 *path = result;
196}
197
198static void add_path_segment(int index, SkPath* path) {
199 SkPath result;
200 SkPoint pts[4];
201 SkPoint firstPt = { 0, 0 }; // init to avoid warning
202 SkPoint lastPt = { 0, 0 }; // init to avoid warning
203 SkPath::Verb verb;
204 SkPath::RawIter iter(*path);
205 int counter = -1;
206 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
207 SkScalar weight SK_INIT_TO_AVOID_WARNING;
208 if (++counter == index) {
209 switch (verb) {
210 case SkPath::kLine_Verb:
211 result.lineTo((pts[0].fX + pts[1].fX) / 2, (pts[0].fY + pts[1].fY) / 2);
212 break;
213 case SkPath::kQuad_Verb: {
214 SkPoint chop[5];
215 SkChopQuadAtHalf(pts, chop);
216 result.quadTo(chop[1], chop[2]);
217 pts[1] = chop[3];
218 } break;
219 case SkPath::kConic_Verb: {
220 SkConic chop[2];
221 SkConic conic;
222 conic.set(pts, iter.conicWeight());
223 conic.chopAt(0.5f, chop);
224 result.conicTo(chop[0].fPts[1], chop[0].fPts[2], chop[0].fW);
225 pts[1] = chop[1].fPts[1];
226 weight = chop[1].fW;
227 } break;
228 case SkPath::kCubic_Verb: {
229 SkPoint chop[7];
230 SkChopCubicAtHalf(pts, chop);
231 result.cubicTo(chop[1], chop[2], chop[3]);
232 pts[1] = chop[4];
233 pts[2] = chop[5];
234 } break;
235 case SkPath::kClose_Verb: {
236 result.lineTo((lastPt.fX + firstPt.fX) / 2, (lastPt.fY + firstPt.fY) / 2);
237 } break;
238 default:
239 SkASSERT(0);
240 }
241 } else if (verb == SkPath::kConic_Verb) {
242 weight = iter.conicWeight();
243 }
244 switch (verb) {
245 case SkPath::kMove_Verb:
246 result.moveTo(firstPt = pts[0]);
247 break;
248 case SkPath::kLine_Verb:
249 result.lineTo(lastPt = pts[1]);
250 break;
251 case SkPath::kQuad_Verb:
252 result.quadTo(pts[1], lastPt = pts[2]);
253 break;
254 case SkPath::kConic_Verb:
255 result.conicTo(pts[1], lastPt = pts[2], weight);
256 break;
257 case SkPath::kCubic_Verb:
258 result.cubicTo(pts[1], pts[2], lastPt = pts[3]);
259 break;
260 case SkPath::kClose_Verb:
261 result.close();
262 break;
263 case SkPath::kDone_Verb:
264 break;
265 default:
266 SkASSERT(0);
267 }
268 }
269 *path = result;
270}
271
272static void delete_path_segment(int index, SkPath* path) {
273 SkPath result;
274 SkPoint pts[4];
275 SkPath::Verb verb;
276 SkPath::RawIter iter(*path);
277 int counter = -1;
278 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
279 if (++counter == index) {
280 continue;
281 }
282 switch (verb) {
283 case SkPath::kMove_Verb:
284 result.moveTo(pts[0]);
285 break;
286 case SkPath::kLine_Verb:
287 result.lineTo(pts[1]);
288 break;
289 case SkPath::kQuad_Verb:
290 result.quadTo(pts[1], pts[2]);
291 break;
292 case SkPath::kConic_Verb:
293 result.conicTo(pts[1], pts[2], iter.conicWeight());
294 break;
295 case SkPath::kCubic_Verb:
296 result.cubicTo(pts[1], pts[2], pts[3]);
297 break;
298 case SkPath::kClose_Verb:
299 result.close();
300 break;
301 case SkPath::kDone_Verb:
302 break;
303 default:
304 SkASSERT(0);
305 }
306 }
307 *path = result;
308}
309
310static void set_path_weight(int index, SkScalar w, SkPath* path) {
311 SkPath result;
312 SkPoint pts[4];
313 SkPath::Verb verb;
314 SkPath::Iter iter(*path, true);
315 int counter = -1;
316 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
317 ++counter;
318 switch (verb) {
319 case SkPath::kMove_Verb:
320 result.moveTo(pts[0]);
321 break;
322 case SkPath::kLine_Verb:
323 result.lineTo(pts[1]);
324 break;
325 case SkPath::kQuad_Verb:
326 result.quadTo(pts[1], pts[2]);
327 break;
328 case SkPath::kConic_Verb:
329 result.conicTo(pts[1], pts[2], counter == index ? w : iter.conicWeight());
330 break;
331 case SkPath::kCubic_Verb:
332 result.cubicTo(pts[1], pts[2], pts[3]);
333 break;
334 case SkPath::kClose_Verb:
335 result.close();
336 break;
337 case SkPath::kDone_Verb:
338 break;
339 default:
340 SkASSERT(0);
341 }
342 }
343 *path = result;
344}
345
346static void set_path_verb(int index, SkPath::Verb v, SkPath* path, SkScalar w) {
347 SkASSERT(SkPath::kLine_Verb <= v && v <= SkPath::kCubic_Verb);
348 SkPath result;
349 SkPoint pts[4];
350 SkPath::Verb verb;
351 SkPath::Iter iter(*path, true);
352 int counter = -1;
353 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
354 SkScalar weight = verb == SkPath::kConic_Verb ? iter.conicWeight() : 1;
355 if (++counter == index && v != verb) {
356 SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
357 switch (verb) {
358 case SkPath::kLine_Verb:
359 switch (v) {
360 case SkPath::kConic_Verb:
361 weight = w;
362 case SkPath::kQuad_Verb:
363 pts[2] = pts[1];
364 pts[1].fX = (pts[0].fX + pts[2].fX) / 2;
365 pts[1].fY = (pts[0].fY + pts[2].fY) / 2;
366 break;
367 case SkPath::kCubic_Verb:
368 pts[3] = pts[1];
369 pts[1].fX = (pts[0].fX * 2 + pts[3].fX) / 3;
370 pts[1].fY = (pts[0].fY * 2 + pts[3].fY) / 3;
371 pts[2].fX = (pts[0].fX + pts[3].fX * 2) / 3;
372 pts[2].fY = (pts[0].fY + pts[3].fY * 2) / 3;
373 break;
374 default:
375 SkASSERT(0);
376 break;
377 }
378 break;
379 case SkPath::kQuad_Verb:
380 case SkPath::kConic_Verb:
381 switch (v) {
382 case SkPath::kLine_Verb:
383 pts[1] = pts[2];
384 break;
385 case SkPath::kConic_Verb:
386 weight = w;
387 case SkPath::kQuad_Verb:
388 break;
389 case SkPath::kCubic_Verb: {
390 SkDQuad dQuad;
391 dQuad.set(pts);
392 SkDCubic dCubic = dQuad.debugToCubic();
393 pts[3] = pts[2];
394 pts[1] = dCubic[1].asSkPoint();
395 pts[2] = dCubic[2].asSkPoint();
396 } break;
397 default:
398 SkASSERT(0);
399 break;
400 }
401 break;
402 case SkPath::kCubic_Verb:
403 switch (v) {
404 case SkPath::kLine_Verb:
405 pts[1] = pts[3];
406 break;
407 case SkPath::kConic_Verb:
408 weight = w;
409 case SkPath::kQuad_Verb: {
410 SkDCubic dCubic;
411 dCubic.set(pts);
412 SkDQuad dQuad = dCubic.toQuad();
413 pts[1] = dQuad[1].asSkPoint();
414 pts[2] = pts[3];
415 } break;
416 default:
417 SkASSERT(0);
418 break;
419 }
420 break;
421 default:
422 SkASSERT(0);
423 break;
424 }
425 verb = v;
426 }
427 switch (verb) {
428 case SkPath::kMove_Verb:
429 result.moveTo(pts[0]);
430 break;
431 case SkPath::kLine_Verb:
432 result.lineTo(pts[1]);
433 break;
434 case SkPath::kQuad_Verb:
435 result.quadTo(pts[1], pts[2]);
436 break;
437 case SkPath::kConic_Verb:
438 result.conicTo(pts[1], pts[2], weight);
439 break;
440 case SkPath::kCubic_Verb:
441 result.cubicTo(pts[1], pts[2], pts[3]);
442 break;
443 case SkPath::kClose_Verb:
444 result.close();
445 break;
446 default:
447 SkASSERT(0);
448 break;
449 }
450 }
451 *path = result;
452}
453
454static void add_to_map(SkScalar coverage, int x, int y, uint8_t* distanceMap, int w, int h) {
455 int byteCoverage = (int) (coverage * 256);
456 if (byteCoverage < 0) {
457 byteCoverage = 0;
458 } else if (byteCoverage > 255) {
459 byteCoverage = 255;
460 }
461 SkASSERT(x < w);
462 SkASSERT(y < h);
463 distanceMap[y * w + x] = SkTMax(distanceMap[y * w + x], (uint8_t) byteCoverage);
464}
465
466static void filter_coverage(const uint8_t* map, int len, uint8_t min, uint8_t max,
467 uint8_t* filter) {
468 for (int index = 0; index < len; ++index) {
469 uint8_t in = map[index];
470 filter[index] = in < min ? 0 : max < in ? 0 : in;
471 }
472}
473
474static void construct_path(SkPath& path) {
475 path.reset();
476 path.moveTo(442, 101.5f);
477 path.quadTo(413.5f, 691, 772, 514);
478 path.lineTo(346, 721.5f);
479 path.lineTo(154, 209);
480 path.lineTo(442, 101.5f);
481 path.close();
482}
483
484struct ButtonPaints {
485 static const int kMaxStateCount = 3;
486 SkPaint fDisabled;
487 SkPaint fStates[kMaxStateCount];
488 SkPaint fLabel;
489
490 ButtonPaints() {
491 fStates[0].setAntiAlias(true);
492 fStates[0].setStyle(SkPaint::kStroke_Style);
493 fStates[0].setColor(0xFF3F0000);
494 fStates[1] = fStates[0];
495 fStates[1].setStrokeWidth(3);
496 fStates[2] = fStates[1];
497 fStates[2].setColor(0xFFcf0000);
498 fLabel.setAntiAlias(true);
499 fLabel.setTextSize(25.0f);
500 fLabel.setTextAlign(SkPaint::kCenter_Align);
501 fLabel.setStyle(SkPaint::kFill_Style);
502 }
503};
504
505struct Button {
506 SkRect fBounds;
507 int fStateCount;
508 int fState;
509 char fLabel;
510 bool fVisible;
511
512 Button(char label) {
513 fStateCount = 2;
514 fState = 0;
515 fLabel = label;
516 fVisible = false;
517 }
518
519 Button(char label, int stateCount) {
520 SkASSERT(stateCount <= ButtonPaints::kMaxStateCount);
521 fStateCount = stateCount;
522 fState = 0;
523 fLabel = label;
524 fVisible = false;
525 }
526
527 bool contains(const SkRect& rect) {
528 return fVisible && fBounds.contains(rect);
529 }
530
531 bool enabled() {
532 return SkToBool(fState);
533 }
534
535 void draw(SkCanvas* canvas, const ButtonPaints& paints) {
536 if (!fVisible) {
537 return;
538 }
539 canvas->drawRect(fBounds, paints.fStates[fState]);
540 canvas->drawText(&fLabel, 1, fBounds.centerX(), fBounds.fBottom - 5, paints.fLabel);
541 }
542
543 void toggle() {
544 if (++fState == fStateCount) {
545 fState = 0;
546 }
547 }
548
549 void setEnabled(bool enabled) {
550 fState = (int) enabled;
551 }
552};
553
554struct ControlPaints {
555 SkPaint fOutline;
556 SkPaint fIndicator;
557 SkPaint fFill;
558 SkPaint fLabel;
559 SkPaint fValue;
560
561 ControlPaints() {
562 fOutline.setAntiAlias(true);
563 fOutline.setStyle(SkPaint::kStroke_Style);
564 fIndicator = fOutline;
565 fIndicator.setColor(SK_ColorRED);
566 fFill.setAntiAlias(true);
567 fFill.setColor(0x7fff0000);
568 fLabel.setAntiAlias(true);
569 fLabel.setTextSize(13.0f);
570 fValue.setAntiAlias(true);
571 fValue.setTextSize(11.0f);
572 }
573};
574
575struct UniControl {
576 SkString fName;
577 SkRect fBounds;
578 SkScalar fMin;
579 SkScalar fMax;
580 SkScalar fValLo;
581 SkScalar fYLo;
582 bool fVisible;
583
584 UniControl(const char* name, SkScalar min, SkScalar max) {
585 fName = name;
586 fValLo = fMin = min;
587 fMax = max;
588 fVisible = false;
589
590 }
591
592 virtual ~UniControl() {}
593
594 bool contains(const SkRect& rect) {
595 return fVisible && fBounds.contains(rect);
596 }
597
598 virtual void draw(SkCanvas* canvas, const ControlPaints& paints) {
599 if (!fVisible) {
600 return;
601 }
602 canvas->drawRect(fBounds, paints.fOutline);
603 fYLo = fBounds.fTop + (fValLo - fMin) * fBounds.height() / (fMax - fMin);
604 canvas->drawLine(fBounds.fLeft - 5, fYLo, fBounds.fRight + 5, fYLo, paints.fIndicator);
605 SkString label;
606 label.printf("%0.3g", fValLo);
607 canvas->drawText(label.c_str(), label.size(), fBounds.fLeft + 5, fYLo - 5, paints.fValue);
608 canvas->drawText(fName.c_str(), fName.size(), fBounds.fLeft, fBounds.bottom() + 11,
609 paints.fLabel);
610 }
611};
612
613struct BiControl : public UniControl {
614 SkScalar fValHi;
615
616 BiControl(const char* name, SkScalar min, SkScalar max)
617 : UniControl(name, min, max)
618 , fValHi(fMax) {
619 }
620
621 virtual ~BiControl() {}
622
623 virtual void draw(SkCanvas* canvas, const ControlPaints& paints) {
624 UniControl::draw(canvas, paints);
625 if (!fVisible || fValHi == fValLo) {
626 return;
627 }
628 SkScalar yPos = fBounds.fTop + (fValHi - fMin) * fBounds.height() / (fMax - fMin);
629 canvas->drawLine(fBounds.fLeft - 5, yPos, fBounds.fRight + 5, yPos, paints.fIndicator);
630 SkString label;
631 label.printf("%0.3g", fValHi);
632 if (yPos < fYLo + 10) {
633 yPos = fYLo + 10;
634 }
635 canvas->drawText(label.c_str(), label.size(), fBounds.fLeft + 5, yPos - 5, paints.fValue);
636 SkRect fill = { fBounds.fLeft, fYLo, fBounds.fRight, yPos };
637 canvas->drawRect(fill, paints.fFill);
638 }
639};
640
641
642class MyClick : public SampleView::Click {
643public:
644 enum ClickType {
645 kInvalidType = -1,
646 kPtType,
647 kVerbType,
648 kControlType,
649 kPathType,
650 } fType;
651
652 enum ControlType {
653 kInvalidControl = -1,
654 kFirstControl,
655 kFilterControl = kFirstControl,
656 kResControl,
657 kWeightControl,
658 kWidthControl,
659 kLastControl = kWidthControl,
660 kFirstButton,
661 kCubicButton = kFirstButton,
662 kConicButton,
663 kQuadButton,
664 kLineButton,
665 kLastVerbButton = kLineButton,
666 kAddButton,
667 kDeleteButton,
668 kInOutButton,
669 kFillButton,
670 kSkeletonButton,
671 kFilterButton,
672 kBisectButton,
673 kJoinButton,
674 kLastButton = kJoinButton,
675 kPathMove,
676 } fControl;
677
678 SkPath::Verb fVerb;
679 SkScalar fWeight;
680
681 MyClick(SkView* target, ClickType type, ControlType control)
682 : Click(target)
683 , fType(type)
684 , fControl(control)
685 , fVerb((SkPath::Verb) -1)
686 , fWeight(1) {
687 }
688
689 MyClick(SkView* target, ClickType type, int index)
690 : Click(target)
691 , fType(type)
692 , fControl((ControlType) index)
693 , fVerb((SkPath::Verb) -1)
694 , fWeight(1) {
695 }
696
697 MyClick(SkView* target, ClickType type, int index, SkPath::Verb verb, SkScalar weight)
698 : Click(target)
699 , fType(type)
700 , fControl((ControlType) index)
701 , fVerb(verb)
702 , fWeight(weight) {
703 }
704
705 bool isButton() {
706 return kFirstButton <= fControl && fControl <= kLastButton;
707 }
708
709 int ptHit() const {
710 SkASSERT(fType == kPtType);
711 return (int) fControl;
712 }
713
714 int verbHit() const {
715 SkASSERT(fType == kVerbType);
716 return (int) fControl;
717 }
718};
719
720enum {
721 kControlCount = MyClick::kLastControl - MyClick::kFirstControl + 1,
722};
723
724static struct ControlPair {
725 UniControl* fControl;
726 MyClick::ControlType fControlType;
727} kControlList[kControlCount];
728
729enum {
730 kButtonCount = MyClick::kLastButton - MyClick::kFirstButton + 1,
731 kVerbCount = MyClick::kLastVerbButton - MyClick::kFirstButton + 1,
732};
733
734static struct ButtonPair {
735 Button* fButton;
736 MyClick::ControlType fButtonType;
737} kButtonList[kButtonCount];
738
739static void enable_verb_button(MyClick::ControlType type) {
740 for (int index = 0; index < kButtonCount; ++index) {
741 MyClick::ControlType testType = kButtonList[index].fButtonType;
742 if (MyClick::kFirstButton <= testType && testType <= MyClick::kLastVerbButton) {
743 Button* button = kButtonList[index].fButton;
744 button->setEnabled(testType == type);
745 }
746 }
747}
748
749struct Stroke;
750
751struct Active {
752 Active* fNext;
753 Stroke* fParent;
754 SkScalar fStart;
755 SkScalar fEnd;
756
757 void reset() {
758 fNext = NULL;
759 fStart = 0;
760 fEnd = 1;
761 }
762};
763
764struct Stroke {
765 SkPath fPath;
766 Active fActive;
767 bool fInner;
768
769 void reset() {
770 fPath.reset();
771 fActive.reset();
772 }
773};
774
775struct PathUndo {
776 SkPath fPath;
777 PathUndo* fNext;
778};
779
780class AAGeometryView : public SampleView {
781 SkPaint fActivePaint;
782 SkPaint fComplexPaint;
783 SkPaint fCoveragePaint;
784 SkPaint fLegendLeftPaint;
785 SkPaint fLegendRightPaint;
786 SkPaint fPointPaint;
787 SkPaint fSkeletonPaint;
788 SkPaint fLightSkeletonPaint;
789 SkPath fPath;
790 ControlPaints fControlPaints;
791 UniControl fResControl;
792 UniControl fWeightControl;
793 UniControl fWidthControl;
794 BiControl fFilterControl;
795 ButtonPaints fButtonPaints;
796 Button fCubicButton;
797 Button fConicButton;
798 Button fQuadButton;
799 Button fLineButton;
800 Button fAddButton;
801 Button fDeleteButton;
802 Button fFillButton;
803 Button fSkeletonButton;
804 Button fFilterButton;
805 Button fBisectButton;
806 Button fJoinButton;
807 Button fInOutButton;
808 SkTArray<Stroke> fStrokes;
809 PathUndo* fUndo;
810 int fActivePt;
811 int fActiveVerb;
812 bool fHandlePathMove;
813 bool fShowLegend;
814 bool fHideAll;
815 const int kHitToleranace = 5;
816
817public:
818
819 AAGeometryView()
820 : fResControl("error", 0, 10)
821 , fWeightControl("weight", 0, 5)
822 , fWidthControl("width", FLT_EPSILON, 100)
823 , fFilterControl("filter", 0, 255)
824 , fCubicButton('C')
825 , fConicButton('K')
826 , fQuadButton('Q')
827 , fLineButton('L')
828 , fAddButton('+')
829 , fDeleteButton('x')
830 , fFillButton('p')
831 , fSkeletonButton('s')
832 , fFilterButton('f', 3)
833 , fBisectButton('b')
834 , fJoinButton('j')
835 , fInOutButton('|')
836 , fUndo(NULL)
837 , fActivePt(-1)
838 , fActiveVerb(-1)
839 , fHandlePathMove(true)
840 , fShowLegend(false)
841 , fHideAll(false)
842 {
843 fCoveragePaint.setAntiAlias(true);
844 fCoveragePaint.setColor(SK_ColorBLUE);
845 SkPaint strokePaint;
846 strokePaint.setAntiAlias(true);
847 strokePaint.setStyle(SkPaint::kStroke_Style);
848 fPointPaint = strokePaint;
849 fPointPaint.setColor(0x99ee3300);
850 fSkeletonPaint = strokePaint;
851 fSkeletonPaint.setColor(SK_ColorRED);
852 fLightSkeletonPaint = fSkeletonPaint;
853 fLightSkeletonPaint.setColor(0xFFFF7f7f);
854 fActivePaint = strokePaint;
855 fActivePaint.setColor(0x99ee3300);
856 fActivePaint.setStrokeWidth(5);
857 fComplexPaint = fActivePaint;
858 fComplexPaint.setColor(SK_ColorBLUE);
859 fLegendLeftPaint.setAntiAlias(true);
860 fLegendLeftPaint.setTextSize(13);
861 fLegendRightPaint = fLegendLeftPaint;
862 fLegendRightPaint.setTextAlign(SkPaint::kRight_Align);
863 construct_path(fPath);
864 fFillButton.fVisible = fSkeletonButton.fVisible = fFilterButton.fVisible
865 = fBisectButton.fVisible = fJoinButton.fVisible = fInOutButton.fVisible = true;
866 fSkeletonButton.setEnabled(true);
867 fInOutButton.setEnabled(true);
868 fJoinButton.setEnabled(true);
869 fFilterControl.fValLo = 120;
870 fFilterControl.fValHi = 141;
871 fFilterControl.fVisible = fFilterButton.fState == 2;
872 fResControl.fValLo = 5;
873 fResControl.fVisible = true;
874 fWidthControl.fValLo = 50;
875 fWidthControl.fVisible = true;
876 init_controlList();
877 init_buttonList();
878 }
879
880 bool constructPath() {
881 construct_path(fPath);
882 this->inval(NULL);
883 return true;
884 }
885
886 void savePath(Click::State state) {
887 if (state != Click::kDown_State) {
888 return;
889 }
890 if (fUndo && fUndo->fPath == fPath) {
891 return;
892 }
893 PathUndo* undo = new PathUndo;
894 undo->fPath = fPath;
895 undo->fNext = fUndo;
896 fUndo = undo;
897 }
898
899 bool undo() {
900 if (!fUndo) {
901 return false;
902 }
903 fPath = fUndo->fPath;
904 validatePath();
905 PathUndo* next = fUndo->fNext;
906 delete fUndo;
907 fUndo = next;
908 this->inval(NULL);
909 return true;
910 }
911
912 void validatePath() {
913 PathUndo* undo = fUndo;
914 int match = 0;
915 while (undo) {
916 match += fPath == undo->fPath;
917 undo = undo->fNext;
918 }
919 }
920
921 void set_controlList(int index, UniControl* control, MyClick::ControlType type) {
922 kControlList[index].fControl = control;
923 kControlList[index].fControlType = type;
924 }
925
926 #define SET_CONTROL(Name) set_controlList(index++, &f##Name##Control, \
927 MyClick::k##Name##Control)
928
929 bool hideAll() {
930 fHideAll ^= true;
931 this->inval(NULL);
932 return true;
933 }
934
935 void init_controlList() {
936 int index = 0;
937 SET_CONTROL(Width);
938 SET_CONTROL(Res);
939 SET_CONTROL(Filter);
940 SET_CONTROL(Weight);
941 };
942
943 #undef SET_CONTROL
944
945 void set_buttonList(int index, Button* button, MyClick::ControlType type) {
946 kButtonList[index].fButton = button;
947 kButtonList[index].fButtonType = type;
948 }
949
950 #define SET_BUTTON(Name) set_buttonList(index++, &f##Name##Button, \
951 MyClick::k##Name##Button)
952
953 void init_buttonList() {
954 int index = 0;
955 SET_BUTTON(Fill);
956 SET_BUTTON(Skeleton);
957 SET_BUTTON(Filter);
958 SET_BUTTON(Bisect);
959 SET_BUTTON(Join);
960 SET_BUTTON(InOut);
961 SET_BUTTON(Cubic);
962 SET_BUTTON(Conic);
963 SET_BUTTON(Quad);
964 SET_BUTTON(Line);
965 SET_BUTTON(Add);
966 SET_BUTTON(Delete);
967 }
968
969 #undef SET_BUTTON
970
971 // overrides from SkEventSink
972 bool onQuery(SkEvent* evt) override;
973
974 void onSizeChange() override {
975 setControlButtonsPos();
976 this->INHERITED::onSizeChange();
977 }
978
979 bool pathDump() {
980 fPath.dump();
981 return true;
982 }
983
984 bool scaleDown() {
985 SkMatrix matrix;
986 SkRect bounds = fPath.getBounds();
987 matrix.setScale(1.f / 1.5f, 1.f / 1.5f, bounds.centerX(), bounds.centerY());
988 fPath.transform(matrix);
989 validatePath();
990 this->inval(NULL);
991 return true;
992 }
993
994 bool scaleToFit() {
995 SkMatrix matrix;
996 SkRect bounds = fPath.getBounds();
997 SkScalar scale = SkTMin(this->width() / bounds.width(), this->height() / bounds.height())
998 * 0.8f;
999 matrix.setScale(scale, scale, bounds.centerX(), bounds.centerY());
1000 fPath.transform(matrix);
1001 bounds = fPath.getBounds();
1002 SkScalar offsetX = (this->width() - bounds.width()) / 2 - bounds.fLeft;
1003 SkScalar offsetY = (this->height() - bounds.height()) / 2 - bounds.fTop;
1004 fPath.offset(offsetX, offsetY);
1005 validatePath();
1006 this->inval(NULL);
1007 return true;
1008 }
1009
1010 bool scaleUp() {
1011 SkMatrix matrix;
1012 SkRect bounds = fPath.getBounds();
1013 matrix.setScale(1.5f, 1.5f, bounds.centerX(), bounds.centerY());
1014 fPath.transform(matrix);
1015 validatePath();
1016 this->inval(NULL);
1017 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;
1037 this->inval(NULL);
1038 return true;
1039 }
1040
1041 void draw_bisect(SkCanvas* canvas, const SkVector& lastVector, const SkVector& vector,
1042 const SkPoint& pt) {
1043 SkVector lastV = lastVector;
1044 SkScalar lastLen = lastVector.length();
1045 SkVector nextV = vector;
1046 SkScalar nextLen = vector.length();
1047 if (lastLen < nextLen) {
1048 lastV.setLength(nextLen);
1049 } else {
1050 nextV.setLength(lastLen);
1051 }
1052
1053 SkVector bisect = { (lastV.fX + nextV.fX) / 2, (lastV.fY + nextV.fY) / 2 };
1054 bisect.setLength(fWidthControl.fValLo * 2);
1055 if (fBisectButton.enabled()) {
1056 canvas->drawLine(pt.fX, pt.fY, pt.fX + bisect.fX, pt.fY + bisect.fY, fSkeletonPaint);
1057 }
1058 lastV.setLength(fWidthControl.fValLo);
1059 if (fBisectButton.enabled()) {
1060 canvas->drawLine(pt.fX, pt.fY, pt.fX - lastV.fY, pt.fY + lastV.fX, fSkeletonPaint);
1061 }
1062 nextV.setLength(fWidthControl.fValLo);
1063 if (fBisectButton.enabled()) {
1064 canvas->drawLine(pt.fX, pt.fY, pt.fX + nextV.fY, pt.fY - nextV.fX, fSkeletonPaint);
1065 }
1066 if (fJoinButton.enabled()) {
1067 SkScalar r = fWidthControl.fValLo;
1068 SkRect oval = { pt.fX - r, pt.fY - r, pt.fX + r, pt.fY + r};
1069 SkScalar startAngle = SkScalarATan2(lastV.fX, -lastV.fY) * 180.f / SK_ScalarPI;
1070 SkScalar endAngle = SkScalarATan2(-nextV.fX, nextV.fY) * 180.f / SK_ScalarPI;
1071 if (endAngle > startAngle) {
1072 canvas->drawArc(oval, startAngle, endAngle - startAngle, false, fSkeletonPaint);
1073 } else {
1074 canvas->drawArc(oval, startAngle, 360 - (startAngle - endAngle), false,
1075 fSkeletonPaint);
1076 }
1077 }
1078 }
1079
1080 void draw_bisects(SkCanvas* canvas, bool activeOnly) {
1081 SkVector firstVector, lastVector, nextLast, vector;
1082 SkPoint pts[4];
1083 SkPoint firstPt = { 0, 0 }; // init to avoid warning;
1084 SkPath::Verb verb;
1085 SkPath::Iter iter(fPath, true);
1086 bool foundFirst = false;
1087 int counter = -1;
1088 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1089 ++counter;
1090 if (activeOnly && counter != fActiveVerb && counter - 1 != fActiveVerb
1091 && counter + 1 != fActiveVerb
1092 && (fActiveVerb != 1 || counter != fPath.countVerbs())) {
1093 continue;
1094 }
1095 switch (verb) {
1096 case SkPath::kLine_Verb:
1097 nextLast = pts[0] - pts[1];
1098 vector = pts[1] - pts[0];
1099 break;
1100 case SkPath::kQuad_Verb: {
1101 nextLast = pts[1] - pts[2];
1102 if (SkScalarNearlyZero(nextLast.length())) {
1103 nextLast = pts[0] - pts[2];
1104 }
1105 vector = pts[1] - pts[0];
1106 if (SkScalarNearlyZero(vector.length())) {
1107 vector = pts[2] - pts[0];
1108 }
1109 if (!fBisectButton.enabled()) {
1110 break;
1111 }
1112 SkScalar t = SkFindQuadMaxCurvature(pts);
1113 if (0 < t && t < 1) {
1114 SkPoint maxPt = SkEvalQuadAt(pts, t);
1115 SkVector tangent = SkEvalQuadTangentAt(pts, t);
1116 tangent.setLength(fWidthControl.fValLo * 2);
1117 canvas->drawLine(maxPt.fX, maxPt.fY,
1118 maxPt.fX + tangent.fY, maxPt.fY - tangent.fX, fSkeletonPaint);
1119 }
1120 } break;
1121 case SkPath::kConic_Verb:
1122 nextLast = pts[1] - pts[2];
1123 if (SkScalarNearlyZero(nextLast.length())) {
1124 nextLast = pts[0] - pts[2];
1125 }
1126 vector = pts[1] - pts[0];
1127 if (SkScalarNearlyZero(vector.length())) {
1128 vector = pts[2] - pts[0];
1129 }
1130 if (!fBisectButton.enabled()) {
1131 break;
1132 }
1133 // FIXME : need max curvature or equivalent here
1134 break;
1135 case SkPath::kCubic_Verb: {
1136 nextLast = pts[2] - pts[3];
1137 if (SkScalarNearlyZero(nextLast.length())) {
1138 nextLast = pts[1] - pts[3];
1139 if (SkScalarNearlyZero(nextLast.length())) {
1140 nextLast = pts[0] - pts[3];
1141 }
1142 }
1143 vector = pts[0] - pts[1];
1144 if (SkScalarNearlyZero(vector.length())) {
1145 vector = pts[0] - pts[2];
1146 if (SkScalarNearlyZero(vector.length())) {
1147 vector = pts[0] - pts[3];
1148 }
1149 }
1150 if (!fBisectButton.enabled()) {
1151 break;
1152 }
1153 SkScalar tMax[2];
1154 int tMaxCount = SkFindCubicMaxCurvature(pts, tMax);
1155 for (int tIndex = 0; tIndex < tMaxCount; ++tIndex) {
1156 if (0 >= tMax[tIndex] || tMax[tIndex] >= 1) {
1157 continue;
1158 }
1159 SkPoint maxPt;
1160 SkVector tangent;
1161 SkEvalCubicAt(pts, tMax[tIndex], &maxPt, &tangent, NULL);
1162 tangent.setLength(fWidthControl.fValLo * 2);
1163 canvas->drawLine(maxPt.fX, maxPt.fY,
1164 maxPt.fX + tangent.fY, maxPt.fY - tangent.fX, fSkeletonPaint);
1165 }
1166 } break;
1167 case SkPath::kClose_Verb:
1168 if (foundFirst) {
1169 draw_bisect(canvas, lastVector, firstVector, firstPt);
1170 foundFirst = false;
1171 }
1172 break;
1173 default:
1174 break;
1175 }
1176 if (SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb) {
1177 if (!foundFirst) {
1178 firstPt = pts[0];
1179 firstVector = vector;
1180 foundFirst = true;
1181 } else {
1182 draw_bisect(canvas, lastVector, vector, pts[0]);
1183 }
1184 lastVector = nextLast;
1185 }
1186 }
1187 }
1188
1189 void draw_legend(SkCanvas* canvas);
1190
1191 void draw_segment(SkCanvas* canvas) {
1192 SkPoint pts[4];
1193 SkPath::Verb verb;
1194 SkPath::Iter iter(fPath, true);
1195 int counter = -1;
1196 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1197 if (++counter < fActiveVerb) {
1198 continue;
1199 }
1200 switch (verb) {
1201 case SkPath::kLine_Verb:
1202 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, fActivePaint);
1203 draw_points(canvas, pts, 2);
1204 break;
1205 case SkPath::kQuad_Verb: {
1206 SkPath qPath;
1207 qPath.moveTo(pts[0]);
1208 qPath.quadTo(pts[1], pts[2]);
1209 canvas->drawPath(qPath, fActivePaint);
1210 draw_points(canvas, pts, 3);
1211 } break;
1212 case SkPath::kConic_Verb: {
1213 SkPath conicPath;
1214 conicPath.moveTo(pts[0]);
1215 conicPath.conicTo(pts[1], pts[2], iter.conicWeight());
1216 canvas->drawPath(conicPath, fActivePaint);
1217 draw_points(canvas, pts, 3);
1218 } break;
1219 case SkPath::kCubic_Verb: {
1220 SkScalar loopT;
1221 bool complex = SkDCubic::ComplexBreak(pts, &loopT);
1222 SkPath cPath;
1223 cPath.moveTo(pts[0]);
1224 cPath.cubicTo(pts[1], pts[2], pts[3]);
1225 canvas->drawPath(cPath, complex ? fComplexPaint : fActivePaint);
1226 draw_points(canvas, pts, 4);
1227 } break;
1228 default:
1229 break;
1230 }
1231 return;
1232 }
1233 }
1234
1235 void draw_points(SkCanvas* canvas, SkPoint* points, int count) {
1236 for (int index = 0; index < count; ++index) {
1237 canvas->drawCircle(points[index].fX, points[index].fY, 10, fPointPaint);
1238 }
1239 }
1240
1241 int hittest_verb(SkPoint pt, SkPath::Verb* verbPtr, SkScalar* weight) {
1242 SkIntersections i;
1243 SkDLine hHit = {{{pt.fX - kHitToleranace, pt.fY }, {pt.fX + kHitToleranace, pt.fY}}};
1244 SkDLine vHit = {{{pt.fX, pt.fY - kHitToleranace }, {pt.fX, pt.fY + kHitToleranace}}};
1245 SkPoint pts[4];
1246 SkPath::Verb verb;
1247 SkPath::Iter iter(fPath, true);
1248 int counter = -1;
1249 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
1250 ++counter;
1251 switch (verb) {
1252 case SkPath::kLine_Verb: {
1253 SkDLine line;
1254 line.set(pts);
1255 if (i.intersect(line, hHit) || i.intersect(line, vHit)) {
1256 *verbPtr = verb;
1257 *weight = 1;
1258 return counter;
1259 }
1260 } break;
1261 case SkPath::kQuad_Verb: {
1262 SkDQuad quad;
1263 quad.set(pts);
1264 if (i.intersect(quad, hHit) || i.intersect(quad, vHit)) {
1265 *verbPtr = verb;
1266 *weight = 1;
1267 return counter;
1268 }
1269 } break;
1270 case SkPath::kConic_Verb: {
1271 SkDConic conic;
1272 SkScalar w = iter.conicWeight();
1273 conic.set(pts, w);
1274 if (i.intersect(conic, hHit) || i.intersect(conic, vHit)) {
1275 *verbPtr = verb;
1276 *weight = w;
1277 return counter;
1278 }
1279 } break;
1280 case SkPath::kCubic_Verb: {
1281 SkDCubic cubic;
1282 cubic.set(pts);
1283 if (i.intersect(cubic, hHit) || i.intersect(cubic, vHit)) {
1284 *verbPtr = verb;
1285 *weight = 1;
1286 return counter;
1287 }
1288 } break;
1289 default:
1290 break;
1291 }
1292 }
1293 return -1;
1294 }
1295
1296 SkScalar pt_to_line(SkPoint s, SkPoint e, int x, int y) {
1297 SkScalar radius = fWidthControl.fValLo;
1298 SkVector adjOpp = e - s;
1299 SkScalar lenSq = adjOpp.lengthSqd();
1300 SkPoint rotated = {
1301 (y - s.fY) * adjOpp.fY + (x - s.fX) * adjOpp.fX,
1302 (y - s.fY) * adjOpp.fX - (x - s.fX) * adjOpp.fY,
1303 };
1304 if (rotated.fX < 0 || rotated.fX > lenSq) {
1305 return -radius;
1306 }
1307 rotated.fY /= SkScalarSqrt(lenSq);
1308 return SkTMax(-radius, SkTMin(radius, rotated.fY));
1309 }
1310
1311 // given a line, compute the interior and exterior gradient coverage
1312 bool coverage(SkPoint s, SkPoint e, uint8_t* distanceMap, int w, int h) {
1313 SkScalar radius = fWidthControl.fValLo;
1314 int minX = SkTMax(0, (int) (SkTMin(s.fX, e.fX) - radius));
1315 int minY = SkTMax(0, (int) (SkTMin(s.fY, e.fY) - radius));
1316 int maxX = SkTMin(w, (int) (SkTMax(s.fX, e.fX) + radius) + 1);
1317 int maxY = SkTMin(h, (int) (SkTMax(s.fY, e.fY) + radius) + 1);
1318 for (int y = minY; y < maxY; ++y) {
1319 for (int x = minX; x < maxX; ++x) {
1320 SkScalar ptToLineDist = pt_to_line(s, e, x, y);
1321 if (ptToLineDist > -radius && ptToLineDist < radius) {
1322 SkScalar coverage = ptToLineDist / radius;
1323 add_to_map(1 - SkScalarAbs(coverage), x, y, distanceMap, w, h);
1324 }
1325 SkVector ptToS = { x - s.fX, y - s.fY };
1326 SkScalar dist = ptToS.length();
1327 if (dist < radius) {
1328 SkScalar coverage = dist / radius;
1329 add_to_map(1 - SkScalarAbs(coverage), x, y, distanceMap, w, h);
1330 }
1331 SkVector ptToE = { x - e.fX, y - e.fY };
1332 dist = ptToE.length();
1333 if (dist < radius) {
1334 SkScalar coverage = dist / radius;
1335 add_to_map(1 - SkScalarAbs(coverage), x, y, distanceMap, w, h);
1336 }
1337 }
1338 }
1339 return true;
1340 }
1341
1342 void quad_coverage(SkPoint pts[3], uint8_t* distanceMap, int w, int h) {
1343 SkScalar dist = pts[0].Distance(pts[0], pts[2]);
1344 if (dist < gCurveDistance) {
1345 (void) coverage(pts[0], pts[2], distanceMap, w, h);
1346 return;
1347 }
1348 SkPoint split[5];
1349 SkChopQuadAt(pts, split, 0.5f);
1350 quad_coverage(&split[0], distanceMap, w, h);
1351 quad_coverage(&split[2], distanceMap, w, h);
1352 }
1353
1354 void conic_coverage(SkPoint pts[3], SkScalar weight, uint8_t* distanceMap, int w, int h) {
1355 SkScalar dist = pts[0].Distance(pts[0], pts[2]);
1356 if (dist < gCurveDistance) {
1357 (void) coverage(pts[0], pts[2], distanceMap, w, h);
1358 return;
1359 }
1360 SkConic split[2];
1361 SkConic conic;
1362 conic.set(pts, weight);
1363 conic.chopAt(0.5f, split);
1364 conic_coverage(split[0].fPts, split[0].fW, distanceMap, w, h);
1365 conic_coverage(split[1].fPts, split[1].fW, distanceMap, w, h);
1366 }
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;
1399 default:
1400 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 }
1512
1513 }
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();
1668 this->inval(NULL);
1669 return true;
1670 }
1671 case MyClick::kPathType:
1672 savePath(click->fState);
1673 fPath.offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX),
1674 SkIntToScalar(click->fICurr.fY - click->fIPrev.fY));
1675 validatePath();
1676 this->inval(NULL);
1677 return true;
1678 case MyClick::kVerbType: {
1679 fActiveVerb = myClick->verbHit();
1680 fLineButton.fVisible = fQuadButton.fVisible = fConicButton.fVisible
1681 = fCubicButton.fVisible = fAddButton.fVisible = fDeleteButton.fVisible
1682 = true;
1683 fLineButton.setEnabled(myClick->fVerb == SkPath::kLine_Verb);
1684 fQuadButton.setEnabled(myClick->fVerb == SkPath::kQuad_Verb);
1685 fConicButton.setEnabled(myClick->fVerb == SkPath::kConic_Verb);
1686 fCubicButton.setEnabled(myClick->fVerb == SkPath::kCubic_Verb);
1687 fWeightControl.fValLo = myClick->fWeight;
1688 fWeightControl.fVisible = myClick->fVerb == SkPath::kConic_Verb;
1689 } break;
1690 case MyClick::kControlType: {
1691 if (click->fState != Click::kDown_State && myClick->isButton()) {
1692 return true;
1693 }
1694 switch (myClick->fControl) {
1695 case MyClick::kFilterControl: {
1696 SkScalar val = MapScreenYtoValue(click->fICurr.fY, fFilterControl);
1697 if (val - fFilterControl.fValLo < fFilterControl.fValHi - val) {
1698 fFilterControl.fValLo = SkTMax(0.f, val);
1699 } else {
1700 fFilterControl.fValHi = SkTMin(255.f, val);
1701 }
1702 } break;
1703 case MyClick::kResControl:
1704 fResControl.fValLo = MapScreenYtoValue(click->fICurr.fY, fResControl);
1705 break;
1706 case MyClick::kWeightControl: {
1707 savePath(click->fState);
1708 SkScalar w = MapScreenYtoValue(click->fICurr.fY, fWeightControl);
1709 set_path_weight(fActiveVerb, w, &fPath);
1710 validatePath();
1711 fWeightControl.fValLo = w;
1712 } break;
1713 case MyClick::kWidthControl:
1714 fWidthControl.fValLo = MapScreenYtoValue(click->fICurr.fY, fWidthControl);
1715 break;
1716 case MyClick::kLineButton:
1717 savePath(click->fState);
1718 enable_verb_button(myClick->fControl);
1719 fWeightControl.fVisible = false;
1720 set_path_verb(fActiveVerb, SkPath::kLine_Verb, &fPath, 1);
1721 validatePath();
1722 break;
1723 case MyClick::kQuadButton:
1724 savePath(click->fState);
1725 enable_verb_button(myClick->fControl);
1726 fWeightControl.fVisible = false;
1727 set_path_verb(fActiveVerb, SkPath::kQuad_Verb, &fPath, 1);
1728 validatePath();
1729 break;
1730 case MyClick::kConicButton: {
1731 savePath(click->fState);
1732 enable_verb_button(myClick->fControl);
1733 fWeightControl.fVisible = true;
1734 const SkScalar defaultConicWeight = 1.f / SkScalarSqrt(2);
1735 set_path_verb(fActiveVerb, SkPath::kConic_Verb, &fPath, defaultConicWeight);
1736 validatePath();
1737 fWeightControl.fValLo = get_path_weight(fActiveVerb, fPath);
1738 } break;
1739 case MyClick::kCubicButton:
1740 savePath(click->fState);
1741 enable_verb_button(myClick->fControl);
1742 fWeightControl.fVisible = false;
1743 set_path_verb(fActiveVerb, SkPath::kCubic_Verb, &fPath, 1);
1744 validatePath();
1745 break;
1746 case MyClick::kAddButton:
1747 savePath(click->fState);
1748 add_path_segment(fActiveVerb, &fPath);
1749 validatePath();
1750 if (fWeightControl.fVisible) {
1751 fWeightControl.fValLo = get_path_weight(fActiveVerb, fPath);
1752 }
1753 break;
1754 case MyClick::kDeleteButton:
1755 savePath(click->fState);
1756 delete_path_segment(fActiveVerb, &fPath);
1757 validatePath();
1758 break;
1759 case MyClick::kFillButton:
1760 fFillButton.toggle();
1761 break;
1762 case MyClick::kSkeletonButton:
1763 fSkeletonButton.toggle();
1764 break;
1765 case MyClick::kFilterButton:
1766 fFilterButton.toggle();
1767 fFilterControl.fVisible = fFilterButton.fState == 2;
1768 break;
1769 case MyClick::kBisectButton:
1770 fBisectButton.toggle();
1771 break;
1772 case MyClick::kJoinButton:
1773 fJoinButton.toggle();
1774 break;
1775 case MyClick::kInOutButton:
1776 fInOutButton.toggle();
1777 break;
1778 default:
1779 SkASSERT(0);
1780 break;
1781 }
1782 } break;
1783 default:
1784 SkASSERT(0);
1785 break;
1786 }
1787 setControlButtonsPos();
1788 this->inval(NULL);
1789 return true;
1790 }
1791
1792private:
1793 typedef SampleView INHERITED;
1794};
1795
1796static struct KeyCommand {
1797 char fKey;
1798 char fAlternate;
1799 const char* fDescriptionL;
1800 const char* fDescriptionR;
1801 bool (AAGeometryView::*fFunction)();
1802} kKeyCommandList[] = {
1803 { ' ', 0, "space", "center path", &AAGeometryView::scaleToFit },
1804 { '-', 0, "-", "zoom out", &AAGeometryView::scaleDown },
1805 { '+', '=', "+/=", "zoom in", &AAGeometryView::scaleUp },
1806 { 'd', 0, "d", "dump to console", &AAGeometryView::pathDump },
1807 { 'h', 0, "h", "hide controls", &AAGeometryView::hideAll },
1808 { 'r', 0, "r", "reset path", &AAGeometryView::constructPath },
1809 { 'z', 0, "z", "undo", &AAGeometryView::undo },
1810 { '?', 0, "?", "show legend", &AAGeometryView::showLegend },
1811};
1812
1813const int kKeyCommandCount = (int) SK_ARRAY_COUNT(kKeyCommandList);
1814
1815void AAGeometryView::draw_legend(SkCanvas* canvas) {
1816 SkScalar bottomOffset = this->height() - 10;
1817 for (int index = kKeyCommandCount - 1; index >= 0; --index) {
1818 bottomOffset -= 15;
1819 canvas->drawText(kKeyCommandList[index].fDescriptionL,
1820 strlen(kKeyCommandList[index].fDescriptionL), this->width() - 160, bottomOffset,
1821 fLegendLeftPaint);
1822 canvas->drawText(kKeyCommandList[index].fDescriptionR,
1823 strlen(kKeyCommandList[index].fDescriptionR), this->width() - 20, bottomOffset,
1824 fLegendRightPaint);
1825 }
1826}
1827
1828// overrides from SkEventSink
1829bool AAGeometryView::onQuery(SkEvent* evt) {
1830 if (SampleCode::TitleQ(*evt)) {
1831 SampleCode::TitleR(evt, "AAGeometry");
1832 return true;
1833 }
1834 SkUnichar uni;
1835 if (false) {
1836 return this->INHERITED::onQuery(evt);
1837 }
1838 if (SampleCode::CharQ(*evt, &uni)) {
1839 for (int index = 0; index < kButtonCount; ++index) {
1840 Button* button = kButtonList[index].fButton;
1841 if (button->fVisible && uni == button->fLabel) {
1842 MyClick click(this, MyClick::kControlType, kButtonList[index].fButtonType);
1843 click.fState = Click::kDown_State;
1844 (void) this->onClick(&click);
1845 return true;
1846 }
1847 }
1848 for (int index = 0; index < kKeyCommandCount; ++index) {
1849 KeyCommand& keyCommand = kKeyCommandList[index];
1850 if (uni == keyCommand.fKey || uni == keyCommand.fAlternate) {
1851 return (this->*keyCommand.fFunction)();
1852 }
1853 }
1854 if (('A' <= uni && uni <= 'Z') || ('a' <= uni && uni <= 'z')) {
1855 for (int index = 0; index < kButtonCount; ++index) {
1856 Button* button = kButtonList[index].fButton;
1857 if (button->fVisible && (uni & ~0x20) == (button->fLabel & ~0x20)) {
1858 MyClick click(this, MyClick::kControlType, kButtonList[index].fButtonType);
1859 click.fState = Click::kDown_State;
1860 (void) this->onClick(&click);
1861 return true;
1862 }
1863 }
1864 }
1865 }
1866 return this->INHERITED::onQuery(evt);
1867}
1868
1869DEF_SAMPLE( return new AAGeometryView; )