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