Home | History | Annotate | Download | only in docs
      1 #Topic Path
      2 #Alias Path_Reference ##
      3 #Alias Paths ##
      4 
      5 #Class SkPath
      6 
      7 #Code
      8 #Populate
      9 ##
     10 
     11 Paths contain geometry. Paths may be empty, or contain one or more Verbs that
     12 outline a figure. Path always starts with a move verb to a Cartesian_Coordinate,
     13 and may be followed by additional verbs that add lines or curves.
     14 Adding a close verb makes the geometry into a continuous loop, a closed contour.
     15 Paths may contain any number of contours, each beginning with a move verb.
     16 
     17 Path contours may contain only a move verb, or may also contain lines,
     18 Quadratic_Beziers, Conics, and Cubic_Beziers. Path contours may be open or
     19 closed.
     20 
     21 When used to draw a filled area, Path describes whether the fill is inside or
     22 outside the geometry. Path also describes the winding rule used to fill
     23 overlapping contours.
     24 
     25 Internally, Path lazily computes metrics likes bounds and convexity. Call
     26 SkPath::updateBoundsCache to make Path thread safe.
     27 
     28 #Subtopic Verb
     29 #Alias Verbs ##
     30 #Line # line and curve type ##
     31 #Enum Verb
     32 #Line # controls how Path Points are interpreted ##
     33 
     34 #Code
     35     enum Verb {
     36         kMove_Verb,
     37         kLine_Verb,
     38         kQuad_Verb,
     39         kConic_Verb,
     40         kCubic_Verb,
     41         kClose_Verb,
     42         kDone_Verb,
     43     };
     44 ##
     45 
     46 Verb instructs Path how to interpret one or more Point and optional Conic_Weight;
     47 manage Contour, and terminate Path.
     48 
     49 #Const kMove_Verb 0
     50 #Line # starts new Contour at next Point ##
     51     Consecutive kMove_Verb are preserved but all but the last kMove_Verb is
     52     ignored. kMove_Verb after other Verbs implicitly closes the previous Contour
     53     if SkPaint::kFill_Style is set when drawn; otherwise, stroke is drawn open.
     54     kMove_Verb as the last Verb is preserved but ignored.
     55 ##
     56 #Const kLine_Verb 1
     57 #Line # adds Line from Last_Point to next Point ##
     58     Line is a straight segment from Point to Point. Consecutive kLine_Verb
     59     extend Contour. kLine_Verb at same position as prior kMove_Verb is
     60     preserved, and draws Point if SkPaint::kStroke_Style is set, and
     61     SkPaint::Cap is SkPaint::kSquare_Cap or SkPaint::kRound_Cap. kLine_Verb
     62     at same position as prior line or curve Verb is preserved but is ignored.
     63 ##
     64 #Const kQuad_Verb 2
     65 #Line # adds Quad from Last_Point ##
     66     Adds Quad from Last_Point, using control Point, and end Point.
     67     Quad is a parabolic section within tangents from Last_Point to control Point,
     68     and control Point to end Point.
     69 ##
     70 #Const kConic_Verb 3
     71 #Line # adds Conic from Last_Point ##
     72     Adds Conic from Last_Point, using control Point, end Point, and Conic_Weight.
     73     Conic is a elliptical, parabolic, or hyperbolic section within tangents
     74     from Last_Point to control Point, and control Point to end Point, constrained
     75     by Conic_Weight. Conic_Weight less than one is elliptical; equal to one is
     76     parabolic (and identical to Quad); greater than one hyperbolic.
     77 ##
     78 #Const kCubic_Verb 4
     79 #Line # adds Cubic from Last_Point ##
     80     Adds Cubic from Last_Point, using two control Points, and end Point.
     81     Cubic is a third-order Bezier_Curve section within tangents from Last_Point
     82     to first control Point, and from second control Point to end Point.
     83 ##
     84 #Const kClose_Verb 5
     85 #Line # closes Contour ##
     86     Closes Contour, connecting Last_Point to kMove_Verb Point. Consecutive
     87     kClose_Verb are preserved but only first has an effect. kClose_Verb after
     88     kMove_Verb has no effect.
     89 ##
     90 #Const kDone_Verb 6
     91 #Line # terminates Path ##
     92     Not in Verb_Array, but returned by Path iterator.
     93 ##
     94 
     95 Each Verb has zero or more Points stored in Path.
     96 Path iterator returns complete curve descriptions, duplicating shared Points
     97 for consecutive entries.
     98 
     99 #Table
    100 #Legend
    101 # Verb        # Allocated Points # Iterated Points # Weights ##
    102 ##
    103 # kMove_Verb  # 1                # 1               # 0       ##
    104 # kLine_Verb  # 1                # 2               # 0       ##
    105 # kQuad_Verb  # 2                # 3               # 0       ##
    106 # kConic_Verb # 2                # 3               # 1       ##
    107 # kCubic_Verb # 3                # 4               # 0       ##
    108 # kClose_Verb # 0                # 1               # 0       ##
    109 # kDone_Verb  # --               # 0               # 0       ##
    110 ##
    111 
    112 #Example
    113 void draw(SkCanvas* canvas) {
    114     SkPath path;
    115     path.lineTo(20, 20);
    116     path.quadTo(-10, -10, 30, 30);
    117     path.close();
    118     path.cubicTo(1, 2, 3, 4, 5, 6);
    119     path.conicTo(0, 0, 0, 0, 2);
    120     uint8_t verbs[7];
    121     int count = path.getVerbs(verbs, (int) SK_ARRAY_COUNT(verbs));
    122     const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close" };
    123     SkDebugf("verb count: %d\nverbs: ", count);
    124     for (int i = 0; i < count; ++i) {
    125         SkDebugf("k%s_Verb ", verbStr[verbs[i]]);
    126     }
    127     SkDebugf("\n");
    128 }
    129 #StdOut
    130 verb count: 7
    131 verbs: kMove_Verb kLine_Verb kQuad_Verb kClose_Verb kMove_Verb kCubic_Verb kConic_Verb
    132 ##
    133 ##
    134 
    135 #Enum Verb ##
    136 #Subtopic Verb ##
    137 
    138 # ------------------------------------------------------------------------------
    139 #Subtopic Direction
    140 #Line # contour orientation, clockwise or counterclockwise ##
    141 #Alias Directions ##
    142 
    143 #Enum Direction
    144 #Line # sets Contour clockwise or counterclockwise ##
    145 
    146 #Code
    147     enum Direction : int {
    148         kCW_Direction,
    149         kCCW_Direction,
    150     };
    151 ##
    152 
    153 Direction describes whether Contour is clockwise or counterclockwise.
    154 When Path contains multiple overlapping Contours, Direction together with
    155 Fill_Type determines whether overlaps are filled or form holes.
    156 
    157 Direction also determines how Contour is measured. For instance, dashing
    158 measures along Path to determine where to start and stop stroke; Direction
    159 will change dashed results as it steps clockwise or counterclockwise.
    160 
    161 Closed Contours like Rect, Round_Rect, Circle, and Oval added with
    162 kCW_Direction travel clockwise; the same added with kCCW_Direction
    163 travel counterclockwise.
    164 
    165 #Const kCW_Direction 0
    166 #Line # contour travels clockwise ##
    167 ##
    168 #Const kCCW_Direction 1
    169 #Line # contour travels counterclockwise ##
    170 ##
    171 
    172 
    173 #Example
    174 #Height 100
    175 void draw(SkCanvas* canvas) {
    176     const SkPoint arrow[] = { {40, -5}, {45, 0}, {40, 5} };
    177     const SkRect rect = {10, 10, 90, 90};
    178     SkPaint rectPaint;
    179     rectPaint.setAntiAlias(true);
    180     SkPaint textPaint(rectPaint);
    181     rectPaint.setStyle(SkPaint::kStroke_Style);
    182     SkPaint arrowPaint(rectPaint);
    183     SkPath arrowPath;
    184     arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
    185     arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 320, 0,
    186                              SkPath1DPathEffect::kRotate_Style));
    187     for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
    188         canvas->drawRect(rect, rectPaint);
    189         for (unsigned start : { 0, 1, 2, 3 } ) {
    190            SkPath path;
    191            path.addRect(rect, direction, start);
    192            canvas->drawPath(path, arrowPaint);
    193        }
    194        canvas->drawString(SkPath::kCW_Direction == direction ? "CW" : "CCW",  rect.centerX(),
    195             rect.centerY(), textPaint);
    196        canvas->translate(120, 0);
    197     }
    198 }
    199 ##
    200 
    201 #SeeAlso arcTo rArcTo isRect isNestedFillRects addRect addOval
    202 
    203 #Enum Direction ##
    204 #Subtopic Direction ##
    205 
    206 # ------------------------------------------------------------------------------
    207 
    208 #Method SkPath()
    209 #In Constructors
    210 #Line # constructs with default values ##
    211 #Populate
    212 
    213 #Example
    214     SkPath path;
    215     SkDebugf("path is " "%s" "empty", path.isEmpty() ? "" : "not ");
    216 #StdOut
    217 path is empty
    218 ##
    219 ##
    220 
    221 #SeeAlso reset rewind
    222 
    223 ##
    224 
    225 # ------------------------------------------------------------------------------
    226 
    227 #Method SkPath(const SkPath& path)
    228 #In Constructors
    229 #Line # makes a shallow copy ##
    230 #Populate
    231 
    232 #Example
    233 #Description
    234     Modifying one path does not effect another, even if they started as copies
    235     of each other.
    236 ##
    237     SkPath path;
    238     path.lineTo(20, 20);
    239     SkPath path2(path);
    240     path2.close();
    241     SkDebugf("path verbs: %d\n", path.countVerbs());
    242     SkDebugf("path2 verbs: %d\n", path2.countVerbs());
    243     path.reset();
    244     SkDebugf("after reset\n" "path verbs: %d\n", path.countVerbs());
    245     SkDebugf("path2 verbs: %d\n", path2.countVerbs());
    246 #StdOut
    247 path verbs: 2
    248 path2 verbs: 3
    249 after reset
    250 path verbs: 0
    251 path2 verbs: 3
    252 ##
    253 ##
    254 
    255 #SeeAlso operator=(const SkPath& path)
    256 
    257 ##
    258 
    259 # ------------------------------------------------------------------------------
    260 
    261 #Method ~SkPath()
    262 
    263 #Line # decreases Reference_Count of owned objects ##
    264 #Populate
    265 
    266 #Example
    267 #Description
    268 delete calls Path destructor, but copy of original in path2 is unaffected.
    269 ##
    270 void draw(SkCanvas* canvas) {
    271     SkPath* path = new SkPath();
    272     path->lineTo(20, 20);
    273     SkPath path2(*path);
    274     delete path;
    275     SkDebugf("path2 is " "%s" "empty", path2.isEmpty() ? "" : "not ");
    276 }
    277 ##
    278 
    279 #SeeAlso SkPath() SkPath(const SkPath& path) operator=(const SkPath& path)
    280 
    281 ##
    282 
    283 # ------------------------------------------------------------------------------
    284 
    285 #Method SkPath& operator=(const SkPath& path)
    286 
    287 #Line # makes a shallow copy ##
    288 #Populate
    289 
    290 #Example
    291 SkPath path1;
    292 path1.addRect({10, 20, 30, 40});
    293 SkPath path2 = path1;
    294 const SkRect& b1 = path1.getBounds();
    295 SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom);
    296 const SkRect& b2 = path2.getBounds();
    297 SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom);
    298 #StdOut
    299 path1 bounds = 10, 20, 30, 40
    300 path2 bounds = 10, 20, 30, 40
    301 #StdOut ##
    302 ##
    303 
    304 #SeeAlso swap SkPath(const SkPath& path)
    305 
    306 ##
    307 
    308 # ------------------------------------------------------------------------------
    309 
    310 #Method bool operator==(const SkPath& a, const SkPath& b)
    311 
    312 #Line # compares Paths for equality ##
    313 #Populate
    314 
    315 #Example
    316 #Description
    317 rewind() removes Verb_Array but leaves storage; since storage is not compared,
    318 Path pair are equivalent.
    319 ##
    320 void draw(SkCanvas* canvas) {
    321     auto debugster = [](const char* prefix, const SkPath& a, const SkPath& b) -> void {
    322                 SkDebugf("%s one %c= two\n", prefix, a == b ? '=' : '!');
    323     };
    324     SkPath one;
    325     SkPath two;
    326     debugster("empty", one, two);
    327     one.moveTo(0, 0);
    328     debugster("moveTo", one, two);
    329     one.rewind();
    330     debugster("rewind", one, two);
    331     one.moveTo(0, 0);
    332     one.reset();
    333     debugster("reset", one, two);
    334 }
    335 #StdOut
    336 empty one == two
    337 moveTo one != two
    338 rewind one == two
    339 reset one == two
    340 ##
    341 ##
    342 
    343 #SeeAlso operator!=(const SkPath& a, const SkPath& b) operator=(const SkPath& path)
    344 
    345 ##
    346 
    347 # ------------------------------------------------------------------------------
    348 
    349 #Method bool operator!=(const SkPath& a, const SkPath& b)
    350 
    351 #Line # compares paths for inequality ##
    352 #Populate
    353 
    354 #Example
    355 #Description
    356 Path pair are equal though their convexity is not equal.
    357 ##
    358 void draw(SkCanvas* canvas) {
    359     auto debugster = [](const char* prefix, const SkPath& a, const SkPath& b) -> void {
    360                 SkDebugf("%s one %c= two\n", prefix, a != b ? '!' : '=');
    361     };
    362     SkPath one;
    363     SkPath two;
    364     debugster("empty", one, two);
    365     one.addRect({10, 20, 30, 40});
    366     two.addRect({10, 20, 30, 40});
    367     debugster("add rect", one, two);
    368     one.setConvexity(SkPath::kConcave_Convexity);
    369     debugster("setConvexity", one, two);
    370     SkDebugf("convexity %c=\n", one.getConvexity() == two.getConvexity() ? '=' : '!');
    371 }
    372 #StdOut
    373 empty one == two
    374 add rect one == two
    375 setConvexity one == two
    376 convexity !=
    377 ##
    378 ##
    379 
    380 ##
    381 
    382 # ------------------------------------------------------------------------------
    383 
    384 #Subtopic Property
    385 #Line # metrics and attributes ##
    386 ##
    387 
    388 #Method bool isInterpolatable(const SkPath& compare) const
    389 #In Property
    390 #In Interpolate
    391 #Line # returns if pair contains equal counts of Verb_Array and Weights ##
    392 #Populate
    393 
    394 #Example
    395     SkPath path, path2;
    396     path.moveTo(20, 20);
    397     path.lineTo(40, 40);
    398     path.lineTo(20, 20);
    399     path.lineTo(40, 40);
    400     path.close();
    401     path2.addRect({20, 20, 40, 40});
    402     SkDebugf("paths are " "%s" "interpolatable", path.isInterpolatable(path2) ? "" : "not ");
    403 #StdOut
    404 paths are interpolatable
    405 ##
    406 ##
    407 
    408 #SeeAlso isInterpolatable
    409 
    410 ##
    411 
    412 # ------------------------------------------------------------------------------
    413 
    414 #Subtopic Interpolate
    415 #Line # weighted average of Path pair ##
    416 ##
    417 
    418 #Method bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const
    419 #In Interpolate
    420 #Line # interpolates between Path pair ##
    421 Interpolates between Paths with Point_Array of equal size.
    422 Copy Verb_Array and Weights to out, and set out Point_Array to a weighted
    423 average of this Point_Array and ending Point_Array, using the formula:
    424 #Formula # (Path Point * weight) + ending Point * (1 - weight) ##.
    425 
    426 weight is most useful when between zero (ending Point_Array) and
    427 one (this Point_Array); will work with values outside of this
    428 range.
    429 
    430 interpolate() returns false and leaves out unchanged if Point_Array is not
    431 the same size as ending Point_Array. Call isInterpolatable to check Path
    432 compatibility prior to calling interpolate().
    433 
    434 #Param ending  Point_Array averaged with this Point_Array ##
    435 #Param weight  contribution of this Point_Array, and
    436                one minus contribution of ending Point_Array
    437 ##
    438 #Param out     Path replaced by interpolated averages ##
    439 
    440 #Return  true if Paths contain same number of Points ##
    441 
    442 #Example
    443 #Height 60
    444 void draw(SkCanvas* canvas) {
    445     SkPaint paint;
    446     paint.setAntiAlias(true);
    447     paint.setStyle(SkPaint::kStroke_Style);
    448     SkPath path, path2;
    449     path.moveTo(20, 20);
    450     path.lineTo(40, 40);
    451     path.lineTo(20, 40);
    452     path.lineTo(40, 20);
    453     path.close();
    454     path2.addRect({20, 20, 40, 40});
    455     for (SkScalar i = 0; i <= 1; i += 1.f / 6) {
    456       SkPath interp;
    457       path.interpolate(path2, i, &interp);
    458       canvas->drawPath(interp, paint);
    459       canvas->translate(30, 0);
    460     }
    461 }
    462 ##
    463 
    464 #SeeAlso isInterpolatable
    465 
    466 ##
    467 
    468 # ------------------------------------------------------------------------------
    469 #Subtopic Fill_Type
    470 #Line # fill rule, normal and inverted ##
    471 
    472 #Enum FillType
    473 #Line # sets winding rule and inverse fill ##
    474 
    475 #Code
    476     enum FillType {
    477         kWinding_FillType,
    478         kEvenOdd_FillType,
    479         kInverseWinding_FillType,
    480         kInverseEvenOdd_FillType,
    481     };
    482 ##
    483 
    484 Fill_Type selects the rule used to fill Path. Path set to kWinding_FillType
    485 fills if the sum of Contour edges is not zero, where clockwise edges add one, and
    486 counterclockwise edges subtract one. Path set to kEvenOdd_FillType fills if the
    487 number of Contour edges is odd. Each Fill_Type has an inverse variant that
    488 reverses the rule:
    489 kInverseWinding_FillType fills where the sum of Contour edges is zero;
    490 kInverseEvenOdd_FillType fills where the number of Contour edges is even.
    491 
    492 #Example
    493 #Height 100
    494 #Description
    495 The top row has two clockwise rectangles. The second row has one clockwise and
    496 one counterclockwise rectangle. The even-odd variants draw the same. The
    497 winding variants draw the top rectangle overlap, which has a winding of 2, the
    498 same as the outer parts of the top rectangles, which have a winding of 1.
    499 ##
    500 void draw(SkCanvas* canvas) {
    501    SkPath path;
    502    path.addRect({10, 10, 30, 30}, SkPath::kCW_Direction);
    503    path.addRect({20, 20, 40, 40}, SkPath::kCW_Direction);
    504    path.addRect({10, 60, 30, 80}, SkPath::kCW_Direction);
    505    path.addRect({20, 70, 40, 90}, SkPath::kCCW_Direction);
    506    SkPaint strokePaint;
    507    strokePaint.setStyle(SkPaint::kStroke_Style);
    508    SkRect clipRect = {0, 0, 51, 100};
    509    canvas->drawPath(path, strokePaint);
    510    SkPaint fillPaint;
    511    for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType,
    512                       SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) {
    513         canvas->translate(51, 0);
    514         canvas->save();
    515         canvas->clipRect(clipRect);
    516         path.setFillType(fillType);
    517         canvas->drawPath(path, fillPaint);
    518         canvas->restore();
    519     }
    520 }
    521 ##
    522 
    523 #Const kWinding_FillType 0
    524 #Line # is enclosed by a non-zero sum of Contour Directions ##
    525 ##
    526 #Const kEvenOdd_FillType 1
    527 #Line # is enclosed by an odd number of Contours ##
    528 ##
    529 #Const kInverseWinding_FillType 2
    530 #Line # is enclosed by a zero sum of Contour Directions ##
    531 ##
    532 #Const kInverseEvenOdd_FillType 3
    533 #Line # is enclosed by an even number of Contours ##
    534 ##
    535 
    536 #Example
    537 #Height 230
    538 void draw(SkCanvas* canvas) {
    539    SkPath path;
    540    path.addRect({20, 10, 80, 70}, SkPath::kCW_Direction);
    541    path.addRect({40, 30, 100, 90}, SkPath::kCW_Direction);
    542    SkPaint strokePaint;
    543    strokePaint.setStyle(SkPaint::kStroke_Style);
    544    SkRect clipRect = {0, 0, 128, 128};
    545    canvas->drawPath(path, strokePaint);
    546    canvas->drawLine({0, 50}, {120, 50}, strokePaint);
    547    SkPaint textPaint;
    548    textPaint.setAntiAlias(true);
    549    SkScalar textHPos[] = { 10, 30, 60, 90, 110 };
    550    canvas->drawPosTextH("01210", 5, textHPos, 48, textPaint);
    551    textPaint.setTextSize(18);
    552    canvas->translate(0, 128);
    553    canvas->scale(.5f, .5f);
    554    canvas->drawString("inverse", 384, 150, textPaint);
    555    SkPaint fillPaint;
    556    for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType,
    557                       SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) {
    558         canvas->save();
    559         canvas->clipRect(clipRect);
    560         path.setFillType(fillType);
    561         canvas->drawPath(path, fillPaint);
    562         canvas->restore();
    563         canvas->drawString(fillType & 1 ? "even-odd" : "winding", 64, 170, textPaint);
    564         canvas->translate(128, 0);
    565     }
    566 }
    567 ##
    568 
    569 #SeeAlso SkPaint::Style Direction getFillType setFillType
    570 
    571 ##
    572 
    573 # ------------------------------------------------------------------------------
    574 
    575 #Method FillType getFillType() const
    576 
    577 #In Fill_Type
    578 #Line # returns Fill_Type: winding, even-odd, inverse ##
    579 #Populate
    580 
    581 #Example
    582     SkPath path;
    583     SkDebugf("default path fill type is %s\n",
    584             path.getFillType() == SkPath::kWinding_FillType ? "kWinding_FillType" :
    585             path.getFillType() == SkPath::kEvenOdd_FillType ? "kEvenOdd_FillType" :
    586             path.getFillType() == SkPath::kInverseWinding_FillType ? "kInverseWinding_FillType" :
    587                                                                      "kInverseEvenOdd_FillType");
    588 #StdOut
    589 default path fill type is kWinding_FillType
    590 ##
    591 ##
    592 
    593 #SeeAlso FillType setFillType isInverseFillType
    594 
    595 ##
    596 
    597 # ------------------------------------------------------------------------------
    598 
    599 #Method void setFillType(FillType ft)
    600 
    601 #In Fill_Type
    602 #Line # sets Fill_Type: winding, even-odd, inverse ##
    603 #Populate
    604 
    605 #Example
    606 #Description
    607 If empty Path is set to inverse FillType, it fills all pixels.
    608 ##
    609 #Height 64
    610      SkPath path;
    611      path.setFillType(SkPath::kInverseWinding_FillType);
    612      SkPaint paint;
    613      paint.setColor(SK_ColorBLUE);
    614      canvas->drawPath(path, paint);
    615 ##
    616 
    617 #SeeAlso FillType getFillType toggleInverseFillType
    618 
    619 ##
    620 
    621 # ------------------------------------------------------------------------------
    622 
    623 #Method bool isInverseFillType() const
    624 
    625 #In Fill_Type
    626 #Line # returns if Fill_Type fills outside geometry ##
    627 #Populate
    628 
    629 #Example
    630     SkPath path;
    631     SkDebugf("default path fill type is inverse: %s\n",
    632             path.isInverseFillType() ? "true" : "false");
    633 #StdOut
    634 default path fill type is inverse: false
    635 ##
    636 ##
    637 
    638 #SeeAlso FillType getFillType setFillType toggleInverseFillType
    639 
    640 ##
    641 
    642 # ------------------------------------------------------------------------------
    643 
    644 #Method void toggleInverseFillType()
    645 
    646 #In Fill_Type
    647 #Line # toggles Fill_Type between inside and outside geometry ##
    648 Replaces FillType with its inverse. The inverse of FillType describes the area
    649 unmodified by the original FillType.
    650 
    651 #Table
    652 #Legend
    653 # FillType                 # toggled FillType         ##
    654 ##
    655 # kWinding_FillType        # kInverseWinding_FillType ##
    656 # kEvenOdd_FillType        # kInverseEvenOdd_FillType ##
    657 # kInverseWinding_FillType # kWinding_FillType        ##
    658 # kInverseEvenOdd_FillType # kEvenOdd_FillType        ##
    659 ##
    660 
    661 #Example
    662 #Description
    663 Path drawn normally and through its inverse touches every pixel once.
    664 ##
    665 #Height 100
    666 SkPath path;
    667 SkPaint paint;
    668 paint.setColor(SK_ColorRED);
    669 paint.setTextSize(80);
    670 paint.getTextPath("ABC", 3, 20, 80, &path);
    671 canvas->drawPath(path, paint);
    672 path.toggleInverseFillType();
    673 paint.setColor(SK_ColorGREEN);
    674 canvas->drawPath(path, paint);
    675 ##
    676 
    677 #SeeAlso FillType getFillType setFillType isInverseFillType
    678 
    679 ##
    680 
    681 #Subtopic Fill_Type ##
    682 
    683 # ------------------------------------------------------------------------------
    684 
    685 #Subtopic Convexity
    686 #Line # if Path is concave or convex ##
    687 
    688 #Enum Convexity
    689 #Line # returns if Path is convex or concave ##
    690 
    691 #Code
    692     enum Convexity : uint8_t {
    693         kUnknown_Convexity,
    694         kConvex_Convexity,
    695         kConcave_Convexity,
    696     };
    697 ##
    698 
    699 Path is convex if it contains one Contour and Contour loops no more than
    700 360 degrees, and Contour angles all have same Direction. Convex Path
    701 may have better performance and require fewer resources on GPU_Surface.
    702 
    703 Path is concave when either at least one Direction change is clockwise and
    704 another is counterclockwise, or the sum of the changes in Direction is not 360
    705 degrees.
    706 
    707 Initially Path Convexity is kUnknown_Convexity. Path Convexity is computed
    708 if needed by destination Surface.
    709 
    710 #Const kUnknown_Convexity 0
    711 #Line # indicates Convexity has not been determined ##
    712 ##
    713 #Const kConvex_Convexity 1
    714 #Line # one Contour made of a simple geometry without indentations ##
    715 ##
    716 #Const kConcave_Convexity 2
    717 #Line # more than one Contour, or a geometry with indentations ##
    718 ##
    719 
    720 #Example
    721 void draw(SkCanvas* canvas) {
    722     SkPaint paint;
    723     SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};
    724     const char* labels[] = { "unknown", "convex", "concave" };
    725     for (SkScalar x : { 40, 100 } ) {
    726         SkPath path;
    727         quad[0].fX = x;
    728         path.addPoly(quad, SK_ARRAY_COUNT(quad), true);
    729         canvas->drawPath(path, paint);
    730         canvas->drawString(labels[(int) path.getConvexity()], 30, 100, paint);
    731         canvas->translate(100, 100);
    732     }
    733 }
    734 ##
    735 
    736 #SeeAlso Contour Direction getConvexity getConvexityOrUnknown setConvexity isConvex
    737 
    738 #Enum Convexity ##
    739 
    740 #Method Convexity getConvexity() const
    741 
    742 #In Convexity
    743 #Line # returns geometry convexity, computing if necessary ##
    744 #Populate
    745 
    746 #Example
    747 void draw(SkCanvas* canvas) {
    748     auto debugster = [](const char* prefix, const SkPath& path) -> void {
    749         SkDebugf("%s path convexity is %s\n", prefix,
    750                 SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" :
    751                 SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); };
    752     SkPath path;
    753     debugster("initial", path);
    754     path.lineTo(50, 0);
    755     debugster("first line", path);
    756     path.lineTo(50, 50);
    757     debugster("second line", path);
    758     path.lineTo(100, 50);
    759     debugster("third line", path);
    760 }
    761 ##
    762 
    763 #SeeAlso Convexity Contour Direction getConvexityOrUnknown setConvexity isConvex
    764 
    765 ##
    766 
    767 # ------------------------------------------------------------------------------
    768 
    769 #Method Convexity getConvexityOrUnknown() const
    770 
    771 #In Convexity
    772 #Line # returns geometry convexity if known ##
    773 #Populate
    774 
    775 #Example
    776 #Description
    777 Convexity is unknown unless getConvexity is called without a subsequent call
    778 that alters the path.
    779 ##
    780 void draw(SkCanvas* canvas) {
    781     auto debugster = [](const char* prefix, const SkPath& path) -> void {
    782         SkDebugf("%s path convexity is %s\n", prefix,
    783             SkPath::kUnknown_Convexity == path.getConvexityOrUnknown() ? "unknown" :
    784             SkPath::kConvex_Convexity == path.getConvexityOrUnknown() ? "convex" : "concave"); };
    785     SkPath path;
    786     debugster("initial", path);
    787     path.lineTo(50, 0);
    788     debugster("first line", path);
    789     path.getConvexity();
    790     path.lineTo(50, 50);
    791     debugster("second line", path);
    792     path.lineTo(100, 50);
    793     path.getConvexity();
    794     debugster("third line", path);
    795 }
    796 ##
    797 
    798 #SeeAlso Convexity Contour Direction getConvexity setConvexity isConvex
    799 
    800 ##
    801 
    802 # ------------------------------------------------------------------------------
    803 
    804 #Method void setConvexity(Convexity convexity)
    805 
    806 #In Convexity
    807 #Line # sets if geometry is convex to avoid future computation ##
    808 #Populate
    809 
    810 #Example
    811 void draw(SkCanvas* canvas) {
    812     auto debugster = [](const char* prefix, const SkPath& path) -> void {
    813         SkDebugf("%s path convexity is %s\n", prefix,
    814                 SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" :
    815                 SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); };
    816         SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};
    817         SkPath path;
    818         path.addPoly(quad, SK_ARRAY_COUNT(quad), true);
    819         debugster("initial", path);
    820         path.setConvexity(SkPath::kConcave_Convexity);
    821         debugster("after forcing concave", path);
    822         path.setConvexity(SkPath::kUnknown_Convexity);
    823         debugster("after forcing unknown", path);
    824 }
    825 ##
    826 
    827 #SeeAlso Convexity Contour Direction getConvexity getConvexityOrUnknown isConvex
    828 
    829 ##
    830 
    831 # ------------------------------------------------------------------------------
    832 
    833 #Method bool isConvex() const
    834 
    835 #In Convexity
    836 #Line # returns if geometry is convex ##
    837 #Populate
    838 
    839 #Example
    840 #Description
    841 Concave shape is erroneously considered convex after a forced call to
    842 setConvexity.
    843 ##
    844 void draw(SkCanvas* canvas) {
    845     SkPaint paint;
    846     SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};
    847     for (SkScalar x : { 40, 100 } ) {
    848         SkPath path;
    849         quad[0].fX = x;
    850         path.addPoly(quad, SK_ARRAY_COUNT(quad), true);
    851         path.setConvexity(SkPath::kConvex_Convexity);
    852         canvas->drawPath(path, paint);
    853         canvas->drawString(path.isConvex() ? "convex" : "not convex", 30, 100, paint);
    854         canvas->translate(100, 100);
    855     }
    856 }
    857 ##
    858 
    859 #SeeAlso Convexity Contour Direction getConvexity getConvexityOrUnknown setConvexity
    860 
    861 ##
    862 
    863 #Subtopic Convexity ##
    864 
    865 # ------------------------------------------------------------------------------
    866 
    867 #Method bool isOval(SkRect* bounds) const
    868 #In Property
    869 #Line # returns if describes Oval ##
    870 #Populate
    871 
    872 #Example
    873 void draw(SkCanvas* canvas) {
    874     SkPaint paint;
    875     SkPath path;
    876     path.addOval({20, 20, 220, 220});
    877     SkRect bounds;
    878     if (path.isOval(&bounds)) {
    879         paint.setColor(0xFF9FBFFF);
    880         canvas->drawRect(bounds, paint);
    881     }
    882     paint.setColor(0x3f000000);
    883     canvas->drawPath(path, paint);
    884 }
    885 ##
    886 
    887 #SeeAlso Oval addCircle addOval
    888 
    889 ##
    890 
    891 # ------------------------------------------------------------------------------
    892 
    893 #Method bool isRRect(SkRRect* rrect) const
    894 #In Property
    895 #Line # returns if describes Round_Rect ##
    896 #Populate
    897 
    898 #Example
    899 #Description
    900 Draw rounded rectangle and its bounds.
    901 ##
    902 void draw(SkCanvas* canvas) {
    903     SkPaint paint;
    904     SkPath path;
    905     path.addRRect(SkRRect::MakeRectXY({20, 20, 220, 220}, 30, 50));
    906     SkRRect rrect;
    907     if (path.isRRect(&rrect)) {
    908         const SkRect& bounds = rrect.rect();
    909         paint.setColor(0xFF9FBFFF);
    910         canvas->drawRect(bounds, paint);
    911     }
    912     paint.setColor(0x3f000000);
    913     canvas->drawPath(path, paint);
    914 }
    915 ##
    916 
    917 #SeeAlso Round_Rect addRoundRect addRRect
    918 
    919 ##
    920 
    921 # ------------------------------------------------------------------------------
    922 
    923 #Method SkPath& reset()
    924 #In Constructors
    925 #Line # removes Verb_Array, Point_Array, and Weights; frees memory ##
    926 #Populate
    927 
    928 #Example
    929    SkPath path1, path2;
    930    path1.setFillType(SkPath::kInverseWinding_FillType);
    931    path1.addRect({10, 20, 30, 40});
    932    SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
    933    path1.reset();
    934    SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
    935 ##
    936 
    937 #SeeAlso rewind()
    938 
    939 ##
    940 
    941 # ------------------------------------------------------------------------------
    942 
    943 #Method SkPath& rewind()
    944 #In Constructors
    945 #Line # removes Verb_Array, Point_Array, and Weights, keeping memory ##
    946 #Populate
    947 
    948 #Example
    949 #Description
    950 Although path1 retains its internal storage, it is indistinguishable from
    951 a newly initialized path.
    952 ##
    953    SkPath path1, path2;
    954    path1.setFillType(SkPath::kInverseWinding_FillType);
    955    path1.addRect({10, 20, 30, 40});
    956    SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
    957    path1.rewind();
    958    SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
    959 ##
    960 
    961 #SeeAlso reset()
    962 
    963 ##
    964 
    965 # ------------------------------------------------------------------------------
    966 
    967 #Method bool isEmpty() const
    968 #In Property
    969 #Line # returns if verb count is zero ##
    970 #Populate
    971 
    972 #Example
    973 void draw(SkCanvas* canvas) {
    974     auto debugster = [](const char* prefix, const SkPath& path) -> void {
    975         SkDebugf("%s path is %s" "empty\n", prefix, path.isEmpty() ? "" : "not ");
    976     };
    977     SkPath path;
    978     debugster("initial", path);
    979     path.moveTo(0, 0);
    980     debugster("after moveTo", path);
    981     path.rewind();
    982     debugster("after rewind", path);
    983     path.lineTo(0, 0);
    984     debugster("after lineTo", path);
    985     path.reset();
    986     debugster("after reset", path);
    987 }
    988 #StdOut
    989 initial path is empty
    990 after moveTo path is not empty
    991 after rewind path is empty
    992 after lineTo path is not empty
    993 after reset path is empty
    994 ##
    995 ##
    996 
    997 #SeeAlso SkPath() reset() rewind()
    998 
    999 ##
   1000 
   1001 # ------------------------------------------------------------------------------
   1002 
   1003 #Method bool isLastContourClosed() const
   1004 #In Property
   1005 #Line # returns if final Contour forms a loop ##
   1006 #Populate
   1007 
   1008 #Example
   1009 #Description
   1010 close() has no effect if Path is empty; isLastContourClosed() returns
   1011 false until Path has geometry followed by close().
   1012 ##
   1013 void draw(SkCanvas* canvas) {
   1014     auto debugster = [](const char* prefix, const SkPath& path) -> void {
   1015         SkDebugf("%s last contour is %s" "closed\n", prefix,
   1016                  path.isLastContourClosed() ? "" : "not ");
   1017     };
   1018     SkPath path;
   1019     debugster("initial", path);
   1020     path.close();
   1021     debugster("after close", path);
   1022     path.lineTo(0, 0);
   1023     debugster("after lineTo", path);
   1024     path.close();
   1025     debugster("after close", path);
   1026 }
   1027 #StdOut
   1028 initial last contour is not closed
   1029 after close last contour is not closed
   1030 after lineTo last contour is not closed
   1031 after close last contour is closed
   1032 ##
   1033 ##
   1034 
   1035 #SeeAlso close()
   1036 
   1037 ##
   1038 
   1039 # ------------------------------------------------------------------------------
   1040 
   1041 #Method bool isFinite() const
   1042 #In Property
   1043 #Line # returns if all Point values are finite ##
   1044 #Populate
   1045 
   1046 #Example
   1047 void draw(SkCanvas* canvas) {
   1048     auto debugster = [](const char* prefix, const SkPath& path) -> void {
   1049         SkDebugf("%s path is %s" "finite\n", prefix, path.isFinite() ? "" : "not ");
   1050     };
   1051     SkPath path;
   1052     debugster("initial", path);
   1053     path.lineTo(SK_ScalarMax, SK_ScalarMax);
   1054     debugster("after line", path);
   1055     SkMatrix matrix;
   1056     matrix.setScale(2, 2);
   1057     path.transform(matrix);
   1058     debugster("after scale", path);
   1059 }
   1060 #StdOut
   1061 initial path is finite
   1062 after line path is finite
   1063 after scale path is not finite
   1064 ##
   1065 ##
   1066 
   1067 #SeeAlso SkScalar
   1068 ##
   1069 
   1070 # ------------------------------------------------------------------------------
   1071 
   1072 #Method bool isVolatile() const
   1073 #In Property
   1074 #In Volatile
   1075 #Line # returns if Device should not cache ##
   1076 #Populate
   1077 
   1078 #Example
   1079     SkPath path;
   1080     SkDebugf("volatile by default is %s\n", path.isVolatile() ? "true" : "false");
   1081 #StdOut
   1082 volatile by default is false
   1083 ##
   1084 ##
   1085 
   1086 #SeeAlso setIsVolatile
   1087 
   1088 ##
   1089 
   1090 # ------------------------------------------------------------------------------
   1091 #Subtopic Volatile
   1092 #Line # caching attribute ##
   1093 ##
   1094 
   1095 #Method void setIsVolatile(bool isVolatile)
   1096 #In Volatile
   1097 #Line # sets if Device should not cache ##
   1098 #Populate
   1099 
   1100 #Example
   1101 #Height 50
   1102 #Width 50
   1103     SkPaint paint;
   1104     paint.setStyle(SkPaint::kStroke_Style);
   1105     SkPath path;
   1106     path.setIsVolatile(true);
   1107     path.lineTo(40, 40);
   1108     canvas->drawPath(path, paint);
   1109     path.rewind();
   1110     path.moveTo(0, 40);
   1111     path.lineTo(40, 0);
   1112     canvas->drawPath(path, paint);
   1113 ##
   1114 
   1115 #ToDo tie example to bench to show how volatile affects speed or dm to show resource usage ##
   1116 
   1117 #SeeAlso isVolatile
   1118 
   1119 ##
   1120 
   1121 # ------------------------------------------------------------------------------
   1122 
   1123 #Method static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact)
   1124 #In Property
   1125 #Line # returns if Line is very small ##
   1126 #Populate
   1127 
   1128 #Example
   1129 #Description
   1130 As single precision floats, 100 and 100.000001 have the same bit representation,
   1131 and are exactly equal. 100 and 100.0001 have different bit representations, and
   1132 are not exactly equal, but are nearly equal.
   1133 ##
   1134 void draw(SkCanvas* canvas) {
   1135     SkPoint points[] = { {100, 100}, {100.000001f, 100.000001f}, {100.0001f, 100.0001f} };
   1136     for (size_t i = 0; i < SK_ARRAY_COUNT(points) - 1; ++i) {
   1137         for (bool exact : { false, true } ) {
   1138             SkDebugf("line from (%1.8g,%1.8g) to (%1.8g,%1.8g) is %s" "degenerate, %s\n",
   1139                     points[i].fX, points[i].fY, points[i + 1].fX, points[i + 1].fY,
   1140                     SkPath::IsLineDegenerate(points[i], points[i + 1], exact)
   1141                     ? "" : "not ", exact ? "exactly" : "nearly");
   1142         }
   1143     }
   1144 }
   1145 #StdOut
   1146 line from (100,100) to (100,100) is degenerate, nearly
   1147 line from (100,100) to (100,100) is degenerate, exactly
   1148 line from (100,100) to (100.0001,100.0001) is degenerate, nearly
   1149 line from (100,100) to (100.0001,100.0001) is not degenerate, exactly
   1150 #StdOut ##
   1151 ##
   1152 
   1153 #SeeAlso IsQuadDegenerate IsCubicDegenerate
   1154 ##
   1155 
   1156 # ------------------------------------------------------------------------------
   1157 
   1158 #Method static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
   1159                                  const SkPoint& p3, bool exact)
   1160 #In Property
   1161 #Line # returns if Quad is very small ##
   1162 #Populate
   1163 
   1164 #Example
   1165 #Description
   1166 As single precision floats: 100, 100.00001, and 100.00002 have different bit representations
   1167 but nearly the same value. Translating all three by 1000 gives them the same bit representation;
   1168 the fractional portion of the number can not be represented by the float and is lost.
   1169 ##
   1170 void draw(SkCanvas* canvas) {
   1171     auto debugster = [](const SkPath& path, bool exact) -> void {
   1172         SkDebugf("quad (%1.8g,%1.8g), (%1.8g,%1.8g), (%1.8g,%1.8g) is %s" "degenerate, %s\n",
   1173             path.getPoint(0).fX, path.getPoint(0).fY, path.getPoint(1).fX,
   1174             path.getPoint(1).fY, path.getPoint(2).fX, path.getPoint(2).fY,
   1175             SkPath::IsQuadDegenerate(path.getPoint(0), path.getPoint(1), path.getPoint(2), exact) ?
   1176             "" : "not ", exact ? "exactly" : "nearly");
   1177     };
   1178     SkPath path, offset;
   1179     path.moveTo({100, 100});
   1180     path.quadTo({100.00001f, 100.00001f}, {100.00002f, 100.00002f});
   1181     offset.addPath(path, 1000, 1000);
   1182     for (bool exact : { false, true } ) {
   1183         debugster(path, exact);
   1184         debugster(offset, exact);
   1185     }
   1186 }
   1187 #StdOut
   1188 quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is degenerate, nearly
   1189 quad (1100,1100), (1100,1100), (1100,1100) is degenerate, nearly
   1190 quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is not degenerate, exactly
   1191 quad (1100,1100), (1100,1100), (1100,1100) is degenerate, exactly
   1192 #StdOut ##
   1193 ##
   1194 
   1195 #SeeAlso IsLineDegenerate IsCubicDegenerate
   1196 ##
   1197 
   1198 # ------------------------------------------------------------------------------
   1199 
   1200 #Method static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
   1201                                   const SkPoint& p3, const SkPoint& p4, bool exact)
   1202 #In Property
   1203 #Line # returns if Cubic is very small ##
   1204 #Populate
   1205 
   1206 #Example
   1207 void draw(SkCanvas* canvas) {
   1208     SkPoint points[] = {{1, 0}, {0, 0}, {0, 0}, {0, 0}};
   1209     SkScalar step = 1;
   1210     SkScalar prior, length = 0, degenerate = 0;
   1211     do {
   1212         prior = points[0].fX;
   1213         step /= 2;
   1214         if (SkPath::IsCubicDegenerate(points[0], points[1], points[2], points[3], false)) {
   1215             degenerate = prior;
   1216             points[0].fX += step;
   1217         } else {
   1218             length = prior;
   1219             points[0].fX -= step;
   1220         }
   1221     } while (prior != points[0].fX);
   1222     SkDebugf("%1.8g is degenerate\n", degenerate);
   1223     SkDebugf("%1.8g is length\n", length);
   1224 }
   1225 #StdOut
   1226 0.00024414062 is degenerate
   1227 0.00024414065 is length
   1228 #StdOut ##
   1229 ##
   1230 
   1231 ##
   1232 
   1233 # ------------------------------------------------------------------------------
   1234 
   1235 #Method bool isLine(SkPoint line[2]) const
   1236 #In Property
   1237 #Line # returns if describes Line ##
   1238 #Populate
   1239 
   1240 #Example
   1241 void draw(SkCanvas* canvas) {
   1242     auto debugster = [](const char* prefix, const SkPath& path) -> void {
   1243         SkPoint line[2];
   1244         if (path.isLine(line)) {
   1245             SkDebugf("%s is line (%1.8g,%1.8g) (%1.8g,%1.8g)\n", prefix,
   1246                  line[0].fX, line[0].fY, line[1].fX, line[1].fY);
   1247         } else {
   1248             SkDebugf("%s is not line\n", prefix);
   1249         }
   1250     };
   1251     SkPath path;
   1252     debugster("empty", path);
   1253     path.lineTo(0, 0);
   1254     debugster("zero line", path);
   1255     path.rewind();
   1256     path.moveTo(10, 10);
   1257     path.lineTo(20, 20);
   1258     debugster("line", path);
   1259     path.moveTo(20, 20);
   1260     debugster("second move", path);
   1261 }
   1262 #StdOut
   1263 empty is not line
   1264 zero line is line (0,0) (0,0)
   1265 line is line (10,10) (20,20)
   1266 second move is not line
   1267 ##
   1268 ##
   1269 
   1270 ##
   1271 
   1272 # ------------------------------------------------------------------------------
   1273 
   1274 #Subtopic Point_Array
   1275 #Line # end points and control points for lines and curves ##
   1276 #Substitute SkPoint array
   1277 
   1278 Point_Array contains Points satisfying the allocated Points for
   1279 each Verb in Verb_Array. For instance, Path containing one Contour with Line
   1280 and Quad is described by Verb_Array: kMove_Verb, kLine_Verb, kQuad_Verb; and
   1281 one Point for move, one Point for Line, two Points for Quad; totaling four Points.
   1282 
   1283 Point_Array may be read directly from Path with getPoints, or inspected with
   1284 getPoint, with Iter, or with RawIter.
   1285 
   1286 #Method int getPoints(SkPoint points[], int max) const
   1287 
   1288 #In Point_Array
   1289 #Line # returns Point_Array ##
   1290 #Populate
   1291 
   1292 #Example
   1293 void draw(SkCanvas* canvas) {
   1294     auto debugster = [](const char* prefix, const SkPath& path, SkPoint* points, int max) -> void {
   1295          int count = path.getPoints(points, max);
   1296          SkDebugf("%s point count: %d  ", prefix, count);
   1297          for (int i = 0; i < SkTMin(count, max) && points; ++i) {
   1298              SkDebugf("(%1.8g,%1.8g) ", points[i].fX, points[i].fY);
   1299          }
   1300          SkDebugf("\n");
   1301     };
   1302     SkPath path;
   1303     path.lineTo(20, 20);
   1304     path.lineTo(-10, -10);
   1305     SkPoint points[3];
   1306     debugster("no points",  path, nullptr, 0);
   1307     debugster("zero max",  path, points, 0);
   1308     debugster("too small",  path, points, 2);
   1309     debugster("just right",  path, points, path.countPoints());
   1310 }
   1311 #StdOut
   1312 no points point count: 3
   1313 zero max point count: 3
   1314 too small point count: 3  (0,0) (20,20)
   1315 just right point count: 3  (0,0) (20,20) (-10,-10)
   1316 ##
   1317 ##
   1318 
   1319 #SeeAlso countPoints getPoint
   1320 ##
   1321 
   1322 #Method int countPoints() const
   1323 
   1324 #In Point_Array
   1325 #Line # returns Point_Array length ##
   1326 #Populate
   1327 
   1328 #Example
   1329 void draw(SkCanvas* canvas) {
   1330     auto debugster = [](const char* prefix, const SkPath& path) -> void {
   1331          SkDebugf("%s point count: %d\n", prefix, path.countPoints());
   1332     };
   1333     SkPath path;
   1334     debugster("empty", path);
   1335     path.lineTo(0, 0);
   1336     debugster("zero line", path);
   1337     path.rewind();
   1338     path.moveTo(10, 10);
   1339     path.lineTo(20, 20);
   1340     debugster("line", path);
   1341     path.moveTo(20, 20);
   1342     debugster("second move", path);
   1343 }
   1344 #StdOut
   1345 empty point count: 0
   1346 zero line point count: 2
   1347 line point count: 2
   1348 second move point count: 3
   1349 ##
   1350 ##
   1351 
   1352 #SeeAlso getPoints
   1353 ##
   1354 
   1355 #Method SkPoint getPoint(int index) const
   1356 
   1357 #In Point_Array
   1358 #Line # returns entry from Point_Array ##
   1359 #Populate
   1360 
   1361 #Example
   1362 void draw(SkCanvas* canvas) {
   1363     SkPath path;
   1364     path.lineTo(20, 20);
   1365     path.offset(-10, -10);
   1366     for (int i= 0; i < path.countPoints(); ++i) {
   1367          SkDebugf("point %d: (%1.8g,%1.8g)\n", i, path.getPoint(i).fX, path.getPoint(i).fY);
   1368     }
   1369 }
   1370 #StdOut
   1371 point 0: (-10,-10)
   1372 point 1: (10,10)
   1373 ##
   1374 ##
   1375 
   1376 #SeeAlso countPoints getPoints
   1377 ##
   1378 
   1379 
   1380 #Subtopic Point_Array ##
   1381 
   1382 # ------------------------------------------------------------------------------
   1383 #Subtopic Verb_Array
   1384 #Line # line and curve type for points ##
   1385 
   1386 Verb_Array always starts with kMove_Verb.
   1387 If kClose_Verb is not the last entry, it is always followed by kMove_Verb;
   1388 the quantity of kMove_Verb equals the Contour count.
   1389 Verb_Array does not include or count kDone_Verb; it is a convenience
   1390 returned when iterating through Verb_Array.
   1391 
   1392 Verb_Array may be read directly from Path with getVerbs, or inspected with Iter,
   1393 or with RawIter.
   1394 
   1395 #Method int countVerbs() const
   1396 
   1397 #In Verb_Array
   1398 #Line # returns Verb_Array length ##
   1399 #Populate
   1400 
   1401 #Example
   1402 SkPath path;
   1403 SkDebugf("empty verb count: %d\n", path.countVerbs());
   1404 path.addRoundRect({10, 20, 30, 40}, 5, 5);
   1405 SkDebugf("round rect verb count: %d\n", path.countVerbs());
   1406 #StdOut
   1407 empty verb count: 0
   1408 round rect verb count: 10
   1409 ##
   1410 ##
   1411 
   1412 #SeeAlso getVerbs Iter RawIter
   1413 
   1414 ##
   1415 
   1416 #Method int getVerbs(uint8_t verbs[], int max) const
   1417 
   1418 #In Verb_Array
   1419 #Line # returns Verb_Array ##
   1420 #Populate
   1421 
   1422 #Example
   1423 void draw(SkCanvas* canvas) {
   1424     auto debugster = [](const char* prefix, const SkPath& path, uint8_t* verbs, int max) -> void {
   1425          int count = path.getVerbs(verbs, max);
   1426          SkDebugf("%s verb count: %d  ", prefix, count);
   1427          const char* verbStr[] = { "move", "line", "quad", "conic", "cubic", "close" };
   1428          for (int i = 0; i < SkTMin(count, max) && verbs; ++i) {
   1429              SkDebugf("%s ", verbStr[verbs[i]]);
   1430          }
   1431          SkDebugf("\n");
   1432     };
   1433     SkPath path;
   1434     path.lineTo(20, 20);
   1435     path.lineTo(-10, -10);
   1436     uint8_t verbs[3];
   1437     debugster("no verbs",  path, nullptr, 0);
   1438     debugster("zero max",  path, verbs, 0);
   1439     debugster("too small",  path, verbs, 2);
   1440     debugster("just right",  path, verbs, path.countVerbs());
   1441 }
   1442 #StdOut
   1443 no verbs verb count: 3
   1444 zero max verb count: 3
   1445 too small verb count: 3  move line
   1446 just right verb count: 3  move line line
   1447 ##
   1448 ##
   1449 
   1450 #SeeAlso countVerbs getPoints Iter RawIter
   1451 ##
   1452 
   1453 #Subtopic Verb_Array ##
   1454 
   1455 # ------------------------------------------------------------------------------
   1456 
   1457 #Method void swap(SkPath& other)
   1458 #In Operators
   1459 #Line # exchanges Path pair ##
   1460 #Populate
   1461 
   1462 #Example
   1463 SkPath path1, path2;
   1464 path1.addRect({10, 20, 30, 40});
   1465 path1.swap(path2);
   1466 const SkRect& b1 = path1.getBounds();
   1467 SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom);
   1468 const SkRect& b2 = path2.getBounds();
   1469 SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom);
   1470 #StdOut
   1471 path1 bounds = 0, 0, 0, 0
   1472 path2 bounds = 10, 20, 30, 40
   1473 #StdOut ##
   1474 ##
   1475 
   1476 #SeeAlso operator=(const SkPath& path)
   1477 
   1478 ##
   1479 
   1480 # ------------------------------------------------------------------------------
   1481 
   1482 #Method const SkRect& getBounds() const
   1483 #In Property
   1484 #Line # returns maximum and minimum of Point_Array ##
   1485 #Populate
   1486 
   1487 #Example
   1488 #Description
   1489 Bounds of upright Circle can be predicted from center and radius.
   1490 Bounds of rotated Circle includes control Points outside of filled area.
   1491 ##
   1492     auto debugster = [](const char* prefix, const SkPath& path) -> void {
   1493             const SkRect& bounds = path.getBounds();
   1494             SkDebugf("%s bounds = %g, %g, %g, %g\n", prefix,
   1495                      bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
   1496     };
   1497     SkPath path;
   1498     debugster("empty", path);
   1499     path.addCircle(50, 45, 25);
   1500     debugster("circle", path);
   1501     SkMatrix matrix;
   1502     matrix.setRotate(45, 50, 45);
   1503     path.transform(matrix);
   1504     debugster("rotated circle", path);
   1505 #StdOut
   1506 empty bounds = 0, 0, 0, 0
   1507 circle bounds = 25, 20, 75, 70
   1508 rotated circle bounds = 14.6447, 9.64466, 85.3553, 80.3553
   1509 ##
   1510 ##
   1511 
   1512 #SeeAlso computeTightBounds updateBoundsCache
   1513 
   1514 ##
   1515 
   1516 # ------------------------------------------------------------------------------
   1517 #Subtopic Utility
   1518 #Line # rarely called management functions ##
   1519 ##
   1520 
   1521 #Method void updateBoundsCache() const
   1522 #In Utility
   1523 #Line # refreshes result of getBounds ##
   1524 #Populate
   1525 
   1526 #Example
   1527     double times[2] = { 0, 0 };
   1528     for (int i = 0; i < 10000; ++i) {
   1529       SkPath path;
   1530       for (int j = 1; j < 100; ++ j) {
   1531         path.addCircle(50 + j, 45 + j, 25 + j);
   1532       }
   1533       if (1 & i) {
   1534         path.updateBoundsCache();
   1535       }
   1536       double start = SkTime::GetNSecs();
   1537       (void) path.getBounds();
   1538       times[1 & i] += SkTime::GetNSecs() - start;
   1539     }
   1540     SkDebugf("uncached avg: %g ms\n", times[0] * 1e-6);
   1541     SkDebugf("cached avg: %g ms\n", times[1] * 1e-6);
   1542 #StdOut
   1543 #Volatile
   1544 uncached avg: 0.18048 ms
   1545 cached avg: 0.182784 ms
   1546 ##
   1547 ##
   1548 
   1549 #SeeAlso getBounds
   1550 #ToDo the results don't make sense, need to profile to figure this out ##
   1551 
   1552 ##
   1553 
   1554 # ------------------------------------------------------------------------------
   1555 
   1556 #Method SkRect computeTightBounds() const
   1557 #In Property
   1558 #Line # returns extent of geometry ##
   1559 #Populate
   1560 
   1561 #Example
   1562     auto debugster = [](const char* prefix, const SkPath& path) -> void {
   1563             const SkRect& bounds = path.computeTightBounds();
   1564             SkDebugf("%s bounds = %g, %g, %g, %g\n", prefix,
   1565                      bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
   1566     };
   1567     SkPath path;
   1568     debugster("empty", path);
   1569     path.addCircle(50, 45, 25);
   1570     debugster("circle", path);
   1571     SkMatrix matrix;
   1572     matrix.setRotate(45, 50, 45);
   1573     path.transform(matrix);
   1574     debugster("rotated circle", path);
   1575 #StdOut
   1576 empty bounds = 0, 0, 0, 0
   1577 circle bounds = 25, 20, 75, 70
   1578 rotated circle bounds = 25, 20, 75, 70
   1579 ##
   1580 ##
   1581 
   1582 #SeeAlso getBounds
   1583 
   1584 ##
   1585 
   1586 # ------------------------------------------------------------------------------
   1587 
   1588 #Method bool conservativelyContainsRect(const SkRect& rect) const
   1589 #In Property
   1590 #Line # returns true if Rect may be inside ##
   1591 #Populate
   1592 
   1593 #Example
   1594 #Height 140
   1595 #Description
   1596 Rect is drawn in blue if it is contained by red Path.
   1597 ##
   1598 void draw(SkCanvas* canvas) {
   1599     SkPath path;
   1600     path.addRoundRect({10, 20, 54, 120}, 10, 20);
   1601     SkRect tests[] = {
   1602       { 10, 40, 54, 80 },
   1603       { 25, 20, 39, 120 },
   1604       { 15, 25, 49, 115 },
   1605       { 13, 27, 51, 113 },
   1606     };
   1607     for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); ++i) {
   1608       SkPaint paint;
   1609       paint.setColor(SK_ColorRED);
   1610       canvas->drawPath(path, paint);
   1611       bool rectInPath = path.conservativelyContainsRect(tests[i]);
   1612       paint.setColor(rectInPath ? SK_ColorBLUE : SK_ColorBLACK);
   1613       canvas->drawRect(tests[i], paint);
   1614       canvas->translate(64, 0);
   1615     }
   1616 }
   1617 ##
   1618 
   1619 #SeeAlso contains Op Rect Convexity
   1620 
   1621 ##
   1622 
   1623 # ------------------------------------------------------------------------------
   1624 
   1625 #Method void incReserve(int extraPtCount)
   1626 #In Utility
   1627 #Line # reserves space for additional data ##
   1628 #Populate
   1629 
   1630 #Example
   1631 #Height 192
   1632 void draw(SkCanvas* canvas) {
   1633     auto addPoly = [](int sides, SkScalar size, SkPath* path) -> void {
   1634         path->moveTo(size, 0);
   1635         for (int i = 1; i < sides; i++) {
   1636             SkScalar c, s = SkScalarSinCos(SK_ScalarPI * 2 * i / sides, &c);
   1637             path->lineTo(c * size, s * size);
   1638         }
   1639         path->close();
   1640     };
   1641     SkPath path;
   1642     path.incReserve(3 + 4 + 5 + 6 + 7 + 8 + 9);
   1643     for (int sides = 3; sides < 10; ++sides) {
   1644        addPoly(sides, sides, &path);
   1645     }
   1646     SkMatrix matrix;
   1647     matrix.setScale(10, 10, -10, -10);
   1648     path.transform(matrix);
   1649     SkPaint paint;
   1650     paint.setStyle(SkPaint::kStroke_Style);
   1651     canvas->drawPath(path, paint);
   1652 }
   1653 ##
   1654 
   1655 #SeeAlso Point_Array
   1656 
   1657 ##
   1658 
   1659 #Method void shrinkToFit()
   1660 #In Utility
   1661 #Line # removes unused reserved space ##
   1662 #Populate
   1663 
   1664 #NoExample
   1665 #Height 192
   1666 void draw(SkCanvas* canvas) {
   1667     SkPath skinnyPath;
   1668     skinnyPath.lineTo(100, 100);
   1669     skinnyPath.shrinkToFit();
   1670 
   1671     SkDebugf("no wasted Path capacity in my house!\n");
   1672 }
   1673 ##
   1674 
   1675 #SeeAlso incReserve
   1676 
   1677 ##
   1678 
   1679 # ------------------------------------------------------------------------------
   1680 #Subtopic Build
   1681 #Line # adds points and verbs to path ##
   1682 ##
   1683 
   1684 #Method SkPath& moveTo(SkScalar x, SkScalar y)
   1685 #In Build
   1686 #Line # starts Contour ##
   1687 #Populate
   1688 
   1689 #Example
   1690     #Width 140
   1691     #Height 100
   1692     void draw(SkCanvas* canvas) {
   1693         SkRect rect = { 20, 20, 120, 80 };
   1694         SkPath path;
   1695         path.addRect(rect);
   1696         path.moveTo(rect.fLeft, rect.fTop);
   1697         path.lineTo(rect.fRight, rect.fBottom);
   1698         path.moveTo(rect.fLeft, rect.fBottom);
   1699         path.lineTo(rect.fRight, rect.fTop);
   1700         SkPaint paint;
   1701         paint.setStyle(SkPaint::kStroke_Style);
   1702         canvas->drawPath(path, paint);
   1703     }
   1704 ##
   1705 
   1706 #SeeAlso Contour lineTo rMoveTo quadTo conicTo cubicTo close()
   1707 
   1708 ##
   1709 
   1710 #Method SkPath& moveTo(const SkPoint& p)
   1711 #Populate
   1712 
   1713 #Example
   1714     #Width 128
   1715     #Height 128
   1716 void draw(SkCanvas* canvas) {
   1717     SkPoint data[][3] = {{{30,40},{60,60},{90,30}}, {{30,120},{60,100},{90,120}},
   1718                          {{60,100},{60,40},{70,30}}, {{60,40},{50,20},{70,30}}};
   1719     SkPath path;
   1720     for (unsigned i = 0; i < SK_ARRAY_COUNT(data); ++i) {
   1721         path.moveTo(data[i][0]);
   1722         path.lineTo(data[i][1]);
   1723         path.lineTo(data[i][2]);
   1724     }
   1725     SkPaint paint;
   1726     paint.setStyle(SkPaint::kStroke_Style);
   1727     canvas->drawPath(path, paint);
   1728 }
   1729 ##
   1730 
   1731 #SeeAlso Contour lineTo rMoveTo quadTo conicTo cubicTo close()
   1732 
   1733 ##
   1734 
   1735 #Method SkPath& rMoveTo(SkScalar dx, SkScalar dy)
   1736 #In Build
   1737 #Line # starts Contour relative to Last_Point ##
   1738 #Populate
   1739 
   1740 #Example
   1741     #Height 100
   1742     SkPath path;
   1743     path.addRect({20, 20, 80, 80}, SkPath::kCW_Direction, 2);
   1744     path.rMoveTo(25, 2);
   1745     SkVector arrow[] = {{0, -4}, {-20, 0}, {0, -3}, {-5, 5}, {5, 5}, {0, -3}, {20, 0}};
   1746     for (unsigned i = 0; i < SK_ARRAY_COUNT(arrow); ++i) {
   1747         path.rLineTo(arrow[i].fX, arrow[i].fY);
   1748     }
   1749     SkPaint paint;
   1750     canvas->drawPath(path, paint);
   1751     SkPoint lastPt;
   1752     path.getLastPt(&lastPt);
   1753     canvas->drawString("start", lastPt.fX, lastPt.fY, paint);
   1754 ##
   1755 
   1756 #SeeAlso Contour lineTo moveTo quadTo conicTo cubicTo close()
   1757 
   1758 ##
   1759 
   1760 # ------------------------------------------------------------------------------
   1761 
   1762 #Method SkPath& lineTo(SkScalar x, SkScalar y)
   1763 #In Build
   1764 #Line # appends Line ##
   1765 #Populate
   1766 
   1767 #Example
   1768 #Height 100
   1769 ###$
   1770 void draw(SkCanvas* canvas) {
   1771     SkPaint paint;
   1772     paint.setAntiAlias(true);
   1773     paint.setTextSize(72);
   1774     canvas->drawString("#", 120, 80, paint);
   1775     paint.setStyle(SkPaint::kStroke_Style);
   1776     paint.setStrokeWidth(5);
   1777     SkPath path;
   1778     SkPoint hash[] = {{58, 28}, {43, 80}, {37, 45}, {85, 45}};
   1779     SkVector offsets[] = {{0, 0}, {17, 0}, {0, 0}, {-5, 17}};
   1780     unsigned o = 0;
   1781     for (unsigned i = 0; i < SK_ARRAY_COUNT(hash); i += 2) {
   1782         for (unsigned j = 0; j < 2; o++, j++) {
   1783             path.moveTo(hash[i].fX + offsets[o].fX, hash[i].fY + offsets[o].fY);
   1784             path.lineTo(hash[i + 1].fX + offsets[o].fX, hash[i + 1].fY + offsets[o].fY);
   1785         }
   1786     }
   1787     canvas->drawPath(path, paint);
   1788 }
   1789 $$$#
   1790 ##
   1791 
   1792 #SeeAlso Contour moveTo rLineTo addRect
   1793 
   1794 ##
   1795 
   1796 # ------------------------------------------------------------------------------
   1797 
   1798 #Method SkPath& lineTo(const SkPoint& p)
   1799 #Populate
   1800 
   1801 #Example
   1802 #Height 100
   1803     SkPath path;
   1804     SkVector oxo[] = {{25, 25}, {35, 35}, {25, 35}, {35, 25},
   1805                       {40, 20}, {40, 80}, {60, 20}, {60, 80},
   1806                       {20, 40}, {80, 40}, {20, 60}, {80, 60}};
   1807     for (unsigned i = 0; i < SK_ARRAY_COUNT(oxo); i += 2) {
   1808         path.moveTo(oxo[i]);
   1809         path.lineTo(oxo[i + 1]);
   1810     }
   1811     SkPaint paint;
   1812     paint.setStyle(SkPaint::kStroke_Style);
   1813     canvas->drawPath(path, paint);
   1814 ##
   1815 
   1816 #SeeAlso Contour moveTo rLineTo addRect
   1817 
   1818 ##
   1819 
   1820 # ------------------------------------------------------------------------------
   1821 
   1822 #Method SkPath& rLineTo(SkScalar dx, SkScalar dy)
   1823 #In Build
   1824 #Line # appends Line relative to Last_Point ##
   1825 #Populate
   1826 
   1827 #Example
   1828 #Height 128
   1829 void draw(SkCanvas* canvas) {
   1830     SkPaint paint;
   1831     paint.setAntiAlias(true);
   1832     paint.setStyle(SkPaint::kStroke_Style);
   1833     SkPath path;
   1834     path.moveTo(10, 98);
   1835     SkScalar x = 0, y = 0;
   1836     for (int i = 10; i < 100; i += 5) {
   1837         x += i * ((i & 2) - 1);
   1838         y += i * (((i + 1) & 2) - 1);
   1839         path.rLineTo(x, y);
   1840 
   1841     }
   1842     canvas->drawPath(path, paint);
   1843 }
   1844 ##
   1845 
   1846 #SeeAlso Contour moveTo lineTo addRect
   1847 
   1848 ##
   1849 
   1850 # ------------------------------------------------------------------------------
   1851 #Subtopic Quad
   1852 #Alias Quad ##
   1853 #Alias Quads ##
   1854 #Alias Quadratic_Bezier ##
   1855 #Alias Quadratic_Beziers ##
   1856 #Line # curve described by second-order polynomial ##
   1857 
   1858 Quad describes a Quadratic_Bezier, a second-order curve identical to a section
   1859 of a parabola. Quad begins at a start Point, curves towards a control Point,
   1860 and then curves to an end Point.
   1861 
   1862 #Example
   1863 #Height 110
   1864 void draw(SkCanvas* canvas) {
   1865     SkPaint paint;
   1866     paint.setAntiAlias(true);
   1867     paint.setStyle(SkPaint::kStroke_Style);
   1868     SkPoint quadPts[] = {{20, 90}, {120, 10}, {220, 90}};
   1869     canvas->drawLine(quadPts[0], quadPts[1], paint);
   1870     canvas->drawLine(quadPts[1], quadPts[2], paint);
   1871     SkPath path;
   1872     path.moveTo(quadPts[0]);
   1873     path.quadTo(quadPts[1], quadPts[2]);
   1874     paint.setStrokeWidth(3);
   1875     canvas->drawPath(path, paint);
   1876 }
   1877 ##
   1878 
   1879 Quad is a special case of Conic where Conic_Weight is set to one.
   1880 
   1881 Quad is always contained by the triangle connecting its three Points. Quad
   1882 begins tangent to the line between start Point and control Point, and ends
   1883 tangent to the line between control Point and end Point.
   1884 
   1885 #Example
   1886 #Height 160
   1887 void draw(SkCanvas* canvas) {
   1888     SkPaint paint;
   1889     paint.setAntiAlias(true);
   1890     paint.setStyle(SkPaint::kStroke_Style);
   1891     SkPoint quadPts[] = {{20, 150}, {120, 10}, {220, 150}};
   1892     SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 };
   1893     for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
   1894         paint.setColor(0x7fffffff & colors[i]);
   1895         paint.setStrokeWidth(1);
   1896         canvas->drawLine(quadPts[0], quadPts[1], paint);
   1897         canvas->drawLine(quadPts[1], quadPts[2], paint);
   1898         SkPath path;
   1899         path.moveTo(quadPts[0]);
   1900         path.quadTo(quadPts[1], quadPts[2]);
   1901         paint.setStrokeWidth(3);
   1902         paint.setColor(colors[i]);
   1903         canvas->drawPath(path, paint);
   1904         quadPts[1].fY += 30;
   1905    }
   1906 }
   1907 ##
   1908 
   1909 #Method SkPath& quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
   1910 
   1911 #In Quad
   1912 #Line # appends Quad ##
   1913 #Populate
   1914 
   1915 #Example
   1916     void draw(SkCanvas* canvas) {
   1917         SkPaint paint;
   1918         paint.setAntiAlias(true);
   1919         paint.setStyle(SkPaint::kStroke_Style);
   1920         SkPath path;
   1921         path.moveTo(0, -10);
   1922         for (int i = 0; i < 128; i += 16) {
   1923             path.quadTo( 10 + i, -10 - i,  10 + i,       0);
   1924             path.quadTo( 14 + i,  14 + i,       0,  14 + i);
   1925             path.quadTo(-18 - i,  18 + i, -18 - i,       0);
   1926             path.quadTo(-22 - i, -22 - i,       0, -22 - i);
   1927         }
   1928         path.offset(128, 128);
   1929         canvas->drawPath(path, paint);
   1930     }
   1931     ##
   1932 
   1933     #SeeAlso Contour moveTo conicTo rQuadTo
   1934 
   1935 ##
   1936 
   1937 #Method SkPath& quadTo(const SkPoint& p1, const SkPoint& p2)
   1938 #In Build
   1939 #In Quad
   1940 #Populate
   1941 
   1942 #Example
   1943     void draw(SkCanvas* canvas) {
   1944         SkPaint paint;
   1945         paint.setStyle(SkPaint::kStroke_Style);
   1946         paint.setAntiAlias(true);
   1947         SkPath path;
   1948         SkPoint pts[] = {{128, 10}, {10, 214}, {236, 214}};
   1949         path.moveTo(pts[1]);
   1950         for (int i = 0; i < 3; ++i) {
   1951             path.quadTo(pts[i % 3],  pts[(i + 2) % 3]);
   1952         }
   1953         canvas->drawPath(path, paint);
   1954     }
   1955     ##
   1956 
   1957     #SeeAlso Contour moveTo conicTo rQuadTo
   1958 
   1959 ##
   1960 
   1961 #Method SkPath& rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2)
   1962 #In Build
   1963 #In Quad
   1964 #Line # appends Quad relative to Last_Point ##
   1965 #Populate
   1966 
   1967 #Example
   1968     void draw(SkCanvas* canvas) {
   1969         SkPaint paint;
   1970         paint.setAntiAlias(true);
   1971         SkPath path;
   1972         path.moveTo(128, 20);
   1973         path.rQuadTo(-6, 10, -7, 10);
   1974         for (int i = 1; i < 32; i += 4) {
   1975            path.rQuadTo(10 + i, 10 + i, 10 + i * 4, 10);
   1976            path.rQuadTo(-10 - i, 10 + i, -10 - (i + 2) * 4, 10);
   1977         }
   1978         path.quadTo(92, 220, 128, 215);
   1979         canvas->drawPath(path, paint);
   1980     }
   1981     ##
   1982 
   1983     #SeeAlso Contour moveTo conicTo quadTo
   1984 
   1985 ##
   1986 
   1987 #Subtopic Quad ##
   1988 
   1989 # ------------------------------------------------------------------------------
   1990 
   1991 #Subtopic Conic
   1992 #Alias Conic ##
   1993 #Alias Conics ##
   1994 #Line # conic section defined by three points and a weight ##
   1995 
   1996 Conic describes a conical section: a piece of an ellipse, or a piece of a
   1997 parabola, or a piece of a hyperbola. Conic begins at a start Point,
   1998 curves towards a control Point, and then curves to an end Point. The influence
   1999 of the control Point is determined by Conic_Weight.
   2000 
   2001 Each Conic in Path adds two Points and one Conic_Weight. Conic_Weights in Path
   2002 may be inspected with Iter, or with RawIter.
   2003 
   2004 #Subtopic Weight
   2005 #Alias Conic_Weights ##
   2006 #Alias Weights ##
   2007 #Line # strength of Conic control Point ##
   2008 
   2009 Weight determines both the strength of the control Point and the type of Conic.
   2010 Weight varies from zero to infinity. At zero, Weight causes the control Point to
   2011 have no effect; Conic is identical to a line segment from start Point to end
   2012 point. If Weight is less than one, Conic follows an elliptical arc.
   2013 If Weight is exactly one, then Conic is identical to Quad; Conic follows a
   2014 parabolic arc. If Weight is greater than one, Conic follows a hyperbolic
   2015 arc. If Weight is infinity, Conic is identical to two line segments, connecting
   2016 start Point to control Point, and control Point to end Point.
   2017 
   2018 #Example
   2019 #Description
   2020 When Conic_Weight is one, Quad is added to path; the two are identical.
   2021 ##
   2022 void draw(SkCanvas* canvas) {
   2023     const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
   2024     const int pointCount[]  = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  };
   2025     SkPath path;
   2026     path.conicTo(20, 30, 50, 60, 1);
   2027     SkPath::Iter iter(path, false);
   2028     SkPath::Verb verb;
   2029     do {
   2030        SkPoint points[4];
   2031        verb = iter.next(points);
   2032        SkDebugf("%s ", verbNames[(int) verb]);
   2033        for (int i = 0; i < pointCount[(int) verb]; ++i) {
   2034             SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
   2035        }
   2036        if (SkPath::kConic_Verb == verb) {
   2037            SkDebugf("weight = %g", iter.conicWeight());
   2038        }
   2039        SkDebugf("\n");
   2040     } while (SkPath::kDone_Verb != verb);
   2041 }
   2042 #StdOut
   2043 move {0, 0},
   2044 quad {0, 0}, {20, 30}, {50, 60},
   2045 done
   2046 ##
   2047 ##
   2048 
   2049 If weight is less than one, Conic is an elliptical segment.
   2050 
   2051 #Example
   2052 #Description
   2053 A 90 degree circular arc has the weight #Formula # 1 / sqrt(2) ##.
   2054 ##
   2055 void draw(SkCanvas* canvas) {
   2056     const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
   2057     const int pointCount[]  = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  };
   2058     SkPath path;
   2059     path.arcTo(20, 0, 20, 20, 20);
   2060     SkPath::Iter iter(path, false);
   2061     SkPath::Verb verb;
   2062     do {
   2063        SkPoint points[4];
   2064        verb = iter.next(points);
   2065        SkDebugf("%s ", verbNames[(int) verb]);
   2066        for (int i = 0; i < pointCount[(int) verb]; ++i) {
   2067             SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
   2068        }
   2069        if (SkPath::kConic_Verb == verb) {
   2070            SkDebugf("weight = %g", iter.conicWeight());
   2071        }
   2072        SkDebugf("\n");
   2073     } while (SkPath::kDone_Verb != verb);
   2074 }
   2075 #StdOut
   2076 move {0, 0},
   2077 conic {0, 0}, {20, 0}, {20, 20}, weight = 0.707107
   2078 done
   2079 ##
   2080 ##
   2081 
   2082 If weight is greater than one, Conic is a hyperbolic segment. As weight gets large,
   2083 a hyperbolic segment can be approximated by straight lines connecting the
   2084 control Point with the end Points.
   2085 
   2086 #Example
   2087 void draw(SkCanvas* canvas) {
   2088     const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
   2089     const int pointCount[]  = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  };
   2090     SkPath path;
   2091     path.conicTo(20, 0, 20, 20, SK_ScalarInfinity);
   2092     SkPath::Iter iter(path, false);
   2093     SkPath::Verb verb;
   2094     do {
   2095        SkPoint points[4];
   2096        verb = iter.next(points);
   2097        SkDebugf("%s ", verbNames[(int) verb]);
   2098        for (int i = 0; i < pointCount[(int) verb]; ++i) {
   2099             SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
   2100        }
   2101        if (SkPath::kConic_Verb == verb) {
   2102            SkDebugf("weight = %g", iter.conicWeight());
   2103        }
   2104        SkDebugf("\n");
   2105     } while (SkPath::kDone_Verb != verb);
   2106 }
   2107 #StdOut
   2108 move {0, 0},
   2109 line {0, 0}, {20, 0},
   2110 line {20, 0}, {20, 20},
   2111 done
   2112 ##
   2113 ##
   2114 
   2115 #Subtopic Weight ##
   2116 
   2117 #Method SkPath& conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
   2118                  SkScalar w)
   2119 #In Conic
   2120 #In Build
   2121 #Line # appends Conic ##
   2122 #Populate
   2123 
   2124 #Example
   2125     #Height 160
   2126     #Description
   2127     As weight increases, curve is pulled towards control point.
   2128     The bottom two curves are elliptical; the next is parabolic; the
   2129     top curve is hyperbolic.
   2130     ##
   2131 void draw(SkCanvas* canvas) {
   2132     SkPaint paint;
   2133     paint.setAntiAlias(true);
   2134     paint.setStyle(SkPaint::kStroke_Style);
   2135     SkPoint conicPts[] = {{20, 150}, {120, 10}, {220, 150}};
   2136     canvas->drawLine(conicPts[0], conicPts[1], paint);
   2137     canvas->drawLine(conicPts[1], conicPts[2], paint);
   2138     SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 };
   2139     paint.setStrokeWidth(3);
   2140     SkScalar weight = 0.5f;
   2141     for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
   2142         SkPath path;
   2143         path.moveTo(conicPts[0]);
   2144         path.conicTo(conicPts[1], conicPts[2], weight);
   2145         paint.setColor(colors[i]);
   2146         canvas->drawPath(path, paint);
   2147         weight += 0.25f;
   2148    }
   2149 }
   2150     ##
   2151 
   2152     #SeeAlso rConicTo arcTo addArc quadTo
   2153 
   2154 ##
   2155 
   2156 #Method SkPath& conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w)
   2157 #In Build
   2158 #In Conic
   2159 #Populate
   2160 
   2161 #Example
   2162     #Height 128
   2163     #Description
   2164     Conics and arcs use identical representations. As the arc sweep increases
   2165     the Conic_Weight also increases, but remains smaller than one.
   2166     ##
   2167 void draw(SkCanvas* canvas) {
   2168     SkPaint paint;
   2169     paint.setAntiAlias(true);
   2170     paint.setStyle(SkPaint::kStroke_Style);
   2171     SkRect oval = {0, 20, 120, 140};
   2172     SkPath path;
   2173     for (int i = 0; i < 4; ++i) {
   2174         path.moveTo(oval.centerX(), oval.fTop);
   2175         path.arcTo(oval, -90, 90 - 20 * i, false);
   2176         oval.inset(15, 15);
   2177     }
   2178     path.offset(100, 0);
   2179     SkScalar conicWeights[] = { 0.707107f, 0.819152f, 0.906308f, 0.965926f };
   2180     SkPoint conicPts[][3] = { { {40, 20}, {100, 20}, {100, 80} },
   2181                               { {40, 35}, {71.509f, 35}, {82.286f, 64.6091f} },
   2182                               { {40, 50}, {53.9892f, 50}, {62.981f, 60.7164f} },
   2183                               { {40, 65}, {44.0192f, 65}, {47.5f, 67.0096f} } };
   2184     for (int i = 0; i < 4; ++i) {
   2185          path.moveTo(conicPts[i][0]);
   2186          path.conicTo(conicPts[i][1], conicPts[i][2], conicWeights[i]);
   2187     }
   2188     canvas->drawPath(path, paint);
   2189 }
   2190     ##
   2191 
   2192     #SeeAlso rConicTo arcTo addArc quadTo
   2193 
   2194 ##
   2195 
   2196 #Method SkPath& rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
   2197                   SkScalar w)
   2198 #In Build
   2199 #In Conic
   2200 #Line # appends Conic relative to Last_Point ##
   2201 #Populate
   2202 
   2203 #Example
   2204     #Height 140
   2205     void draw(SkCanvas* canvas) {
   2206         SkPaint paint;
   2207         paint.setAntiAlias(true);
   2208         paint.setStyle(SkPaint::kStroke_Style);
   2209         SkPath path;
   2210         path.moveTo(20, 80);
   2211         path.rConicTo( 60,   0,  60,  60, 0.707107f);
   2212         path.rConicTo(  0, -60,  60, -60, 0.707107f);
   2213         path.rConicTo(-60,   0, -60, -60, 0.707107f);
   2214         path.rConicTo(  0,  60, -60,  60, 0.707107f);
   2215         canvas->drawPath(path, paint);
   2216     }
   2217     ##
   2218 
   2219     #SeeAlso conicTo arcTo addArc quadTo
   2220 
   2221 ##
   2222 
   2223 #Subtopic Conic ##
   2224 
   2225 # ------------------------------------------------------------------------------
   2226 #Subtopic Cubic
   2227 #Alias Cubic ##
   2228 #Alias Cubics ##
   2229 #Alias Cubic_Bezier ##
   2230 #Alias Cubic_Beziers ##
   2231 #Line # curve described by third-order polynomial ##
   2232 
   2233 Cubic describes a Bezier_Curve segment described by a third-order polynomial.
   2234 Cubic begins at a start Point, curving towards the first control Point;
   2235 and curves from the end Point towards the second control Point.
   2236 
   2237 #Example
   2238 #Height 160
   2239 void draw(SkCanvas* canvas) {
   2240     SkPaint paint;
   2241     paint.setAntiAlias(true);
   2242     paint.setStyle(SkPaint::kStroke_Style);
   2243     SkPoint cubicPts[] = {{20, 150}, {90, 10}, {160, 150}, {230, 10}};
   2244     SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 };
   2245     for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
   2246         paint.setColor(0x7fffffff & colors[i]);
   2247         paint.setStrokeWidth(1);
   2248         for (unsigned j = 0; j < 3; ++j) {
   2249             canvas->drawLine(cubicPts[j], cubicPts[j + 1], paint);
   2250         }
   2251         SkPath path;
   2252         path.moveTo(cubicPts[0]);
   2253         path.cubicTo(cubicPts[1], cubicPts[2], cubicPts[3]);
   2254         paint.setStrokeWidth(3);
   2255         paint.setColor(colors[i]);
   2256         canvas->drawPath(path, paint);
   2257         cubicPts[1].fY += 30;
   2258         cubicPts[2].fX += 30;
   2259    }
   2260 }
   2261 ##
   2262 
   2263 #Method SkPath& cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
   2264                  SkScalar x3, SkScalar y3)
   2265 #In Build
   2266 #In Cubic
   2267 #Line # appends Cubic ##
   2268 #Populate
   2269 
   2270 #Example
   2271 void draw(SkCanvas* canvas) {
   2272     SkPaint paint;
   2273     paint.setAntiAlias(true);
   2274     paint.setStyle(SkPaint::kStroke_Style);
   2275     SkPath path;
   2276     path.moveTo(0, -10);
   2277     for (int i = 0; i < 128; i += 16) {
   2278         SkScalar c = i * 0.5f;
   2279         path.cubicTo( 10 + c, -10 - i,  10 + i, -10 - c,  10 + i,       0);
   2280         path.cubicTo( 14 + i,  14 + c,  14 + c,  14 + i,       0,  14 + i);
   2281         path.cubicTo(-18 - c,  18 + i, -18 - i,  18 + c, -18 - i,       0);
   2282         path.cubicTo(-22 - i, -22 - c, -22 - c, -22 - i,       0, -22 - i);
   2283     }
   2284     path.offset(128, 128);
   2285     canvas->drawPath(path, paint);
   2286 }
   2287 ##
   2288 
   2289 #SeeAlso Contour moveTo rCubicTo quadTo
   2290 
   2291 ##
   2292 
   2293 # ------------------------------------------------------------------------------
   2294 
   2295 #Method SkPath& cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3)
   2296 
   2297 #In Build
   2298 #In Cubic
   2299 #Populate
   2300 
   2301 #Example
   2302 #Height 84
   2303     SkPaint paint;
   2304     paint.setAntiAlias(true);
   2305     paint.setStyle(SkPaint::kStroke_Style);
   2306     SkPoint pts[] = { {20, 20}, {300, 80}, {-140, 90}, {220, 10} };
   2307     SkPath path;
   2308     path.moveTo(pts[0]);
   2309     path.cubicTo(pts[1], pts[2], pts[3]);
   2310     canvas->drawPath(path, paint);
   2311 ##
   2312 
   2313 #SeeAlso Contour moveTo rCubicTo quadTo
   2314 
   2315 ##
   2316 
   2317 # ------------------------------------------------------------------------------
   2318 
   2319 #Method SkPath& rCubicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
   2320                   SkScalar dx3, SkScalar dy3)
   2321 #In Build
   2322 #In Cubic
   2323 #Line # appends Cubic relative to Last_Point ##
   2324 #Populate
   2325 
   2326 #Example
   2327     void draw(SkCanvas* canvas) {
   2328         SkPaint paint;
   2329         paint.setAntiAlias(true);
   2330         paint.setStyle(SkPaint::kStroke_Style);
   2331         SkPath path;
   2332         path.moveTo(24, 108);
   2333         for (int i = 0; i < 16; i++) {
   2334            SkScalar sx, sy;
   2335            sx = SkScalarSinCos(i * SK_ScalarPI / 8, &sy);
   2336            path.rCubicTo(40 * sx, 4 * sy, 4 * sx, 40 * sy, 40 * sx, 40 * sy);
   2337         }
   2338         canvas->drawPath(path, paint);
   2339     }
   2340 ##
   2341 
   2342 #SeeAlso Contour moveTo cubicTo quadTo
   2343 
   2344 ##
   2345 
   2346 #Subtopic Cubic ##
   2347 
   2348 # ------------------------------------------------------------------------------
   2349 
   2350 #Subtopic Arc
   2351 #Line # part of Oval or Circle ##
   2352 Arc can be constructed in a number of ways. Arc may be described by part of Oval and angles,
   2353 by start point and end point, and by radius and tangent lines. Each construction has advantages,
   2354 and some constructions correspond to Arc drawing in graphics standards.
   2355 
   2356 All Arc draws are implemented by one or more Conic draws. When Conic_Weight is less than one,
   2357 Conic describes an Arc of some Oval or Circle.
   2358 
   2359 arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo)
   2360 describes Arc as a piece of Oval, beginning at start angle, sweeping clockwise or counterclockwise,
   2361 which may continue Contour or start a new one. This construction is similar to PostScript and
   2362 HTML_Canvas arcs. Variation addArc always starts new Contour. SkCanvas::drawArc draws without
   2363 requiring Path.
   2364 
   2365 arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius)
   2366 describes Arc as tangent to the line segment from last Point added to Path to (x1, y1); and tangent
   2367 to the line segment from (x1, y1) to (x2, y2). This construction is similar to PostScript and
   2368 HTML_Canvas arcs.
   2369 
   2370 arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
   2371       SkScalar x, SkScalar y)
   2372 describes Arc as part of Oval with radii (rx, ry), beginning at
   2373 last Point added to Path and ending at (x, y). More than one Arc satisfies this criteria,
   2374 so additional values choose a single solution. This construction is similar to SVG arcs.
   2375 
   2376 conicTo describes Arc of less than 180 degrees as a pair of tangent lines and Conic_Weight.
   2377 conicTo can represent any Arc with a sweep less than 180 degrees at any rotation. All arcTo
   2378 constructions are converted to Conic data when added to Path.
   2379 
   2380 #ToDo  example is spaced correctly on fiddle but spacing is too wide on pc
   2381 ##
   2382 
   2383 #Illustration
   2384 
   2385 #List
   2386 # <sup>1</sup> arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) ##
   2387 # <sup>2</sup> parameter adds move to first point  ##
   2388 # <sup>3</sup> start angle must be multiple of 90 degrees ##
   2389 # <sup>4</sup> arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) ##
   2390 # <sup>5</sup> arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
   2391                Direction sweep, SkScalar x, SkScalar y) ##
   2392 #List ##
   2393 
   2394 #Example
   2395 #Height 128
   2396 void draw(SkCanvas* canvas) {
   2397     SkRect oval = {8, 8, 56, 56};
   2398     SkPaint ovalPaint;
   2399     ovalPaint.setAntiAlias(true);
   2400     SkPaint textPaint(ovalPaint);
   2401     ovalPaint.setStyle(SkPaint::kStroke_Style);
   2402     SkPaint arcPaint(ovalPaint);
   2403     arcPaint.setStrokeWidth(5);
   2404     arcPaint.setColor(SK_ColorBLUE);
   2405     canvas->translate(-64, 0);
   2406     for (char arcStyle = '1'; arcStyle <= '6'; ++arcStyle) {
   2407         '4' == arcStyle ? canvas->translate(-96, 55) : canvas->translate(64, 0);
   2408         canvas->drawText(&arcStyle, 1, 30, 36, textPaint);
   2409         canvas->drawOval(oval, ovalPaint);
   2410         SkPath path;
   2411         path.moveTo({56, 32});
   2412         switch (arcStyle) {
   2413             case '1':
   2414                 path.arcTo(oval, 0, 90, false);
   2415                 break;
   2416             case '2':
   2417                 canvas->drawArc(oval, 0, 90, false, arcPaint);
   2418                 continue;
   2419             case '3':
   2420                 path.addArc(oval, 0, 90);
   2421                 break;
   2422             case '4':
   2423                 path.arcTo({56, 56}, {32, 56}, 24);
   2424                 break;
   2425             case '5':
   2426                 path.arcTo({24, 24}, 0, SkPath::kSmall_ArcSize, SkPath::kCW_Direction, {32, 56});
   2427                 break;
   2428             case '6':
   2429                 path.conicTo({56, 56}, {32, 56}, SK_ScalarRoot2Over2);
   2430                 break;
   2431          }
   2432          canvas->drawPath(path, arcPaint);
   2433      }
   2434 }
   2435 #Example ##
   2436 
   2437 In the example above:
   2438 #List
   2439 # 1 describes an arc from an oval, a starting angle, and a sweep angle. ##
   2440 # 2 is similar to 1, but does not require building a path to draw. ##
   2441 # 3 is similar to 1, but always begins new Contour. ##
   2442 # 4 describes an arc from a pair of tangent lines and a radius. ##
   2443 # 5 describes an arc from Oval center, arc start Point and arc end Point. ##
   2444 # 6 describes an arc from a pair of tangent lines and a Conic_Weight. ##
   2445 #List ##
   2446 
   2447 #Method SkPath& arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo)
   2448 #In Build
   2449 #In Arc
   2450 #Line # appends Arc ##
   2451 #Populate
   2452 
   2453 #Example
   2454 #Height 200
   2455 #Description
   2456 arcTo continues a previous contour when forceMoveTo is false and when Path
   2457 is not empty.
   2458 ##
   2459 void draw(SkCanvas* canvas) {
   2460     SkPaint paint;
   2461     SkPath path;
   2462     paint.setStyle(SkPaint::kStroke_Style);
   2463     paint.setStrokeWidth(4);
   2464     path.moveTo(0, 0);
   2465     path.arcTo({20, 20, 120, 120}, -90, 90, false);
   2466     canvas->drawPath(path, paint);
   2467     path.rewind();
   2468     path.arcTo({120, 20, 220, 120}, -90, 90, false);
   2469     canvas->drawPath(path, paint);
   2470     path.rewind();
   2471     path.moveTo(0, 0);
   2472     path.arcTo({20, 120, 120, 220}, -90, 90, true);
   2473     canvas->drawPath(path, paint);
   2474 }
   2475 ##
   2476 
   2477 #SeeAlso addArc SkCanvas::drawArc conicTo
   2478 
   2479 ##
   2480 
   2481 # ------------------------------------------------------------------------------
   2482 
   2483 #Method SkPath& arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius)
   2484 #In Build
   2485 #In Arc
   2486 #Populate
   2487 
   2488 #Example
   2489 #Height 226
   2490 void draw(SkCanvas* canvas) {
   2491     SkPaint tangentPaint;
   2492     tangentPaint.setAntiAlias(true);
   2493     SkPaint textPaint(tangentPaint);
   2494     tangentPaint.setStyle(SkPaint::kStroke_Style);
   2495     tangentPaint.setColor(SK_ColorGRAY);
   2496     SkPaint arcPaint(tangentPaint);
   2497     arcPaint.setStrokeWidth(5);
   2498     arcPaint.setColor(SK_ColorBLUE);
   2499     SkPath path;
   2500     SkPoint pts[] = { {56, 20}, {200, 20}, {90, 190} };
   2501     SkScalar radius = 50;
   2502     path.moveTo(pts[0]);
   2503     path.arcTo(pts[1], pts[2], radius);
   2504     canvas->drawLine(pts[0], pts[1], tangentPaint);
   2505     canvas->drawLine(pts[1], pts[2], tangentPaint);
   2506     SkPoint lastPt;
   2507     (void) path.getLastPt(&lastPt);
   2508     SkVector radial = pts[2] - pts[1];
   2509     radial.setLength(radius);
   2510     SkPoint center = { lastPt.fX - radial.fY, lastPt.fY + radial.fX };
   2511     canvas->drawCircle(center, radius, tangentPaint);
   2512     canvas->drawLine(lastPt, center, tangentPaint);
   2513     radial = pts[1] - pts[0];
   2514     radial.setLength(radius);
   2515     SkPoint arcStart = { center.fX + radial.fY, center.fY - radial.fX };
   2516     canvas->drawLine(center, arcStart, tangentPaint);
   2517     canvas->drawPath(path, arcPaint);
   2518     canvas->drawString("(x0, y0)", pts[0].fX - 5, pts[0].fY, textPaint);
   2519     canvas->drawString("(x1, y1)", pts[1].fX + 5, pts[1].fY, textPaint);
   2520     canvas->drawString("(x2, y2)", pts[2].fX, pts[2].fY + 15, textPaint);
   2521     canvas->drawString("radius", center.fX + 15, center.fY + 25, textPaint);
   2522     canvas->drawString("radius", center.fX - 3, center.fY - 16, textPaint);
   2523 }
   2524 ##
   2525 
   2526 #Example
   2527 #Height 128
   2528 void draw(SkCanvas* canvas) {
   2529     SkPaint tangentPaint;
   2530     tangentPaint.setAntiAlias(true);
   2531     SkPaint textPaint(tangentPaint);
   2532     tangentPaint.setStyle(SkPaint::kStroke_Style);
   2533     tangentPaint.setColor(SK_ColorGRAY);
   2534     SkPaint arcPaint(tangentPaint);
   2535     arcPaint.setStrokeWidth(5);
   2536     arcPaint.setColor(SK_ColorBLUE);
   2537     SkPath path;
   2538     SkPoint pts[] = { {156, 20}, {200, 20}, {170, 50} };
   2539     SkScalar radius = 50;
   2540     path.moveTo(pts[0]);
   2541     path.arcTo(pts[1], pts[2], radius);
   2542     canvas->drawLine(pts[0], pts[1], tangentPaint);
   2543     canvas->drawLine(pts[1], pts[2], tangentPaint);
   2544     SkPoint lastPt;
   2545     (void) path.getLastPt(&lastPt);
   2546     SkVector radial = pts[2] - pts[1];
   2547     radial.setLength(radius);
   2548     SkPoint center = { lastPt.fX - radial.fY, lastPt.fY + radial.fX };
   2549     canvas->drawLine(lastPt, center, tangentPaint);
   2550     radial = pts[1] - pts[0];
   2551     radial.setLength(radius);
   2552     SkPoint arcStart = { center.fX + radial.fY, center.fY - radial.fX };
   2553     canvas->drawLine(center, arcStart, tangentPaint);
   2554     canvas->drawPath(path, arcPaint);
   2555     canvas->drawString("(x0, y0)", pts[0].fX, pts[0].fY - 7, textPaint);
   2556     canvas->drawString("(x1, y1)", pts[1].fX + 5, pts[1].fY, textPaint);
   2557     canvas->drawString("(x2, y2)", pts[2].fX, pts[2].fY + 15, textPaint);
   2558     canvas->drawString("radius", center.fX + 15, center.fY + 25, textPaint);
   2559     canvas->drawString("radius", center.fX - 5, center.fY - 20, textPaint);
   2560 }
   2561 ##
   2562 
   2563 #Example
   2564 #Description
   2565 arcTo is represented by Line and circular Conic in Path.
   2566 ##
   2567 void draw(SkCanvas* canvas) {
   2568     SkPath path;
   2569     path.moveTo({156, 20});
   2570     path.arcTo(200, 20, 170, 50, 50);
   2571     SkPath::Iter iter(path, false);
   2572     SkPoint p[4];
   2573     SkPath::Verb verb;
   2574     while (SkPath::kDone_Verb != (verb = iter.next(p))) {
   2575         switch (verb) {
   2576             case SkPath::kMove_Verb:
   2577                 SkDebugf("move to (%g,%g)\n", p[0].fX, p[0].fY);
   2578                 break;
   2579             case SkPath::kLine_Verb:
   2580                 SkDebugf("line (%g,%g),(%g,%g)\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY);
   2581                 break;
   2582             case SkPath::kConic_Verb:
   2583                 SkDebugf("conic (%g,%g),(%g,%g),(%g,%g) weight %g\n",
   2584                          p[0].fX, p[0].fY, p[1].fX, p[1].fY, p[2].fX, p[2].fY, iter.conicWeight());
   2585                 break;
   2586             default:
   2587                 SkDebugf("unexpected verb\n");
   2588         }
   2589     }
   2590 }
   2591 #StdOut
   2592 move to (156,20)
   2593 line (156,20),(79.2893,20)
   2594 conic (79.2893,20),(200,20),(114.645,105.355) weight 0.382683
   2595 ##
   2596 ##
   2597 
   2598 #SeeAlso conicTo
   2599 
   2600 ##
   2601 
   2602 # ------------------------------------------------------------------------------
   2603 
   2604 #Method SkPath& arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius)
   2605 #In Build
   2606 #In Arc
   2607 #Populate
   2608 
   2609 #Example
   2610 #Description
   2611 Because tangent lines are parallel, arcTo appends line from last Path Point to
   2612 p1, but does not append a circular Conic.
   2613 ##
   2614 void draw(SkCanvas* canvas) {
   2615     SkPath path;
   2616     path.moveTo({156, 20});
   2617     path.arcTo({200, 20}, {170, 20}, 50);
   2618     SkPath::Iter iter(path, false);
   2619     SkPoint p[4];
   2620     SkPath::Verb verb;
   2621     while (SkPath::kDone_Verb != (verb = iter.next(p))) {
   2622         switch (verb) {
   2623             case SkPath::kMove_Verb:
   2624                 SkDebugf("move to (%g,%g)\n", p[0].fX, p[0].fY);
   2625                 break;
   2626             case SkPath::kLine_Verb:
   2627                 SkDebugf("line (%g,%g),(%g,%g)\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY);
   2628                 break;
   2629             case SkPath::kConic_Verb:
   2630                 SkDebugf("conic (%g,%g),(%g,%g),(%g,%g) weight %g\n",
   2631                           p[0].fX, p[0].fY, p[1].fX, p[1].fY, p[2].fX, p[2].fY, iter.conicWeight());
   2632                 break;
   2633             default:
   2634                 SkDebugf("unexpected verb\n");
   2635         }
   2636     }
   2637 }
   2638 #StdOut
   2639 move to (156,20)
   2640 line (156,20),(200,20)
   2641 ##
   2642 ##
   2643 
   2644 #SeeAlso conicTo
   2645 
   2646 ##
   2647 
   2648 # ------------------------------------------------------------------------------
   2649 
   2650 #Enum ArcSize
   2651 #Line # used by arcTo variation ##
   2652 
   2653 #Code
   2654     enum ArcSize {
   2655         kSmall_ArcSize,
   2656         kLarge_ArcSize,
   2657     };
   2658 ##
   2659 
   2660 Four axis-aligned Ovals with the same height and width intersect a pair of Points.
   2661 ArcSize and Direction select one of the four Ovals, by choosing the larger or smaller
   2662 arc between the Points; and by choosing the arc Direction, clockwise
   2663 or counterclockwise.
   2664 
   2665 #Const kSmall_ArcSize 0
   2666 #Line # smaller of Arc pair ##
   2667 ##
   2668 #Const kLarge_ArcSize 1
   2669 #Line # larger of Arc pair ##
   2670 ##
   2671 
   2672 #Example
   2673 #Height 160
   2674 #Description
   2675 Arc begins at top of Oval pair and ends at bottom. Arc can take four routes to get there.
   2676 Two routes are large, and two routes are counterclockwise. The one route both large
   2677 and counterclockwise is blue.
   2678 ##
   2679 void draw(SkCanvas* canvas) {
   2680     SkPaint paint;
   2681     paint.setAntiAlias(true);
   2682     paint.setStyle(SkPaint::kStroke_Style);
   2683     for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
   2684         for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) {
   2685             SkPath path;
   2686             path.moveTo({120, 50});
   2687             path.arcTo(70, 40, 30, arcSize, sweep, 156, 100);
   2688             if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) {
   2689                 paint.setColor(SK_ColorBLUE);
   2690                 paint.setStrokeWidth(3);
   2691             }
   2692             canvas->drawPath(path, paint);
   2693          }
   2694     }
   2695 }
   2696 ##
   2697 
   2698 #SeeAlso arcTo Direction
   2699 
   2700 ##
   2701 
   2702 # ------------------------------------------------------------------------------
   2703 
   2704 #Method SkPath& arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
   2705                Direction sweep, SkScalar x, SkScalar y)
   2706 #In Build
   2707 #In Arc
   2708 #Populate
   2709 
   2710 #Example
   2711 #Height 160
   2712 void draw(SkCanvas* canvas) {
   2713     SkPaint paint;
   2714     paint.setAntiAlias(true);
   2715     paint.setStyle(SkPaint::kStroke_Style);
   2716     for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
   2717         for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) {
   2718             SkPath path;
   2719             path.moveTo({120, 50});
   2720             path.arcTo(70, 40, 30, arcSize, sweep, 120.1, 50);
   2721             if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) {
   2722                 paint.setColor(SK_ColorBLUE);
   2723                 paint.setStrokeWidth(3);
   2724             }
   2725             canvas->drawPath(path, paint);
   2726          }
   2727     }
   2728 }
   2729 ##
   2730 
   2731 #SeeAlso rArcTo ArcSize Direction
   2732 
   2733 ##
   2734 
   2735 # ------------------------------------------------------------------------------
   2736 
   2737 #Method SkPath& arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
   2738                const SkPoint xy)
   2739 #In Build
   2740 #In Arc
   2741 #Populate
   2742 
   2743 #Example
   2744 #Height 108
   2745 void draw(SkCanvas* canvas) {
   2746     SkPaint paint;
   2747     SkPath path;
   2748     const SkPoint starts[] = {{20, 20}, {120, 20}, {70, 60}};
   2749     for (auto start : starts) {
   2750         path.moveTo(start.fX, start.fY);
   2751         path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPath::kCCW_Direction, 60, 0);
   2752     }
   2753     canvas->drawPath(path, paint);
   2754 }
   2755 ##
   2756 
   2757 #SeeAlso rArcTo ArcSize Direction
   2758 
   2759 ##
   2760 
   2761 # ------------------------------------------------------------------------------
   2762 
   2763 #Method SkPath& rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
   2764                 Direction sweep, SkScalar dx, SkScalar dy)
   2765 #In Build
   2766 #In Arc
   2767 #Line # appends Arc relative to Last_Point ##
   2768 
   2769 Appends Arc to Path, relative to last Path Point. Arc is implemented by one or
   2770 more Conic, weighted to describe part of Oval with radii (rx, ry) rotated by
   2771 xAxisRotate degrees. Arc curves from last Path Point to relative end Point
   2772 (dx, dy), choosing one of four possible routes: clockwise or
   2773 counterclockwise, and smaller or larger. If Path is empty, the start Arc Point
   2774 is (0, 0).
   2775 
   2776 Arc sweep is always less than 360 degrees. arcTo appends Line to end Point
   2777 if either radii are zero, or if last Path Point equals end Point.
   2778 arcTo scales radii (rx, ry) to fit last Path Point and end Point if both are
   2779 greater than zero but too small to describe an arc.
   2780 
   2781 arcTo appends up to four Conic curves.
   2782 arcTo implements the functionality of SVG_Arc, although SVG "sweep-flag" value is
   2783 opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while
   2784 kCW_Direction cast to int is zero.
   2785 
   2786 #Param rx           radius before x-axis rotation ##
   2787 #Param ry           radius before x-axis rotation ##
   2788 #Param xAxisRotate  x-axis rotation in degrees; positive values are clockwise ##
   2789 #Param largeArc     chooses smaller or larger Arc ##
   2790 #Param sweep        chooses clockwise or counterclockwise Arc ##
   2791 #Param dx           x-axis offset end of Arc from last Path Point ##
   2792 #Param dy           y-axis offset end of Arc from last Path Point ##
   2793 
   2794 #Return reference to Path ##
   2795 
   2796 #Example
   2797 #Height 108
   2798 void draw(SkCanvas* canvas) {
   2799     SkPaint paint;
   2800     SkPath path;
   2801     const SkPoint starts[] = {{20, 20}, {120, 20}, {70, 60}};
   2802     for (auto start : starts) {
   2803         path.moveTo(start.fX, start.fY);
   2804         path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPath::kCCW_Direction, 60, 0);
   2805     }
   2806     canvas->drawPath(path, paint);
   2807 }
   2808 ##
   2809 
   2810 #SeeAlso arcTo ArcSize Direction
   2811 
   2812 ##
   2813 
   2814 #Subtopic Arc ##
   2815 
   2816 # ------------------------------------------------------------------------------
   2817 
   2818 #Method SkPath& close()
   2819 #In Build
   2820 #Line # makes last Contour a loop ##
   2821 #Populate
   2822 
   2823 #Example
   2824 void draw(SkCanvas* canvas) {
   2825     SkPaint paint;
   2826     paint.setStrokeWidth(15);
   2827     paint.setStrokeCap(SkPaint::kRound_Cap);
   2828     SkPath path;
   2829     const SkPoint points[] = {{20, 20}, {70, 20}, {40, 90}};
   2830     path.addPoly(points, SK_ARRAY_COUNT(points), false);
   2831     for (int loop = 0; loop < 2; ++loop) {
   2832         for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style,
   2833                 SkPaint::kStrokeAndFill_Style} ) {
   2834             paint.setStyle(style);
   2835             canvas->drawPath(path, paint);
   2836             canvas->translate(85, 0);
   2837         }
   2838         path.close();
   2839         canvas->translate(-255, 128);
   2840     }
   2841 }
   2842 ##
   2843 
   2844 #SeeAlso
   2845 
   2846 ##
   2847 
   2848 # ------------------------------------------------------------------------------
   2849 
   2850 #Method static bool IsInverseFillType(FillType fill)
   2851 #In Property
   2852 #Line # returns if Fill_Type represents outside geometry ##
   2853 Returns true if fill is inverted and Path with fill represents area outside
   2854 of its geometric bounds.
   2855 
   2856 #Table
   2857 #Legend
   2858 # FillType                 # is inverse ##
   2859 ##
   2860 # kWinding_FillType        # false      ##
   2861 # kEvenOdd_FillType        # false      ##
   2862 # kInverseWinding_FillType # true       ##
   2863 # kInverseEvenOdd_FillType # true       ##
   2864 ##
   2865 
   2866 #Param fill  one of: kWinding_FillType, kEvenOdd_FillType,
   2867              kInverseWinding_FillType, kInverseEvenOdd_FillType
   2868 ##
   2869 
   2870 #Return  true if Path fills outside its bounds ##
   2871 
   2872 #Example
   2873 #Function
   2874 ###$
   2875 #define nameValue(fill) { SkPath::fill, #fill }
   2876 
   2877 $$$#
   2878 ##
   2879 void draw(SkCanvas* canvas) {
   2880     struct {
   2881         SkPath::FillType fill;
   2882         const char* name;
   2883     } fills[] = {
   2884         nameValue(kWinding_FillType),
   2885         nameValue(kEvenOdd_FillType),
   2886         nameValue(kInverseWinding_FillType),
   2887         nameValue(kInverseEvenOdd_FillType),
   2888     };
   2889     for (auto fill: fills ) {
   2890         SkDebugf("IsInverseFillType(%s) == %s\n", fill.name, SkPath::IsInverseFillType(fill.fill) ?
   2891                  "true" : "false");
   2892     }
   2893 }
   2894 #StdOut
   2895 IsInverseFillType(kWinding_FillType) == false
   2896 IsInverseFillType(kEvenOdd_FillType) == false
   2897 IsInverseFillType(kInverseWinding_FillType) == true
   2898 IsInverseFillType(kInverseEvenOdd_FillType) == true
   2899 ##
   2900 ##
   2901 
   2902 #SeeAlso FillType getFillType setFillType ConvertToNonInverseFillType
   2903 
   2904 ##
   2905 
   2906 # ------------------------------------------------------------------------------
   2907 
   2908 #Method static FillType ConvertToNonInverseFillType(FillType fill)
   2909 #In Utility
   2910 #Line # returns Fill_Type representing inside geometry ##
   2911 Returns equivalent Fill_Type representing Path fill inside its bounds.
   2912 
   2913 #Table
   2914 #Legend
   2915 # FillType                 # inside FillType   ##
   2916 ##
   2917 # kWinding_FillType        # kWinding_FillType ##
   2918 # kEvenOdd_FillType        # kEvenOdd_FillType ##
   2919 # kInverseWinding_FillType # kWinding_FillType ##
   2920 # kInverseEvenOdd_FillType # kEvenOdd_FillType ##
   2921 ##
   2922 
   2923 #Param fill  one of: kWinding_FillType, kEvenOdd_FillType,
   2924              kInverseWinding_FillType, kInverseEvenOdd_FillType
   2925 ##
   2926 
   2927 #Return  fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted ##
   2928 
   2929 #Example
   2930 #Function
   2931 ###$
   2932 #define nameValue(fill) { SkPath::fill, #fill }
   2933 
   2934 $$$#
   2935 ##
   2936 void draw(SkCanvas* canvas) {
   2937     struct {
   2938         SkPath::FillType fill;
   2939         const char* name;
   2940     } fills[] = {
   2941         nameValue(kWinding_FillType),
   2942         nameValue(kEvenOdd_FillType),
   2943         nameValue(kInverseWinding_FillType),
   2944         nameValue(kInverseEvenOdd_FillType),
   2945     };
   2946     for (unsigned i = 0; i < SK_ARRAY_COUNT(fills); ++i) {
   2947         if (fills[i].fill != (SkPath::FillType) i) {
   2948             SkDebugf("fills array order does not match FillType enum order");
   2949             break;
   2950         }
   2951         SkDebugf("ConvertToNonInverseFillType(%s) == %s\n", fills[i].name,
   2952                 fills[(int) SkPath::ConvertToNonInverseFillType(fills[i].fill)].name);
   2953     }
   2954 }
   2955 #StdOut
   2956 ConvertToNonInverseFillType(kWinding_FillType) == kWinding_FillType
   2957 ConvertToNonInverseFillType(kEvenOdd_FillType) == kEvenOdd_FillType
   2958 ConvertToNonInverseFillType(kInverseWinding_FillType) == kWinding_FillType
   2959 ConvertToNonInverseFillType(kInverseEvenOdd_FillType) == kEvenOdd_FillType
   2960 ##
   2961 ##
   2962 
   2963 #SeeAlso FillType getFillType setFillType IsInverseFillType
   2964 
   2965 ##
   2966 
   2967 # ------------------------------------------------------------------------------
   2968 
   2969 #Method static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
   2970                                    SkScalar w, SkPoint pts[], int pow2)
   2971 #In Utility
   2972 #Line # approximates Conic with Quad array ##
   2973 
   2974 Approximates Conic with Quad array. Conic is constructed from start Point p0,
   2975 control Point p1, end Point p2, and weight w.
   2976 Quad array is stored in pts; this storage is supplied by caller.
   2977 Maximum Quad count is 2 to the pow2.
   2978 Every third point in array shares last Point of previous Quad and first Point of
   2979 next Quad. Maximum pts storage size is given by:
   2980 #Formula # (1 + 2 * (1 << pow2)) * sizeof(SkPoint) ##.
   2981 
   2982 Returns Quad count used the approximation, which may be smaller
   2983 than the number requested.
   2984 
   2985 Conic_Weight determines the amount of influence Conic control point has on the curve.
   2986 w less than one represents an elliptical section. w greater than one represents
   2987 a hyperbolic section. w equal to one represents a parabolic section.
   2988 
   2989 Two Quad curves are sufficient to approximate an elliptical Conic with a sweep
   2990 of up to 90 degrees; in this case, set pow2 to one.
   2991 
   2992 #Param p0    Conic start Point ##
   2993 #Param p1    Conic control Point ##
   2994 #Param p2    Conic end Point ##
   2995 #Param w     Conic weight ##
   2996 #Param pts   storage for Quad array ##
   2997 #Param pow2  Quad count, as power of two, normally 0 to 5 (1 to 32 Quad curves) ##
   2998 
   2999 #Return  number of Quad curves written to pts ##
   3000 
   3001 #Example
   3002 #Description
   3003 A pair of Quad curves are drawn in red on top of the elliptical Conic curve in black.
   3004 The middle curve is nearly circular. The top-right curve is parabolic, which can
   3005 be drawn exactly with a single Quad.
   3006 ##
   3007 void draw(SkCanvas* canvas) {
   3008       SkPaint conicPaint;
   3009       conicPaint.setAntiAlias(true);
   3010       conicPaint.setStyle(SkPaint::kStroke_Style);
   3011       SkPaint quadPaint(conicPaint);
   3012       quadPaint.setColor(SK_ColorRED);
   3013       SkPoint conic[] = { {20, 170}, {80, 170}, {80, 230} };
   3014       for (auto weight : { .25f, .5f, .707f, .85f, 1.f } ) {
   3015           SkPoint quads[5];
   3016           SkPath::ConvertConicToQuads(conic[0], conic[1], conic[2], weight, quads, 1);
   3017           SkPath path;
   3018           path.moveTo(conic[0]);
   3019           path.conicTo(conic[1], conic[2], weight);
   3020           canvas->drawPath(path, conicPaint);
   3021           path.rewind();
   3022           path.moveTo(quads[0]);
   3023           path.quadTo(quads[1], quads[2]);
   3024           path.quadTo(quads[3], quads[4]);
   3025           canvas->drawPath(path, quadPaint);
   3026           canvas->translate(50, -50);
   3027       }
   3028 }
   3029 ##
   3030 
   3031 #SeeAlso Conic Quad
   3032 
   3033 ##
   3034 
   3035 # ------------------------------------------------------------------------------
   3036 
   3037 #Method bool isRect(SkRect* rect, bool* isClosed = nullptr, Direction* direction = nullptr) const
   3038 #In Property
   3039 #Line # returns if describes Rect ##
   3040 #Populate
   3041 
   3042 #Example
   3043 #Description
   3044 After addRect, isRect returns true. Following moveTo permits isRect to return true, but
   3045 following lineTo does not. addPoly returns true even though rect is not closed, and one
   3046 side of rect is made up of consecutive line segments.
   3047 ##
   3048 void draw(SkCanvas* canvas) {
   3049     auto debugster = [](const char* prefix, const SkPath& path) -> void {
   3050         SkRect rect;
   3051         SkPath::Direction direction;
   3052         bool isClosed;
   3053         path.isRect(&rect, &isClosed, &direction) ?
   3054                 SkDebugf("%s is rect (%g, %g, %g, %g); is %s" "closed; direction %s\n", prefix,
   3055                          rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, isClosed ? "" : "not ",
   3056                          SkPath::kCW_Direction == direction ? "CW" : "CCW") :
   3057                 SkDebugf("%s is not rect\n", prefix);
   3058     };
   3059     SkPath path;
   3060     debugster("empty", path);
   3061     path.addRect({10, 20, 30, 40});
   3062     debugster("addRect", path);
   3063     path.moveTo(60, 70);
   3064     debugster("moveTo", path);
   3065     path.lineTo(60, 70);
   3066     debugster("lineTo", path);
   3067     path.reset();
   3068     const SkPoint pts[] = { {0, 0}, {0, 80}, {80, 80}, {80, 0}, {40, 0}, {20, 0} };
   3069     path.addPoly(pts, SK_ARRAY_COUNT(pts), false);
   3070     debugster("addPoly", path);
   3071 }
   3072 #StdOut
   3073 empty is not rect
   3074 addRect is rect (10, 20, 30, 40); is closed; direction CW
   3075 moveTo is rect (10, 20, 30, 40); is closed; direction CW
   3076 lineTo is not rect
   3077 addPoly is rect (0, 0, 80, 80); is not closed; direction CCW
   3078 ##
   3079 ##
   3080 
   3081 #SeeAlso computeTightBounds conservativelyContainsRect getBounds isConvex isLastContourClosed isNestedFillRects
   3082 
   3083 ##
   3084 
   3085 # ------------------------------------------------------------------------------
   3086 
   3087 #Method bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = nullptr) const
   3088 #In Property
   3089 #Line # returns if describes Rect pair, one inside the other ##
   3090 #Populate
   3091 
   3092 #Example
   3093 void draw(SkCanvas* canvas) {
   3094     SkPaint paint;
   3095     paint.setStyle(SkPaint::kStroke_Style);
   3096     paint.setStrokeWidth(5);
   3097     SkPath path;
   3098     path.addRect({10, 20, 30, 40});
   3099     paint.getFillPath(path, &path);
   3100     SkRect rects[2];
   3101     SkPath::Direction directions[2];
   3102     if (path.isNestedFillRects(rects, directions)) {
   3103         for (int i = 0; i < 2; ++i) {
   3104             SkDebugf("%s (%g, %g, %g, %g); direction %s\n", i ? "inner" : "outer",
   3105                      rects[i].fLeft, rects[i].fTop, rects[i].fRight, rects[i].fBottom,
   3106                      SkPath::kCW_Direction == directions[i] ? "CW" : "CCW");
   3107         }
   3108     } else {
   3109         SkDebugf("is not nested rectangles\n");
   3110     }
   3111 }
   3112 #StdOut
   3113 outer (7.5, 17.5, 32.5, 42.5); direction CW
   3114 inner (12.5, 22.5, 27.5, 37.5); direction CCW
   3115 ##
   3116 ##
   3117 
   3118 #SeeAlso computeTightBounds conservativelyContainsRect getBounds isConvex isLastContourClosed isRect
   3119 
   3120 ##
   3121 
   3122 # ------------------------------------------------------------------------------
   3123 
   3124 #Method SkPath& addRect(const SkRect& rect, Direction dir = kCW_Direction)
   3125 #In Build
   3126 #Line # adds one Contour containing Rect ##
   3127 #Populate
   3128 
   3129 #Example
   3130 #Description
   3131 The left Rect dashes starting at the top-left corner, to the right.
   3132 The right Rect dashes starting at the top-left corner, towards the bottom.
   3133 ##
   3134 #Height 128
   3135 void draw(SkCanvas* canvas) {
   3136     SkPaint paint;
   3137     paint.setStrokeWidth(15);
   3138     paint.setStrokeCap(SkPaint::kSquare_Cap);
   3139     float intervals[] = { 5, 21.75f };
   3140     paint.setStyle(SkPaint::kStroke_Style);
   3141     paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
   3142     SkPath path;
   3143     path.addRect({20, 20, 100, 100}, SkPath::kCW_Direction);
   3144     canvas->drawPath(path, paint);
   3145     path.rewind();
   3146     path.addRect({140, 20, 220, 100}, SkPath::kCCW_Direction);
   3147     canvas->drawPath(path, paint);
   3148 }
   3149 ##
   3150 
   3151 #SeeAlso SkCanvas::drawRect Direction
   3152 
   3153 ##
   3154 
   3155 # ------------------------------------------------------------------------------
   3156 
   3157 #Method SkPath& addRect(const SkRect& rect, Direction dir, unsigned start)
   3158 
   3159 Adds Rect to Path, appending kMove_Verb, three kLine_Verb, and kClose_Verb.
   3160 If dir is kCW_Direction, Rect corners are added clockwise; if dir is
   3161 kCCW_Direction, Rect corners are added counterclockwise.
   3162 start determines the first corner added.
   3163 
   3164 #Table
   3165 #Legend
   3166 # start # first corner ##
   3167 #Legend ##
   3168 # 0     # top-left ##
   3169 # 1     # top-right ##
   3170 # 2     # bottom-right ##
   3171 # 3     # bottom-left ##
   3172 #Table ##
   3173 
   3174 #Param rect   Rect to add as a closed contour ##
   3175 #Param dir    Direction to wind added contour ##
   3176 #Param start  initial corner of Rect to add ##
   3177 
   3178 #Return reference to Path ##
   3179 
   3180 #Example
   3181 #Height 128
   3182 #Description
   3183 The arrow is just after the initial corner and points towards the next
   3184 corner appended to Path.
   3185 ##
   3186 void draw(SkCanvas* canvas) {
   3187     const SkPoint arrow[] = { {5, -5}, {15, -5}, {20, 0}, {15, 5}, {5, 5}, {10, 0} };
   3188     const SkRect rect = {10, 10, 54, 54};
   3189     SkPaint rectPaint;
   3190     rectPaint.setAntiAlias(true);
   3191     rectPaint.setStyle(SkPaint::kStroke_Style);
   3192     SkPaint arrowPaint(rectPaint);
   3193     SkPath arrowPath;
   3194     arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
   3195     arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0,
   3196                              SkPath1DPathEffect::kRotate_Style));
   3197     for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
   3198         for (unsigned start : { 0, 1, 2, 3 } ) {
   3199            SkPath path;
   3200            path.addRect(rect, direction, start);
   3201            canvas->drawPath(path, rectPaint);
   3202            canvas->drawPath(path, arrowPaint);
   3203            canvas->translate(64, 0);
   3204        }
   3205        canvas->translate(-256, 64);
   3206     }
   3207 }
   3208 ##
   3209 
   3210 #SeeAlso SkCanvas::drawRect Direction
   3211 
   3212 ##
   3213 
   3214 # ------------------------------------------------------------------------------
   3215 
   3216 #Method SkPath& addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
   3217                  Direction dir = kCW_Direction)
   3218 #Populate
   3219 
   3220 #Example
   3221 #Description
   3222 The left Rect dashes start at the top-left corner, and continue to the right.
   3223 The right Rect dashes start at the top-left corner, and continue down.
   3224 ##
   3225 #Height 128
   3226 void draw(SkCanvas* canvas) {
   3227     SkPaint paint;
   3228     paint.setStrokeWidth(15);
   3229     paint.setStrokeCap(SkPaint::kSquare_Cap);
   3230     float intervals[] = { 5, 21.75f };
   3231     paint.setStyle(SkPaint::kStroke_Style);
   3232     paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
   3233     for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
   3234         SkPath path;
   3235         path.addRect(20, 20, 100, 100, direction);
   3236         canvas->drawPath(path, paint);
   3237         canvas->translate(128, 0);
   3238     }
   3239 }
   3240 ##
   3241 
   3242 #SeeAlso SkCanvas::drawRect Direction
   3243 
   3244 ##
   3245 
   3246 # ------------------------------------------------------------------------------
   3247 
   3248 #Method SkPath& addOval(const SkRect& oval, Direction dir = kCW_Direction)
   3249 #In Build
   3250 #Line # adds one Contour containing Oval ##
   3251 #Populate
   3252 
   3253 #Example
   3254 #Height 120
   3255     SkPaint paint;
   3256     SkPath oval;
   3257     oval.addOval({20, 20, 160, 80});
   3258     canvas->drawPath(oval, paint);
   3259 ##
   3260 
   3261 #SeeAlso SkCanvas::drawOval Direction Oval
   3262 
   3263 ##
   3264 
   3265 # ------------------------------------------------------------------------------
   3266 
   3267 #Method SkPath& addOval(const SkRect& oval, Direction dir, unsigned start)
   3268 
   3269 Adds Oval to Path, appending kMove_Verb, four kConic_Verb, and kClose_Verb.
   3270 Oval is upright ellipse bounded by Rect oval with radii equal to half oval width
   3271 and half oval height. Oval begins at start and continues
   3272 clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction.
   3273 
   3274 #Table
   3275 #Legend
   3276 # start # Point                        ##
   3277 #Legend ##
   3278 # 0     # oval.centerX(), oval.fTop    ##
   3279 # 1     # oval.fRight, oval.centerY()  ##
   3280 # 2     # oval.centerX(), oval.fBottom ##
   3281 # 3     # oval.fLeft, oval.centerY()   ##
   3282 #Table ##
   3283 
   3284 #Param oval   bounds of ellipse added ##
   3285 #Param dir    Direction to wind ellipse ##
   3286 #Param start  index of initial point of ellipse ##
   3287 
   3288 #Return reference to Path ##
   3289 
   3290 #Example
   3291 #Height 160
   3292 void draw(SkCanvas* canvas) {
   3293     const SkPoint arrow[] = { {0, -5}, {10, 0}, {0, 5} };
   3294     const SkRect rect = {10, 10, 54, 54};
   3295     SkPaint ovalPaint;
   3296     ovalPaint.setAntiAlias(true);
   3297     SkPaint textPaint(ovalPaint);
   3298     ovalPaint.setStyle(SkPaint::kStroke_Style);
   3299     SkPaint arrowPaint(ovalPaint);
   3300     SkPath arrowPath;
   3301     arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
   3302     arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0,
   3303                              SkPath1DPathEffect::kRotate_Style));
   3304     for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
   3305         for (unsigned start : { 0, 1, 2, 3 } ) {
   3306            SkPath path;
   3307            path.addOval(rect, direction, start);
   3308            canvas->drawPath(path, ovalPaint);
   3309            canvas->drawPath(path, arrowPaint);
   3310            canvas->drawText(&"0123"[start], 1, rect.centerX(), rect.centerY() + 5, textPaint);
   3311            canvas->translate(64, 0);
   3312        }
   3313        canvas->translate(-256, 72);
   3314        canvas->drawString(SkPath::kCW_Direction == direction ? "clockwise" : "counterclockwise",
   3315                           128, 0, textPaint);
   3316     }
   3317 }
   3318 ##
   3319 
   3320 #SeeAlso SkCanvas::drawOval Direction Oval
   3321 
   3322 ##
   3323 
   3324 # ------------------------------------------------------------------------------
   3325 
   3326 #Method SkPath& addCircle(SkScalar x, SkScalar y, SkScalar radius,
   3327                    Direction dir = kCW_Direction)
   3328 #In Build
   3329 #Line # adds one Contour containing Circle ##
   3330 
   3331 Adds Circle centered at (x, y) of size radius to Path, appending kMove_Verb,
   3332 four kConic_Verb, and kClose_Verb. Circle begins at: #Formula # (x + radius, y) ##, continuing
   3333 clockwise if dir is kCW_Direction, and counterclockwise if dir is kCCW_Direction.
   3334 
   3335 Has no effect if radius is zero or negative.
   3336 
   3337 #Param x       center of Circle ##
   3338 #Param y       center of Circle  ##
   3339 #Param radius  distance from center to edge ##
   3340 #Param dir     Direction to wind Circle ##
   3341 
   3342 #Return reference to Path ##
   3343 
   3344 #Example
   3345 void draw(SkCanvas* canvas) {
   3346     SkPaint paint;
   3347     paint.setAntiAlias(true);
   3348     paint.setStyle(SkPaint::kStroke_Style);
   3349     paint.setStrokeWidth(10);
   3350     for (int size = 10; size < 300; size += 20) {
   3351         SkPath path;
   3352         path.addCircle(128, 128, size, SkPath::kCW_Direction);
   3353         canvas->drawPath(path, paint);
   3354     }
   3355 }
   3356 ##
   3357 
   3358 #SeeAlso SkCanvas::drawCircle Direction Circle
   3359 
   3360 ##
   3361 
   3362 # ------------------------------------------------------------------------------
   3363 
   3364 #Method SkPath& addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle)
   3365 #In Build
   3366 #Line # adds one Contour containing Arc ##
   3367 #Populate
   3368 
   3369 #Example
   3370 #Description
   3371 The middle row of the left and right columns draw differently from the entries
   3372 above and below because sweepAngle is outside of the range of +/-360,
   3373 and startAngle modulo 90 is not zero.
   3374 ##
   3375 void draw(SkCanvas* canvas) {
   3376     SkPaint paint;
   3377     for (auto start : { 0, 90, 135, 180, 270 } ) {
   3378         for (auto sweep : { -450.f, -180.f, -90.f, 90.f, 180.f, 360.1f } ) {
   3379             SkPath path;
   3380             path.addArc({10, 10, 35, 45}, start, sweep);
   3381             canvas->drawPath(path, paint);
   3382             canvas->translate(252 / 6, 0);
   3383         }
   3384         canvas->translate(-252, 255 / 5);
   3385     }
   3386 }
   3387 ##
   3388 
   3389 #SeeAlso Arc arcTo SkCanvas::drawArc
   3390 
   3391 ##
   3392 
   3393 # ------------------------------------------------------------------------------
   3394 
   3395 #Method SkPath& addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
   3396                       Direction dir = kCW_Direction)
   3397 #In Build
   3398 #Line # adds one Contour containing Round_Rect with common corner radii ##
   3399 #Populate
   3400 
   3401 #Example
   3402 #Description
   3403 If either radius is zero, path contains Rect and is drawn red.
   3404 If sides are only radii, path contains Oval and is drawn blue.
   3405 All remaining path draws are convex, and are drawn in gray; no
   3406 paths constructed from addRoundRect are concave, so none are
   3407 drawn in green.
   3408 ##
   3409 void draw(SkCanvas* canvas) {
   3410     SkPaint paint;
   3411     paint.setAntiAlias(true);
   3412     for (auto xradius : { 0, 7, 13, 20 } ) {
   3413         for (auto yradius : { 0, 9, 18, 40 } ) {
   3414             SkPath path;
   3415             path.addRoundRect({10, 10, 36, 46}, xradius, yradius);
   3416             paint.setColor(path.isRect(nullptr) ? SK_ColorRED : path.isOval(nullptr) ?
   3417                            SK_ColorBLUE : path.isConvex() ? SK_ColorGRAY : SK_ColorGREEN);
   3418             canvas->drawPath(path, paint);
   3419             canvas->translate(64, 0);
   3420         }
   3421         canvas->translate(-256, 64);
   3422     }
   3423 }
   3424 ##
   3425 
   3426 #SeeAlso addRRect SkCanvas::drawRoundRect
   3427 
   3428 ##
   3429 
   3430 # ------------------------------------------------------------------------------
   3431 
   3432 #Method SkPath& addRoundRect(const SkRect& rect, const SkScalar radii[],
   3433                       Direction dir = kCW_Direction)
   3434 
   3435 Appends Round_Rect to Path, creating a new closed Contour. Round_Rect has bounds
   3436 equal to rect; each corner is 90 degrees of an ellipse with radii from the
   3437 array.
   3438 
   3439 #Table
   3440 #Legend
   3441 # radii index # location                        ##
   3442 #Legend ##
   3443 # 0           # x-axis radius of top-left corner     ##
   3444 # 1           # y-axis radius of top-left corner     ##
   3445 # 2           # x-axis radius of top-right corner    ##
   3446 # 3           # y-axis radius of top-right corner    ##
   3447 # 4           # x-axis radius of bottom-right corner ##
   3448 # 5           # y-axis radius of bottom-right corner ##
   3449 # 6           # x-axis radius of bottom-left corner  ##
   3450 # 7           # y-axis radius of bottom-left corner  ##
   3451 #Table ##
   3452 
   3453 If dir is kCW_Direction, Round_Rect starts at top-left of the lower-left corner
   3454 and winds clockwise. If dir is kCCW_Direction, Round_Rect starts at the
   3455 bottom-left of the upper-left corner and winds counterclockwise.
   3456 
   3457 If both radii on any side of rect exceed its length, all radii are scaled
   3458 uniformly until the corners fit. If either radius of a corner is less than or
   3459 equal to zero, both are treated as zero.
   3460 
   3461 After appending, Path may be empty, or may contain: Rect, Oval, or Round_Rect.
   3462 
   3463 #Param rect   bounds of Round_Rect ##
   3464 #Param radii  array of 8 SkScalar values, a radius pair for each corner ##
   3465 #Param dir    Direction to wind Round_Rect ##
   3466 
   3467 #Return reference to Path ##
   3468 
   3469 #Example
   3470 void draw(SkCanvas* canvas) {
   3471     SkPaint paint;
   3472     paint.setAntiAlias(true);
   3473     SkScalar radii[] = { 80, 100, 0, 0, 40, 60, 0, 0 };
   3474     SkPath path;
   3475     SkMatrix rotate90;
   3476     rotate90.setRotate(90, 128, 128);
   3477     for (int i = 0; i < 4; ++i) {
   3478         path.addRoundRect({10, 10, 110, 110}, radii);
   3479         path.transform(rotate90);
   3480     }
   3481     canvas->drawPath(path, paint);
   3482 }
   3483 ##
   3484 
   3485 #SeeAlso addRRect SkCanvas::drawRoundRect
   3486 
   3487 ##
   3488 
   3489 # ------------------------------------------------------------------------------
   3490 
   3491 #Method SkPath& addRRect(const SkRRect& rrect, Direction dir = kCW_Direction)
   3492 #In Build
   3493 #Line # adds one Contour containing Round_Rect ##
   3494 #Populate
   3495 
   3496 #Example
   3497 void draw(SkCanvas* canvas) {
   3498     SkPaint paint;
   3499     paint.setAntiAlias(true);
   3500     SkRRect rrect;
   3501     SkVector radii[] = {{50, 50}, {0, 0}, {0, 0}, {50, 50}};
   3502     rrect.setRectRadii({10, 10, 110, 110}, radii);
   3503     SkPath path;
   3504     SkMatrix rotate90;
   3505     rotate90.setRotate(90, 128, 128);
   3506     for (int i = 0; i < 4; ++i) {
   3507         path.addRRect(rrect);
   3508         path.transform(rotate90);
   3509     }
   3510     canvas->drawPath(path, paint);
   3511 }
   3512 ##
   3513 
   3514 #SeeAlso addRoundRect SkCanvas::drawRRect
   3515 
   3516 ##
   3517 
   3518 # ------------------------------------------------------------------------------
   3519 
   3520 #Method SkPath& addRRect(const SkRRect& rrect, Direction dir, unsigned start)
   3521 
   3522 Adds rrect to Path, creating a new closed Contour. If dir is kCW_Direction, rrect
   3523 winds clockwise; if dir is kCCW_Direction, rrect winds counterclockwise.
   3524 start determines the first point of rrect to add.
   3525 
   3526 #Table
   3527 #Legend
   3528 # start       # location                    ##
   3529 #Legend ##
   3530 # 0           # right of top-left corner    ##
   3531 # 1           # left of top-right corner    ##
   3532 # 2           # bottom of top-right corner  ##
   3533 # 3           # top of bottom-right corner  ##
   3534 # 4           # left of bottom-right corner ##
   3535 # 5           # right of bottom-left corner ##
   3536 # 6           # top of bottom-left corner   ##
   3537 # 7           # bottom of top-left corner   ##
   3538 #Table ##
   3539 
   3540 After appending, Path may be empty, or may contain: Rect, Oval, or Round_Rect.
   3541 
   3542 #Param rrect  bounds and radii of rounded rectangle ##
   3543 #Param dir    Direction to wind Round_Rect ##
   3544 #Param start  index of initial point of Round_Rect ##
   3545 
   3546 #Return reference to Path ##
   3547 
   3548 #Example
   3549 void draw(SkCanvas* canvas) {
   3550     SkPaint paint;
   3551     paint.setAntiAlias(true);
   3552     SkRRect rrect;
   3553     rrect.setRectXY({40, 40, 215, 215}, 50, 50);
   3554     SkPath path;
   3555     path.addRRect(rrect);
   3556     canvas->drawPath(path, paint);
   3557     for (int start = 0; start < 8; ++start) {
   3558         SkPath textPath;
   3559         textPath.addRRect(rrect, SkPath::kCW_Direction, start);
   3560         SkPathMeasure pathMeasure(textPath, false);
   3561         SkPoint position;
   3562         SkVector tangent;
   3563         if (!pathMeasure.getPosTan(0, &position, &tangent)) {
   3564             continue;
   3565         }
   3566         SkRSXform rsxForm = SkRSXform::Make(tangent.fX, tangent.fY,
   3567                position.fX + tangent.fY * 5, position.fY - tangent.fX * 5);
   3568         canvas->drawTextRSXform(&"01234567"[start], 1, &rsxForm, nullptr, paint);
   3569     }
   3570 }
   3571 ##
   3572 
   3573 #SeeAlso addRoundRect SkCanvas::drawRRect
   3574 
   3575 ##
   3576 
   3577 # ------------------------------------------------------------------------------
   3578 
   3579 #Method SkPath& addPoly(const SkPoint pts[], int count, bool close)
   3580 #In Build
   3581 #Line # adds one Contour containing connected lines ##
   3582 #Populate
   3583 
   3584 #Example
   3585 void draw(SkCanvas* canvas) {
   3586     SkPaint paint;
   3587     paint.setStrokeWidth(15);
   3588     paint.setStrokeCap(SkPaint::kRound_Cap);
   3589     const SkPoint points[] = {{20, 20}, {70, 20}, {40, 90}};
   3590     for (bool close : { false, true } ) {
   3591         SkPath path;
   3592         path.addPoly(points, SK_ARRAY_COUNT(points), close);
   3593         for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style,
   3594                 SkPaint::kStrokeAndFill_Style} ) {
   3595             paint.setStyle(style);
   3596             canvas->drawPath(path, paint);
   3597             canvas->translate(85, 0);
   3598         }
   3599         canvas->translate(-255, 128);
   3600     }
   3601 }
   3602 ##
   3603 
   3604 #SeeAlso SkCanvas::drawPoints
   3605 
   3606 ##
   3607 
   3608 #Method SkPath& addPoly(const std::initializer_list<SkPoint>& list, bool close)
   3609 #Populate
   3610 
   3611 #Example
   3612 void draw(SkCanvas* canvas) {
   3613     SkPaint paint;
   3614     paint.setStrokeWidth(15);
   3615     paint.setStrokeCap(SkPaint::kRound_Cap);
   3616     for (bool close : { false, true } ) {
   3617         SkPath path;
   3618         path.addPoly({{20, 20}, {70, 20}, {40, 90}}, close);
   3619         for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style,
   3620                 SkPaint::kStrokeAndFill_Style} ) {
   3621             paint.setStyle(style);
   3622             canvas->drawPath(path, paint);
   3623             canvas->translate(85, 0);
   3624         }
   3625         canvas->translate(-255, 128);
   3626     }
   3627 }
   3628 ##
   3629 
   3630 #SeeAlso SkCanvas::drawPoints
   3631 
   3632 ##
   3633 
   3634 # ------------------------------------------------------------------------------
   3635 
   3636 #Enum AddPathMode
   3637 #Line # sets addPath options ##
   3638 
   3639 #Code
   3640     enum AddPathMode {
   3641         kAppend_AddPathMode,
   3642         kExtend_AddPathMode,
   3643     };
   3644 ##
   3645 
   3646 AddPathMode chooses how addPath appends. Adding one Path to another can extend
   3647 the last Contour or start a new Contour.
   3648 
   3649 #Const kAppend_AddPathMode
   3650 #Line # appended to destination unaltered ##
   3651     Path Verbs, Points, and Conic_Weights are appended to destination unaltered.
   3652     Since Path Verb_Array begins with kMove_Verb if src is not empty, this
   3653     starts a new Contour.
   3654 ##
   3655 #Const kExtend_AddPathMode
   3656 #Line # add line if prior Contour is not closed ##
   3657     If destination is closed or empty, start a new Contour. If destination
   3658     is not empty, add Line from Last_Point to added Path first Point. Skip added
   3659     Path initial kMove_Verb, then append remaining Verbs, Points, and Conic_Weights.
   3660 ##
   3661 
   3662 #Example
   3663 #Description
   3664 test is built from path, open on the top row, and closed on the bottom row.
   3665 The left column uses kAppend_AddPathMode; the right uses kExtend_AddPathMode.
   3666 The top right composition is made up of one contour; the other three have two.
   3667 ##
   3668 #Height 180
   3669     SkPath path, path2;
   3670     path.moveTo(20, 20);
   3671     path.lineTo(20, 40);
   3672     path.lineTo(40, 20);
   3673     path2.moveTo(60, 60);
   3674     path2.lineTo(80, 60);
   3675     path2.lineTo(80, 40);
   3676     SkPaint paint;
   3677     paint.setStyle(SkPaint::kStroke_Style);
   3678     for (int i = 0; i < 2; i++) {
   3679         for (auto addPathMode : { SkPath::kAppend_AddPathMode, SkPath::kExtend_AddPathMode } ) {
   3680             SkPath test(path);
   3681             test.addPath(path2, addPathMode);
   3682             canvas->drawPath(test, paint);
   3683             canvas->translate(100, 0);
   3684         }
   3685         canvas->translate(-200, 100);
   3686         path.close();
   3687     }
   3688 ##
   3689 
   3690 #SeeAlso addPath reverseAddPath
   3691 
   3692 ##
   3693 
   3694 # ------------------------------------------------------------------------------
   3695 
   3696 #Method SkPath& addPath(const SkPath& src, SkScalar dx, SkScalar dy,
   3697                  AddPathMode mode = kAppend_AddPathMode)
   3698 #In Build
   3699 #Line # adds contents of Path ##
   3700 #Populate
   3701 
   3702 #Example
   3703 #Height 180
   3704     SkPaint paint;
   3705     paint.setTextSize(128);
   3706     paint.setFakeBoldText(true);
   3707     SkPath dest, text;
   3708     paint.getTextPath("O", 1, 50, 120, &text);
   3709     for (int i = 0; i < 3; i++) {
   3710         dest.addPath(text, i * 20, i * 20);
   3711     }
   3712     Simplify(dest, &dest);
   3713     paint.setStyle(SkPaint::kStroke_Style);
   3714     paint.setStrokeWidth(3);
   3715     canvas->drawPath(dest, paint);
   3716 ##
   3717 
   3718 #SeeAlso AddPathMode offset reverseAddPath
   3719 
   3720 ##
   3721 
   3722 # ------------------------------------------------------------------------------
   3723 
   3724 #Method SkPath& addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode)
   3725 #Populate
   3726 
   3727 #Example
   3728 #Height 80
   3729     SkPaint paint;
   3730     paint.setStyle(SkPaint::kStroke_Style);
   3731     SkPath dest, path;
   3732     path.addOval({-80, 20, 0, 60}, SkPath::kCW_Direction, 1);
   3733     for (int i = 0; i < 2; i++) {
   3734         dest.addPath(path, SkPath::kExtend_AddPathMode);
   3735         dest.offset(100, 0);
   3736     }
   3737     canvas->drawPath(dest, paint);
   3738 ##
   3739 
   3740 #SeeAlso AddPathMode reverseAddPath
   3741 
   3742 ##
   3743 
   3744 # ------------------------------------------------------------------------------
   3745 
   3746 #Method SkPath& addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode)
   3747 #Populate
   3748 
   3749 #Example
   3750 #Height 160
   3751     SkPaint paint;
   3752     paint.setStyle(SkPaint::kStroke_Style);
   3753     SkPath dest, path;
   3754     path.addOval({20, 20, 200, 120}, SkPath::kCW_Direction, 1);
   3755     for (int i = 0; i < 6; i++) {
   3756         SkMatrix matrix;
   3757         matrix.reset();
   3758         matrix.setPerspX(i / 400.f);
   3759         dest.addPath(path, matrix);
   3760     }
   3761     canvas->drawPath(dest, paint);
   3762 ##
   3763 
   3764 #SeeAlso AddPathMode transform offset reverseAddPath
   3765 
   3766 ##
   3767 
   3768 # ------------------------------------------------------------------------------
   3769 
   3770 #Method SkPath& reverseAddPath(const SkPath& src)
   3771 #In Build
   3772 #Line # adds contents of Path back to front ##
   3773 #Populate
   3774 
   3775 #Example
   3776 #Height 200
   3777     SkPath path;
   3778     path.moveTo(20, 20);
   3779     path.lineTo(20, 40);
   3780     path.lineTo(40, 20);
   3781     SkPaint paint;
   3782     paint.setStyle(SkPaint::kStroke_Style);
   3783     for (int i = 0; i < 2; i++) {
   3784         SkPath path2;
   3785         path2.moveTo(60, 60);
   3786         path2.lineTo(80, 60);
   3787         path2.lineTo(80, 40);
   3788         for (int j = 0; j < 2; j++) {
   3789             SkPath test(path);
   3790             test.reverseAddPath(path2);
   3791             canvas->drawPath(test, paint);
   3792             canvas->translate(100, 0);
   3793             path2.close();
   3794         }
   3795         canvas->translate(-200, 100);
   3796         path.close();
   3797     }
   3798 ##
   3799 
   3800 #SeeAlso AddPathMode transform offset addPath
   3801 
   3802 ##
   3803 
   3804 # ------------------------------------------------------------------------------
   3805 
   3806 #Method void offset(SkScalar dx, SkScalar dy, SkPath* dst) const
   3807 #In Transform
   3808 #Line # translates Point_Array ##
   3809 #Populate
   3810 
   3811 #Example
   3812 #Height 60
   3813     SkPath pattern;
   3814     pattern.moveTo(20, 20);
   3815     pattern.lineTo(20, 40);
   3816     pattern.lineTo(40, 20);
   3817     SkPaint paint;
   3818     paint.setStyle(SkPaint::kStroke_Style);
   3819     for (int i = 0; i < 10; i++) {
   3820         SkPath path;
   3821         pattern.offset(20 * i, 0, &path);
   3822         canvas->drawPath(path, paint);
   3823     }
   3824 ##
   3825 
   3826 #SeeAlso addPath transform
   3827 
   3828 ##
   3829 
   3830 # ------------------------------------------------------------------------------
   3831 #Subtopic Transform
   3832 #Line # modify all points ##
   3833 ##
   3834 
   3835 #Method void offset(SkScalar dx, SkScalar dy)
   3836 #In Transform
   3837 #Populate
   3838 
   3839 #Example
   3840 #Height 60
   3841     SkPath path;
   3842     path.moveTo(20, 20);
   3843     path.lineTo(20, 40);
   3844     path.lineTo(40, 20);
   3845     SkPaint paint;
   3846     paint.setStyle(SkPaint::kStroke_Style);
   3847     for (int i = 0; i < 10; i++) {
   3848         canvas->drawPath(path, paint);
   3849         path.offset(20, 0);
   3850     }
   3851 ##
   3852 
   3853 #SeeAlso addPath transform SkCanvas::translate()
   3854 
   3855 ##
   3856 
   3857 # ------------------------------------------------------------------------------
   3858 
   3859 #Method void transform(const SkMatrix& matrix, SkPath* dst) const
   3860 #In Transform
   3861 #Line # applies Matrix to Point_Array and Weights ##
   3862 #Populate
   3863 
   3864 #Example
   3865 #Height 200
   3866     SkPath pattern;
   3867     pattern.moveTo(100, 100);
   3868     pattern.lineTo(100, 20);
   3869     pattern.lineTo(20, 100);
   3870     SkPaint paint;
   3871     paint.setStyle(SkPaint::kStroke_Style);
   3872     for (int i = 0; i < 10; i++) {
   3873         SkPath path;
   3874         SkMatrix matrix;
   3875         matrix.setRotate(36 * i, 100, 100);
   3876         pattern.transform(matrix, &path);
   3877         canvas->drawPath(path, paint);
   3878     }
   3879 ##
   3880 
   3881 #SeeAlso addPath offset SkCanvas::concat() SkMatrix
   3882 
   3883 ##
   3884 
   3885 # ------------------------------------------------------------------------------
   3886 
   3887 #Method void transform(const SkMatrix& matrix)
   3888 #Populate
   3889 
   3890 #Example
   3891 #Height 200
   3892     SkPath path;
   3893     path.moveTo(100, 100);
   3894     path.quadTo(100, 20, 20, 100);
   3895     SkPaint paint;
   3896     paint.setStyle(SkPaint::kStroke_Style);
   3897     for (int i = 0; i < 10; i++) {
   3898         SkMatrix matrix;
   3899         matrix.setRotate(36, 100, 100);
   3900         path.transform(matrix);
   3901         canvas->drawPath(path, paint);
   3902     }
   3903 ##
   3904 
   3905 #SeeAlso addPath offset SkCanvas::concat() SkMatrix
   3906 
   3907 ##
   3908 
   3909 # ------------------------------------------------------------------------------
   3910 
   3911 #Subtopic Last_Point
   3912 #Line # final Point in Contour ##
   3913 
   3914 Path is defined cumulatively, often by adding a segment to the end of last
   3915 Contour. Last_Point of Contour is shared as first Point of added Line or Curve.
   3916 Last_Point can be read and written directly with getLastPt and setLastPt.
   3917 
   3918 #Method bool getLastPt(SkPoint* lastPt) const
   3919 #In Property
   3920 #In Last_Point
   3921 #Line # returns Last_Point ##
   3922 #Populate
   3923 
   3924 #Example
   3925         SkPath path;
   3926         path.moveTo(100, 100);
   3927         path.quadTo(100, 20, 20, 100);
   3928         SkMatrix matrix;
   3929         matrix.setRotate(36, 100, 100);
   3930         path.transform(matrix);
   3931         SkPoint last;
   3932         path.getLastPt(&last);
   3933         SkDebugf("last point: %g, %g\n", last.fX, last.fY);
   3934     #StdOut
   3935     last point: 35.2786, 52.9772
   3936     ##
   3937     ##
   3938 
   3939     #SeeAlso setLastPt
   3940 
   3941 ##
   3942 
   3943 #Method void setLastPt(SkScalar x, SkScalar y)
   3944 #In Utility
   3945 #In Last_Point
   3946 #Line # replaces Last_Point ##
   3947 #Populate
   3948 
   3949 #Example
   3950     #Height 128
   3951         SkPaint paint;
   3952         paint.setTextSize(128);
   3953         SkPath path;
   3954         paint.getTextPath("@", 1, 60, 100, &path);
   3955         path.setLastPt(20, 120);
   3956         canvas->drawPath(path, paint);
   3957     ##
   3958 
   3959     #SeeAlso getLastPt
   3960 
   3961 ##
   3962 
   3963 #Method void setLastPt(const SkPoint& p)
   3964 #Populate
   3965 
   3966 #Example
   3967     #Height 128
   3968         SkPaint paint;
   3969         paint.setTextSize(128);
   3970         SkPath path, path2;
   3971         paint.getTextPath("A", 1, 60, 100, &path);
   3972         paint.getTextPath("Z", 1, 60, 100, &path2);
   3973         SkPoint pt, pt2;
   3974         path.getLastPt(&pt);
   3975         path2.getLastPt(&pt2);
   3976         path.setLastPt(pt2);
   3977         path2.setLastPt(pt);
   3978         canvas->drawPath(path, paint);
   3979         canvas->drawPath(path2, paint);
   3980     ##
   3981 
   3982     #SeeAlso getLastPt
   3983 
   3984 ##
   3985 
   3986 #Subtopic Last_Point ##
   3987 
   3988 # ------------------------------------------------------------------------------
   3989 
   3990 #Enum SegmentMask
   3991 #Line # returns Verb types in Path ##
   3992 
   3993 #Code
   3994     enum SegmentMask {
   3995         kLine_SegmentMask = 1 << 0,
   3996         kQuad_SegmentMask = 1 << 1,
   3997         kConic_SegmentMask = 1 << 2,
   3998         kCubic_SegmentMask = 1 << 3,
   3999     };
   4000 ##
   4001 
   4002 SegmentMask constants correspond to each drawing Verb type in Path; for
   4003 instance, if Path only contains Lines, only the kLine_SegmentMask bit is set.
   4004 
   4005 #Bug 6785
   4006 #Const kLine_SegmentMask 1
   4007 #Line # contains one or more Lines ##
   4008 Set if Verb_Array contains kLine_Verb.
   4009 ##
   4010 #Const kQuad_SegmentMask 2
   4011 #Line # contains one or more Quads ##
   4012 Set if Verb_Array contains kQuad_Verb. Note that conicTo may add a Quad.
   4013 ##
   4014 #Const kConic_SegmentMask 4
   4015 #Line # contains one or more Conics ##
   4016 Set if Verb_Array contains kConic_Verb.
   4017 ##
   4018 #Const kCubic_SegmentMask 8
   4019 #Line # contains one or more Cubics ##
   4020 Set if Verb_Array contains kCubic_Verb.
   4021 ##
   4022 
   4023 #Example
   4024 #Description
   4025 When conicTo has a weight of one, Quad is added to Path.
   4026 ##
   4027     SkPath path;
   4028     path.conicTo(10, 10, 20, 30, 1);
   4029     SkDebugf("Path kConic_SegmentMask is %s\n", path.getSegmentMasks() &
   4030           SkPath::kConic_SegmentMask ? "set" : "clear");
   4031     SkDebugf("Path kQuad_SegmentMask is %s\n", path.getSegmentMasks() &
   4032           SkPath::kQuad_SegmentMask ? "set" : "clear");
   4033 #StdOut
   4034 Path kConic_SegmentMask is clear
   4035 Path kQuad_SegmentMask is set
   4036 ##
   4037 ##
   4038 
   4039 #SeeAlso getSegmentMasks Verb
   4040 
   4041 ##
   4042 
   4043 # ------------------------------------------------------------------------------
   4044 
   4045 #Method uint32_t getSegmentMasks() const
   4046 #In Utility
   4047 #In Property
   4048 #Line # returns types in Verb_Array ##
   4049 #Populate
   4050 
   4051 #Example
   4052 SkPath path;
   4053 path.quadTo(20, 30, 40, 50);
   4054 path.close();
   4055 const char* masks[] = { "line", "quad", "conic", "cubic" };
   4056 int index = 0;
   4057 for (auto mask : { SkPath::kLine_SegmentMask, SkPath::kQuad_SegmentMask,
   4058         SkPath::kConic_SegmentMask, SkPath::kCubic_SegmentMask } ) {
   4059     if (mask & path.getSegmentMasks()) {
   4060        SkDebugf("mask %s set\n", masks[index]);
   4061     }
   4062     ++index;
   4063 }
   4064 #StdOut
   4065 mask quad set
   4066 ##
   4067 ##
   4068 
   4069 #SeeAlso getSegmentMasks Verb
   4070 
   4071 ##
   4072 
   4073 # ------------------------------------------------------------------------------
   4074 
   4075 #Method bool contains(SkScalar x, SkScalar y) const
   4076 #In Property
   4077 #Line # returns if Point is in fill area ##
   4078 Returns true if the point (x, y) is contained by Path, taking into
   4079 account FillType.
   4080 
   4081 #Table
   4082 #Legend
   4083 # FillType                 # contains() returns true if Point is enclosed by ##
   4084 ##
   4085 # kWinding_FillType        # a non-zero sum of Contour Directions. ##
   4086 # kEvenOdd_FillType        # an odd number of Contours.            ##
   4087 # kInverseWinding_FillType # a zero sum of Contour Directions.     ##
   4088 # kInverseEvenOdd_FillType # and even number of Contours.          ##
   4089 ##
   4090 
   4091 #Param x  x-axis value of containment test ##
   4092 #Param y  y-axis value of containment test ##
   4093 
   4094 #Return  true if Point is in Path ##
   4095 
   4096 #Example
   4097 SkPath path;
   4098 SkPaint paint;
   4099 paint.setTextSize(256);
   4100 paint.getTextPath("&", 1, 30, 220, &path);
   4101 for (int y = 2; y < 256; y += 9) {
   4102    for (int x = 2; x < 256; x += 9) {
   4103        int coverage = 0;
   4104        for (int iy = -4; iy <= 4; iy += 2) {
   4105            for (int ix = -4; ix <= 4; ix += 2) {
   4106                coverage += path.contains(x + ix, y + iy);
   4107            }
   4108        }
   4109        paint.setColor(SkColorSetARGB(0x5f, 0xff * coverage / 25, 0, 0xff * (25 - coverage) / 25));
   4110        canvas->drawCircle(x, y, 8, paint);
   4111    }
   4112 }
   4113 ##
   4114 
   4115 #SeeAlso conservativelyContainsRect Fill_Type Op
   4116 
   4117 ##
   4118 
   4119 # ------------------------------------------------------------------------------
   4120 
   4121 #Method void dump(SkWStream* stream, bool forceClose, bool dumpAsHex) const
   4122 #In Utility
   4123 #Line # sends text representation to stream ##
   4124 #Populate
   4125 
   4126 #Example
   4127    SkPath path;
   4128    path.quadTo(20, 30, 40, 50);
   4129    for (bool forceClose : { false, true } ) {
   4130        for (bool dumpAsHex : { false, true } ) {
   4131            path.dump(nullptr, forceClose, dumpAsHex);
   4132            SkDebugf("\n");
   4133        }
   4134    }
   4135 #StdOut
   4136 path.setFillType(SkPath::kWinding_FillType);
   4137 path.moveTo(0, 0);
   4138 path.quadTo(20, 30, 40, 50);
   4139 
   4140 path.setFillType(SkPath::kWinding_FillType);
   4141 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
   4142 path.quadTo(SkBits2Float(0x41a00000), SkBits2Float(0x41f00000), SkBits2Float(0x42200000), SkBits2Float(0x42480000));  // 20, 30, 40, 50
   4143 
   4144 path.setFillType(SkPath::kWinding_FillType);
   4145 path.moveTo(0, 0);
   4146 path.quadTo(20, 30, 40, 50);
   4147 path.lineTo(0, 0);
   4148 path.close();
   4149 
   4150 path.setFillType(SkPath::kWinding_FillType);
   4151 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
   4152 path.quadTo(SkBits2Float(0x41a00000), SkBits2Float(0x41f00000), SkBits2Float(0x42200000), SkBits2Float(0x42480000));  // 20, 30, 40, 50
   4153 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
   4154 path.close();
   4155 ##
   4156 ##
   4157 
   4158 #SeeAlso dumpHex SkRect::dump() SkRRect::dump() SkPathMeasure::dump()
   4159 
   4160 ##
   4161 
   4162 # ------------------------------------------------------------------------------
   4163 
   4164 #Method void dump() const
   4165 #Populate
   4166 
   4167 #Example
   4168 SkPath path, copy;
   4169 path.lineTo(6.f / 7, 2.f / 3);
   4170 path.dump();
   4171 copy.setFillType(SkPath::kWinding_FillType);
   4172 copy.moveTo(0, 0);
   4173 copy.lineTo(0.857143f, 0.666667f);
   4174 SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
   4175 #StdOut
   4176 path.setFillType(SkPath::kWinding_FillType);
   4177 path.moveTo(0, 0);
   4178 path.lineTo(0.857143f, 0.666667f);
   4179 path is not equal to copy
   4180 ##
   4181 ##
   4182 
   4183 #SeeAlso dumpHex SkRect::dump() SkRRect::dump() SkPathMeasure::dump() writeToMemory
   4184 
   4185 ##
   4186 
   4187 # ------------------------------------------------------------------------------
   4188 
   4189 #Method void dumpHex() const
   4190 #In Utility
   4191 #Line # sends text representation using hexadecimal to standard output ##
   4192 Writes text representation of Path to standard output. The representation may be
   4193 directly compiled as C++ code. Floating point values are written
   4194 in hexadecimal to preserve their exact bit pattern. The output reconstructs the
   4195 original Path.
   4196 
   4197 Use instead of dump() when submitting
   4198 #A bug reports against Skia # https://bug.skia.org ##
   4199 .
   4200 
   4201 #Example
   4202 SkPath path, copy;
   4203 path.lineTo(6.f / 7, 2.f / 3);
   4204 path.dumpHex();
   4205 copy.setFillType(SkPath::kWinding_FillType);
   4206 copy.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
   4207 copy.lineTo(SkBits2Float(0x3f5b6db7), SkBits2Float(0x3f2aaaab));  // 0.857143f, 0.666667f
   4208 SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
   4209 #StdOut
   4210 path.setFillType(SkPath::kWinding_FillType);
   4211 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
   4212 path.lineTo(SkBits2Float(0x3f5b6db7), SkBits2Float(0x3f2aaaab));  // 0.857143f, 0.666667f
   4213 path is equal to copy
   4214 ##
   4215 ##
   4216 
   4217 #SeeAlso dump SkRect::dumpHex SkRRect::dumpHex writeToMemory
   4218 
   4219 ##
   4220 
   4221 # ------------------------------------------------------------------------------
   4222 
   4223 #Method size_t writeToMemory(void* buffer) const
   4224 #In Utility
   4225 #Line # copies data to buffer ##
   4226 #Populate
   4227 
   4228 #Example
   4229 void draw(SkCanvas* canvas) {
   4230     SkPath path, copy;
   4231     path.lineTo(6.f / 7, 2.f / 3);
   4232     size_t size = path.writeToMemory(nullptr);
   4233     SkTDArray<char> storage;
   4234     storage.setCount(size);
   4235     path.writeToMemory(storage.begin());
   4236     copy.readFromMemory(storage.begin(), size);
   4237     SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
   4238 }
   4239 #StdOut
   4240 path is equal to copy
   4241 ##
   4242 ##
   4243 
   4244 #SeeAlso serialize readFromMemory dump dumpHex
   4245 
   4246 ##
   4247 
   4248 #Method sk_sp<SkData> serialize() const
   4249 #In Utility
   4250 #Line # copies data to buffer ##
   4251 #Populate
   4252 
   4253 #Example
   4254 void draw(SkCanvas* canvas) {
   4255     SkPath path, copy;
   4256     path.lineTo(6.f / 7, 2.f / 3);
   4257     sk_sp<SkData> data = path.serialize();
   4258     copy.readFromMemory(data->data(), data->size());
   4259     SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
   4260 }
   4261 #StdOut
   4262 path is equal to copy
   4263 ##
   4264 ##
   4265 
   4266 #SeeAlso writeToMemory readFromMemory dump dumpHex
   4267 ##
   4268 
   4269 # ------------------------------------------------------------------------------
   4270 
   4271 #Method size_t readFromMemory(const void* buffer, size_t length)
   4272 #In Utility
   4273 #Line # initializes from buffer ##
   4274 #Populate
   4275 
   4276 #Example
   4277 void draw(SkCanvas* canvas) {
   4278     SkPath path, copy;
   4279     path.lineTo(6.f / 7, 2.f / 3);
   4280     size_t size = path.writeToMemory(nullptr);
   4281     SkTDArray<char> storage;
   4282     storage.setCount(size);
   4283     path.writeToMemory(storage.begin());
   4284     size_t wrongSize = size - 4;
   4285     size_t bytesRead = copy.readFromMemory(storage.begin(), wrongSize);
   4286     SkDebugf("length = %u; returned by readFromMemory = %u\n", wrongSize, bytesRead);
   4287     size_t largerSize = size + 4;
   4288     bytesRead = copy.readFromMemory(storage.begin(), largerSize);
   4289     SkDebugf("length = %u; returned by readFromMemory = %u\n", largerSize, bytesRead);
   4290 }
   4291 #StdOut
   4292 length = 32; returned by readFromMemory = 0
   4293 length = 40; returned by readFromMemory = 36
   4294 ##
   4295 ##
   4296 
   4297 #SeeAlso writeToMemory
   4298 
   4299 ##
   4300 
   4301 # ------------------------------------------------------------------------------
   4302 #Subtopic Generation_ID
   4303 #Alias Generation_IDs ##
   4304 #Line # value reflecting contents change ##
   4305 Generation_ID provides a quick way to check if Verb_Array, Point_Array, or
   4306 Conic_Weight has changed. Generation_ID is not a hash; identical Paths will
   4307 not necessarily have matching Generation_IDs.
   4308 
   4309 Empty Paths have a Generation_ID of one.
   4310 
   4311 #Method uint32_t getGenerationID() const
   4312 
   4313 #In Generation_ID
   4314 #Line # returns unique ID ##
   4315 #Populate
   4316 
   4317 #Example
   4318 SkPath path;
   4319 SkDebugf("empty genID = %u\n", path.getGenerationID());
   4320 path.lineTo(1, 2);
   4321 SkDebugf("1st lineTo genID = %u\n", path.getGenerationID());
   4322 path.rewind();
   4323 SkDebugf("empty genID = %u\n", path.getGenerationID());
   4324 path.lineTo(1, 2);
   4325 SkDebugf("2nd lineTo genID = %u\n", path.getGenerationID());
   4326 #StdOut
   4327 empty genID = 1
   4328 1st lineTo genID = 2
   4329 empty genID = 1
   4330 2nd lineTo genID = 3
   4331 ##
   4332 ##
   4333 
   4334 #SeeAlso operator==(const SkPath& a, const SkPath& b)
   4335 
   4336 ##
   4337 
   4338 #Subtopic ##
   4339 
   4340 # ------------------------------------------------------------------------------
   4341 
   4342 #Method bool isValid() const
   4343 #In Property
   4344 #In Utility
   4345 #Line # returns if data is internally consistent ##
   4346 #Populate
   4347 
   4348 #NoExample
   4349     ##
   4350 
   4351 ##
   4352 
   4353 # ------------------------------------------------------------------------------
   4354 
   4355 #Class Iter
   4356 #Line # data iterator ##
   4357 
   4358 #Code
   4359 #Populate
   4360 ##
   4361 
   4362 Iterates through Verb_Array, and associated Point_Array and Conic_Weight.
   4363 Provides options to treat open Contours as closed, and to ignore
   4364 degenerate data.
   4365 
   4366 #Example
   4367 #Height 128
   4368 #Description
   4369 Ignoring the actual Verbs and replacing them with Quads rounds the
   4370 path of the glyph.
   4371 ##
   4372 void draw(SkCanvas* canvas) {
   4373     SkPaint paint;
   4374     paint.setAntiAlias(true);
   4375     paint.setTextSize(256);
   4376     SkPath asterisk, path;
   4377     paint.getTextPath("*", 1, 50, 192, &asterisk);
   4378     SkPath::Iter iter(asterisk, true);
   4379     SkPoint start[4], pts[4];
   4380     iter.next(start);  // skip moveTo
   4381     iter.next(start);  // first quadTo
   4382     path.moveTo((start[0] + start[1]) * 0.5f);
   4383     while (SkPath::kClose_Verb != iter.next(pts)) {
   4384         path.quadTo(pts[0], (pts[0] + pts[1]) * 0.5f);
   4385     }
   4386     path.quadTo(start[0], (start[0] + start[1]) * 0.5f);
   4387     canvas->drawPath(path, paint);
   4388 }
   4389 ##
   4390 
   4391 #SeeAlso RawIter
   4392 
   4393 #Method Iter()
   4394 #Line # constructs Path iterator ##
   4395 #Populate
   4396 
   4397 #Example
   4398 void draw(SkCanvas* canvas) {
   4399     SkPath::Iter iter;
   4400     SkPoint points[4];
   4401     SkDebugf("iter is " "%s" "done\n", SkPath::kDone_Verb == iter.next(points) ? "" : "not ");
   4402     SkPath path;
   4403     iter.setPath(path, false);
   4404     SkDebugf("iter is " "%s" "done\n", SkPath::kDone_Verb == iter.next(points) ? "" : "not ");
   4405 }
   4406 #StdOut
   4407 iter is done
   4408 iter is done
   4409 ##
   4410 ##
   4411 
   4412 #SeeAlso setPath
   4413 
   4414 ##
   4415 
   4416 #Method Iter(const SkPath& path, bool forceClose)
   4417 #Line # constructs Path iterator ##
   4418 #Populate
   4419 
   4420 #Example
   4421 void draw(SkCanvas* canvas) {
   4422     auto debugster = [](const char* prefix, SkPath::Iter& iter) -> void {
   4423         SkDebugf("%s:\n", prefix);
   4424         const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
   4425         const int pointCount[] = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  };
   4426         SkPath::Verb verb;
   4427         do {
   4428            SkPoint points[4];
   4429            verb = iter.next(points);
   4430            SkDebugf("k%s_Verb ", verbStr[(int) verb]);
   4431            for (int i = 0; i < pointCount[(int) verb]; ++i) {
   4432                 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
   4433            }
   4434            if (SkPath::kConic_Verb == verb) {
   4435                SkDebugf("weight = %g", iter.conicWeight());
   4436            }
   4437            SkDebugf("\n");
   4438         } while (SkPath::kDone_Verb != verb);
   4439         SkDebugf("\n");
   4440     };
   4441 
   4442     SkPath path;
   4443     path.quadTo(10, 20, 30, 40);
   4444     SkPath::Iter openIter(path, false);
   4445     debugster("open", openIter);
   4446     SkPath::Iter closedIter(path, true);
   4447     debugster("closed", closedIter);
   4448 }
   4449 #StdOut
   4450 open:
   4451 kMove_Verb {0, 0},
   4452 kQuad_Verb {0, 0}, {10, 20}, {30, 40},
   4453 kDone_Verb
   4454 
   4455 closed:
   4456 kMove_Verb {0, 0},
   4457 kQuad_Verb {0, 0}, {10, 20}, {30, 40},
   4458 kLine_Verb {30, 40}, {0, 0},
   4459 kClose_Verb {0, 0},
   4460 kDone_Verb
   4461 ##
   4462 ##
   4463 
   4464 #SeeAlso setPath
   4465 
   4466 ##
   4467 
   4468 #Method void setPath(const SkPath& path, bool forceClose)
   4469 #Line # resets Iter to Path ##
   4470 #Populate
   4471 
   4472 #Example
   4473 void draw(SkCanvas* canvas) {
   4474     auto debugster = [](const char* prefix, SkPath::Iter& iter) -> void {
   4475         SkDebugf("%s:\n", prefix);
   4476         const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
   4477         const int pointCount[] = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  };
   4478         SkPath::Verb verb;
   4479         do {
   4480            SkPoint points[4];
   4481            verb = iter.next(points);
   4482            SkDebugf("k%s_Verb ", verbStr[(int) verb]);
   4483            for (int i = 0; i < pointCount[(int) verb]; ++i) {
   4484                 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
   4485            }
   4486            if (SkPath::kConic_Verb == verb) {
   4487                SkDebugf("weight = %g", iter.conicWeight());
   4488            }
   4489            SkDebugf("\n");
   4490         } while (SkPath::kDone_Verb != verb);
   4491         SkDebugf("\n");
   4492     };
   4493 
   4494     SkPath path;
   4495     path.quadTo(10, 20, 30, 40);
   4496     SkPath::Iter iter(path, false);
   4497     debugster("quad open", iter);
   4498     SkPath path2;
   4499     path2.conicTo(1, 2, 3, 4, .5f);
   4500     iter.setPath(path2, true);
   4501     debugster("conic closed", iter);
   4502 }
   4503 #StdOut
   4504 quad open:
   4505 kMove_Verb {0, 0},
   4506 kQuad_Verb {0, 0}, {10, 20}, {30, 40},
   4507 kDone_Verb
   4508 
   4509 conic closed:
   4510 kMove_Verb {0, 0},
   4511 kConic_Verb {0, 0}, {1, 2}, {3, 4}, weight = 0.5
   4512 kLine_Verb {3, 4}, {0, 0},
   4513 kClose_Verb {0, 0},
   4514 kDone_Verb
   4515 ##
   4516 ##
   4517 
   4518 #SeeAlso Iter(const SkPath& path, bool forceClose)
   4519 
   4520 ##
   4521 
   4522 #Method Verb next(SkPoint pts[4], bool doConsumeDegenerates = true, bool exact = false)
   4523 #Line # returns next Verb ##
   4524 #Populate
   4525 
   4526 #Example
   4527 #Description
   4528 skip degenerate skips the first in a kMove_Verb pair, the kMove_Verb
   4529 followed by the kClose_Verb, the zero length Line and the very small Line.
   4530 
   4531 skip degenerate if exact skips the same as skip degenerate, but shows
   4532 the very small Line.
   4533 
   4534 skip none shows all of the Verbs and Points in Path.
   4535 ##
   4536 void draw(SkCanvas* canvas) {
   4537     auto debugster = [](const char* prefix, const SkPath& path, bool degen, bool exact) -> void {
   4538         SkPath::Iter iter(path, false);
   4539         SkDebugf("%s:\n", prefix);
   4540         const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
   4541         const int pointCount[] = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  };
   4542         SkPath::Verb verb;
   4543         do {
   4544            SkPoint points[4];
   4545            verb = iter.next(points, degen, exact);
   4546            SkDebugf("k%s_Verb ", verbStr[(int) verb]);
   4547            for (int i = 0; i < pointCount[(int) verb]; ++i) {
   4548                 SkDebugf("{%1.8g, %1.8g}, ", points[i].fX, points[i].fY);
   4549            }
   4550            SkDebugf("\n");
   4551         } while (SkPath::kDone_Verb != verb);
   4552         SkDebugf("\n");
   4553     };
   4554 
   4555     SkPath path;
   4556     path.moveTo(10, 10);
   4557     path.moveTo(20, 20);
   4558     path.quadTo(10, 20, 30, 40);
   4559     path.moveTo(1, 1);
   4560     path.close();
   4561     path.moveTo(30, 30);
   4562     path.lineTo(30, 30);
   4563     path.moveTo(30, 30);
   4564     path.lineTo(30.00001f, 30);
   4565     debugster("skip degenerate", path, true, false);
   4566     debugster("skip degenerate if exact", path, true, true);
   4567     debugster("skip none", path, false, false);
   4568 }
   4569 #StdOut
   4570 skip degenerate:
   4571 kMove_Verb {20, 20},
   4572 kQuad_Verb {20, 20}, {10, 20}, {30, 40},
   4573 kDone_Verb
   4574 
   4575 skip degenerate if exact:
   4576 kMove_Verb {20, 20},
   4577 kQuad_Verb {20, 20}, {10, 20}, {30, 40},
   4578 kMove_Verb {30, 30},
   4579 kLine_Verb {30, 30}, {30.00001, 30},
   4580 kDone_Verb
   4581 
   4582 skip none:
   4583 kMove_Verb {10, 10},
   4584 kMove_Verb {20, 20},
   4585 kQuad_Verb {20, 20}, {10, 20}, {30, 40},
   4586 kMove_Verb {1, 1},
   4587 kClose_Verb {1, 1},
   4588 kMove_Verb {30, 30},
   4589 kLine_Verb {30, 30}, {30, 30},
   4590 kMove_Verb {30, 30},
   4591 kLine_Verb {30, 30}, {30.00001, 30},
   4592 kDone_Verb
   4593 ##
   4594 ##
   4595 
   4596 #SeeAlso Verb IsLineDegenerate IsCubicDegenerate IsQuadDegenerate
   4597 
   4598 ##
   4599 
   4600 #Method SkScalar conicWeight() const
   4601 #Line # returns Conic_Weight ##
   4602 #Populate
   4603 
   4604 #Example
   4605     void draw(SkCanvas* canvas) {
   4606        SkPath path;
   4607        path.conicTo(1, 2, 3, 4, .5f);
   4608        SkPath::Iter iter(path, false);
   4609        SkPoint p[4];
   4610        SkDebugf("first verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not ");
   4611        SkDebugf("next verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not ");
   4612        SkDebugf("conic points: {%g,%g}, {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY,
   4613                     p[2].fX, p[2].fY);
   4614        SkDebugf("conic weight: %g\n", iter.conicWeight());
   4615     }
   4616     #StdOut
   4617 first verb is move
   4618 next verb is conic
   4619 conic points: {0,0}, {1,2}, {3,4}
   4620 conic weight: 0.5
   4621     ##
   4622     ##
   4623 
   4624     #SeeAlso Conic_Weight
   4625 
   4626 ##
   4627 
   4628 #Method bool isCloseLine() const
   4629 #Line # returns if Line was generated by kClose_Verb ##
   4630 #Populate
   4631 
   4632 #Example
   4633 void draw(SkCanvas* canvas) {
   4634    SkPath path;
   4635    path.moveTo(6, 7);
   4636    path.conicTo(1, 2, 3, 4, .5f);
   4637    path.close();
   4638    SkPath::Iter iter(path, false);
   4639    SkPoint p[4];
   4640    SkDebugf("1st verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not ");
   4641    SkDebugf("moveTo point: {%g,%g}\n", p[0].fX, p[0].fY);
   4642    SkDebugf("2nd verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not ");
   4643    SkDebugf("3rd verb is " "%s" "line\n", SkPath::kLine_Verb == iter.next(p) ? "" : "not ");
   4644    SkDebugf("line points: {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY);
   4645    SkDebugf("line " "%s" "generated by close\n", iter.isCloseLine() ? "" : "not ");
   4646    SkDebugf("4th verb is " "%s" "close\n", SkPath::kClose_Verb == iter.next(p) ? "" : "not ");
   4647 }
   4648     #StdOut
   4649 1st verb is move
   4650 moveTo point: {6,7}
   4651 2nd verb is conic
   4652 3rd verb is line
   4653 line points: {3,4}, {6,7}
   4654 line generated by close
   4655 4th verb is close
   4656     ##
   4657     ##
   4658 
   4659     #SeeAlso close()
   4660 ##
   4661 
   4662 #Method bool isClosedContour() const
   4663 #Line # returns if Contour has kClose_Verb ##
   4664 #Populate
   4665 
   4666 #Example
   4667 void draw(SkCanvas* canvas) {
   4668    for (bool forceClose : { false, true } ) {
   4669        SkPath path;
   4670        path.conicTo(1, 2, 3, 4, .5f);
   4671        SkPath::Iter iter(path, forceClose);
   4672        SkDebugf("without close(), forceClose is %s: isClosedContour returns %s\n",
   4673            forceClose ? "true " : "false", iter.isClosedContour() ? "true" : "false");
   4674        path.close();
   4675        iter.setPath(path, forceClose);
   4676        SkDebugf("with close(),    forceClose is %s: isClosedContour returns %s\n",
   4677            forceClose ? "true " : "false", iter.isClosedContour() ? "true" : "false");
   4678     }
   4679 }
   4680 #StdOut
   4681 without close(), forceClose is false: isClosedContour returns false
   4682 with close(),    forceClose is false: isClosedContour returns true
   4683 without close(), forceClose is true : isClosedContour returns true
   4684 with close(),    forceClose is true : isClosedContour returns true
   4685 ##
   4686 ##
   4687 
   4688 #SeeAlso Iter(const SkPath& path, bool forceClose)
   4689 
   4690 ##
   4691 
   4692 #Class Iter ##
   4693 
   4694 #Class RawIter
   4695 #Line # raw data iterator ##
   4696 
   4697 #Code
   4698 #Populate
   4699 ##
   4700 
   4701 Iterates through Verb_Array, and associated Point_Array and Conic_Weight.
   4702 Verb_Array, Point_Array, and Conic_Weight are returned unaltered.
   4703 
   4704 
   4705     #Method RawIter()
   4706     #Line # constructs empty Path iterator ##
   4707 #Populate
   4708 
   4709 #NoExample
   4710         ##
   4711     ##
   4712 
   4713     #Method RawIter(const SkPath& path)
   4714     #Line # constructs with Path to iterate over ##
   4715 #Populate
   4716 
   4717 #NoExample
   4718         ##
   4719     ##
   4720 
   4721     #Method void setPath(const SkPath& path)
   4722     #Line # sets Path to iterate over ##
   4723 #Populate
   4724 
   4725 #NoExample
   4726         ##
   4727    ##
   4728 
   4729     #Method Verb next(SkPoint pts[4])
   4730     #Line # returns next Verb and associated Points ##
   4731 #Populate
   4732 
   4733 #Example
   4734         void draw(SkCanvas* canvas) {
   4735             SkPath path;
   4736             path.moveTo(50, 60);
   4737             path.quadTo(10, 20, 30, 40);
   4738             path.close();
   4739             path.lineTo(30, 30);
   4740             path.conicTo(1, 2, 3, 4, .5f);
   4741             path.cubicTo(-1, -2, -3, -4, -5, -6);
   4742             SkPath::RawIter iter(path);
   4743             const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
   4744             const int pointCount[] = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  };
   4745             SkPath::Verb verb;
   4746             do {
   4747                 SkPoint points[4];
   4748                 verb = iter.next(points);
   4749                 SkDebugf("k%s_Verb ", verbStr[(int) verb]);
   4750                 for (int i = 0; i < pointCount[(int) verb]; ++i) {
   4751                     SkDebugf("{%1.8g, %1.8g}, ", points[i].fX, points[i].fY);
   4752                 }
   4753                 if (SkPath::kConic_Verb == verb) {
   4754                     SkDebugf("weight = %g", iter.conicWeight());
   4755                 }
   4756                 SkDebugf("\n");
   4757             } while (SkPath::kDone_Verb != verb);
   4758         }
   4759     #StdOut
   4760         kMove_Verb {50, 60},
   4761         kQuad_Verb {50, 60}, {10, 20}, {30, 40},
   4762         kClose_Verb {50, 60},
   4763         kMove_Verb {50, 60},
   4764         kLine_Verb {50, 60}, {30, 30},
   4765         kConic_Verb {30, 30}, {1, 2}, {3, 4}, weight = 0.5
   4766         kCubic_Verb {3, 4}, {-1, -2}, {-3, -4}, {-5, -6},
   4767         kDone_Verb
   4768     ##
   4769     ##
   4770 
   4771     #SeeAlso peek()
   4772 
   4773     ##
   4774 
   4775     #Method Verb peek() const
   4776     #Line # returns next Verb ##
   4777     #Populate
   4778 
   4779         #Example
   4780             SkPath path;
   4781             path.quadTo(10, 20, 30, 40);
   4782             path.conicTo(1, 2, 3, 4, .5f);
   4783             path.cubicTo(1, 2, 3, 4, .5, 6);
   4784             SkPath::RawIter iter(path);
   4785             SkPath::Verb verb, peek = iter.peek();
   4786             const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
   4787             do {
   4788                 SkPoint points[4];
   4789                 verb = iter.next(points);
   4790                 SkDebugf("peek %s %c= verb %s\n", verbStr[peek], peek == verb ? '=' : '!', verbStr[verb]);
   4791                 peek = iter.peek();
   4792             } while (SkPath::kDone_Verb != verb);
   4793             SkDebugf("peek %s %c= verb %s\n", verbStr[peek], peek == verb ? '=' : '!', verbStr[verb]);
   4794             #StdOut
   4795                 #Volatile
   4796                 peek Move == verb Move
   4797                 peek Quad == verb Quad
   4798                 peek Conic == verb Conic
   4799                 peek Cubic == verb Cubic
   4800                 peek Done == verb Done
   4801                 peek Done == verb Done
   4802             ##
   4803         ##
   4804 
   4805         #Bug 6832
   4806         # StdOut is not really volatile, it just produces the wrong result.
   4807         # A simple fix changes the output of hairlines and needs to be
   4808         # investigated to see if the change is correct or not.
   4809         # see change 21340 (abandoned for now)
   4810 
   4811         #SeeAlso next
   4812 
   4813     ##
   4814 
   4815     #Method SkScalar conicWeight() const
   4816     #Line # returns Conic_Weight ##
   4817 #Populate
   4818 
   4819 #Example
   4820     void draw(SkCanvas* canvas) {
   4821        SkPath path;
   4822        path.conicTo(1, 2, 3, 4, .5f);
   4823        SkPath::RawIter iter(path);
   4824        SkPoint p[4];
   4825        SkDebugf("first verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not ");
   4826        SkDebugf("next verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not ");
   4827        SkDebugf("conic points: {%g,%g}, {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY,
   4828                     p[2].fX, p[2].fY);
   4829        SkDebugf("conic weight: %g\n", iter.conicWeight());
   4830     }
   4831     #StdOut
   4832         first verb is move
   4833         next verb is conic
   4834         conic points: {0,0}, {1,2}, {3,4}
   4835         conic weight: 0.5
   4836     ##
   4837     ##
   4838 
   4839     #SeeAlso Conic_Weight
   4840 
   4841     ##
   4842 
   4843 #Class RawIter ##
   4844 
   4845 #Class SkPath ##
   4846 
   4847 #Topic Path ##
   4848