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