| #Topic Path | 
 | #Alias Path_Reference | 
 | #Alias Paths | 
 |  | 
 | Path contains Lines and Curves which can be stroked or filled. Contour is  | 
 | composed of a series of connected Lines and Curves. Path may contain zero,  | 
 | one, or more Contours. | 
 | Each Line and Curve are described by Verb, Points, and optional Conic_Weight. | 
 |  | 
 | Each pair of connected Lines and Curves share common Point; for instance, Path | 
 | containing two connected Lines are described the Verb sequence:  | 
 | SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb; and a Point sequence | 
 | with three entries, sharing | 
 | the middle entry as the end of the first Line and the start of the second Line. | 
 |  | 
 | Path components Arc, Rect, Round_Rect, Circle, and Oval are composed of | 
 | Lines and Curves with as many Verbs and Points required | 
 | for an exact description. Once added to Path, these components may lose their | 
 | identity; although Path can be inspected to determine if it describes a single | 
 | Rect, Oval, Round_Rect, and so on. | 
 |  | 
 | #Example | 
 | #Height 192 | 
 | #Description | 
 | Path contains three Contours: Line, Circle, and Quad. Line is stroked but | 
 | not filled. Circle is stroked and filled; Circle stroke forms a loop. Quad | 
 | is stroked and filled, but since it is not closed, Quad does not stroke a loop. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     SkPath path; | 
 |     path.moveTo(124, 108); | 
 |     path.lineTo(172, 24); | 
 |     path.addCircle(50, 50, 30); | 
 |     path.moveTo(36, 148); | 
 |     path.quadTo(66, 188, 120, 136); | 
 |     canvas->drawPath(path, paint); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setColor(SK_ColorBLUE); | 
 |     paint.setStrokeWidth(3); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | Path contains a Fill_Type which determines whether overlapping Contours | 
 | form fills or holes. Fill_Type also determines whether area inside or outside | 
 | Lines and Curves is filled. | 
 |  | 
 | #Example | 
 | #Height 192 | 
 | #Description | 
 | Path is drawn filled, then stroked, then stroked and filled. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     SkPath path; | 
 |     path.moveTo(36, 48); | 
 |     path.quadTo(66, 88, 120, 36); | 
 |     canvas->drawPath(path, paint); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setColor(SK_ColorBLUE); | 
 |     paint.setStrokeWidth(8); | 
 |     canvas->translate(0, 50); | 
 |     canvas->drawPath(path, paint); | 
 |     paint.setStyle(SkPaint::kStrokeAndFill_Style); | 
 |     paint.setColor(SK_ColorRED); | 
 |     canvas->translate(0, 50); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | Path contents are never shared. Copying Path by value effectively creates | 
 | a new Path independent of the original. Internally, the copy does not duplicate | 
 | its contents until it is edited, to reduce memory use and improve performance. | 
 |  | 
 | #Subtopic Contour | 
 | #Alias Contours | 
 | Contour contains one or more Verbs, and as many Points as | 
 | are required to satisfy Verb_Array. First Verb in Path is always | 
 | SkPath::kMove_Verb; each SkPath::kMove_Verb that follows starts a new Contour. | 
 |  | 
 | #Example | 
 | #Description  | 
 | Each SkPath::moveTo starts a new Contour, and content after SkPath::close() | 
 | also starts a new Contour. Since SkPath::conicTo is not preceded by  | 
 | SkPath::moveTo, the first Point of the third Contour starts at the last Point | 
 | of the second Contour. | 
 | ## | 
 | #Height 192 | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     canvas->drawString("1st contour", 150, 100, paint); | 
 |     canvas->drawString("2nd contour", 130, 160, paint); | 
 |     canvas->drawString("3rd contour", 40, 30, paint); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPath path; | 
 |     path.moveTo(124, 108); | 
 |     path.lineTo(172, 24); | 
 |     path.moveTo(36, 148); | 
 |     path.quadTo(66, 188, 120, 136); | 
 |     path.close(); | 
 |     path.conicTo(70, 20, 110, 40, 0.6f); | 
 |     canvas->drawPath(path, paint); | 
 | ## | 
 |  | 
 | If final Verb in Contour is SkPath::kClose_Verb, Line connects Last_Point in | 
 | Contour with first Point. A closed Contour, stroked, draws  | 
 | Paint_Stroke_Join at Last_Point and first Point. Without SkPath::kClose_Verb | 
 | as final Verb, Last_Point and first Point are not connected; Contour | 
 | remains open. An open Contour, stroked, draws Paint_Stroke_Cap at  | 
 | Last_Point and first Point. | 
 |  | 
 | #Example | 
 | #Height 160 | 
 | #Description | 
 | Path is drawn stroked, with an open Contour and a closed Contour. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setStrokeWidth(8); | 
 |     SkPath path; | 
 |     path.moveTo(36, 48); | 
 |     path.quadTo(66, 88, 120, 36); | 
 |     canvas->drawPath(path, paint); | 
 |     path.close(); | 
 |     canvas->translate(0, 50); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #Subtopic Zero_Length | 
 | #Alias Zero_Length_Contour | 
 | Contour length is distance traveled from first Point to Last_Point, | 
 | plus, if Contour is closed, distance from Last_Point to first Point. | 
 | Even if Contour length is zero, stroked Lines are drawn if Paint_Stroke_Cap | 
 | makes them visible. | 
 |  | 
 | #Example | 
 | #Height 64 | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setStrokeWidth(8); | 
 |     paint.setStrokeCap(SkPaint::kRound_Cap); | 
 |     SkPath path; | 
 |     path.moveTo(36, 48); | 
 |     path.lineTo(36, 48); | 
 |     canvas->drawPath(path, paint); | 
 |     path.reset(); | 
 |     paint.setStrokeCap(SkPaint::kSquare_Cap); | 
 |     path.moveTo(56, 48); | 
 |     path.close(); | 
 |     canvas->drawPath(path, paint); | 
 | ## | 
 |  | 
 | #Subtopic Zero_Length ## | 
 |  | 
 | #Subtopic Contour ## | 
 |   | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Class SkPath | 
 |  | 
 | Paths contain geometry. Paths may be empty, or contain one or more Verbs that | 
 | outline a figure. Path always starts with a move verb to a Cartesian_Coordinate, | 
 | and may be followed by additional verbs that add lines or curves. | 
 | Adding a close verb makes the geometry into a continuous loop, a closed contour. | 
 | Paths may contain any number of contours, each beginning with a move verb. | 
 |  | 
 | Path contours may contain only a move verb, or may also contain lines, | 
 | Quadratic_Beziers, Conics, and Cubic_Beziers. Path contours may be open or | 
 | closed. | 
 |  | 
 | When used to draw a filled area, Path describes whether the fill is inside or | 
 | outside the geometry. Path also describes the winding rule used to fill | 
 | overlapping contours. | 
 |  | 
 | Internally, Path lazily computes metrics likes bounds and convexity. Call | 
 | SkPath::updateBoundsCache to make Path thread safe.  | 
 |  | 
 | #Topic Overview | 
 |  | 
 | #Subtopic Subtopics | 
 | #Table | 
 | #Legend | 
 | # name                  # description                                        ## | 
 | #Legend ## | 
 | # Classes_and_Structs   # embedded struct and class members                  ## | 
 | # Constants             # enum and enum class, const values                  ## | 
 | # Constructors          # functions that construct SkPath                    ## | 
 | # Member_Functions      # static functions and member methods                ## | 
 | # Operators             # operator overloading methods                       ## | 
 | # Related_Functions     # similar methods grouped together                   ## | 
 | #Table ## | 
 | #Subtopic ## | 
 |  | 
 |  | 
 | #Subtopic Related_Functions | 
 | #Table | 
 | #Legend | 
 | # name                  # description                                        ## | 
 | #Legend ## | 
 | # Conic                 # conic section defined by three points and a weight ## | 
 | # Conic_Weight          # strength of control Point in Conic                 ## | 
 | # Contour               # loop of lines and curves                           ## | 
 | # Convexity             # if Path is concave or convex                       ## | 
 | # Cubic_Bezier          # third order curve defined by four points           ## | 
 | # Direction             # Path contour orientation                           ## | 
 | # Fill_Type             # Path fill rule, normal and inverted                ## | 
 | # Last_Point            # final Point in Contour                             ## | 
 | # Point_Array           # end points and control points for lines and curves ## | 
 | # Quadratic_Bezier      # parabolic section defined by three points          ## | 
 | # Verb_Array            # line and curve type for points                     ## | 
 | # Verbs                 # Path line and curve type                           ## | 
 | # Zero_Length_Contour   # consideration when contour has no length           ## | 
 | #Table ## | 
 | #Subtopic ## | 
 |  | 
 | #Subtopic Constants | 
 | #Table | 
 | #Legend | 
 | # name         # description                                 ## | 
 | #Legend ## | 
 | # AddPathMode  # sets addPath options                        ## | 
 | # ArcSize      # used by arcTo variation                     ## | 
 | # Convexity    # returns if Path is convex or concave        ## | 
 | # Direction    # sets Contour clockwise or counterclockwise  ## | 
 | # FillType     # sets winding rule and inverse fill          ## | 
 | # SegmentMask  # returns Verb types in Path                  ##   | 
 | # Verb         # controls how Path Points are interpreted    ## | 
 | #Table ## | 
 | #Subtopic ## | 
 |  | 
 | #Subtopic Classes_and_Structs | 
 | #Table | 
 | #Legend | 
 | # name         # description                                              ## | 
 | #Legend ## | 
 | # Iter         # iterates through lines and curves, skipping degenerates  ## | 
 | # RawIter      # iterates through lines and curves, including degenerates ## | 
 | #Table ## | 
 | #Subtopic ## | 
 |  | 
 | #Subtopic Constructors | 
 | #Table | 
 | #Legend | 
 | # name                           # description                                ## | 
 | #Legend ## | 
 | # SkPath()                       # constructs with default values             ## | 
 | # SkPath(const SkPath& path)     # makes a shallow copy                       ## | 
 | # ~SkPath()                      # decreases Reference_Count of owned objects ## | 
 | #Table ## | 
 | #Subtopic ## | 
 |  | 
 | #Subtopic Operators | 
 | #Table | 
 | #Legend | 
 | # name                                         # description                    ## | 
 | #Legend ## | 
 | # operator!=(const SkPath& a, const SkPath& b) # compares paths for inequality  ## | 
 | # operator=(const SkPath& path)                # makes a shallow copy           ## | 
 | # operator==(const SkPath& a, const SkPath& b) # compares paths for equality    ## | 
 | #Table ## | 
 | #Subtopic ## | 
 |  | 
 | #Subtopic Member_Functions | 
 | #Table | 
 | #Legend | 
 | # name                        # description                                                     ## | 
 | #Legend ## | 
 | # ConvertConicToQuads         # approximates Conic with Quad array                              ## | 
 | # ConvertToNonInverseFillType # returns Fill_Type representing inside geometry                  ## | 
 | # IsCubicDegenerate           # returns if Cubic is very small                                  ## | 
 | # IsInverseFillType           # returns if Fill_Type represents outside geometry                ## | 
 | # IsLineDegenerate            # returns if Line is very small                                   ## | 
 | # IsQuadDegenerate            # returns if Quad is very small                                   ## | 
 | # addArc                      # adds one Contour containing Arc                                 ## | 
 | # addCircle                   # adds one Contour containing Circle                              ## | 
 | # addOval                     # adds one Contour containing Oval                                ## | 
 | # addPath                     # adds contents of Path                                           ## | 
 | # addPoly                     # adds one Contour containing connected lines                     ## | 
 | # addRRect                    # adds one Contour containing Round_Rect                          ## | 
 | # addRect                     # adds one Contour containing Rect                                ## | 
 | # addRoundRect                # adds one Contour containing Round_Rect with common corner radii ## | 
 | # arcTo                       # appends Arc                                                     ## | 
 | # close()                     # makes last Contour a loop                                       ## | 
 | # computeTightBounds          # returns extent of geometry                                      ## | 
 | # conicTo                     # appends Conic                                                   ## | 
 | # conservativelyContainsRect  # returns true if Rect may be inside                              ## | 
 | # contains()                  # returns if Point is in fill area                                ## | 
 | # countPoints                 # returns Point_Array length                                      ## | 
 | # countVerbs                  # returns Verb_Array length                                       ## | 
 | # cubicTo                     # appends Cubic                                                   ## | 
 | # dump()                      # sends text representation using floats to standard output       ## | 
 | # dumpHex                     # sends text representation using hexadecimal to standard output  ## | 
 | # getBounds                   # returns maximum and minimum of Point_Array                      ## | 
 | # getConvexity                # returns geometry convexity, computing if necessary              ## | 
 | # getConvexityOrUnknown       # returns geometry convexity if known                             ## | 
 | # getFillType                 # returns Fill_Type: winding, even-odd, inverse                   ## | 
 | # getGenerationID             # returns unique ID                                               ## | 
 | # getLastPt                   # returns Last_Point                                              ## | 
 | # getPoint                    # returns entry from Point_Array                                  ## | 
 | # getPoints                   # returns Point_Array                                             ## | 
 | # getSegmentMasks             # returns types in Verb_Array                                     ## | 
 | # getVerbs                    # returns Verb_Array                                              ## | 
 | # incReserve                  # reserves space for additional data                              ## | 
 | # interpolate()               # interpolates between Path pair                                  ## | 
 | # isConvex                    # returns if geometry is convex                                   ## | 
 | # isEmpty                     # returns if verb count is zero                                   ## | 
 | # isFinite                    # returns if all Point values are finite                          ## | 
 | # isInterpolatable            # returns if pair contains equal counts of Verb_Array and Weights ## | 
 | # isInverseFillType           # returns if Fill_Type fills outside geometry                     ## | 
 | # isLastContourClosed         # returns if final Contour forms a loop                           ## | 
 | # isLine                      # returns if describes Line                                       ## | 
 | # isNestedFillRects           # returns if describes Rect pair, one inside the other            ## | 
 | # isOval                      # returns if describes Oval                                       ## | 
 | # isRRect                     # returns if describes Round_Rect                                 ## | 
 | # isRect                      # returns if describes Rect                                       ## | 
 | # isValid                     # returns if data is internally consistent                        ## | 
 | # isVolatile                  # returns if Device should not cache                              ## | 
 | # lineTo                      # appends Line                                                    ## | 
 | # moveTo                      # starts Contour                                                  ## | 
 | # offset()                    # translates Point_Array                                          ## | 
 | # pathRefIsValid              # to be deprecated                                                ## | 
 | # quadTo                      # appends Quad                                                    ## | 
 | # rArcTo                      # appends Arc relative to Last_Point                              ## | 
 | # rConicTo                    # appends Conic relative to Last_Point                            ## | 
 | # rCubicTo                    # appends Cubic relative to Last_Point                            ## | 
 | # rLineTo                     # appends Line relative to Last_Point                             ## | 
 | # rMoveTo                     # starts Contour relative to Last_Point                           ## | 
 | # rQuadTo                     # appends Quad relative to Last_Point                             ## | 
 | # readFromMemory              # Initializes from buffer                                         ## | 
 | # reset()                     # removes Verb_Array, Point_Array, and Weights; frees memory      ## | 
 | # reverseAddPath              # adds contents of Path back to front                             ## | 
 | # rewind()                    # removes Verb_Array, Point_Array, and Weights, keeping memory    ## | 
 | # serialize()                 # copies data to buffer                                           ## | 
 | # setConvexity                # sets if geometry is convex to avoid future computation          ## | 
 | # setFillType                 # sets Fill_Type: winding, even-odd, inverse                      ## | 
 | # setIsConvex                 # deprecated                                                      ## | 
 | # setIsVolatile               # sets if Device should not cache                                 ## | 
 | # setLastPt                   # replaces Last_Point                                             ## | 
 | # swap()                      # exchanges Path pair                                             ## | 
 | # toggleInverseFillType       # toggles Fill_Type between inside and outside geometry           ## | 
 | # transform()                 # applies Matrix to Point_Array and Weights                       ## | 
 | # unique()                    # returns if data has single owner                                ## | 
 | # updateBoundsCache           # refreshes result of getBounds                                   ## | 
 | # writeToMemory               # copies data to buffer                                           ## | 
 | #Table ## | 
 | #Subtopic Path_Member_Functions ## | 
 | #Topic Overview ## | 
 |  | 
 | #Subtopic Verb | 
 | #Alias Verbs | 
 |  | 
 | #Enum Verb | 
 |  | 
 | #Code | 
 |     enum Verb { | 
 |         kMove_Verb,  | 
 |         kLine_Verb,  | 
 |         kQuad_Verb,  | 
 |         kConic_Verb,  | 
 |         kCubic_Verb,  | 
 |         kClose_Verb,  | 
 |         kDone_Verb,  | 
 |     }; | 
 | ## | 
 |  | 
 | Verb instructs Path how to interpret one or more Point and optional Conic_Weight; | 
 | manage Contour, and terminate Path. | 
 |  | 
 | #Const kMove_Verb 0 | 
 |     Starts new Contour at next Point. | 
 | ## | 
 | #Const kLine_Verb 1 | 
 |     Adds Line from Last_Point to next Point. | 
 |     Line is a straight segment from Point to Point. | 
 | ## | 
 | #Const kQuad_Verb 2 | 
 |     Adds Quad from Last_Point, using control Point, and end Point.  | 
 |     Quad is a parabolic section within tangents from Last_Point to control Point, | 
 |     and control Point to end Point. | 
 | ## | 
 | #Const kConic_Verb 3 | 
 |     Adds Conic from Last_Point, using control Point, end Point, and Conic_Weight. | 
 |     Conic is a elliptical, parabolic, or hyperbolic section within tangents  | 
 |     from Last_Point to control Point, and control Point to end Point, constrained | 
 |     by Conic_Weight. Conic_Weight less than one is elliptical; equal to one is | 
 |     parabolic (and identical to Quad); greater than one hyperbolic. | 
 | ## | 
 | #Const kCubic_Verb 4 | 
 |     Adds Cubic from Last_Point, using two control Points, and end Point.  | 
 |     Cubic is a third-order Bezier_Curve section within tangents from Last_Point | 
 |     to first control Point, and from second control Point to end Point. | 
 | ## | 
 | #Const kClose_Verb 5 | 
 |     Closes Contour, connecting Last_Point to kMove_Verb Point. | 
 | ## | 
 | #Const kDone_Verb 6 | 
 |     Terminates Path. Not in Verb_Array, but returned by Path iterator. | 
 | ## | 
 |  | 
 | Each Verb has zero or more Points stored in Path. | 
 | Path iterator returns complete curve descriptions, duplicating shared Points | 
 | for consecutive entries. | 
 |  | 
 | #Table | 
 | #Legend | 
 | # Verb        # Allocated Points # Iterated Points # Weights ## | 
 | ## | 
 | # kMove_Verb  # 1                # 1               # 0       ## | 
 | # kLine_Verb  # 1                # 2               # 0       ## | 
 | # kQuad_Verb  # 2                # 3               # 0       ## | 
 | # kConic_Verb # 2                # 3               # 1       ## | 
 | # kCubic_Verb # 3                # 4               # 0       ## | 
 | # kClose_Verb # 0                # 1               # 0       ## | 
 | # kDone_Verb  # --               # 0               # 0       ## | 
 | ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPath path; | 
 |     path.lineTo(20, 20); | 
 |     path.quadTo(-10, -10, 30, 30); | 
 |     path.close(); | 
 |     path.cubicTo(1, 2, 3, 4, 5, 6); | 
 |     path.conicTo(0, 0, 0, 0, 2); | 
 |     uint8_t verbs[7]; | 
 |     int count = path.getVerbs(verbs, (int) SK_ARRAY_COUNT(verbs)); | 
 |     const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close" }; | 
 |     SkDebugf("verb count: %d\nverbs: ", count); | 
 |     for (int i = 0; i < count; ++i) { | 
 |         SkDebugf("k%s_Verb ", verbStr[verbs[i]]); | 
 |     } | 
 |     SkDebugf("\n"); | 
 | } | 
 | #StdOut | 
 | verb count: 7 | 
 | verbs: kMove_Verb kLine_Verb kQuad_Verb kClose_Verb kMove_Verb kCubic_Verb kConic_Verb  | 
 | ## | 
 | ## | 
 |  | 
 | #Enum Verb ## | 
 | #Subtopic Verb ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 | #Subtopic Direction | 
 | #Alias Directions | 
 |  | 
 | #Enum Direction | 
 |  | 
 | #Code | 
 |     enum Direction { | 
 |         kCW_Direction,  | 
 |         kCCW_Direction,  | 
 |     }; | 
 | ## | 
 |  | 
 | Direction describes whether Contour is clockwise or counterclockwise. | 
 | When Path contains multiple overlapping Contours, Direction together with | 
 | Fill_Type determines whether overlaps are filled or form holes. | 
 |  | 
 | Direction also determines how Contour is measured. For instance, dashing | 
 | measures along Path to determine where to start and stop stroke; Direction | 
 | will change dashed results as it steps clockwise or counterclockwise. | 
 |  | 
 | Closed Contours like Rect, Round_Rect, Circle, and Oval added with  | 
 | kCW_Direction travel clockwise; the same added with kCCW_Direction | 
 | travel counterclockwise. | 
 |  | 
 | #Const kCW_Direction 0 | 
 |     Contour travels in a clockwise direction | 
 | ## | 
 | #Const kCCW_Direction 1 | 
 |     Contour travels in a counterclockwise direction | 
 | ## | 
 |  | 
 |  | 
 | #Example | 
 | #Height 100 | 
 | void draw(SkCanvas* canvas) { | 
 |     const SkPoint arrow[] = { {40, -5}, {45, 0}, {40, 5} }; | 
 |     const SkRect rect = {10, 10, 90, 90}; | 
 |     SkPaint rectPaint; | 
 |     rectPaint.setAntiAlias(true); | 
 |     SkPaint textPaint(rectPaint); | 
 |     textPaint.setTextAlign(SkPaint::kCenter_Align); | 
 |     rectPaint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPaint arrowPaint(rectPaint); | 
 |     SkPath arrowPath; | 
 |     arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true); | 
 |     arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 320, 0, | 
 |                              SkPath1DPathEffect::kRotate_Style)); | 
 |     for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { | 
 |         canvas->drawRect(rect, rectPaint); | 
 |         for (unsigned start : { 0, 1, 2, 3 } ) { | 
 |            SkPath path; | 
 |            path.addRect(rect, direction, start); | 
 |            canvas->drawPath(path, arrowPaint); | 
 |        } | 
 |        canvas->drawString(SkPath::kCW_Direction == direction ? "CW" : "CCW",  rect.centerX(), | 
 |             rect.centerY(), textPaint); | 
 |        canvas->translate(120, 0); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso arcTo rArcTo isRect isNestedFillRects addRect addOval  | 
 |  | 
 | #Enum Direction ## | 
 | #Subtopic Direction ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method SkPath() | 
 |  | 
 | By default, Path has no Verbs, no Points, and no Weights. | 
 | Fill_Type is set to kWinding_FillType. | 
 |  | 
 | #Return  empty Path ## | 
 |  | 
 | #Example | 
 |     SkPath path; | 
 |     SkDebugf("path is " "%s" "empty", path.isEmpty() ? "" : "not "); | 
 | #StdOut | 
 | path is empty | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso reset rewind | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method SkPath(const SkPath& path) | 
 |  | 
 | Copy constructor makes two paths identical by value. Internally, path and | 
 | the returned result share pointer values. The underlying Verb_Array, Point_Array | 
 | and Weights are copied when modified. | 
 |  | 
 | Creating a Path copy is very efficient and never allocates memory. | 
 | Paths are always copied by value from the interface; the underlying shared | 
 | pointers are not exposed. | 
 |  | 
 | #Param path  Path to copy by value ## | 
 |  | 
 | #Return  copy of Path ## | 
 |  | 
 | #Example | 
 | #Description | 
 |     Modifying one path does not effect another, even if they started as copies | 
 |     of each other. | 
 | ## | 
 |     SkPath path; | 
 |     path.lineTo(20, 20); | 
 |     SkPath path2(path); | 
 |     path2.close(); | 
 |     SkDebugf("path verbs: %d\n", path.countVerbs()); | 
 |     SkDebugf("path2 verbs: %d\n", path2.countVerbs()); | 
 |     path.reset(); | 
 |     SkDebugf("after reset\n" "path verbs: %d\n", path.countVerbs()); | 
 |     SkDebugf("path2 verbs: %d\n", path2.countVerbs()); | 
 | #StdOut | 
 | path verbs: 2 | 
 | path2 verbs: 3 | 
 | after reset | 
 | path verbs: 0 | 
 | path2 verbs: 3 | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso operator=(const SkPath& path) | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method ~SkPath() | 
 |  | 
 | Releases ownership of any shared data and deletes data if Path is sole owner. | 
 |  | 
 | #Example | 
 | #Description | 
 | delete calls Path Destructor, but copy of original in path2 is unaffected. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPath* path = new SkPath(); | 
 |     path->lineTo(20, 20); | 
 |     SkPath path2(*path); | 
 |     delete path; | 
 |     SkDebugf("path2 is " "%s" "empty", path2.isEmpty() ? "" : "not "); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso SkPath() SkPath(const SkPath& path) operator=(const SkPath& path) | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method SkPath& operator=(const SkPath& path) | 
 |  | 
 | Path assignment makes two paths identical by value. Internally, assignment | 
 | shares pointer values. The underlying Verb_Array, Point_Array and Weights | 
 | are copied when modified. | 
 |  | 
 | Copying Paths by assignment is very efficient and never allocates memory. | 
 | Paths are always copied by value from the interface; the underlying shared | 
 | pointers are not exposed. | 
 |  | 
 | #Param path  Verb_Array, Point_Array, Weights, and Fill_Type to copy ## | 
 |  | 
 | #Return  Path copied by value ## | 
 |  | 
 | #Example | 
 | SkPath path1; | 
 | path1.addRect({10, 20, 30, 40}); | 
 | SkPath path2 = path1; | 
 | const SkRect& b1 = path1.getBounds(); | 
 | SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom); | 
 | const SkRect& b2 = path2.getBounds(); | 
 | SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom); | 
 | #StdOut | 
 | path1 bounds = 10, 20, 30, 40 | 
 | path2 bounds = 10, 20, 30, 40 | 
 | #StdOut ## | 
 | ## | 
 |  | 
 | #SeeAlso swap() SkPath(const SkPath& path) | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool operator==(const SkPath& a, const SkPath& b) | 
 |  | 
 | Compares a and b; returns true if Fill_Type, Verb_Array, Point_Array, and Weights | 
 | are equivalent. | 
 |  | 
 | #Param a  Path to compare ## | 
 | #Param b  Path to compare ## | 
 |  | 
 | #Return  true if Path pair are equivalent ## | 
 |  | 
 | #Example | 
 | #Description | 
 | Rewind removes Verb_Array but leaves storage; since storage is not compared, | 
 | Path pair are equivalent. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& a, const SkPath& b) -> void { | 
 |                 SkDebugf("%s one %c= two\n", prefix, a == b ? '=' : '!'); | 
 |     }; | 
 |     SkPath one; | 
 |     SkPath two; | 
 |     debugster("empty", one, two); | 
 |     one.moveTo(0, 0); | 
 |     debugster("moveTo", one, two); | 
 |     one.rewind(); | 
 |     debugster("rewind", one, two); | 
 |     one.moveTo(0, 0); | 
 |     one.reset(); | 
 |     debugster("reset", one, two); | 
 | } | 
 | #StdOut | 
 | empty one == two | 
 | moveTo one != two | 
 | rewind one == two | 
 | reset one == two | 
 | ## | 
 | ## | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool operator!=(const SkPath& a, const SkPath& b)  | 
 |  | 
 | Compares a and b; returns true if Fill_Type, Verb_Array, Point_Array, and Weights | 
 | are not equivalent. | 
 |  | 
 | #Param a  Path to compare ## | 
 | #Param b  Path to compare ## | 
 |  | 
 | #Return  true if Path pair are not equivalent ## | 
 |  | 
 | #Example | 
 | #Description | 
 | Path pair are equal though their convexity is not equal. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& a, const SkPath& b) -> void { | 
 |                 SkDebugf("%s one %c= two\n", prefix, a != b ? '!' : '='); | 
 |     }; | 
 |     SkPath one; | 
 |     SkPath two; | 
 |     debugster("empty", one, two); | 
 |     one.addRect({10, 20, 30, 40}); | 
 |     two.addRect({10, 20, 30, 40}); | 
 |     debugster("addRect", one, two); | 
 |     one.setConvexity(SkPath::kConcave_Convexity); | 
 |     debugster("setConvexity", one, two); | 
 |     SkDebugf("convexity %c=\n", one.getConvexity() == two.getConvexity() ? '=' : '!'); | 
 | } | 
 | #StdOut | 
 | empty one == two | 
 | addRect one == two | 
 | setConvexity one == two | 
 | convexity != | 
 | ## | 
 | ## | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isInterpolatable(const SkPath& compare) const | 
 |  | 
 | Return true if Paths contain equal Verbs and equal Weights. | 
 | If Paths contain one or more Conics, the Weights must match. | 
 |  | 
 | conicTo may add different Verbs depending on Conic_Weight, so it is not | 
 | trivial to interpolate a pair of Paths containing Conics with different | 
 | Conic_Weight values.  | 
 |  | 
 | #Param compare  Path to compare ## | 
 |  | 
 | #Return  true if Paths Verb_Array and Weights are equivalent ## | 
 |  | 
 | #Example | 
 |     SkPath path, path2; | 
 |     path.moveTo(20, 20); | 
 |     path.lineTo(40, 40); | 
 |     path.lineTo(20, 20); | 
 |     path.lineTo(40, 40); | 
 |     path.close(); | 
 |     path2.addRect({20, 20, 40, 40}); | 
 |     SkDebugf("paths are " "%s" "interpolatable", path.isInterpolatable(path2) ? "" : "not "); | 
 | #StdOut | 
 | paths are interpolatable | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso isInterpolatable | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const | 
 |  | 
 | Interpolate between Paths with Point_Array of equal size. | 
 | Copy Verb_Array and Weights to out, and set out Point_Array to a weighted | 
 | average of this Point_Array and ending Point_Array, using the formula: | 
 | #Formula | 
 | (Path Point * weight) + ending Point * (1 - weight) | 
 | ## | 
 | . | 
 |  | 
 | weight is most useful when between zero (ending Point_Array) and  | 
 | one (this Point_Array); will work with values outside of this  | 
 | range. | 
 |  | 
 | interpolate() returns false and leaves out unchanged if Point_Array is not | 
 | the same size as ending Point_Array. Call isInterpolatable to check Path  | 
 | compatibility prior to calling interpolate(). | 
 |  | 
 | #Param ending  Point_Array averaged with this Point_Array ## | 
 | #Param weight  contribution of this Point_Array, and  | 
 |                one minus contribution of ending Point_Array  | 
 | ## | 
 | #Param out     Path replaced by interpolated averages ## | 
 |  | 
 | #Return  true if Paths contain same number of Points ## | 
 |  | 
 | #Example | 
 | #Height 60 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPath path, path2; | 
 |     path.moveTo(20, 20); | 
 |     path.lineTo(40, 40); | 
 |     path.lineTo(20, 40); | 
 |     path.lineTo(40, 20); | 
 |     path.close(); | 
 |     path2.addRect({20, 20, 40, 40}); | 
 |     for (SkScalar i = 0; i <= 1; i += 1.f / 6) { | 
 |       SkPath interp; | 
 |       path.interpolate(path2, i, &interp); | 
 |       canvas->drawPath(interp, paint); | 
 |       canvas->translate(30, 0); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso isInterpolatable | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool unique() const  | 
 |  | 
 | #Private | 
 | To be deprecated; only valid for Android framework. | 
 | ## | 
 |  | 
 | #Return  true if Path has one owner ## | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 | #Subtopic Fill_Type | 
 |  | 
 | #Enum FillType | 
 |  | 
 | #Code | 
 |     enum FillType { | 
 |         kWinding_FillType,  | 
 |         kEvenOdd_FillType,  | 
 |         kInverseWinding_FillType,  | 
 |         kInverseEvenOdd_FillType,  | 
 |     }; | 
 | ## | 
 |  | 
 | Fill_Type selects the rule used to fill Path. Path set to kWinding_FillType  | 
 | fills if the sum of Contour edges is not zero, where clockwise edges add one, and | 
 | counterclockwise edges subtract one. Path set to kEvenOdd_FillType fills if the | 
 | number of Contour edges is odd. Each Fill_Type has an inverse variant that  | 
 | reverses the rule: | 
 | kInverseWinding_FillType fills where the sum of Contour edges is zero; | 
 | kInverseEvenOdd_FillType fills where the number of Contour edges is even. | 
 |  | 
 | #Example | 
 | #Height 100 | 
 | #Description | 
 | The top row has two clockwise rectangles. The second row has one clockwise and | 
 | one counterclockwise rectangle. The even-odd variants draw the same. The | 
 | winding variants draw the top rectangle overlap, which has a winding of 2, the | 
 | same as the outer parts of the top rectangles, which have a winding of 1. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |    SkPath path; | 
 |    path.addRect({10, 10, 30, 30}, SkPath::kCW_Direction); | 
 |    path.addRect({20, 20, 40, 40}, SkPath::kCW_Direction); | 
 |    path.addRect({10, 60, 30, 80}, SkPath::kCW_Direction); | 
 |    path.addRect({20, 70, 40, 90}, SkPath::kCCW_Direction); | 
 |    SkPaint strokePaint; | 
 |    strokePaint.setStyle(SkPaint::kStroke_Style); | 
 |    SkRect clipRect = {0, 0, 51, 100}; | 
 |    canvas->drawPath(path, strokePaint); | 
 |    SkPaint fillPaint; | 
 |    for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType,  | 
 |                       SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) { | 
 |         canvas->translate(51, 0); | 
 |         canvas->save(); | 
 |         canvas->clipRect(clipRect); | 
 |         path.setFillType(fillType); | 
 |         canvas->drawPath(path, fillPaint); | 
 |         canvas->restore(); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #Const kWinding_FillType 0 | 
 | Specifies fill as area is enclosed by a non-zero sum of Contour Directions. | 
 | ## | 
 | #Const kEvenOdd_FillType 1 | 
 | Specifies fill as area enclosed by an odd number of Contours. | 
 | ## | 
 | #Const kInverseWinding_FillType 2 | 
 | Specifies fill as area is enclosed by a zero sum of Contour Directions. | 
 | ## | 
 | #Const kInverseEvenOdd_FillType 3 | 
 | Specifies fill as area enclosed by an even number of Contours. | 
 | ## | 
 |  | 
 | #Example | 
 | #Height 230 | 
 | void draw(SkCanvas* canvas) { | 
 |    SkPath path; | 
 |    path.addRect({20, 10, 80, 70}, SkPath::kCW_Direction); | 
 |    path.addRect({40, 30, 100, 90}, SkPath::kCW_Direction); | 
 |    SkPaint strokePaint; | 
 |    strokePaint.setStyle(SkPaint::kStroke_Style); | 
 |    SkRect clipRect = {0, 0, 128, 128}; | 
 |    canvas->drawPath(path, strokePaint); | 
 |    canvas->drawLine({0, 50}, {120, 50}, strokePaint); | 
 |    SkPaint textPaint; | 
 |    textPaint.setAntiAlias(true); | 
 |    textPaint.setTextAlign(SkPaint::kCenter_Align); | 
 |    SkScalar textHPos[] = { 10, 30, 60, 90, 110 }; | 
 |    canvas->drawPosTextH("01210", 5, textHPos, 48, textPaint); | 
 |    textPaint.setTextSize(18); | 
 |    canvas->translate(0, 128); | 
 |    canvas->scale(.5f, .5f); | 
 |    canvas->drawString("inverse", 384, 150, textPaint); | 
 |    SkPaint fillPaint; | 
 |    for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType,  | 
 |                       SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) { | 
 |         canvas->save(); | 
 |         canvas->clipRect(clipRect); | 
 |         path.setFillType(fillType); | 
 |         canvas->drawPath(path, fillPaint); | 
 |         canvas->restore(); | 
 |         canvas->drawString(fillType & 1 ? "even-odd" : "winding", 64, 170, textPaint); | 
 |         canvas->translate(128, 0); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso SkPaint::Style Direction getFillType setFillType | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method FillType getFillType() const  | 
 |  | 
 | Returns FillType, the rule used to fill Path. FillType of a new Path is | 
 | kWinding_FillType. | 
 |  | 
 | #Return  one of: kWinding_FillType, kEvenOdd_FillType,  kInverseWinding_FillType,  | 
 | kInverseEvenOdd_FillType  | 
 | ## | 
 |  | 
 | #Example | 
 |     SkPath path; | 
 |     SkDebugf("default path fill type is %s\n", | 
 |             path.getFillType() == SkPath::kWinding_FillType ? "kWinding_FillType" : | 
 |             path.getFillType() == SkPath::kEvenOdd_FillType ? "kEvenOdd_FillType" :  | 
 |             path.getFillType() == SkPath::kInverseWinding_FillType ? "kInverseWinding_FillType" : | 
 |                                                                      "kInverseEvenOdd_FillType"); | 
 | #StdOut | 
 | default path fill type is kWinding_FillType | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso FillType setFillType isInverseFillType | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void setFillType(FillType ft)  | 
 |  | 
 | Sets FillType, the rule used to fill Path. While there is no check | 
 | that ft is legal, values outside of FillType are not supported. | 
 |  | 
 | #Param ft  one of: kWinding_FillType, kEvenOdd_FillType,  kInverseWinding_FillType,  | 
 | kInverseEvenOdd_FillType  | 
 | ## | 
 |  | 
 | #Example | 
 | #Description | 
 | If empty Path is set to inverse FillType, it fills all pixels. | 
 | ## | 
 | #Height 64 | 
 |      SkPath path; | 
 |      path.setFillType(SkPath::kInverseWinding_FillType); | 
 |      SkPaint paint; | 
 |      paint.setColor(SK_ColorBLUE); | 
 |      canvas->drawPath(path, paint); | 
 | ## | 
 |  | 
 | #SeeAlso FillType getFillType toggleInverseFillType | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isInverseFillType() const  | 
 |  | 
 | Returns if FillType describes area outside Path geometry. The inverse fill area | 
 | extends indefinitely. | 
 |  | 
 | #Return  true if FillType is kInverseWinding_FillType or kInverseEvenOdd_FillType ## | 
 |  | 
 | #Example | 
 |     SkPath path; | 
 |     SkDebugf("default path fill type is inverse: %s\n", | 
 |             path.isInverseFillType() ? "true" : "false"); | 
 | #StdOut | 
 | default path fill type is inverse: false | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso FillType getFillType setFillType toggleInverseFillType | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void toggleInverseFillType()  | 
 |  | 
 | Replace FillType with its inverse. The inverse of FillType describes the area | 
 | unmodified by the original FillType. | 
 |  | 
 | #Table  | 
 | #Legend | 
 | # FillType                 # toggled FillType         ##  | 
 | ## | 
 | # kWinding_FillType        # kInverseWinding_FillType ## | 
 | # kEvenOdd_FillType        # kInverseEvenOdd_FillType ## | 
 | # kInverseWinding_FillType # kWinding_FillType        ## | 
 | # kInverseEvenOdd_FillType # kEvenOdd_FillType        ## | 
 | ## | 
 |  | 
 | #Example | 
 | #Description | 
 | Path drawn normally and through its inverse touches every pixel once. | 
 | ## | 
 | #Height 100 | 
 | SkPath path; | 
 | SkPaint paint; | 
 | paint.setColor(SK_ColorRED); | 
 | paint.setTextSize(80); | 
 | paint.getTextPath("ABC", 3, 20, 80, &path); | 
 | canvas->drawPath(path, paint); | 
 | path.toggleInverseFillType(); | 
 | paint.setColor(SK_ColorGREEN); | 
 | canvas->drawPath(path, paint); | 
 | ## | 
 |  | 
 | #SeeAlso FillType getFillType setFillType isInverseFillType | 
 |  | 
 | ## | 
 |  | 
 | #Subtopic Fill_Type ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Subtopic Convexity | 
 |  | 
 | #Enum Convexity | 
 |  | 
 | #Code | 
 |     enum Convexity : uint8_t { | 
 |         kUnknown_Convexity,  | 
 |         kConvex_Convexity, | 
 |         kConcave_Convexity,  | 
 |     }; | 
 | ## | 
 |  | 
 | Path is convex if it contains one Contour and Contour loops no more than  | 
 | 360 degrees, and Contour angles all have same Direction. Convex Path  | 
 | may have better performance and require fewer resources on GPU_Surface. | 
 |  | 
 | Path is concave when either at least one Direction change is clockwise and | 
 | another is counterclockwise, or the sum of the changes in Direction is not 360 | 
 | degrees. | 
 |  | 
 | Initially Path Convexity is kUnknown_Convexity. Path Convexity is computed  | 
 | if needed by destination Surface. | 
 |  | 
 | #Const kUnknown_Convexity 0 | 
 |     Indicates Convexity has not been determined. | 
 | ## | 
 | #Const kConvex_Convexity 1 | 
 |     Path has one Contour made of a simple geometry without indentations. | 
 | ## | 
 | #Const kConcave_Convexity 2 | 
 |     Path has more than one Contour, or a geometry with indentations. | 
 | ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};  | 
 |     const char* labels[] = { "unknown", "convex", "concave" }; | 
 |     for (SkScalar x : { 40, 100 } ) { | 
 |         SkPath path; | 
 |         quad[0].fX = x; | 
 |         path.addPoly(quad, SK_ARRAY_COUNT(quad), true); | 
 |         canvas->drawPath(path, paint); | 
 |         canvas->drawString(labels[(int) path.getConvexity()], 30, 100, paint); | 
 |         canvas->translate(100, 100); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Contour Direction getConvexity getConvexityOrUnknown setConvexity isConvex | 
 |  | 
 | #Enum Convexity ## | 
 |  | 
 | #Method Convexity getConvexity() const  | 
 |  | 
 | Computes Convexity if required, and returns stored value.  | 
 | Convexity is computed if stored value is kUnknown_Convexity, | 
 | or if Path has been altered since Convexity was computed or set. | 
 |  | 
 | #Return  computed or stored Convexity ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |         SkDebugf("%s path convexity is %s\n", prefix,  | 
 |                 SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" : | 
 |                 SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); }; | 
 |     SkPath path; | 
 |     debugster("initial", path); | 
 |     path.lineTo(50, 0); | 
 |     debugster("first line", path); | 
 |     path.lineTo(50, 50); | 
 |     debugster("second line", path); | 
 |     path.lineTo(100, 50); | 
 |     debugster("third line", path); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Convexity Contour Direction getConvexityOrUnknown setConvexity isConvex | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method Convexity getConvexityOrUnknown() const  | 
 |  | 
 | Returns last computed Convexity, or kUnknown_Convexity if  | 
 | Path has been altered since Convexity was computed or set. | 
 |  | 
 | #Return  stored Convexity ## | 
 |  | 
 | #Example | 
 | #Description | 
 | Convexity is unknown unless getConvexity is called without a subsequent call | 
 | that alters the path. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |         SkDebugf("%s path convexity is %s\n", prefix,  | 
 |             SkPath::kUnknown_Convexity == path.getConvexityOrUnknown() ? "unknown" : | 
 |             SkPath::kConvex_Convexity == path.getConvexityOrUnknown() ? "convex" : "concave"); }; | 
 |     SkPath path; | 
 |     debugster("initial", path); | 
 |     path.lineTo(50, 0); | 
 |     debugster("first line", path); | 
 |     path.getConvexity(); | 
 |     path.lineTo(50, 50); | 
 |     debugster("second line", path); | 
 |     path.lineTo(100, 50); | 
 |     path.getConvexity(); | 
 |     debugster("third line", path); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Convexity Contour Direction getConvexity setConvexity isConvex | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void setConvexity(Convexity convexity) | 
 |  | 
 | Stores convexity so that it is later returned by getConvexity or getConvexityOrUnknown. | 
 | convexity may differ from getConvexity, although setting an incorrect value may | 
 | cause incorrect or inefficient drawing. | 
 |  | 
 | If convexity is kUnknown_Convexity: getConvexity will | 
 | compute Convexity, and getConvexityOrUnknown will return kUnknown_Convexity. | 
 |  | 
 | If convexity is kConvex_Convexity or kConcave_Convexity, getConvexity | 
 | and getConvexityOrUnknown will return convexity until the path is | 
 | altered. | 
 |  | 
 | #Param convexity  one of: kUnknown_Convexity, kConvex_Convexity, or kConcave_Convexity ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |         SkDebugf("%s path convexity is %s\n", prefix,  | 
 |                 SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" : | 
 |                 SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); }; | 
 |         SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};  | 
 |         SkPath path; | 
 |         path.addPoly(quad, SK_ARRAY_COUNT(quad), true); | 
 |         debugster("initial", path); | 
 |         path.setConvexity(SkPath::kConcave_Convexity); | 
 |         debugster("after forcing concave", path); | 
 |         path.setConvexity(SkPath::kUnknown_Convexity); | 
 |         debugster("after forcing unknown", path); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Convexity Contour Direction getConvexity getConvexityOrUnknown isConvex | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isConvex() const  | 
 |  | 
 | Computes Convexity if required, and returns true if value is kConvex_Convexity. | 
 | If setConvexity was called with kConvex_Convexity or kConcave_Convexity, and | 
 | the path has not been altered, Convexity is not recomputed. | 
 |  | 
 | #Return  true if Convexity stored or computed is kConvex_Convexity ## | 
 |  | 
 | #Example | 
 | #Description | 
 | Concave shape is erroneously considered convex after a forced call to  | 
 | setConvexity. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};  | 
 |     for (SkScalar x : { 40, 100 } ) { | 
 |         SkPath path; | 
 |         quad[0].fX = x; | 
 |         path.addPoly(quad, SK_ARRAY_COUNT(quad), true); | 
 |         path.setConvexity(SkPath::kConvex_Convexity); | 
 |         canvas->drawPath(path, paint); | 
 |         canvas->drawString(path.isConvex() ? "convex" : "not convex", 30, 100, paint); | 
 |         canvas->translate(100, 100); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Convexity Contour Direction getConvexity getConvexityOrUnknown setConvexity | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void setIsConvex(bool isConvex)  | 
 |  | 
 | Deprecated. Use setConvexity. | 
 |  | 
 | #Deprecated | 
 | ## | 
 |  | 
 | #NoExample | 
 | ## | 
 |  | 
 | #SeeAlso Convexity setConvexity getConvexity | 
 |  | 
 | ## | 
 |  | 
 | #Subtopic Convexity ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isOval(SkRect* rect, Direction* dir = nullptr, | 
 |                 unsigned* start = nullptr) const  | 
 |  | 
 | Returns true if constructed by addCircle, addOval; and in some cases, | 
 | addRoundRect, addRRect.  Path constructed with conicTo or rConicTo will not | 
 | return true though Path draws Oval. | 
 |  | 
 | rect receives bounds of Oval. | 
 | dir receives Direction of Oval: kCW_Direction if clockwise, kCCW_Direction if | 
 | counterclockwise. | 
 | start receives start of Oval: 0 for top, 1 for right, 2 for bottom, 3 for left. | 
 |  | 
 | rect, dir, and start are unmodified if Oval is not found. | 
 |  | 
 | Triggers performance optimizations on some GPU_Surface implementations. | 
 |  | 
 | #Param rect  storage for bounding Rect of Oval; may be nullptr ## | 
 | #Param dir  storage for Direction; may be nullptr ## | 
 | #Param start  storage for start of Oval; may be nullptr ## | 
 |  | 
 | #Return  true if Path was constructed by method that reduces to Oval ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     SkPath path; | 
 |     path.addOval({20, 20, 220, 220}, SkPath::kCW_Direction, 1); | 
 |     SkRect bounds; | 
 |     SkPath::Direction direction; | 
 |     unsigned start; | 
 |     path.isOval(&bounds, &direction, &start); | 
 |     paint.setColor(0xFF9FBFFF); | 
 |     canvas->drawRect(bounds, paint); | 
 |     paint.setColor(0x3f000000); | 
 |     canvas->drawPath(path, paint); | 
 |     paint.setColor(SK_ColorBLACK); | 
 |     canvas->rotate(start * 90, bounds.centerX(), bounds.centerY()); | 
 |     char startText = '0' + start; | 
 |     paint.setTextSize(20); | 
 |     canvas->drawText(&startText, 1, bounds.centerX(), bounds.fTop + 20, paint); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setStrokeWidth(4); | 
 |     path.reset(); | 
 |     path.addArc(bounds, -90, SkPath::kCW_Direction == direction ? 90 : -90); | 
 |     path.rLineTo(20, -20);  | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Oval addCircle addOval | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isRRect(SkRRect* rrect, Direction* dir = nullptr, | 
 |                  unsigned* start = nullptr) const  | 
 |  | 
 | Returns true if constructed by addRoundRect, addRRect; and if construction | 
 | is not empty, not Rect, and not Oval. Path constructed with other calls | 
 | will not return true though Path draws Round_Rect. | 
 |  | 
 | rrect receives bounds of Round_Rect. | 
 | dir receives Direction of Oval: kCW_Direction if clockwise, kCCW_Direction if | 
 | counterclockwise. | 
 | start receives start of Round_Rect: 0 for top, 1 for right, 2 for bottom, 3 for left. | 
 |  | 
 | rrect, dir, and start are unmodified if Round_Rect is not found. | 
 |  | 
 | Triggers performance optimizations on some GPU_Surface implementations. | 
 |  | 
 | #Param rrect  storage for bounding Rect of Round_Rect; may be nullptr ## | 
 | #Param dir  storage for Direction; may be nullptr ## | 
 | #Param start  storage for start of Round_Rect; may be nullptr ## | 
 |  | 
 | #Return  true if Path contains only Round_Rect ## | 
 |  | 
 | #Example | 
 | #Description | 
 | Draw rounded rectangle and its bounds. Draw an arc indicating where the rounded | 
 | rectangle starts and its direction. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     SkPath path; | 
 |     path.addRRect(SkRRect::MakeRectXY({20, 20, 220, 220}, 30, 50), SkPath::kCCW_Direction, 3); | 
 |     SkRRect rrect; | 
 |     SkPath::Direction direction; | 
 |     unsigned start; | 
 |     path.isRRect(&rrect, &direction, &start); | 
 |     const SkRect& bounds = rrect.rect(); | 
 |     paint.setColor(0xFF9FBFFF); | 
 |     canvas->drawRect(bounds, paint); | 
 |     paint.setColor(0x3f000000); | 
 |     canvas->drawPath(path, paint); | 
 |     paint.setColor(SK_ColorBLACK); | 
 |     canvas->rotate(start * 90, bounds.centerX(), bounds.centerY()); | 
 |     char startText = '0' + start; | 
 |     paint.setTextSize(20); | 
 |     canvas->drawText(&startText, 1, bounds.centerX(), bounds.fTop + 20, paint); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setStrokeWidth(4); | 
 |     path.reset(); | 
 |     path.addArc(bounds, -90, SkPath::kCW_Direction == direction ? 90 : -90); | 
 |     path.rLineTo(20, -20);  | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Round_Rect addRoundRect addRRect  | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void reset() | 
 |  | 
 | Sets Path to its initial state. | 
 | Removes Verb_Array, Point_Array, and Weights, and sets FillType to kWinding_FillType. | 
 | Internal storage associated with Path is released. | 
 |  | 
 | #Example | 
 |    SkPath path1, path2; | 
 |    path1.setFillType(SkPath::kInverseWinding_FillType); | 
 |    path1.addRect({10, 20, 30, 40}); | 
 |    SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!'); | 
 |    path1.reset(); | 
 |    SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!'); | 
 | ## | 
 |  | 
 | #SeeAlso rewind() | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void rewind() | 
 |  | 
 | Sets Path to its initial state, preserving internal storage. | 
 | Removes Verb_Array, Point_Array, and Weights, and sets FillType to kWinding_FillType. | 
 | Internal storage associated with Path is retained. | 
 |  | 
 | Use rewind() instead of reset() if Path storage will be reused and performance | 
 | is critical.  | 
 |  | 
 | #Example | 
 | #Description | 
 | Although path1 retains its internal storage, it is indistinguishable from | 
 | a newly initialized path. | 
 | ## | 
 |    SkPath path1, path2; | 
 |    path1.setFillType(SkPath::kInverseWinding_FillType); | 
 |    path1.addRect({10, 20, 30, 40}); | 
 |    SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!'); | 
 |    path1.rewind(); | 
 |    SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!'); | 
 | ## | 
 |  | 
 | #SeeAlso reset() | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isEmpty() const  | 
 |  | 
 | Empty Path may have FillType but has no SkPoint, Verb, or Conic_Weight. | 
 | SkPath() constructs empty Path; reset() and (rewind) make Path empty.  | 
 |  | 
 | #Return  true if the path contains no Verb array  ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |         SkDebugf("%s path is %s" "empty\n", prefix, path.isEmpty() ? "" : "not "); | 
 |     }; | 
 |     SkPath path; | 
 |     debugster("initial", path); | 
 |     path.moveTo(0, 0); | 
 |     debugster("after moveTo", path); | 
 |     path.rewind(); | 
 |     debugster("after rewind", path); | 
 |     path.lineTo(0, 0); | 
 |     debugster("after lineTo", path); | 
 |     path.reset(); | 
 |     debugster("after reset", path); | 
 | } | 
 | #StdOut | 
 | initial path is empty | 
 | after moveTo path is not empty | 
 | after rewind path is empty | 
 | after lineTo path is not empty | 
 | after reset path is empty | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso SkPath() reset() rewind() | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isLastContourClosed() const | 
 |  | 
 | Contour is closed if Path Verb array was last modified by close(). When stroked, | 
 | closed Contour draws Paint_Stroke_Join instead of Paint_Stroke_Cap at first and last Point.  | 
 |  | 
 | #Return  true if the last Contour ends with a kClose_Verb ## | 
 |  | 
 | #Example | 
 | #Description | 
 | close() has no effect if Path is empty; isLastContourClosed() returns | 
 | false until Path has geometry followed by close(). | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |         SkDebugf("%s last contour is %s" "closed\n", prefix, | 
 |                  path.isLastContourClosed() ? "" : "not "); | 
 |     }; | 
 |     SkPath path; | 
 |     debugster("initial", path); | 
 |     path.close(); | 
 |     debugster("after close", path); | 
 |     path.lineTo(0, 0); | 
 |     debugster("after lineTo", path); | 
 |     path.close(); | 
 |     debugster("after close", path); | 
 | } | 
 | #StdOut | 
 | initial last contour is not closed | 
 | after close last contour is not closed | 
 | after lineTo last contour is not closed | 
 | after close last contour is closed | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso close() | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isFinite() const  | 
 |  | 
 | Returns true for finite Point array values between negative SK_ScalarMax and | 
 | positive SK_ScalarMax. Returns false for any Point array value of | 
 | SK_ScalarInfinity, SK_ScalarNegativeInfinity, or SK_ScalarNaN. | 
 |  | 
 | #Return  true if all Point values are finite ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |         SkDebugf("%s path is %s" "finite\n", prefix, path.isFinite() ? "" : "not "); | 
 |     }; | 
 |     SkPath path; | 
 |     debugster("initial", path); | 
 |     path.lineTo(SK_ScalarMax, SK_ScalarMax); | 
 |     debugster("after line", path); | 
 |     SkMatrix matrix; | 
 |     matrix.setScale(2, 2); | 
 |     path.transform(matrix); | 
 |     debugster("after scale", path); | 
 | } | 
 | #StdOut | 
 | initial path is finite | 
 | after line path is finite | 
 | after scale path is not finite | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso SkScalar | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isVolatile() const  | 
 |  | 
 | Returns true if the path is volatile; it will not be altered or discarded | 
 | by the caller after it is drawn. Paths by default have volatile set false, allowing  | 
 | Surface to attach a cache of data which speeds repeated drawing. If true, Surface | 
 | may not speed repeated drawing. | 
 |  | 
 | #Return  true if caller will alter Path after drawing ## | 
 |  | 
 | #Example | 
 |     SkPath path; | 
 |     SkDebugf("volatile by default is %s\n", path.isVolatile() ? "true" : "false"); | 
 | #StdOut | 
 | volatile by default is false | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso setIsVolatile | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void setIsVolatile(bool isVolatile)  | 
 |  | 
 | Specify whether Path is volatile; whether it will be altered or discarded | 
 | by the caller after it is drawn. Paths by default have volatile set false, allowing  | 
 | Device to attach a cache of data which speeds repeated drawing. | 
 |  | 
 | Mark temporary paths, discarded or modified after use, as volatile | 
 | to inform Device that the path need not be cached. | 
 |  | 
 | Mark animating Path volatile to improve performance. | 
 | Mark unchanging Path non-volatile to improve repeated rendering. | 
 |  | 
 | Raster_Surface Path draws are affected by volatile for some shadows. | 
 | GPU_Surface Path draws are affected by volatile for some shadows and concave geometries. | 
 |  | 
 | #Param isVolatile  true if caller will alter Path after drawing ## | 
 |  | 
 | #Example | 
 | #Height 50 | 
 | #Width 50 | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPath path; | 
 |     path.setIsVolatile(true); | 
 |     path.lineTo(40, 40); | 
 |     canvas->drawPath(path, paint); | 
 |     path.rewind(); | 
 |     path.moveTo(0, 40); | 
 |     path.lineTo(40, 0); | 
 |     canvas->drawPath(path, paint); | 
 | ## | 
 |  | 
 | #ToDo tie example to bench to show how volatile affects speed or dm to show resource usage ## | 
 |  | 
 | #SeeAlso isVolatile | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact)  | 
 |  | 
 | Test if Line between Point pair is degenerate. | 
 | Line with no length or that moves a very short distance is degenerate; it is | 
 | treated as a point.  | 
 |  | 
 | exact changes the equality test. If true, returns true only if p1 equals p2. | 
 | If false, returns true if p1 equals or nearly equals p2. | 
 |  | 
 | #Param p1     line start point ## | 
 | #Param p2     line end point ## | 
 | #Param exact  if false, allow nearly equals ## | 
 |  | 
 | #Return  true if Line is degenerate; its length is effectively zero ## | 
 |  | 
 | #Example | 
 | #Description | 
 | As single precision floats, 100 and 100.000001 have the same bit representation, | 
 | and are exactly equal. 100 and 100.0001 have different bit representations, and | 
 | are not exactly equal, but are nearly equal. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPoint points[] = { {100, 100}, {100.000001f, 100.000001f}, {100.0001f, 100.0001f} }; | 
 |     for (size_t i = 0; i < SK_ARRAY_COUNT(points) - 1; ++i) { | 
 |         for (bool exact : { false, true } ) { | 
 |             SkDebugf("line from (%1.8g,%1.8g) to (%1.8g,%1.8g) is %s" "degenerate, %s\n", | 
 |                     points[i].fX, points[i].fY, points[i + 1].fX, points[i + 1].fY, | 
 |                     SkPath::IsLineDegenerate(points[i], points[i + 1], exact) | 
 |                     ? "" : "not ", exact ? "exactly" : "nearly"); | 
 |         } | 
 |     } | 
 | } | 
 | #StdOut | 
 | line from (100,100) to (100,100) is degenerate, nearly | 
 | line from (100,100) to (100,100) is degenerate, exactly | 
 | line from (100,100) to (100.0001,100.0001) is degenerate, nearly | 
 | line from (100,100) to (100.0001,100.0001) is not degenerate, exactly | 
 | #StdOut ## | 
 | ## | 
 |  | 
 | #SeeAlso IsQuadDegenerate IsCubicDegenerate  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2, | 
 |                                  const SkPoint& p3, bool exact)  | 
 |  | 
 | Test if Quad is degenerate. | 
 | Quad with no length or that moves a very short distance is degenerate; it is | 
 | treated as a point.  | 
 |  | 
 | #Param p1  Quad start point ## | 
 | #Param p2  Quad control point ## | 
 | #Param p3  Quad end point ## | 
 | #Param exact  if true, returns true only if p1, p2, and p3 are equal;  | 
 |               if false, returns true if p1, p2, and p3 are equal or nearly equal  | 
 | ## | 
 |  | 
 | #Return  true if Quad is degenerate; its length is effectively zero ## | 
 |  | 
 | #Example | 
 | #Description | 
 | As single precision floats: 100, 100.00001, and 100.00002 have different bit representations | 
 | but nearly the same value. Translating all three by 1000 gives them the same bit representation; | 
 | the fractional portion of the number can not be represented by the float and is lost. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const SkPath& path, bool exact) -> void { | 
 |         SkDebugf("quad (%1.8g,%1.8g), (%1.8g,%1.8g), (%1.8g,%1.8g) is %s" "degenerate, %s\n",  | 
 |             path.getPoint(0).fX, path.getPoint(0).fY, path.getPoint(1).fX, | 
 |             path.getPoint(1).fY, path.getPoint(2).fX, path.getPoint(2).fY, | 
 |             SkPath::IsQuadDegenerate(path.getPoint(0), path.getPoint(1), path.getPoint(2), exact) ? | 
 |             "" : "not ", exact ? "exactly" : "nearly"); | 
 |     }; | 
 |     SkPath path, offset; | 
 |     path.moveTo({100, 100}); | 
 |     path.quadTo({100.00001f, 100.00001f}, {100.00002f, 100.00002f}); | 
 |     offset.addPath(path, 1000, 1000); | 
 |     for (bool exact : { false, true } ) { | 
 |         debugster(path, exact); | 
 |         debugster(offset, exact); | 
 |     } | 
 | } | 
 | #StdOut | 
 | quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is degenerate, nearly | 
 | quad (1100,1100), (1100,1100), (1100,1100) is degenerate, nearly | 
 | quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is not degenerate, exactly | 
 | quad (1100,1100), (1100,1100), (1100,1100) is degenerate, exactly | 
 | #StdOut ## | 
 | ## | 
 |  | 
 | #SeeAlso IsLineDegenerate IsCubicDegenerate  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2, | 
 |                                   const SkPoint& p3, const SkPoint& p4, bool exact)  | 
 |  | 
 | Test if Cubic is degenerate. | 
 | Cubic with no length or that moves a very short distance is degenerate; it is | 
 | treated as a point.  | 
 |  | 
 | #Param p1  Cubic start point ## | 
 | #Param p2  Cubic control point 1 ## | 
 | #Param p3  Cubic control point 2 ## | 
 | #Param p4  Cubic end point ## | 
 | #Param exact  if true, returns true only if p1, p2, p3, and p4 are equal;  | 
 |               if false, returns true if p1, p2, p3, and p4 are equal or nearly equal | 
 | ## | 
 |  | 
 | #Return  true if Cubic is degenerate; its length is effectively zero ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPoint points[] = {{1, 0}, {0, 0}, {0, 0}, {0, 0}}; | 
 |     SkScalar step = 1; | 
 |     SkScalar prior, length, degenerate; | 
 |     do { | 
 |         prior = points[0].fX; | 
 |         step /= 2; | 
 |         if (SkPath::IsCubicDegenerate(points[0], points[1], points[2], points[3], false)) { | 
 |             degenerate = prior; | 
 |             points[0].fX += step; | 
 |         } else { | 
 |             length = prior; | 
 |             points[0].fX -= step; | 
 |         } | 
 |     } while (prior != points[0].fX); | 
 |     SkDebugf("%1.8g is degenerate\n", degenerate); | 
 |     SkDebugf("%1.8g is length\n", length); | 
 | } | 
 | #StdOut | 
 | 0.00024414062 is degenerate | 
 | 0.00024414065 is length | 
 | #StdOut ## | 
 | ## | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isLine(SkPoint line[2]) const | 
 |  | 
 | Returns true if Path contains only one Line; | 
 | Path_Verb array has two entries: kMove_Verb, kLine_Verb.  | 
 | If Path contains one Line and line is not nullptr, line is set to  | 
 | Line start point and Line end point. | 
 | Returns false if Path is not one Line; line is unaltered. | 
 |  | 
 | #Param line  storage for Line. May be nullptr ## | 
 |  | 
 | #Return  true if Path contains exactly one Line ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |         SkPoint line[2]; | 
 |         if (path.isLine(line)) { | 
 |             SkDebugf("%s is line (%1.8g,%1.8g) (%1.8g,%1.8g)\n", prefix, | 
 |                  line[0].fX, line[0].fY, line[1].fX, line[1].fY); | 
 |         } else { | 
 |             SkDebugf("%s is not line\n", prefix); | 
 |         } | 
 |     }; | 
 |     SkPath path; | 
 |     debugster("empty", path); | 
 |     path.lineTo(0, 0); | 
 |     debugster("zero line", path); | 
 |     path.rewind(); | 
 |     path.moveTo(10, 10); | 
 |     path.lineTo(20, 20); | 
 |     debugster("line", path); | 
 |     path.moveTo(20, 20); | 
 |     debugster("second move", path); | 
 | } | 
 | #StdOut | 
 | empty is not line | 
 | zero line is line (0,0) (0,0) | 
 | line is line (10,10) (20,20) | 
 | second move is not line | 
 | ## | 
 | ## | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Subtopic Point_Array | 
 | #Substitute SkPoint array | 
 |  | 
 | Point_Array contains Points satisfying the allocated Points for | 
 | each Verb in Verb_Array. For instance, Path containing one Contour with Line | 
 | and Quad is described by Verb_Array: Verb::kMoveTo, Verb::kLineTo, Verb::kQuadTo; and | 
 | one Point for move, one Point for Line, two Points for Quad; totaling four Points. | 
 |  | 
 | Point_Array may be read directly from Path with getPoints, or inspected with | 
 | getPoint, with Iter, or with RawIter. | 
 |  | 
 | #Method int getPoints(SkPoint points[], int max) const | 
 |  | 
 | Returns number of points in Path. Up to max points are copied. | 
 | points may be nullptr; then, max must be zero. | 
 | If max is greater than number of points, excess points storage is unaltered.  | 
 |  | 
 | #Param points  storage for Path Point array. May be nullptr ## | 
 | #Param max  maximum to copy; must be greater than or equal to zero ## | 
 |  | 
 | #Return  Path Point array length ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path, SkPoint* points, int max) -> void { | 
 |          int count = path.getPoints(points, max); | 
 |          SkDebugf("%s point count: %d  ", prefix, count); | 
 |          for (int i = 0; i < SkTMin(count, max) && points; ++i) { | 
 |              SkDebugf("(%1.8g,%1.8g) ", points[i].fX, points[i].fY); | 
 |          } | 
 |          SkDebugf("\n"); | 
 |     }; | 
 |     SkPath path; | 
 |     path.lineTo(20, 20); | 
 |     path.lineTo(-10, -10); | 
 |     SkPoint points[3]; | 
 |     debugster("no points",  path, nullptr, 0); | 
 |     debugster("zero max",  path, points, 0); | 
 |     debugster("too small",  path, points, 2); | 
 |     debugster("just right",  path, points, path.countPoints()); | 
 | } | 
 | #StdOut | 
 | no points point count: 3 | 
 | zero max point count: 3 | 
 | too small point count: 3  (0,0) (20,20) | 
 | just right point count: 3  (0,0) (20,20) (-10,-10) | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso countPoints getPoint | 
 | ## | 
 |  | 
 | #Method int countPoints() const | 
 |  | 
 | Returns the number of points in Path. | 
 | Point count is initially zero.  | 
 |  | 
 | #Return  Path Point array length ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |          SkDebugf("%s point count: %d\n", prefix, path.countPoints()); | 
 |     }; | 
 |     SkPath path; | 
 |     debugster("empty", path); | 
 |     path.lineTo(0, 0); | 
 |     debugster("zero line", path); | 
 |     path.rewind(); | 
 |     path.moveTo(10, 10); | 
 |     path.lineTo(20, 20); | 
 |     debugster("line", path); | 
 |     path.moveTo(20, 20); | 
 |     debugster("second move", path); | 
 | } | 
 | #StdOut | 
 | empty point count: 0 | 
 | zero line point count: 2 | 
 | line point count: 2 | 
 | second move point count: 3 | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso getPoints | 
 | ## | 
 |  | 
 | #Method SkPoint getPoint(int index) const | 
 |  | 
 | Returns Point at index in Point_Array. Valid range for index is | 
 | 0 to countPoints - 1. | 
 | Returns (0, 0) if index is out of range.  | 
 |  | 
 | #Param index  Point array element selector ## | 
 |  | 
 | #Return  Point array value or (0, 0) ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |          SkDebugf("%s point count: %d\n", prefix, path.countPoints()); | 
 |     }; | 
 |     SkPath path; | 
 |     path.lineTo(20, 20); | 
 |     path.offset(-10, -10); | 
 |     for (int i= 0; i < path.countPoints(); ++i) { | 
 |          SkDebugf("point %d: (%1.8g,%1.8g)\n", i, path.getPoint(i).fX, path.getPoint(i).fY); | 
 |     }   | 
 | } | 
 | #StdOut | 
 | point 0: (-10,-10) | 
 | point 1: (10,10) | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso countPoints getPoints | 
 | ## | 
 |  | 
 |  | 
 | #Subtopic Point_Array ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 | #Subtopic Verb_Array | 
 |  | 
 | Verb_Array always starts with kMove_Verb. | 
 | If kClose_Verb is not the last entry, it is always followed by kMove_Verb; | 
 | the quantity of kMove_Verb equals the Contour count. | 
 | Verb_Array does not include or count kDone_Verb; it is a convenience | 
 | returned when iterating through Verb_Array. | 
 |  | 
 | Verb_Array may be read directly from Path with getVerbs, or inspected with Iter,  | 
 | or with RawIter. | 
 |  | 
 | #Method int countVerbs() const | 
 |  | 
 | Returns the number of Verbs: kMove_Verb, kLine_Verb, kQuad_Verb, kConic_Verb,  | 
 | kCubic_Verb, and kClose_Verb; added to Path. | 
 |  | 
 | #Return  length of Verb_Array ## | 
 |  | 
 | #Example | 
 | SkPath path; | 
 | SkDebugf("empty verb count: %d\n", path.countVerbs()); | 
 | path.addRoundRect({10, 20, 30, 40}, 5, 5); | 
 | SkDebugf("round rect verb count: %d\n", path.countVerbs()); | 
 | #StdOut | 
 | empty verb count: 0 | 
 | round rect verb count: 10 | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso getVerbs Iter RawIter | 
 |  | 
 | ## | 
 |  | 
 | #Method int getVerbs(uint8_t verbs[], int max) const | 
 |  | 
 | Returns the number of verbs in the path. Up to max verbs are copied. The | 
 | verbs are copied as one byte per verb. | 
 |  | 
 | #Param verbs  storage for verbs, may be nullptr ## | 
 | #Param max    maximum number to copy into verbs ## | 
 |  | 
 | #Return  the actual number of verbs in the path ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path, uint8_t* verbs, int max) -> void { | 
 |          int count = path.getVerbs(verbs, max); | 
 |          SkDebugf("%s verb count: %d  ", prefix, count); | 
 |          const char* verbStr[] = { "move", "line", "quad", "conic", "cubic", "close" }; | 
 |          for (int i = 0; i < SkTMin(count, max) && verbs; ++i) { | 
 |              SkDebugf("%s ", verbStr[verbs[i]]); | 
 |          } | 
 |          SkDebugf("\n"); | 
 |     }; | 
 |     SkPath path; | 
 |     path.lineTo(20, 20); | 
 |     path.lineTo(-10, -10); | 
 |     uint8_t verbs[3]; | 
 |     debugster("no verbs",  path, nullptr, 0); | 
 |     debugster("zero max",  path, verbs, 0); | 
 |     debugster("too small",  path, verbs, 2); | 
 |     debugster("just right",  path, verbs, path.countVerbs()); | 
 | } | 
 | #StdOut | 
 | no verbs verb count: 3   | 
 | zero max verb count: 3   | 
 | too small verb count: 3  move line  | 
 | just right verb count: 3  move line line  | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso countVerbs getPoints Iter RawIter | 
 | ## | 
 |  | 
 | #Subtopic Verb_Array ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void swap(SkPath& other) | 
 |  | 
 | Exchanges the Verb_Array, Point_Array, Weights, and Fill_Type with other. | 
 | Cached state is also exchanged. swap() internally exchanges pointers, so | 
 | it is lightweight and does not allocate memory. | 
 |  | 
 | swap() usage has largely been replaced by operator=(const SkPath& path). | 
 | Paths do not copy their content on assignment until they are written to, | 
 | making assignment as efficient as swap(). | 
 |  | 
 | #Param other  Path exchanged by value ## | 
 |  | 
 | #Example | 
 | SkPath path1, path2; | 
 | path1.addRect({10, 20, 30, 40}); | 
 | path1.swap(path2); | 
 | const SkRect& b1 = path1.getBounds(); | 
 | SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom); | 
 | const SkRect& b2 = path2.getBounds(); | 
 | SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom); | 
 | #StdOut | 
 | path1 bounds = 0, 0, 0, 0 | 
 | path2 bounds = 10, 20, 30, 40 | 
 | #StdOut ## | 
 | ## | 
 |  | 
 | #SeeAlso operator=(const SkPath& path) | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method const SkRect& getBounds() const  | 
 |  | 
 | Returns minimum and maximum x and y values of Point_Array.  | 
 | Returns (0, 0, 0, 0) if Path contains no points. Returned bounds width and height may | 
 | be larger or smaller than area affected when Path is drawn. | 
 |  | 
 | Rect returned includes all Points added to Path, including Points associated with | 
 | kMove_Verb that define empty Contours. | 
 |  | 
 | #Return  bounds of all Points in Point_Array ## | 
 |  | 
 | #Example | 
 | #Description | 
 | Bounds of upright Circle can be predicted from center and radius. | 
 | Bounds of rotated Circle includes control Points outside of filled area. | 
 | ## | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |             const SkRect& bounds = path.getBounds(); | 
 |             SkDebugf("%s bounds = %g, %g, %g, %g\n", prefix,  | 
 |                      bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); | 
 |     }; | 
 |     SkPath path; | 
 |     debugster("empty", path); | 
 |     path.addCircle(50, 45, 25); | 
 |     debugster("circle", path); | 
 |     SkMatrix matrix; | 
 |     matrix.setRotate(45, 50, 45); | 
 |     path.transform(matrix); | 
 |     debugster("rotated circle", path); | 
 | #StdOut | 
 | empty bounds = 0, 0, 0, 0 | 
 | circle bounds = 25, 20, 75, 70 | 
 | rotated circle bounds = 14.6447, 9.64466, 85.3553, 80.3553 | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso computeTightBounds updateBoundsCache | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void updateBoundsCache() const  | 
 |  | 
 | Update internal bounds so that subsequent calls to getBounds are instantaneous. | 
 | Unaltered copies of Path may also access cached bounds through getBounds. | 
 |  | 
 | For now, identical to calling getBounds and ignoring the returned value. | 
 |  | 
 | Call to prepare Path subsequently drawn from multiple threads,  | 
 | to avoid a race condition where each draw separately computes the bounds. | 
 |  | 
 | #Example | 
 |     double times[2] = { 0, 0 }; | 
 |     for (int i = 0; i < 10000; ++i) { | 
 |       SkPath path; | 
 |       for (int j = 1; j < 100; ++ j) { | 
 |         path.addCircle(50 + j, 45 + j, 25 + j); | 
 |       } | 
 |       if (1 & i) { | 
 |         path.updateBoundsCache(); | 
 |       } | 
 |       double start = SkTime::GetNSecs(); | 
 |       (void) path.getBounds(); | 
 |       times[1 & i] += SkTime::GetNSecs() - start; | 
 |     } | 
 |     SkDebugf("uncached avg: %g ms\n", times[0] * 1e-6); | 
 |     SkDebugf("cached avg: %g ms\n", times[1] * 1e-6); | 
 | #StdOut | 
 | #Volatile | 
 | uncached avg: 0.18048 ms | 
 | cached avg: 0.182784 ms | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso getBounds | 
 | #ToDo the results don't make sense, need to profile to figure this out ## | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method SkRect computeTightBounds() const | 
 |  | 
 | Returns minimum and maximum x and y values of the lines and curves in Path. | 
 | Returns (0, 0, 0, 0) if Path contains no points.  | 
 | Returned bounds width and height may be larger or smaller than area affected  | 
 | when Path is drawn. | 
 |  | 
 | Includes Points associated with kMove_Verb that define empty | 
 | Contours. | 
 |  | 
 | Behaves identically to getBounds when Path contains | 
 | only lines. If Path contains curves, computed bounds includes | 
 | the maximum extent of the Quad, Conic, or Cubic; is slower than getBounds; | 
 | and unlike getBounds, does not cache the result. | 
 |  | 
 | #Return  tight bounds of curves in Path ## | 
 |  | 
 | #Example | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |             const SkRect& bounds = path.computeTightBounds(); | 
 |             SkDebugf("%s bounds = %g, %g, %g, %g\n", prefix,  | 
 |                      bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); | 
 |     }; | 
 |     SkPath path; | 
 |     debugster("empty", path); | 
 |     path.addCircle(50, 45, 25); | 
 |     debugster("circle", path); | 
 |     SkMatrix matrix; | 
 |     matrix.setRotate(45, 50, 45); | 
 |     path.transform(matrix); | 
 |     debugster("rotated circle", path); | 
 | #StdOut | 
 | empty bounds = 0, 0, 0, 0 | 
 | circle bounds = 25, 20, 75, 70 | 
 | rotated circle bounds = 25, 20, 75, 70 | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso getBounds | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool conservativelyContainsRect(const SkRect& rect) const | 
 |  | 
 | Returns true if rect is contained by Path.  | 
 | May return false when rect is contained by Path. | 
 |  | 
 | For now, only returns true if Path has one Contour and is convex. | 
 | rect may share points and edges with Path and be contained. | 
 | Returns true if rect is empty, that is, it has zero width or height; and | 
 | the Point or Line described by rect is contained by Path. | 
 |  | 
 | #Param rect  Rect, Line, or Point checked for containment ## | 
 |  | 
 | #Return  true if rect is contained ## | 
 |  | 
 | #Example | 
 | #Height 140 | 
 | #Description | 
 | Rect is drawn in blue if it is contained by red Path. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPath path; | 
 |     path.addRoundRect({10, 20, 54, 120}, 10, 20); | 
 |     SkRect tests[] = { | 
 |       { 10, 40, 54, 80 }, | 
 |       { 25, 20, 39, 120 }, | 
 |       { 15, 25, 49, 115 }, | 
 |       { 13, 27, 51, 113 }, | 
 |     }; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); ++i) { | 
 |       SkPaint paint; | 
 |       paint.setColor(SK_ColorRED); | 
 |       canvas->drawPath(path, paint); | 
 |       bool rectInPath = path.conservativelyContainsRect(tests[i]); | 
 |       paint.setColor(rectInPath ? SK_ColorBLUE : SK_ColorBLACK); | 
 |       canvas->drawRect(tests[i], paint); | 
 |       canvas->translate(64, 0); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso contains Op Rect Convexity | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void incReserve(unsigned extraPtCount) | 
 |  | 
 | grows Path Verb_Array and Point_Array to contain extraPtCount additional Points. | 
 | May improve performance and use less memory by | 
 | reducing the number and size of allocations when creating Path. | 
 |  | 
 | #Param extraPtCount  number of additional Points to allocate ## | 
 |  | 
 | #Example | 
 | #Height 192 | 
 | void draw(SkCanvas* canvas) { | 
 |     auto addPoly = [](int sides, SkScalar size, SkPath* path) -> void { | 
 |         path->moveTo(size, 0); | 
 |         for (int i = 1; i < sides; i++) { | 
 |             SkScalar c, s = SkScalarSinCos(SK_ScalarPI * 2 * i / sides, &c); | 
 |             path->lineTo(c * size, s * size); | 
 |         } | 
 |         path->close(); | 
 |     }; | 
 |     SkPath path; | 
 |     path.incReserve(3 + 4 + 5 + 6 + 7 + 8 + 9); | 
 |     for (int sides = 3; sides < 10; ++sides) { | 
 |        addPoly(sides, sides, &path); | 
 |     } | 
 |     SkMatrix matrix; | 
 |     matrix.setScale(10, 10, -10, -10); | 
 |     path.transform(matrix); | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Point_Array | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void moveTo(SkScalar x, SkScalar y) | 
 |  | 
 | Adds beginning of Contour at Point (x, y). | 
 |  | 
 | #Param x  x-coordinate of Contour start ## | 
 | #Param y  y-coordinate of Contour start ## | 
 |  | 
 | #Example | 
 |     #Width 140 | 
 |     #Height 100 | 
 |     void draw(SkCanvas* canvas) { | 
 |         SkRect rect = { 20, 20, 120, 80 }; | 
 |         SkPath path; | 
 |         path.addRect(rect); | 
 |         path.moveTo(rect.fLeft, rect.fTop); | 
 |         path.lineTo(rect.fRight, rect.fBottom); | 
 |         path.moveTo(rect.fLeft, rect.fBottom); | 
 |         path.lineTo(rect.fRight, rect.fTop); | 
 |         SkPaint paint; | 
 |         paint.setStyle(SkPaint::kStroke_Style); | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 | ## | 
 |  | 
 | #SeeAlso Contour lineTo rMoveTo quadTo conicTo cubicTo close() | 
 |  | 
 | ## | 
 |  | 
 | #Method void moveTo(const SkPoint& p)  | 
 |  | 
 | Adds beginning of Contour at Point p. | 
 |  | 
 | #Param p  contour start ## | 
 |  | 
 | #Example | 
 |     #Width 128 | 
 |     #Height 128 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPoint data[][3] = {{{30,40},{60,60},{90,30}}, {{30,120},{60,100},{90,120}},  | 
 |                          {{60,100},{60,40},{70,30}}, {{60,40},{50,20},{70,30}}}; | 
 |     SkPath path; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(data); ++i) { | 
 |         path.moveTo(data[i][0]); | 
 |         path.lineTo(data[i][1]); | 
 |         path.lineTo(data[i][2]); | 
 |     } | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Contour lineTo rMoveTo quadTo conicTo cubicTo close() | 
 |  | 
 | ## | 
 |  | 
 | #Method void rMoveTo(SkScalar dx, SkScalar dy) | 
 |  | 
 | Adds beginning of Contour relative to Last_Point. | 
 | If Path is empty, starts Contour at (dx, dy). | 
 | Otherwise, start Contour at Last_Point offset by (dx, dy).  | 
 | Function name stands for "relative move to". | 
 |  | 
 | #Param dx  offset from Last_Point x to Contour start x ## | 
 | #Param dy  offset from Last_Point y to Contour start y ## | 
 |  | 
 | #Example | 
 |     #Height 100 | 
 |     SkPath path; | 
 |     path.addRect({20, 20, 80, 80}, SkPath::kCW_Direction, 2); | 
 |     path.rMoveTo(25, 2); | 
 |     SkVector arrow[] = {{0, -4}, {-20, 0}, {0, -3}, {-5, 5}, {5, 5}, {0, -3}, {20, 0}}; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(arrow); ++i) { | 
 |         path.rLineTo(arrow[i].fX, arrow[i].fY); | 
 |     } | 
 |     SkPaint paint; | 
 |     canvas->drawPath(path, paint); | 
 |     SkPoint lastPt; | 
 |     path.getLastPt(&lastPt); | 
 |     canvas->drawString("start", lastPt.fX, lastPt.fY, paint); | 
 | ## | 
 |  | 
 | #SeeAlso Contour lineTo moveTo quadTo conicTo cubicTo close() | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void lineTo(SkScalar x, SkScalar y) | 
 |  | 
 | Adds Line from Last_Point to (x, y). If Path is empty, or last Verb is | 
 | kClose_Verb, Last_Point is set to (0, 0) before adding Line. | 
 |  | 
 | lineTo appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. | 
 | lineTo then appends kLine_Verb to Verb_Array and (x, y) to Point_Array. | 
 |   | 
 | #Param x  end of added Line in x ## | 
 | #Param y  end of added Line in y ## | 
 |  | 
 | #Example | 
 | #Height 100 | 
 | ###$ | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setTextSize(72); | 
 |     canvas->drawString("#", 120, 80, paint); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setStrokeWidth(5); | 
 |     SkPath path; | 
 |     SkPoint hash[] = {{58, 28}, {43, 80}, {37, 45}, {85, 45}}; | 
 |     SkVector offsets[] = {{0, 0}, {17, 0}, {0, 0}, {-5, 17}}; | 
 |     unsigned o = 0; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(hash); i += 2) { | 
 |         for (unsigned j = 0; j < 2; o++, j++) { | 
 |             path.moveTo(hash[i].fX + offsets[o].fX, hash[i].fY + offsets[o].fY); | 
 |             path.lineTo(hash[i + 1].fX + offsets[o].fX, hash[i + 1].fY + offsets[o].fY); | 
 |         } | 
 |     } | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | $$$# | 
 | ## | 
 |  | 
 | #SeeAlso Contour moveTo rLineTo addRect | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void lineTo(const SkPoint& p)  | 
 |  | 
 | Adds Line from Last_Point to Point p. If Path is empty, or last Verb is | 
 | kClose_Verb, Last_Point is set to (0, 0) before adding Line. | 
 |  | 
 | lineTo first appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. | 
 | lineTo then appends kLine_Verb to Verb_Array and Point p to Point_Array. | 
 |  | 
 | #Param p  end Point of added Line ## | 
 |  | 
 | #Example | 
 | #Height 100 | 
 |     SkPath path; | 
 |     SkVector oxo[] = {{25, 25}, {35, 35}, {25, 35}, {35, 25}, | 
 |                       {40, 20}, {40, 80}, {60, 20}, {60, 80}, | 
 |                       {20, 40}, {80, 40}, {20, 60}, {80, 60}}; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(oxo); i += 2) { | 
 |         path.moveTo(oxo[i]); | 
 |         path.lineTo(oxo[i + 1]); | 
 |     } | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     canvas->drawPath(path, paint); | 
 | ## | 
 |  | 
 | #SeeAlso Contour moveTo rLineTo addRect | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void rLineTo(SkScalar dx, SkScalar dy) | 
 |  | 
 | Adds Line from Last_Point to Vector (dx, dy). If Path is empty, or last Verb is | 
 | kClose_Verb, Last_Point is set to (0, 0) before adding Line. | 
 |  | 
 | Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed; | 
 | then appends kLine_Verb to Verb_Array and Line end to Point_Array. | 
 | Line end is Last_Point plus Vector (dx, dy). | 
 | Function name stands for "relative line to". | 
 |  | 
 | #Param dx  offset from Last_Point x to Line end x ## | 
 | #Param dy  offset from Last_Point y to Line end y ## | 
 |  | 
 | #Example | 
 | #Height 128 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPath path; | 
 |     path.moveTo(10, 98); | 
 |     SkScalar x = 0, y = 0; | 
 |     for (int i = 10; i < 100; i += 5) { | 
 |         x += i * ((i & 2) - 1); | 
 |         y += i * (((i + 1) & 2) - 1); | 
 |         path.rLineTo(x, y); | 
 |          | 
 |     } | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Contour moveTo lineTo addRect | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 | #Topic Quad | 
 | #Alias Quad | 
 | #Alias Quads | 
 | #Alias Quadratic_Bezier | 
 | #Alias Quadratic_Beziers | 
 |  | 
 | Quad describes a quadratic Bezier, a second-order curve identical to a section | 
 | of a parabola. Quad begins at a start Point, curves towards a control Point, | 
 | and then curves to an end Point. | 
 |  | 
 | #Example | 
 | #Height 110 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPoint quadPts[] = {{20, 90}, {120, 10}, {220, 90}}; | 
 |     canvas->drawLine(quadPts[0], quadPts[1], paint); | 
 |     canvas->drawLine(quadPts[1], quadPts[2], paint); | 
 |     SkPath path; | 
 |     path.moveTo(quadPts[0]); | 
 |     path.quadTo(quadPts[1], quadPts[2]); | 
 |     paint.setStrokeWidth(3); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | Quad is a special case of Conic where Conic_Weight is set to one. | 
 |  | 
 | Quad is always contained by the triangle connecting its three Points. Quad | 
 | begins tangent to the line between start Point and control Point, and ends | 
 | tangent to the line between control Point and end Point. | 
 |  | 
 | #Example | 
 | #Height 160 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPoint quadPts[] = {{20, 150}, {120, 10}, {220, 150}}; | 
 |     SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 }; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) { | 
 |         paint.setColor(0x7fffffff & colors[i]); | 
 |         paint.setStrokeWidth(1); | 
 |         canvas->drawLine(quadPts[0], quadPts[1], paint); | 
 |         canvas->drawLine(quadPts[1], quadPts[2], paint); | 
 |         SkPath path; | 
 |         path.moveTo(quadPts[0]); | 
 |         path.quadTo(quadPts[1], quadPts[2]); | 
 |         paint.setStrokeWidth(3); | 
 |         paint.setColor(colors[i]); | 
 |         canvas->drawPath(path, paint); | 
 |         quadPts[1].fY += 30; | 
 |    } | 
 | } | 
 | ## | 
 |  | 
 | #Method void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) | 
 |  | 
 |     Adds Quad from Last_Point towards (x1, y1), to (x2, y2).  | 
 |     If Path is empty, or last Verb is kClose_Verb, Last_Point is set to (0, 0) | 
 |     before adding Quad. | 
 |  | 
 |     Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed; | 
 |     then appends kQuad_Verb to Verb_Array; and (x1, y1), (x2, y2) | 
 |     to Point_Array. | 
 |  | 
 |     #Param x1  control Point of Quad in x ## | 
 |     #Param y1  control Point of Quad in y ## | 
 |     #Param x2  end Point of Quad in x ## | 
 |     #Param y2  end Point of Quad in y ## | 
 |  | 
 |     #Example | 
 |     void draw(SkCanvas* canvas) { | 
 |         SkPaint paint; | 
 |         paint.setAntiAlias(true); | 
 |         paint.setStyle(SkPaint::kStroke_Style); | 
 |         SkPath path; | 
 |         path.moveTo(0, -10); | 
 |         for (int i = 0; i < 128; i += 16) { | 
 |             path.quadTo( 10 + i, -10 - i,  10 + i,       0); | 
 |             path.quadTo( 14 + i,  14 + i,       0,  14 + i); | 
 |             path.quadTo(-18 - i,  18 + i, -18 - i,       0); | 
 |             path.quadTo(-22 - i, -22 - i,       0, -22 - i); | 
 |         } | 
 |         path.offset(128, 128); | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 |     ## | 
 |  | 
 |     #SeeAlso Contour moveTo conicTo rQuadTo | 
 |  | 
 | ## | 
 |  | 
 | #Method void quadTo(const SkPoint& p1, const SkPoint& p2)  | 
 |  | 
 |     Adds Quad from Last_Point towards Point p1, to Point p2.  | 
 |     If Path is empty, or last Verb is kClose_Verb, Last_Point is set to (0, 0) | 
 |     before adding Quad. | 
 |  | 
 |     Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed; | 
 |     then appends kQuad_Verb to Verb_Array; and Points p1, p2 | 
 |     to Point_Array. | 
 |  | 
 |     #Param p1  control Point of added Quad ## | 
 |     #Param p2  end Point of added Quad ## | 
 |  | 
 |     #Example | 
 |     void draw(SkCanvas* canvas) { | 
 |         SkPaint paint; | 
 |         paint.setStyle(SkPaint::kStroke_Style); | 
 |         paint.setAntiAlias(true); | 
 |         SkPath path; | 
 |         SkPoint pts[] = {{128, 10}, {10, 214}, {236, 214}}; | 
 |         path.moveTo(pts[1]); | 
 |         for (int i = 0; i < 3; ++i) { | 
 |             path.quadTo(pts[i % 3],  pts[(i + 2) % 3]); | 
 |         } | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 |     ## | 
 |  | 
 |     #SeeAlso Contour moveTo conicTo rQuadTo | 
 |  | 
 | ## | 
 |  | 
 | #Method void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2) | 
 |  | 
 |     Adds Quad from Last_Point towards Vector (dx1, dy1), to Vector (dx2, dy2). | 
 |     If Path is empty, or last Verb | 
 |     is kClose_Verb, Last_Point is set to (0, 0) before adding Quad. | 
 |  | 
 |     Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, | 
 |     if needed; then appends kQuad_Verb to Verb_Array; and appends Quad | 
 |     control and Quad end to Point_Array. | 
 |     Quad control is Last_Point plus Vector (dx1, dy1). | 
 |     Quad end is Last_Point plus Vector (dx2, dy2). | 
 |     Function name stands for "relative quad to". | 
 |  | 
 |     #Param dx1  offset from Last_Point x to Quad control x ## | 
 |     #Param dy1  offset from Last_Point x to Quad control y ## | 
 |     #Param dx2  offset from Last_Point x to Quad end x ## | 
 |     #Param dy2  offset from Last_Point x to Quad end y ## | 
 |  | 
 |     #Example | 
 |     void draw(SkCanvas* canvas) { | 
 |         SkPaint paint; | 
 |         paint.setAntiAlias(true); | 
 |         SkPath path; | 
 |         path.moveTo(128, 20); | 
 |         path.rQuadTo(-6, 10, -7, 10); | 
 |         for (int i = 1; i < 32; i += 4) { | 
 |            path.rQuadTo(10 + i, 10 + i, 10 + i * 4, 10); | 
 |            path.rQuadTo(-10 - i, 10 + i, -10 - (i + 2) * 4, 10); | 
 |         } | 
 |         path.quadTo(92, 220, 128, 215); | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 |     ## | 
 |  | 
 |     #SeeAlso Contour moveTo conicTo quadTo | 
 |  | 
 | ## | 
 |  | 
 | #Topic Quad ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Topic Conic | 
 | #Alias Conics | 
 |  | 
 | Conic describes a conical section: a piece of an ellipse, or a piece of a | 
 | parabola, or a piece of a hyperbola. Conic begins at a start Point,  | 
 | curves towards a control Point, and then curves to an end Point. The influence | 
 | of the control Point is determined by Conic_Weight. | 
 |  | 
 | Each Conic in Path adds two Points and one Conic_Weight. Conic_Weights in Path | 
 | may be inspected with Iter, or with RawIter. | 
 |  | 
 | #Subtopic Weight | 
 | #Alias Conic_Weights | 
 | #Alias Weights | 
 |  | 
 | Weight determines both the strength of the control Point and the type of Conic. | 
 | If Weight is exactly one, then Conic is identical to Quad; it is always a | 
 | parabolic segment.  | 
 |  | 
 |  | 
 |  | 
 | #Example | 
 | #Description | 
 | When Conic_Weight is one, Quad is added to path; the two are identical.  | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" }; | 
 |     const int pointCount[]  = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  }; | 
 |     SkPath path; | 
 |     path.conicTo(20, 30, 50, 60, 1); | 
 |     SkPath::Iter iter(path, false); | 
 |     SkPath::Verb verb; | 
 |     do { | 
 |        SkPoint points[4]; | 
 |        verb = iter.next(points); | 
 |        SkDebugf("%s ", verbNames[(int) verb]); | 
 |        for (int i = 0; i < pointCount[(int) verb]; ++i) { | 
 |             SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); | 
 |        } | 
 |        if (SkPath::kConic_Verb == verb) { | 
 |            SkDebugf("weight = %g", iter.conicWeight()); | 
 |        } | 
 |        SkDebugf("\n"); | 
 |     } while (SkPath::kDone_Verb != verb); | 
 | } | 
 | #StdOut | 
 | move {0, 0},  | 
 | quad {0, 0}, {20, 30}, {50, 60},  | 
 | done  | 
 | ## | 
 | ## | 
 |  | 
 | If weight is less than one, Conic is an elliptical segment. | 
 |  | 
 | #Example  | 
 | #Description | 
 | A 90 degree circular arc has the weight  | 
 | #Formula | 
 | 1 / sqrt(2) | 
 | ## | 
 | . | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" }; | 
 |     const int pointCount[]  = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  }; | 
 |     SkPath path; | 
 |     path.arcTo(20, 0, 20, 20, 20); | 
 |     SkPath::Iter iter(path, false); | 
 |     SkPath::Verb verb; | 
 |     do { | 
 |        SkPoint points[4]; | 
 |        verb = iter.next(points); | 
 |        SkDebugf("%s ", verbNames[(int) verb]); | 
 |        for (int i = 0; i < pointCount[(int) verb]; ++i) { | 
 |             SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); | 
 |        } | 
 |        if (SkPath::kConic_Verb == verb) { | 
 |            SkDebugf("weight = %g", iter.conicWeight()); | 
 |        } | 
 |        SkDebugf("\n"); | 
 |     } while (SkPath::kDone_Verb != verb); | 
 | } | 
 | #StdOut | 
 | move {0, 0},  | 
 | conic {0, 0}, {20, 0}, {20, 20}, weight = 0.707107 | 
 | done  | 
 | ## | 
 | ## | 
 |  | 
 | If weight is greater than one, Conic is a hyperbolic segment. As weight gets large, | 
 | a hyperbolic segment can be approximated by straight lines connecting the | 
 | control Point with the end Points. | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" }; | 
 |     const int pointCount[]  = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  }; | 
 |     SkPath path; | 
 |     path.conicTo(20, 0, 20, 20, SK_ScalarInfinity); | 
 |     SkPath::Iter iter(path, false); | 
 |     SkPath::Verb verb; | 
 |     do { | 
 |        SkPoint points[4]; | 
 |        verb = iter.next(points); | 
 |        SkDebugf("%s ", verbNames[(int) verb]); | 
 |        for (int i = 0; i < pointCount[(int) verb]; ++i) { | 
 |             SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); | 
 |        } | 
 |        if (SkPath::kConic_Verb == verb) { | 
 |            SkDebugf("weight = %g", iter.conicWeight()); | 
 |        } | 
 |        SkDebugf("\n"); | 
 |     } while (SkPath::kDone_Verb != verb); | 
 | } | 
 | #StdOut | 
 | move {0, 0},  | 
 | line {0, 0}, {20, 0},  | 
 | line {20, 0}, {20, 20},  | 
 | done  | 
 | ## | 
 | ## | 
 |  | 
 | #Subtopic Weight ## | 
 |  | 
 | #Method void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, | 
 |                  SkScalar w) | 
 |  | 
 |     Adds Conic from Last_Point towards (x1, y1), to (x2, y2), weighted by w.  | 
 |     If Path is empty, or last Verb is kClose_Verb, Last_Point is set to (0, 0) | 
 |     before adding Conic. | 
 |  | 
 |     Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. | 
 |  | 
 |     If w is finite and not one, appends kConic_Verb to Verb_Array; | 
 |     and (x1, y1), (x2, y2) to Point_Array; and w to Conic_Weights. | 
 |  | 
 |     If w is one, appends kQuad_Verb to Verb_Array, and | 
 |     (x1, y1), (x2, y2) to Point_Array. | 
 |  | 
 |     If w is not finite, appends kLine_Verb twice to Verb_Array, and | 
 |     (x1, y1), (x2, y2) to Point_Array. | 
 |  | 
 |     #Param x1  control Point of Conic in x ## | 
 |     #Param y1  control Point of Conic in y ## | 
 |     #Param x2  end Point of Conic in x ## | 
 |     #Param y2  end Point of Conic in y ## | 
 |     #Param w   weight of added Conic ## | 
 |  | 
 |     #Example | 
 |     #Height 160 | 
 |     #Description | 
 |     As weight increases, curve is pulled towards control point.  | 
 |     The bottom two curves are elliptical; the next is parabolic; the | 
 |     top curve is hyperbolic. | 
 |     ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPoint conicPts[] = {{20, 150}, {120, 10}, {220, 150}}; | 
 |     canvas->drawLine(conicPts[0], conicPts[1], paint); | 
 |     canvas->drawLine(conicPts[1], conicPts[2], paint); | 
 |     SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 }; | 
 |     paint.setStrokeWidth(3); | 
 |     SkScalar weight = 0.5f; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) { | 
 |         SkPath path; | 
 |         path.moveTo(conicPts[0]); | 
 |         path.conicTo(conicPts[1], conicPts[2], weight); | 
 |         paint.setColor(colors[i]); | 
 |         canvas->drawPath(path, paint); | 
 |         weight += 0.25f; | 
 |    } | 
 | } | 
 |     ## | 
 |  | 
 |     #SeeAlso rConicTo arcTo addArc quadTo | 
 |  | 
 | ## | 
 |  | 
 | #Method void conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w)  | 
 |  | 
 |     Adds Conic from Last_Point towards Point p1, to Point p2, weighted by w.  | 
 |     If Path is empty, or last Verb is kClose_Verb, Last_Point is set to (0, 0) | 
 |     before adding Conic. | 
 |  | 
 |     Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. | 
 |  | 
 |     If w is finite and not one, appends kConic_Verb to Verb_Array; | 
 |     and Points p1, p2 to Point_Array; and w to Conic_Weights. | 
 |  | 
 |     If w is one, appends kQuad_Verb to Verb_Array, and Points p1, p2 | 
 |     to Point_Array. | 
 |  | 
 |     If w is not finite, appends kLine_Verb twice to Verb_Array, and | 
 |     Points p1, p2 to Point_Array. | 
 |  | 
 |     #Param p1  control Point of added Conic ## | 
 |     #Param p2  end Point of added Conic ## | 
 |     #Param w   weight of added Conic ## | 
 |  | 
 |     #Example | 
 |     #Height 128 | 
 |     #Description | 
 |     Conics and arcs use identical representations. As the arc sweep increases | 
 |     the Conic_Weight also increases, but remains smaller than one. | 
 |     ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkRect oval = {0, 20, 120, 140}; | 
 |     SkPath path; | 
 |     for (int i = 0; i < 4; ++i) { | 
 |         path.moveTo(oval.centerX(), oval.fTop); | 
 |         path.arcTo(oval, -90, 90 - 20 * i, false); | 
 |         oval.inset(15, 15); | 
 |     } | 
 |     path.offset(100, 0); | 
 |     SkScalar conicWeights[] = { 0.707107f, 0.819152f, 0.906308f, 0.965926f }; | 
 |     SkPoint conicPts[][3] = { { {40, 20}, {100, 20}, {100, 80} }, | 
 |                               { {40, 35}, {71.509f, 35}, {82.286f, 64.6091f} }, | 
 |                               { {40, 50}, {53.9892f, 50}, {62.981f, 60.7164f} }, | 
 |                               { {40, 65}, {44.0192f, 65}, {47.5f, 67.0096f} } }; | 
 |     for (int i = 0; i < 4; ++i) { | 
 |          path.moveTo(conicPts[i][0]); | 
 |          path.conicTo(conicPts[i][1], conicPts[i][2], conicWeights[i]); | 
 |     } | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 |     ## | 
 |  | 
 |     #SeeAlso rConicTo arcTo addArc quadTo | 
 |  | 
 | ## | 
 |  | 
 | #Method void rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, | 
 |                   SkScalar w) | 
 |  | 
 |     Adds Conic from Last_Point towards Vector (dx1, dy1), to Vector (dx2, dy2), | 
 |     weighted by w. If Path is empty, or last Verb | 
 |     is kClose_Verb, Last_Point is set to (0, 0) before adding Conic. | 
 |  | 
 |     Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, | 
 |     if needed.  | 
 |      | 
 |     If w is finite and not one, next appends kConic_Verb to Verb_Array, | 
 |     and w is recorded as Conic_Weight; otherwise, if w is one, appends | 
 |     kQuad_Verb to Verb_Array; or if w is not finite, appends kLine_Verb | 
 |     twice to Verb_Array. | 
 |  | 
 |     In all cases appends Points control and end to Point_Array. | 
 |     control is Last_Point plus Vector (dx1, dy1). | 
 |     end is Last_Point plus Vector (dx2, dy2). | 
 |  | 
 |     Function name stands for "relative conic to". | 
 |  | 
 |     #Param dx1  offset from Last_Point x to Conic control x ## | 
 |     #Param dy1  offset from Last_Point x to Conic control y ## | 
 |     #Param dx2  offset from Last_Point x to Conic end x  ## | 
 |     #Param dy2  offset from Last_Point x to Conic end y  ## | 
 |     #Param w    weight of added Conic ## | 
 |  | 
 |     #Example | 
 |     #Height 140 | 
 |     void draw(SkCanvas* canvas) { | 
 |         SkPaint paint; | 
 |         paint.setAntiAlias(true); | 
 |         paint.setStyle(SkPaint::kStroke_Style); | 
 |         SkPath path; | 
 |         path.moveTo(20, 80); | 
 |         path.rConicTo( 60,   0,  60,  60, 0.707107f); | 
 |         path.rConicTo(  0, -60,  60, -60, 0.707107f); | 
 |         path.rConicTo(-60,   0, -60, -60, 0.707107f); | 
 |         path.rConicTo(  0,  60, -60,  60, 0.707107f); | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 |     ## | 
 |  | 
 |     #SeeAlso conicTo arcTo addArc quadTo | 
 |  | 
 | ## | 
 |  | 
 | #Topic Conic ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 | #Topic Cubic | 
 | #Alias Cubic | 
 | #Alias Cubics | 
 | #Alias Cubic_Bezier | 
 | #Alias Cubic_Beziers | 
 |  | 
 | Cubic describes a Bezier_Curve segment described by a third-order polynomial.  | 
 | Cubic begins at a start Point, curving towards the first control Point; | 
 | and curves from the end Point towards the second control Point. | 
 |  | 
 | #Example | 
 | #Height 160 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPoint cubicPts[] = {{20, 150}, {90, 10}, {160, 150}, {230, 10}}; | 
 |     SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 }; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) { | 
 |         paint.setColor(0x7fffffff & colors[i]); | 
 |         paint.setStrokeWidth(1); | 
 |         for (unsigned j = 0; j < 3; ++j) { | 
 |             canvas->drawLine(cubicPts[j], cubicPts[j + 1], paint); | 
 |         } | 
 |         SkPath path; | 
 |         path.moveTo(cubicPts[0]); | 
 |         path.cubicTo(cubicPts[1], cubicPts[2], cubicPts[3]); | 
 |         paint.setStrokeWidth(3); | 
 |         paint.setColor(colors[i]); | 
 |         canvas->drawPath(path, paint); | 
 |         cubicPts[1].fY += 30; | 
 |         cubicPts[2].fX += 30; | 
 |    } | 
 | } | 
 | ## | 
 |  | 
 | #Method void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, | 
 |                  SkScalar x3, SkScalar y3) | 
 |  | 
 | Adds Cubic from Last_Point towards (x1, y1), then towards (x2, y2), ending at | 
 | (x3, y3). If Path is empty, or last Verb is kClose_Verb, Last_Point is set to | 
 | (0, 0) before adding Cubic. | 
 |  | 
 | Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed; | 
 | then appends kCubic_Verb to Verb_Array; and (x1, y1), (x2, y2), (x3, y3) | 
 | to Point_Array. | 
 |  | 
 | #Param x1  first control Point of Cubic in x ## | 
 | #Param y1  first control Point of Cubic in y ## | 
 | #Param x2  second control Point of Cubic in x ## | 
 | #Param y2  second control Point of Cubic in y ## | 
 | #Param x3  end Point of Cubic in x ## | 
 | #Param y3  end Point of Cubic in y ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPath path; | 
 |     path.moveTo(0, -10); | 
 |     for (int i = 0; i < 128; i += 16) { | 
 |         SkScalar c = i * 0.5f; | 
 |         path.cubicTo( 10 + c, -10 - i,  10 + i, -10 - c,  10 + i,       0); | 
 |         path.cubicTo( 14 + i,  14 + c,  14 + c,  14 + i,       0,  14 + i); | 
 |         path.cubicTo(-18 - c,  18 + i, -18 - i,  18 + c, -18 - i,       0); | 
 |         path.cubicTo(-22 - i, -22 - c, -22 - c, -22 - i,       0, -22 - i); | 
 |     } | 
 |     path.offset(128, 128); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Contour moveTo rCubicTo quadTo | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3)  | 
 |  | 
 | Adds Cubic from Last_Point towards Point p1, then towards Point p2, ending at | 
 | Point p3. If Path is empty, or last Verb is kClose_Verb, Last_Point is set to | 
 | (0, 0) before adding Cubic. | 
 |  | 
 | Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed; | 
 | then appends kCubic_Verb to Verb_Array; and Points p1, p2, p3 | 
 | to Point_Array. | 
 |  | 
 | #Param p1  first control Point of Cubic ## | 
 | #Param p2  second control Point of Cubic ## | 
 | #Param p3  end Point of Cubic ## | 
 |  | 
 | #Example | 
 | #Height 84 | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPoint pts[] = { {20, 20}, {300, 80}, {-140, 90}, {220, 10} }; | 
 |     SkPath path; | 
 |     path.moveTo(pts[0]); | 
 |     path.cubicTo(pts[1], pts[2], pts[3]); | 
 |     canvas->drawPath(path, paint); | 
 | ## | 
 |  | 
 | #SeeAlso Contour moveTo rCubicTo quadTo | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, | 
 |                   SkScalar x3, SkScalar y3) | 
 |  | 
 |     Adds Cubic from Last_Point towards Vector (dx1, dy1), then towards | 
 |     Vector (dx2, dy2), to Vector (dx3, dy3). | 
 |     If Path is empty, or last Verb | 
 |     is kClose_Verb, Last_Point is set to (0, 0) before adding Cubic. | 
 |  | 
 |     Appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, | 
 |     if needed; then appends kCubic_Verb to Verb_Array; and appends Cubic | 
 |     control and Cubic end to Point_Array. | 
 |     Cubic control is Last_Point plus Vector (dx1, dy1). | 
 |     Cubic end is Last_Point plus Vector (dx2, dy2). | 
 |     Function name stands for "relative cubic to". | 
 |  | 
 |     #Param x1  offset from Last_Point x to first Cubic control x ## | 
 |     #Param y1  offset from Last_Point x to first Cubic control y ## | 
 |     #Param x2  offset from Last_Point x to second Cubic control x ## | 
 |     #Param y2  offset from Last_Point x to second Cubic control y ## | 
 |     #Param x3  offset from Last_Point x to Cubic end x ## | 
 |     #Param y3  offset from Last_Point x to Cubic end y ## | 
 |  | 
 | #Example | 
 |     void draw(SkCanvas* canvas) { | 
 |         SkPaint paint; | 
 |         paint.setAntiAlias(true); | 
 |         paint.setStyle(SkPaint::kStroke_Style); | 
 |         SkPath path; | 
 |         path.moveTo(24, 108); | 
 |         for (int i = 0; i < 16; i++) { | 
 |            SkScalar sx, sy; | 
 |            sx = SkScalarSinCos(i * SK_ScalarPI / 8, &sy); | 
 |            path.rCubicTo(40 * sx, 4 * sy, 4 * sx, 40 * sy, 40 * sx, 40 * sy); | 
 |         } | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 | ## | 
 |  | 
 | #SeeAlso Contour moveTo cubicTo quadTo | 
 |  | 
 | ## | 
 |  | 
 | #Topic Cubic ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Topic Arc | 
 |  | 
 | Arc can be constructed in a number of ways. Arc may be described by part of Oval and angles, | 
 | by start point and end point, and by radius and tangent lines. Each construction has advantages, | 
 | and some constructions correspond to Arc drawing in graphics standards. | 
 |  | 
 | All Arc draws are implemented by one or more Conic draws. When Conic_Weight is less than one, | 
 | Conic describes an Arc of some Oval or Circle. | 
 |  | 
 | arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) | 
 | describes Arc as a piece of Oval, beginning at start angle, sweeping clockwise or counterclockwise, | 
 | which may continue Contour or start a new one. This construction is similar to PostScript and  | 
 | HTML_Canvas arcs. Variation addArc always starts new Contour. Canvas::drawArc draws without | 
 | requiring Path. | 
 |  | 
 | arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) | 
 | describes Arc as tangent to the line (x0, y0), (x1, y1) and tangent to the line (x1, y1), (x2, y2) | 
 | where (x0, y0) is the last Point added to Path. This construction is similar to PostScript and | 
 | HTML_Canvas arcs. | 
 |  | 
 | arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep, | 
 |       SkScalar x, SkScalar y)  | 
 | describes Arc as part of Oval with radii (rx, ry), beginning at | 
 | last Point added to Path and ending at (x, y). More than one Arc satisfies this criteria, | 
 | so additional values choose a single solution. This construction is similar to SVG arcs. | 
 |  | 
 | conicTo describes Arc of less than 180 degrees as a pair of tangent lines and Conic_Weight. | 
 | conicTo can represent any Arc with a sweep less than 180 degrees at any rotation. All arcTo | 
 | constructions are converted to Conic data when added to Path.  | 
 |  | 
 | #ToDo  allow example to hide source and not be exposed as fiddle since markdown / html can't | 
 |        do the kind of table shown in the illustration. | 
 |        example is spaced correctly on fiddle but spacing is too wide on pc | 
 | ## | 
 |  | 
 | #Example | 
 | #Height 300 | 
 | #Width 600 | 
 | #Description | 
 | #List | 
 | # <sup>1</sup> arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) ## | 
 | # <sup>2</sup> parameter sets force MoveTo  ## | 
 | # <sup>3</sup> start angle must be multiple of 90 degrees ## | 
 | # <sup>4</sup> arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) ## | 
 | # <sup>5</sup> arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, | 
 |                Direction sweep, SkScalar x, SkScalar y) ## | 
 | #List ## | 
 | #Description ## | 
 | #Function | 
 | struct data { | 
 |    const char* name; | 
 |    char super; | 
 |    int yn[10]; | 
 | }; | 
 |  | 
 | const data dataSet[] = { | 
 | { "arcTo sweep",    '1', {1,  3, 1, 0, 0, 0, 0, 1, 0, 0 }}, | 
 | { "drawArc",         0,  {1, -1, 1, 1, 1, 1, 1, 0, 0, 0 }}, | 
 | { "addArc",          0,  {1,  1, 1, 4, 0, 1, 1, 1, 0, 0 }}, | 
 | { "arcTo tangents", '4', {0,  0, 0, 0, 0, 0, 0, 1, 1, 0 }}, | 
 | { "arcTo radii",    '5', {1,  0, 1, 0, 0, 0, 0, 1, 1, 0 }}, | 
 | { "conicTo",         0,  {1,  1, 0, 0, 0, 0, 0, 1, 1, 1 }} | 
 | }; | 
 |  | 
 | #define __degree_symbol__ "\xC2" "\xB0" | 
 |  | 
 | const char* headers[] = { | 
 |     "Oval part", | 
 |     "force moveTo", | 
 |     "can draw 180" __degree_symbol__, | 
 |     "can draw 360" __degree_symbol__, | 
 |     "can draw greater than 360" __degree_symbol__, | 
 |     "ignored if radius is zero", | 
 |     "ignored if sweep is zero", | 
 |     "requires Path", | 
 |     "describes rotation", | 
 |     "describes perspective", | 
 | }; | 
 |  | 
 | const char* yna[] = { | 
 |      "n/a", | 
 |      "no", | 
 |      "yes" | 
 | }; | 
 |  | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint lp; | 
 |     lp.setAntiAlias(true); | 
 |     SkPaint tp(lp); | 
 |     SkPaint sp(tp); | 
 |     SkPaint bp(tp); | 
 |     bp.setFakeBoldText(true); | 
 |     sp.setTextSize(10); | 
 |     lp.setColor(SK_ColorGRAY); | 
 |     canvas->translate(0, 32); | 
 |     const int tl = 115; | 
 |     for (unsigned col = 0; col <= SK_ARRAY_COUNT(headers); ++col) { | 
 |        canvas->drawLine(tl + col * 35, 100, tl + col * 35, 250, lp); | 
 |        if (0 == col) { | 
 |           continue; | 
 |        } | 
 |        canvas->drawLine(tl + col * 35, 100, tl + 100 + col * 35, 0, lp); | 
 |        SkPath path; | 
 |        path.moveTo(tl - 3 + col * 35, 103); | 
 |        path.lineTo(tl + 124 + col * 35, -24); | 
 |        canvas->drawTextOnPathHV(headers[col -1], strlen(headers[col -1]), path, 0, -9, bp); | 
 |     } | 
 |     for (unsigned row = 0; row <= SK_ARRAY_COUNT(dataSet); ++row) { | 
 |         if (0 == row) { | 
 |             canvas->drawLine(tl, 100, tl + 350, 100, lp); | 
 |         } else { | 
 |             canvas->drawLine(5, 100 + row * 25, tl + 350, 100 + row * 25, lp); | 
 |         } | 
 |         if (row == SK_ARRAY_COUNT(dataSet)) { | 
 |             break; | 
 |         } | 
 |         canvas->drawString(dataSet[row].name, 5, 117 + row * 25, bp); | 
 |         if (dataSet[row].super) { | 
 |             SkScalar width = bp.measureText(dataSet[row].name, strlen(dataSet[row].name)); | 
 |             canvas->drawText(&dataSet[row].super, 1, 8 + width, 112 + row * 25, sp); | 
 |         } | 
 |         for (unsigned col = 0; col < SK_ARRAY_COUNT(headers); ++col) { | 
 |             int val = dataSet[row].yn[col]; | 
 |             canvas->drawString(yna[SkTMin(2, val + 1)], tl + 5 + col * 35, 117 + row * 25, tp); | 
 |             if (val > 1) { | 
 |                 char supe = '0' + val - 1; | 
 |                 canvas->drawText(&supe, 1, tl + 25 + col * 35, 112 + row * 25, sp); | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 | #Example ## | 
 |  | 
 | #Example | 
 | #Height 128 | 
 | #Description | 
 | #ToDo make this a list or table ## | 
 | 1 describes an arc from an oval, a starting angle, and a sweep angle. | 
 | 2 is similar to 1, but does not require building a path to draw. | 
 | 3 is similar to 1, but always begins new Contour. | 
 | 4 describes an arc from a pair of tangent lines and a radius. | 
 | 5 describes an arc from Oval center, arc start Point and arc end Point. | 
 | 6 describes an arc from a pair of tangent lines and a Conic_Weight. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkRect oval = {8, 8, 56, 56}; | 
 |     SkPaint ovalPaint; | 
 |     ovalPaint.setAntiAlias(true); | 
 |     SkPaint textPaint(ovalPaint); | 
 |     ovalPaint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPaint arcPaint(ovalPaint); | 
 |     arcPaint.setStrokeWidth(5); | 
 |     arcPaint.setColor(SK_ColorBLUE); | 
 |     canvas->translate(-64, 0); | 
 |     for (char arcStyle = '1'; arcStyle <= '6'; ++arcStyle) { | 
 |         '4' == arcStyle ? canvas->translate(-96, 55) : canvas->translate(64, 0); | 
 |         canvas->drawText(&arcStyle, 1, 30, 36, textPaint); | 
 |         canvas->drawOval(oval, ovalPaint); | 
 |         SkPath path; | 
 |         path.moveTo({56, 32}); | 
 |         switch (arcStyle) { | 
 |             case '1': | 
 |                 path.arcTo(oval, 0, 90, false); | 
 |                 break; | 
 |             case '2': | 
 |                 canvas->drawArc(oval, 0, 90, false, arcPaint); | 
 |                 continue; | 
 |             case '3': | 
 |                 path.addArc(oval, 0, 90); | 
 |                 break; | 
 |             case '4': | 
 |                 path.arcTo({56, 56}, {32, 56}, 24); | 
 |                 break; | 
 |             case '5': | 
 |                 path.arcTo({24, 24}, 0, SkPath::kSmall_ArcSize, SkPath::kCW_Direction, {32, 56}); | 
 |                 break; | 
 |             case '6': | 
 |                 path.conicTo({56, 56}, {32, 56}, SK_ScalarRoot2Over2); | 
 |                 break; | 
 |          } | 
 |          canvas->drawPath(path, arcPaint); | 
 |      } | 
 | } | 
 | #Example ## | 
 |  | 
 |  | 
 | #Method void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) | 
 |  | 
 | Append Arc to Path. Arc added is part of ellipse | 
 | bounded by oval, from startAngle through sweepAngle. Both startAngle and | 
 | sweepAngle are measured in degrees, where zero degrees is aligned with the | 
 | positive x-axis, and positive sweeps extends Arc clockwise. | 
 |  | 
 | arcTo adds Line connecting Path last Point to initial Arc Point if forceMoveTo | 
 | is false and Path is not empty. Otherwise, added Contour begins with first point | 
 | of Arc. Angles greater than -360 and less than 360 are treated modulo 360. | 
 |  | 
 | #Param oval        bounds of ellipse containing Arc ## | 
 | #Param startAngle  starting angle of Arc in degrees ## | 
 | #Param sweepAngle  sweep, in degrees. Positive is clockwise; treated modulo 360 ## | 
 | #Param forceMoveTo  true to start a new contour with Arc ## | 
 |  | 
 | #Example | 
 | #Height 200 | 
 | #Description | 
 | arcTo continues a previous contour when forceMoveTo is false and when Path | 
 | is not empty. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     SkPath path; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setStrokeWidth(4); | 
 |     path.moveTo(0, 0); | 
 |     path.arcTo({20, 20, 120, 120}, -90, 90, false); | 
 |     canvas->drawPath(path, paint); | 
 |     path.rewind(); | 
 |     path.arcTo({120, 20, 220, 120}, -90, 90, false); | 
 |     canvas->drawPath(path, paint); | 
 |     path.rewind(); | 
 |     path.moveTo(0, 0); | 
 |     path.arcTo({20, 120, 120, 220}, -90, 90, true); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso addArc SkCanvas::drawArc conicTo | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) | 
 |  | 
 | Append Arc to Path, after appending Line if needed. Arc is implemented by Conic | 
 | weighted to describe part of Circle. Arc is contained by tangent from | 
 | last Path point (x0, y0) to (x1, y1), and tangent from (x1, y1) to (x2, y2). Arc | 
 | is part of Circle sized to radius, positioned so it touches both tangent lines.  | 
 |  | 
 | #ToDo  allow example to hide source and not be exposed as fiddle ## | 
 |  | 
 | #Example | 
 | #Height 226 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint tangentPaint; | 
 |     tangentPaint.setAntiAlias(true); | 
 |     SkPaint textPaint(tangentPaint); | 
 |     tangentPaint.setStyle(SkPaint::kStroke_Style); | 
 |     tangentPaint.setColor(SK_ColorGRAY); | 
 |     SkPaint arcPaint(tangentPaint); | 
 |     arcPaint.setStrokeWidth(5); | 
 |     arcPaint.setColor(SK_ColorBLUE); | 
 |     SkPath path; | 
 |     SkPoint pts[] = { {56, 20}, {200, 20}, {90, 190} }; | 
 |     SkScalar radius = 50; | 
 |     path.moveTo(pts[0]); | 
 |     path.arcTo(pts[1], pts[2], radius); | 
 |     canvas->drawLine(pts[0], pts[1], tangentPaint); | 
 |     canvas->drawLine(pts[1], pts[2], tangentPaint); | 
 |     SkPoint lastPt; | 
 |     (void) path.getLastPt(&lastPt); | 
 |     SkVector radial = pts[2] - pts[1]; | 
 |     radial.setLength(radius); | 
 |     SkPoint center = { lastPt.fX - radial.fY, lastPt.fY + radial.fX }; | 
 |     canvas->drawCircle(center, radius, tangentPaint); | 
 |     canvas->drawLine(lastPt, center, tangentPaint); | 
 |     radial = pts[1] - pts[0]; | 
 |     radial.setLength(radius); | 
 |     SkPoint arcStart = { center.fX + radial.fY, center.fY - radial.fX }; | 
 |     canvas->drawLine(center, arcStart, tangentPaint); | 
 |     canvas->drawPath(path, arcPaint); | 
 |     textPaint.setTextAlign(SkPaint::kRight_Align); | 
 |     canvas->drawString("(x0, y0)", pts[0].fX - 5, pts[0].fY, textPaint); | 
 |     textPaint.setTextAlign(SkPaint::kLeft_Align); | 
 |     canvas->drawString("(x1, y1)", pts[1].fX + 5, pts[1].fY, textPaint); | 
 |     textPaint.setTextAlign(SkPaint::kCenter_Align); | 
 |     canvas->drawString("(x2, y2)", pts[2].fX, pts[2].fY + 15, textPaint); | 
 |     textPaint.setTextAlign(SkPaint::kRight_Align); | 
 |     canvas->drawString("radius", center.fX + 15, center.fY + 25, textPaint); | 
 |     canvas->drawString("radius", center.fX - 3, center.fY - 16, textPaint); | 
 | } | 
 | ## | 
 |  | 
 | If last Path Point does not start Arc, arcTo appends connecting Line to Path. | 
 | The length of Vector from (x1, y1) to (x2, y2) does not affect Arc. | 
 |  | 
 | #Example | 
 | #Height 128 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint tangentPaint; | 
 |     tangentPaint.setAntiAlias(true); | 
 |     SkPaint textPaint(tangentPaint); | 
 |     tangentPaint.setStyle(SkPaint::kStroke_Style); | 
 |     tangentPaint.setColor(SK_ColorGRAY); | 
 |     SkPaint arcPaint(tangentPaint); | 
 |     arcPaint.setStrokeWidth(5); | 
 |     arcPaint.setColor(SK_ColorBLUE); | 
 |     SkPath path; | 
 |     SkPoint pts[] = { {156, 20}, {200, 20}, {170, 50} }; | 
 |     SkScalar radius = 50; | 
 |     path.moveTo(pts[0]); | 
 |     path.arcTo(pts[1], pts[2], radius); | 
 |     canvas->drawLine(pts[0], pts[1], tangentPaint); | 
 |     canvas->drawLine(pts[1], pts[2], tangentPaint); | 
 |     SkPoint lastPt; | 
 |     (void) path.getLastPt(&lastPt); | 
 |     SkVector radial = pts[2] - pts[1]; | 
 |     radial.setLength(radius); | 
 |     SkPoint center = { lastPt.fX - radial.fY, lastPt.fY + radial.fX }; | 
 |     canvas->drawLine(lastPt, center, tangentPaint); | 
 |     radial = pts[1] - pts[0]; | 
 |     radial.setLength(radius); | 
 |     SkPoint arcStart = { center.fX + radial.fY, center.fY - radial.fX }; | 
 |     canvas->drawLine(center, arcStart, tangentPaint); | 
 |     canvas->drawPath(path, arcPaint); | 
 |     textPaint.setTextAlign(SkPaint::kCenter_Align); | 
 |     canvas->drawString("(x0, y0)", pts[0].fX, pts[0].fY - 7, textPaint); | 
 |     textPaint.setTextAlign(SkPaint::kLeft_Align); | 
 |     canvas->drawString("(x1, y1)", pts[1].fX + 5, pts[1].fY, textPaint); | 
 |     textPaint.setTextAlign(SkPaint::kCenter_Align); | 
 |     canvas->drawString("(x2, y2)", pts[2].fX, pts[2].fY + 15, textPaint); | 
 |     textPaint.setTextAlign(SkPaint::kRight_Align); | 
 |     canvas->drawString("radius", center.fX + 15, center.fY + 25, textPaint); | 
 |     canvas->drawString("radius", center.fX - 5, center.fY - 20, textPaint); | 
 | } | 
 | ## | 
 |  | 
 | Arc sweep is always less than 180 degrees. If radius is zero, or if | 
 | tangents are nearly parallel, arcTo appends Line from last Path Point to (x1, y1). | 
 |  | 
 | arcTo appends at most one Line and one Conic. | 
 | arcTo implements the functionality of PostScript_Arct and HTML_Canvas_ArcTo. | 
 |  | 
 | #Param x1      x common to pair of tangents ## | 
 | #Param y1      y common to pair of tangents ## | 
 | #Param x2      x end of second tangent ## | 
 | #Param y2      y end of second tangent ## | 
 | #Param radius  distance from Arc to Circle center ## | 
 |  | 
 | #Example | 
 | #Description | 
 | arcTo is represented by Line and circular Conic in Path. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPath path; | 
 |     path.moveTo({156, 20}); | 
 |     path.arcTo(200, 20, 170, 50, 50); | 
 |     SkPath::Iter iter(path, false); | 
 |     SkPoint p[4]; | 
 |     SkPath::Verb verb; | 
 |     while (SkPath::kDone_Verb != (verb = iter.next(p))) { | 
 |         switch (verb) { | 
 |             case SkPath::kMove_Verb: | 
 |                 SkDebugf("move to (%g,%g)\n", p[0].fX, p[0].fY); | 
 |                 break; | 
 |             case SkPath::kLine_Verb: | 
 |                 SkDebugf("line (%g,%g),(%g,%g)\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY); | 
 |                 break; | 
 |             case SkPath::kConic_Verb: | 
 |                 SkDebugf("conic (%g,%g),(%g,%g),(%g,%g) weight %g\n", | 
 |                          p[0].fX, p[0].fY, p[1].fX, p[1].fY, p[2].fX, p[2].fY, iter.conicWeight()); | 
 |                 break; | 
 |             default: | 
 |                 SkDebugf("unexpected verb\n"); | 
 |         } | 
 |     } | 
 | } | 
 | #StdOut | 
 | move to (156,20) | 
 | line (156,20),(79.2893,20) | 
 | conic (79.2893,20),(200,20),(114.645,105.355) weight 0.382683 | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso conicTo  | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius)  | 
 |  | 
 | Append Arc to Path, after appending Line if needed. Arc is implemented by Conic | 
 | weighted to describe part of Circle. Arc is contained by tangent from | 
 | last Path point to p1, and tangent from p1 to p2. Arc | 
 | is part of Circle sized to radius, positioned so it touches both tangent lines.  | 
 |  | 
 | If last Path Point does not start Arc, arcTo appends connecting Line to Path. | 
 | The length of Vector from p1 to p2 does not affect Arc. | 
 |  | 
 | Arc sweep is always less than 180 degrees. If radius is zero, or if | 
 | tangents are nearly parallel, arcTo appends Line from last Path Point to p1. | 
 |  | 
 | arcTo appends at most one Line and one Conic. | 
 | arcTo implements the functionality of PostScript_Arct and HTML_Canvas_ArcTo. | 
 |  | 
 | #Param p1      Point common to pair of tangents ## | 
 | #Param p2      end of second tangent ## | 
 | #Param radius  distance from Arc to Circle center ## | 
 |  | 
 | #Example | 
 | #Description | 
 | Because tangent lines are parallel, arcTo appends line from last Path Point to | 
 | p1, but does not append a circular Conic. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPath path; | 
 |     path.moveTo({156, 20}); | 
 |     path.arcTo({200, 20}, {170, 20}, 50); | 
 |     SkPath::Iter iter(path, false); | 
 |     SkPoint p[4]; | 
 |     SkPath::Verb verb; | 
 |     while (SkPath::kDone_Verb != (verb = iter.next(p))) { | 
 |         switch (verb) { | 
 |             case SkPath::kMove_Verb: | 
 |                 SkDebugf("move to (%g,%g)\n", p[0].fX, p[0].fY); | 
 |                 break; | 
 |             case SkPath::kLine_Verb: | 
 |                 SkDebugf("line (%g,%g),(%g,%g)\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY); | 
 |                 break; | 
 |             case SkPath::kConic_Verb: | 
 |                 SkDebugf("conic (%g,%g),(%g,%g),(%g,%g) weight %g\n", | 
 |                           p[0].fX, p[0].fY, p[1].fX, p[1].fY, p[2].fX, p[2].fY, iter.conicWeight()); | 
 |                 break; | 
 |             default: | 
 |                 SkDebugf("unexpected verb\n"); | 
 |         } | 
 |     } | 
 | } | 
 | #StdOut | 
 | move to (156,20) | 
 | line (156,20),(200,20) | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso conicTo  | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Enum ArcSize | 
 |  | 
 | #Code | 
 |     enum ArcSize { | 
 |         kSmall_ArcSize,  | 
 |         kLarge_ArcSize,  | 
 |     }; | 
 | ## | 
 |  | 
 | Four Oval parts with radii (rx, ry) start at last Path Point and ends at (x, y). | 
 | ArcSize and Direction select one of the four Oval parts. | 
 |  | 
 | #Const kSmall_ArcSize 0 | 
 | smaller of Arc pair | 
 | ## | 
 | #Const kLarge_ArcSize 1 | 
 | larger of Arc pair | 
 | ## | 
 |  | 
 | #Example | 
 | #Height 160 | 
 | #Description | 
 | Arc begins at top of Oval pair and ends at bottom. Arc can take four routes to get there. | 
 | Two routes are large, and two routes are counterclockwise. The one route both large | 
 | and counterclockwise is blue. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { | 
 |         for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) { | 
 |             SkPath path; | 
 |             path.moveTo({120, 50}); | 
 |             path.arcTo(70, 40, 30, arcSize, sweep, 156, 100); | 
 |             if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) { | 
 |                 paint.setColor(SK_ColorBLUE); | 
 |                 paint.setStrokeWidth(3); | 
 |             } | 
 |             canvas->drawPath(path, paint); | 
 |          } | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso arcTo Direction | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, | 
 |                Direction sweep, SkScalar x, SkScalar y) | 
 |  | 
 | Append Arc to Path. Arc is implemented by one or more Conics weighted to | 
 | describe part of Oval with radii (rx, ry) rotated by xAxisRotate degrees. Arc | 
 | curves from last Path Point to (x, y), choosing one of four possible routes: | 
 | clockwise or counterclockwise, and smaller or larger. | 
 |  | 
 | Arc sweep is always less than 360 degrees. arcTo appends Line to (x, y) if | 
 | either radii are zero, or if last Path Point equals (x, y). arcTo scales radii | 
 | (rx, ry) to fit last Path Point and (x, y) if both are greater than zero but | 
 | too small. | 
 |  | 
 | arcTo appends up to four Conic curves. | 
 | arcTo implements the functionality of SVG_Arc, although SVG "sweep-flag" value | 
 | is opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, | 
 | while kCW_Direction  cast to int is zero. | 
 |  | 
 | #Param rx           radius in x before x-axis rotation ## | 
 | #Param ry           radius in y before x-axis rotation ## | 
 | #Param xAxisRotate  x-axis rotation in degrees; positive values are clockwise ## | 
 | #Param largeArc     chooses smaller or larger Arc ## | 
 | #Param sweep        chooses clockwise or counterclockwise Arc ## | 
 | #Param x            end of Arc ## | 
 | #Param y            end of Arc ## | 
 |  | 
 | #Example | 
 | #Height 160 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { | 
 |         for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) { | 
 |             SkPath path; | 
 |             path.moveTo({120, 50}); | 
 |             path.arcTo(70, 40, 30, arcSize, sweep, 120.1, 50); | 
 |             if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) { | 
 |                 paint.setColor(SK_ColorBLUE); | 
 |                 paint.setStrokeWidth(3); | 
 |             } | 
 |             canvas->drawPath(path, paint); | 
 |          } | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso rArcTo ArcSize Direction | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep, | 
 |                const SkPoint xy)  | 
 |  | 
 | Append Arc to Path. Arc is implemented by one or more Conic weighted to describe part of Oval | 
 | with radii (r.fX, r.fY) rotated by xAxisRotate degrees. Arc curves from last Path Point to | 
 | (xy.fX, xy.fY), choosing one of four possible routes: clockwise or counterclockwise, | 
 | and smaller or larger. | 
 |  | 
 | Arc sweep is always less than 360 degrees. arcTo appends Line to xy if either radii are zero, | 
 | or if last Path Point equals (x, y). arcTo scales radii r to fit last Path Point and | 
 | xy if both are greater than zero but too small to describe an arc. | 
 |  | 
 | arcTo appends up to four Conic curves. | 
 | arcTo implements the functionality of SVG_Arc, although SVG "sweep-flag" value is | 
 | opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while | 
 | kCW_Direction cast to int is zero. | 
 |  | 
 | #Param r            radii in x and y before x-axis rotation ## | 
 | #Param xAxisRotate  x-axis rotation in degrees; positive values are clockwise ## | 
 | #Param largeArc     chooses smaller or larger Arc ## | 
 | #Param sweep        chooses clockwise or counterclockwise Arc ## | 
 | #Param xy           end of Arc ## | 
 |  | 
 | #Example | 
 | #Height 108 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     SkPath path; | 
 |     const SkPoint starts[] = {{20, 20}, {120, 20}, {70, 60}}; | 
 |     for (auto start : starts) { | 
 |         path.moveTo(start.fX, start.fY); | 
 |         path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPath::kCCW_Direction, 60, 0); | 
 |     } | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso rArcTo ArcSize Direction | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, | 
 |                 Direction sweep, SkScalar dx, SkScalar dy) | 
 |  | 
 | Append Arc to Path, relative to last Path Point. Arc is implemented by one or  | 
 | more Conic, weighted to describe part of Oval with radii (rx, ry) rotated by | 
 | xAxisRotate degrees. Arc curves from last Path Point (x0, y0) to end Point: | 
 |  | 
 | #Formula | 
 | (x0 + dx, y0 + dy) | 
 | ## | 
 | , choosing one of four possible routes: clockwise or | 
 | counterclockwise, and smaller or larger. If Path is empty, the start Arc Point | 
 | is (0, 0). | 
 |  | 
 | Arc sweep is always less than 360 degrees. arcTo appends Line to end Point | 
 | if either radii are zero, or if last Path Point equals end Point. | 
 | arcTo scales radii (rx, ry) to fit last Path Point and end Point if both are | 
 | greater than zero but too small to describe an arc. | 
 |  | 
 | arcTo appends up to four Conic curves. | 
 | arcTo implements the functionality of SVG_Arc, although SVG "sweep-flag" value is | 
 | opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while | 
 | kCW_Direction cast to int is zero. | 
 |  | 
 | #Param rx           radius in x before x-axis rotation ## | 
 | #Param ry           radius in y before x-axis rotation ## | 
 | #Param xAxisRotate  x-axis rotation in degrees; positive values are clockwise ## | 
 | #Param largeArc     chooses smaller or larger Arc ## | 
 | #Param sweep        chooses clockwise or counterclockwise Arc ## | 
 | #Param dx           x offset end of Arc from last Path Point ## | 
 | #Param dy           y offset end of Arc from last Path Point ## | 
 |  | 
 | #Example | 
 | #Height 108 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     SkPath path; | 
 |     const SkPoint starts[] = {{20, 20}, {120, 20}, {70, 60}}; | 
 |     for (auto start : starts) { | 
 |         path.moveTo(start.fX, start.fY); | 
 |         path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPath::kCCW_Direction, 60, 0); | 
 |     } | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso arcTo ArcSize Direction | 
 |  | 
 | ## | 
 |  | 
 | #Topic Arc ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void close() | 
 |  | 
 | Append kClose_Verb to Path. A closed Contour connects the first and last Point | 
 | with Line, forming a continuous loop. Open and closed Contour draw the same | 
 | with SkPaint::kFill_Style. With SkPaint::kStroke_Style, open Contour draws | 
 | Paint_Stroke_Cap at Contour start and end; closed Contour draws  | 
 | Paint_Stroke_Join at Contour start and end. | 
 |  | 
 | close() has no effect if Path is empty or last Path Verb is kClose_Verb. | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setStrokeWidth(15); | 
 |     paint.setStrokeCap(SkPaint::kRound_Cap); | 
 |     SkPath path; | 
 |     const SkPoint points[] = {{20, 20}, {70, 20}, {40, 90}}; | 
 |     path.addPoly(points, SK_ARRAY_COUNT(points), false); | 
 |     for (int loop = 0; loop < 2; ++loop) { | 
 |         for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style, | 
 |                 SkPaint::kStrokeAndFill_Style} ) { | 
 |             paint.setStyle(style); | 
 |             canvas->drawPath(path, paint); | 
 |             canvas->translate(85, 0); | 
 |         } | 
 |         path.close(); | 
 |         canvas->translate(-255, 128); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso  | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method static bool IsInverseFillType(FillType fill)  | 
 |  | 
 | Returns true if fill is inverted and Path with fill represents area outside | 
 | of its geometric bounds. | 
 |  | 
 | #Table | 
 | #Legend | 
 | # FillType                 # is inverse ## | 
 | ## | 
 | # kWinding_FillType        # false      ## | 
 | # kEvenOdd_FillType        # false      ## | 
 | # kInverseWinding_FillType # true       ## | 
 | # kInverseEvenOdd_FillType # true       ## | 
 | ## | 
 |  | 
 | #Param fill  one of: kWinding_FillType, kEvenOdd_FillType, | 
 |              kInverseWinding_FillType, kInverseEvenOdd_FillType | 
 | ## | 
 |  | 
 | #Return  true if Path fills outside its bounds ## | 
 |  | 
 | #Example | 
 | #Function | 
 | #define nameValue(fill) { SkPath::fill, #fill } | 
 |  | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     struct { | 
 |         SkPath::FillType fill; | 
 |         const char* name; | 
 |     } fills[] = { | 
 |         nameValue(kWinding_FillType), | 
 |         nameValue(kEvenOdd_FillType), | 
 |         nameValue(kInverseWinding_FillType), | 
 |         nameValue(kInverseEvenOdd_FillType), | 
 |     }; | 
 |     for (auto fill: fills ) { | 
 |         SkDebugf("IsInverseFillType(%s) == %s\n", fill.name, SkPath::IsInverseFillType(fill.fill) ? | 
 |                  "true" : "false"); | 
 |     } | 
 | } | 
 | #StdOut | 
 | IsInverseFillType(kWinding_FillType) == false | 
 | IsInverseFillType(kEvenOdd_FillType) == false | 
 | IsInverseFillType(kInverseWinding_FillType) == true | 
 | IsInverseFillType(kInverseEvenOdd_FillType) == true | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso FillType getFillType setFillType ConvertToNonInverseFillType | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method static FillType ConvertToNonInverseFillType(FillType fill)  | 
 |  | 
 | Returns equivalent Fill_Type representing Path fill inside its bounds. | 
 | . | 
 |  | 
 | #Table | 
 | #Legend | 
 | # FillType                 # inside FillType   ## | 
 | ## | 
 | # kWinding_FillType        # kWinding_FillType ## | 
 | # kEvenOdd_FillType        # kEvenOdd_FillType ## | 
 | # kInverseWinding_FillType # kWinding_FillType ## | 
 | # kInverseEvenOdd_FillType # kEvenOdd_FillType ## | 
 | ## | 
 |  | 
 | #Param fill  one of: kWinding_FillType, kEvenOdd_FillType, | 
 |              kInverseWinding_FillType, kInverseEvenOdd_FillType | 
 | ## | 
 |  | 
 | #Return  fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted ## | 
 |  | 
 | #Example | 
 | #Function | 
 | #define nameValue(fill) { SkPath::fill, #fill } | 
 |  | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     struct { | 
 |         SkPath::FillType fill; | 
 |         const char* name; | 
 |     } fills[] = { | 
 |         nameValue(kWinding_FillType), | 
 |         nameValue(kEvenOdd_FillType), | 
 |         nameValue(kInverseWinding_FillType), | 
 |         nameValue(kInverseEvenOdd_FillType), | 
 |     }; | 
 |     for (unsigned i = 0; i < SK_ARRAY_COUNT(fills); ++i) { | 
 |         if (fills[i].fill != (SkPath::FillType) i) { | 
 |             SkDebugf("fills array order does not match FillType enum order"); | 
 |             break; | 
 |         }  | 
 |         SkDebugf("ConvertToNonInverseFillType(%s) == %s\n", fills[i].name, | 
 |                 fills[(int) SkPath::ConvertToNonInverseFillType(fills[i].fill)].name); | 
 |     } | 
 | } | 
 | #StdOut | 
 | ConvertToNonInverseFillType(kWinding_FillType) == kWinding_FillType | 
 | ConvertToNonInverseFillType(kEvenOdd_FillType) == kEvenOdd_FillType | 
 | ConvertToNonInverseFillType(kInverseWinding_FillType) == kWinding_FillType | 
 | ConvertToNonInverseFillType(kInverseEvenOdd_FillType) == kEvenOdd_FillType | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso FillType getFillType setFillType IsInverseFillType | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, | 
 |                                    SkScalar w, SkPoint pts[], int pow2) | 
 |  | 
 | Approximates Conic with Quad array. Conic is constructed from start Point p0, | 
 | control Point p1, end Point p2, and weight w.  | 
 | Quad array is stored in pts; this storage is supplied by caller. | 
 | Maximum Quad count is 2 to the pow2. | 
 | Every third point in array shares last Point of previous Quad and first Point of  | 
 | next Quad. Maximum pts storage size is given by:  | 
 | #Formula | 
 | (1 + 2 * (1 << pow2)) * sizeof(SkPoint) | 
 | ## | 
 | . | 
 |  | 
 | Returns Quad count used the approximation, which may be smaller | 
 | than the number requested. | 
 |   | 
 | Conic_Weight determines the amount of influence Conic control point has on the curve. | 
 | w less than one represents an elliptical section. w greater than one represents | 
 | a hyperbolic section. w equal to one represents a parabolic section. | 
 |  | 
 | Two Quad curves are sufficient to approximate an elliptical Conic with a sweep | 
 | of up to 90 degrees; in this case, set pow2 to one. | 
 |  | 
 | #Param p0    Conic start Point ## | 
 | #Param p1    Conic control Point ## | 
 | #Param p2    Conic end Point ## | 
 | #Param w     Conic weight ## | 
 | #Param pts   storage for Quad array ## | 
 | #Param pow2  Quad count, as power of two, normally 0 to 5 (1 to 32 Quad curves) ## | 
 |  | 
 | #Return  number of Quad curves written to pts ## | 
 |  | 
 | #Example | 
 | #Description | 
 | A pair of Quad curves are drawn in red on top of the elliptical Conic curve in black. | 
 | The middle curve is nearly circular. The top-right curve is parabolic, which can | 
 | be drawn exactly with a single Quad. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |       SkPaint conicPaint; | 
 |       conicPaint.setAntiAlias(true); | 
 |       conicPaint.setStyle(SkPaint::kStroke_Style); | 
 |       SkPaint quadPaint(conicPaint); | 
 |       quadPaint.setColor(SK_ColorRED); | 
 |       SkPoint conic[] = { {20, 170}, {80, 170}, {80, 230} }; | 
 |       for (auto weight : { .25f, .5f, .707f, .85f, 1.f } ) { | 
 |           SkPoint quads[5]; | 
 |           SkPath::ConvertConicToQuads(conic[0], conic[1], conic[2], weight, quads, 1); | 
 |           SkPath path; | 
 |           path.moveTo(conic[0]); | 
 |           path.conicTo(conic[1], conic[2], weight); | 
 |           canvas->drawPath(path, conicPaint); | 
 |           path.rewind(); | 
 |           path.moveTo(quads[0]); | 
 |           path.quadTo(quads[1], quads[2]); | 
 |           path.quadTo(quads[3], quads[4]); | 
 |           canvas->drawPath(path, quadPaint); | 
 |           canvas->translate(50, -50); | 
 |       } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Conic Quad | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isRect(SkRect* rect, bool* isClosed = nullptr, Direction* direction = nullptr) const | 
 |  | 
 | Returns true if Path is equivalent to Rect when filled. | 
 | If false: rect, isClosed, and direction are unchanged. | 
 | If true: rect, isClosed, and direction are written to if not nullptr. | 
 |  | 
 | rect may be smaller than the Path bounds. Path bounds may include kMove_Verb points | 
 | that do not alter the area drawn by the returned rect. | 
 |  | 
 | #Param rect       storage for bounds of Rect; may be nullptr ## | 
 | #Param isClosed   storage set to true if Path is closed; may be nullptr ## | 
 | #Param direction  storage set to Rect direction; may be nullptr ## | 
 |  | 
 | #Return  true if Path contains Rect ## | 
 |  | 
 | #Example | 
 | #Description | 
 | After addRect, isRect returns true. Following moveTo permits isRect to return true, but | 
 | following lineTo does not. addPoly returns true even though rect is not closed, and one | 
 | side of rect is made up of consecutive line segments. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path) -> void { | 
 |         SkRect rect; | 
 |         SkPath::Direction direction; | 
 |         bool isClosed; | 
 |         path.isRect(&rect, &isClosed, &direction) ?  | 
 |                 SkDebugf("%s is rect (%g, %g, %g, %g); is %s" "closed; direction %s\n", prefix, | 
 |                          rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, isClosed ? "" : "not ", | 
 |                          SkPath::kCW_Direction == direction ? "CW" : "CCW") : | 
 |                 SkDebugf("%s is not rect\n", prefix); | 
 |     }; | 
 |     SkPath path; | 
 |     debugster("empty", path); | 
 |     path.addRect({10, 20, 30, 40}); | 
 |     debugster("addRect", path); | 
 |     path.moveTo(60, 70); | 
 |     debugster("moveTo", path); | 
 |     path.lineTo(60, 70); | 
 |     debugster("lineTo", path); | 
 |     path.reset(); | 
 |     const SkPoint pts[] = { {0, 0}, {0, 80}, {80, 80}, {80, 0}, {40, 0}, {20, 0} }; | 
 |     path.addPoly(pts, SK_ARRAY_COUNT(pts), false); | 
 |     debugster("addPoly", path); | 
 | } | 
 | #StdOut | 
 | empty is not rect | 
 | addRect is rect (10, 20, 30, 40); is closed; direction CW | 
 | moveTo is rect (10, 20, 30, 40); is closed; direction CW | 
 | lineTo is not rect | 
 | addPoly is rect (0, 0, 80, 80); is not closed; direction CCW | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso computeTightBounds conservativelyContainsRect getBounds isConvex isLastContourClosed isNestedFillRects | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = nullptr) const | 
 |  | 
 | Returns true if Path is equivalent to nested Rect pair when filled. | 
 | If false, rect and dirs are unchanged. | 
 | If true, rect and dirs are written to if not nullptr: | 
 | setting rect[0] to outer Rect, and rect[1] to inner Rect; | 
 | setting dirs[0] to Direction of outer Rect, and dirs[1] to Direction of inner | 
 | Rect. | 
 |  | 
 | #Param rect  storage for Rect pair; may be nullptr ## | 
 | #Param dirs  storage for Direction pair; may be nullptr ## | 
 |  | 
 | #Return  true if Path contains nested Rect pair ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setStrokeWidth(5); | 
 |     SkPath path; | 
 |     path.addRect({10, 20, 30, 40}); | 
 |     paint.getFillPath(path, &path); | 
 |     SkRect rects[2]; | 
 |     SkPath::Direction directions[2]; | 
 |     if (path.isNestedFillRects(rects, directions)) { | 
 |         for (int i = 0; i < 2; ++i) { | 
 |             SkDebugf("%s (%g, %g, %g, %g); direction %s\n", i ? "inner" : "outer", | 
 |                      rects[i].fLeft, rects[i].fTop, rects[i].fRight, rects[i].fBottom, | 
 |                      SkPath::kCW_Direction == directions[i] ? "CW" : "CCW"); | 
 |         } | 
 |     } else { | 
 |         SkDebugf("is not nested rectangles\n"); | 
 |     } | 
 | } | 
 | #StdOut | 
 | outer (7.5, 17.5, 32.5, 42.5); direction CW | 
 | inner (12.5, 22.5, 27.5, 37.5); direction CCW | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso computeTightBounds conservativelyContainsRect getBounds isConvex isLastContourClosed isRect | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addRect(const SkRect& rect, Direction dir = kCW_Direction) | 
 |  | 
 | Add Rect to Path, appending kMove_Verb, three kLine_Verb, and kClose_Verb, | 
 | starting with top-left corner of Rect; followed by top-right, bottom-right, | 
 | and bottom-left if dir is kCW_Direction; or followed by bottom-left, | 
 | bottom-right, and top-right if dir is kCCW_Direction. | 
 |  | 
 | #Param rect  Rect to add as a closed contour ## | 
 | #Param dir   Direction to wind added contour ## | 
 |  | 
 | #Example | 
 | #Description | 
 | The left Rect dashes starting at the top-left corner, to the right. | 
 | The right Rect dashes starting at the top-left corner, towards the bottom. | 
 | ## | 
 | #Height 128 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setStrokeWidth(15); | 
 |     paint.setStrokeCap(SkPaint::kSquare_Cap); | 
 |     float intervals[] = { 5, 21.75f }; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0)); | 
 |     SkPath path; | 
 |     path.addRect({20, 20, 100, 100}, SkPath::kCW_Direction); | 
 |     canvas->drawPath(path, paint); | 
 |     path.rewind(); | 
 |     path.addRect({140, 20, 220, 100}, SkPath::kCCW_Direction); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso SkCanvas::drawRect Direction | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addRect(const SkRect& rect, Direction dir, unsigned start) | 
 |  | 
 | Add Rect to Path, appending kMove_Verb, three kLine_Verb, and kClose_Verb. | 
 | If dir is kCW_Direction, Rect corners are added clockwise; if dir is | 
 | kCCW_Direction, Rect corners are added counterclockwise. | 
 | start determines the first corner added. | 
 |  | 
 | #Table | 
 | #Legend | 
 | # start # first corner ## | 
 | #Legend ## | 
 | # 0     # top-left ## | 
 | # 1     # top-right ## | 
 | # 2     # bottom-right ## | 
 | # 3     # bottom-left ## | 
 | #Table ## | 
 |  | 
 | #Param rect   Rect to add as a closed contour ## | 
 | #Param dir    Direction to wind added contour ## | 
 | #Param start  initial corner of Rect to add ## | 
 |  | 
 | #Example | 
 | #Height 128 | 
 | #Description | 
 | The arrow is just after the initial corner and points towards the next | 
 | corner appended to Path. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     const SkPoint arrow[] = { {5, -5}, {15, -5}, {20, 0}, {15, 5}, {5, 5}, {10, 0} }; | 
 |     const SkRect rect = {10, 10, 54, 54}; | 
 |     SkPaint rectPaint; | 
 |     rectPaint.setAntiAlias(true); | 
 |     rectPaint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPaint arrowPaint(rectPaint); | 
 |     SkPath arrowPath; | 
 |     arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true); | 
 |     arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0, | 
 |                              SkPath1DPathEffect::kRotate_Style)); | 
 |     for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { | 
 |         for (unsigned start : { 0, 1, 2, 3 } ) { | 
 |            SkPath path; | 
 |            path.addRect(rect, direction, start); | 
 |            canvas->drawPath(path, rectPaint); | 
 |            canvas->drawPath(path, arrowPaint); | 
 |            canvas->translate(64, 0); | 
 |        } | 
 |        canvas->translate(-256, 64); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso SkCanvas::drawRect Direction | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, | 
 |                  Direction dir = kCW_Direction) | 
 |  | 
 | Add Rect (left, top, right, bottom) to Path, | 
 | appending kMove_Verb, three kLine_Verb, and kClose_Verb, | 
 | starting with top-left corner of Rect; followed by top-right, bottom-right, | 
 | and bottom-left if dir is kCW_Direction; or followed by bottom-left, | 
 | bottom-right, and top-right if dir is kCCW_Direction. | 
 |  | 
 | #Param left    smaller x of Rect ## | 
 | #Param top     smaller y of Rect ## | 
 | #Param right   larger x of Rect ## | 
 | #Param bottom  larger y of Rect ## | 
 | #Param dir     Direction to wind added contour ## | 
 |  | 
 | #Example | 
 | #Description | 
 | The left Rect dashes start at the top-left corner, and continue to the right. | 
 | The right Rect dashes start at the top-left corner, and continue down. | 
 | ## | 
 | #Height 128 | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setStrokeWidth(15); | 
 |     paint.setStrokeCap(SkPaint::kSquare_Cap); | 
 |     float intervals[] = { 5, 21.75f }; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0)); | 
 |     for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { | 
 |         SkPath path; | 
 |         path.addRect(20, 20, 100, 100, direction); | 
 |         canvas->drawPath(path, paint); | 
 |         canvas->translate(128, 0); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso SkCanvas::drawRect Direction | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addOval(const SkRect& oval, Direction dir = kCW_Direction) | 
 |  | 
 | Add Oval to path, appending kMove_Verb, four kConic_Verb, and kClose_Verb. | 
 | Oval is upright ellipse bounded by Rect oval with radii equal to half oval width | 
 | and half oval height. Oval begins at (oval.fRight, oval.centerY()) and continues | 
 | clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction. | 
 |  | 
 | This form is identical to addOval(oval, dir, 1). | 
 |  | 
 | #Param oval  bounds of ellipse added ## | 
 | #Param dir   Direction to wind ellipse ## | 
 |  | 
 | #Example | 
 | #Height 120 | 
 |     SkPaint paint; | 
 |     SkPath oval; | 
 |     oval.addOval({20, 20, 160, 80}); | 
 |     canvas->drawPath(oval, paint); | 
 | ## | 
 |  | 
 | #SeeAlso SkCanvas::drawOval Direction Oval | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addOval(const SkRect& oval, Direction dir, unsigned start) | 
 |  | 
 | Add Oval to Path, appending kMove_Verb, four kConic_Verb, and kClose_Verb. | 
 | Oval is upright ellipse bounded by Rect oval with radii equal to half oval width | 
 | and half oval height. Oval begins at start and continues | 
 | clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction. | 
 |  | 
 | #Table | 
 | #Legend | 
 | # start # Point                        ## | 
 | #Legend ## | 
 | # 0     # oval.centerX(), oval.fTop    ## | 
 | # 1     # oval.fRight, oval.centerY()  ## | 
 | # 2     # oval.centerX(), oval.fBottom ## | 
 | # 3     # oval.fLeft, oval.centerY()   ## | 
 | #Table ## | 
 |  | 
 | #Param oval   bounds of ellipse added ## | 
 | #Param dir    Direction to wind ellipse ## | 
 | #Param start  index of initial point of ellipse ## | 
 |  | 
 | #Example | 
 | #Height 160 | 
 | void draw(SkCanvas* canvas) { | 
 |     const SkPoint arrow[] = { {0, -5}, {10, 0}, {0, 5} }; | 
 |     const SkRect rect = {10, 10, 54, 54}; | 
 |     SkPaint ovalPaint; | 
 |     ovalPaint.setAntiAlias(true); | 
 |     SkPaint textPaint(ovalPaint); | 
 |     textPaint.setTextAlign(SkPaint::kCenter_Align); | 
 |     ovalPaint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPaint arrowPaint(ovalPaint); | 
 |     SkPath arrowPath; | 
 |     arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true); | 
 |     arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0, | 
 |                              SkPath1DPathEffect::kRotate_Style)); | 
 |     for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { | 
 |         for (unsigned start : { 0, 1, 2, 3 } ) { | 
 |            SkPath path; | 
 |            path.addOval(rect, direction, start); | 
 |            canvas->drawPath(path, ovalPaint); | 
 |            canvas->drawPath(path, arrowPaint); | 
 |            canvas->drawText(&"0123"[start], 1, rect.centerX(), rect.centerY() + 5, textPaint); | 
 |            canvas->translate(64, 0); | 
 |        } | 
 |        canvas->translate(-256, 72); | 
 |        canvas->drawString(SkPath::kCW_Direction == direction ? "clockwise" : "counterclockwise", | 
 |                           128, 0, textPaint); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso SkCanvas::drawOval Direction Oval | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addCircle(SkScalar x, SkScalar y, SkScalar radius, | 
 |                    Direction dir = kCW_Direction) | 
 |  | 
 | Add Circle centered at (x, y) of size radius to Path, appending kMove_Verb, | 
 | four kConic_Verb, and kClose_Verb. Circle begins at:  | 
 | #Formula | 
 | (x + radius, y) | 
 | ## | 
 | , continuing | 
 | clockwise if dir is kCW_Direction, and counterclockwise if dir is kCCW_Direction. | 
 |  | 
 | Has no effect if radius is zero or negative. | 
 |  | 
 | #Param x       center of Circle ## | 
 | #Param y       center of Circle  ## | 
 | #Param radius  distance from center to edge ## | 
 | #Param dir     Direction to wind Circle ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setStrokeWidth(10); | 
 |     for (int size = 10; size < 300; size += 20) { | 
 |         SkPath path; | 
 |         path.addCircle(128, 128, size, SkPath::kCW_Direction); | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso SkCanvas::drawCircle Direction Circle | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) | 
 |  | 
 | Append Arc to Path, as the start of new Contour. Arc added is part of ellipse | 
 | bounded by oval, from startAngle through sweepAngle. Both startAngle and | 
 | sweepAngle are measured in degrees, where zero degrees is aligned with the | 
 | positive x-axis, and positive sweeps extends Arc clockwise. | 
 |  | 
 | If sweepAngle <= -360, or sweepAngle >= 360; and startAngle modulo 90 is nearly  | 
 | zero, append Oval instead of Arc. Otherwise, sweepAngle values are treated  | 
 | modulo 360, and Arc may or may not draw depending on numeric rounding. | 
 |  | 
 | #Param oval        bounds of ellipse containing Arc ## | 
 | #Param startAngle  starting angle of Arc in degrees ## | 
 | #Param sweepAngle  sweep, in degrees. Positive is clockwise; treated modulo 360 ## | 
 |  | 
 | #Example | 
 | #Description | 
 | The middle row of the left and right columns draw differently from the entries | 
 | above and below because sweepAngle is outside of the range of +/-360,  | 
 | and startAngle modulo 90 is not zero. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     for (auto start : { 0, 90, 135, 180, 270 } ) { | 
 |         for (auto sweep : { -450.f, -180.f, -90.f, 90.f, 180.f, 360.1f } ) { | 
 |             SkPath path; | 
 |             path.addArc({10, 10, 35, 45}, start, sweep); | 
 |             canvas->drawPath(path, paint); | 
 |             canvas->translate(252 / 6, 0); | 
 |         } | 
 |         canvas->translate(-252, 255 / 5); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso Arc arcTo SkCanvas::drawArc | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, | 
 |                       Direction dir = kCW_Direction) | 
 |  | 
 | Append Round_Rect to Path, creating a new closed Contour. Round_Rect has bounds | 
 | equal to rect; each corner is 90 degrees of an ellipse with radii (rx, ry). If | 
 | dir is kCW_Direction, Round_Rect starts at top-left of the lower-left corner and | 
 | winds clockwise. If dir is kCCW_Direction, Round_Rect starts at the bottom-left | 
 | of the upper-left corner and winds counterclockwise. | 
 |  | 
 | If either rx or ry is too large, rx and ry are scaled uniformly until the | 
 | corners fit. If rx or ry is less than or equal to zero, addRoundRect appends | 
 | Rect rect to Path. | 
 |  | 
 | After appending, Path may be empty, or may contain: Rect, Oval, or RoundRect. | 
 |  | 
 | #Param rect  bounds of Round_Rect ## | 
 | #Param rx    x-radius of rounded corners on the Round_Rect ## | 
 | #Param ry    y-radius of rounded corners on the Round_Rect ## | 
 | #Param dir   Direction to wind Round_Rect ## | 
 |  | 
 | #Example | 
 | #Description | 
 | If either radius is zero, path contains Rect and is drawn red. | 
 | If sides are only radii, path contains Oval and is drawn blue. | 
 | All remaining path draws are convex, and are drawn in gray; no | 
 | paths constructed from addRoundRect are concave, so none are | 
 | drawn in green. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     for (auto xradius : { 0, 7, 13, 20 } ) { | 
 |         for (auto yradius : { 0, 9, 18, 40 } ) { | 
 |             SkPath path; | 
 |             path.addRoundRect({10, 10, 36, 46}, xradius, yradius); | 
 |             paint.setColor(path.isRect(nullptr) ? SK_ColorRED : path.isOval(nullptr) ? | 
 |                            SK_ColorBLUE : path.isConvex() ? SK_ColorGRAY : SK_ColorGREEN); | 
 |             canvas->drawPath(path, paint); | 
 |             canvas->translate(64, 0); | 
 |         } | 
 |         canvas->translate(-256, 64); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso addRRect SkCanvas::drawRoundRect | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addRoundRect(const SkRect& rect, const SkScalar radii[], | 
 |                       Direction dir = kCW_Direction) | 
 |  | 
 | Append Round_Rect to Path, creating a new closed Contour. Round_Rect has bounds | 
 | equal to rect; each corner is 90 degrees of an ellipse with radii from the | 
 | array. | 
 |  | 
 | #Table | 
 | #Legend | 
 | # radii index # location                        ## | 
 | #Legend ## | 
 | # 0           # x-radius of top-left corner     ## | 
 | # 1           # y-radius of top-left corner     ## | 
 | # 2           # x-radius of top-right corner    ## | 
 | # 3           # y-radius of top-right corner    ## | 
 | # 4           # x-radius of bottom-right corner ## | 
 | # 5           # y-radius of bottom-right corner ## | 
 | # 6           # x-radius of bottom-left corner  ## | 
 | # 7           # y-radius of bottom-left corner  ## | 
 | #Table ## | 
 |  | 
 | If dir is kCW_Direction, Round_Rect starts at top-left of the lower-left corner  | 
 | and winds clockwise. If dir is kCCW_Direction, Round_Rect starts at the  | 
 | bottom-left of the upper-left corner and winds counterclockwise. | 
 |  | 
 | If both radii on any side of rect exceed its length, all radii are scaled  | 
 | uniformly until the corners fit. If either radius of a corner is less than or | 
 | equal to zero, both are treated as zero. | 
 |  | 
 | After appending, Path may be empty, or may contain: Rect, Oval, or RoundRect. | 
 |  | 
 | #Param rect   bounds of Round_Rect ## | 
 | #Param radii  array of 8 SkScalar values, a radius pair for each corner ## | 
 | #Param dir    Direction to wind Round_Rect ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     SkScalar radii[] = { 80, 100, 0, 0, 40, 60, 0, 0 }; | 
 |     SkPath path; | 
 |     SkMatrix rotate90; | 
 |     rotate90.setRotate(90, 128, 128); | 
 |     for (int i = 0; i < 4; ++i) { | 
 |         path.addRoundRect({10, 10, 110, 110}, radii); | 
 |         path.transform(rotate90); | 
 |     } | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso addRRect SkCanvas::drawRoundRect | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction) | 
 |  | 
 | Add rrect to Path, creating a new closed Contour. If | 
 | dir is kCW_Direction, rrect starts at top-left of the lower-left corner and | 
 | winds clockwise. If dir is kCCW_Direction, rrect starts at the bottom-left | 
 | of the upper-left corner and winds counterclockwise. | 
 |  | 
 | After appending, Path may be empty, or may contain: Rect, Oval, or Round_Rect. | 
 |  | 
 | #Param rrect  bounds and radii of rounded rectangle ## | 
 | #Param dir   Direction to wind Round_Rect ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     SkRRect rrect; | 
 |     SkVector radii[] = {{50, 50}, {0, 0}, {0, 0}, {50, 50}}; | 
 |     rrect.setRectRadii({10, 10, 110, 110}, radii); | 
 |     SkPath path; | 
 |     SkMatrix rotate90; | 
 |     rotate90.setRotate(90, 128, 128); | 
 |     for (int i = 0; i < 4; ++i) { | 
 |         path.addRRect(rrect); | 
 |         path.transform(rotate90); | 
 |     } | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso addRoundRect SkCanvas::drawRRect | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addRRect(const SkRRect& rrect, Direction dir, unsigned start) | 
 |  | 
 | Add rrect to Path, creating a new closed Contour. If dir is kCW_Direction, rrect | 
 | winds clockwise; if dir is kCCW_Direction, rrect winds counterclockwise. | 
 | start determines the first point of rrect to add. | 
 |  | 
 | #Table | 
 | #Legend | 
 | # start       # location                    ## | 
 | #Legend ## | 
 | # 0           # right of top-left corner    ## | 
 | # 1           # left of top-right corner    ## | 
 | # 2           # bottom of top-right corner  ## | 
 | # 3           # top of bottom-right corner  ## | 
 | # 4           # left of bottom-right corner ## | 
 | # 5           # right of bottom-left corner ## | 
 | # 6           # top of bottom-left corner   ## | 
 | # 7           # bottom of top-left corner   ## | 
 | #Table ## | 
 |  | 
 | After appending, Path may be empty, or may contain: Rect, Oval, or Round_Rect. | 
 |  | 
 | #Param rrect  bounds and radii of rounded rectangle ## | 
 | #Param dir    Direction to wind Round_Rect ## | 
 | #Param start  index of initial point of Round_Rect ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     SkRRect rrect; | 
 |     rrect.setRectXY({40, 40, 215, 215}, 50, 50); | 
 |     SkPath path; | 
 |     path.addRRect(rrect); | 
 |     canvas->drawPath(path, paint); | 
 |     for (int start = 0; start < 8; ++start) { | 
 |         SkPath textPath; | 
 |         textPath.addRRect(rrect, SkPath::kCW_Direction, start); | 
 |         canvas->drawTextOnPathHV(&"01234567"[start], 1, textPath, 0, -5, paint); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso addRoundRect SkCanvas::drawRRect  | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addPoly(const SkPoint pts[], int count, bool close) | 
 |  | 
 | Add Contour created from Line array, adding (count - 1) Line segments. | 
 | Contour added starts at pts[0], then adds a line for every additional Point | 
 | in pts array. If close is true,appends kClose_Verb to Path, connecting | 
 | pts[count - 1] and pts[0]. | 
 |  | 
 | If count is zero, append kMove_Verb to path. | 
 | Has no effect if count is less than one. | 
 |    | 
 | #Param pts    array of Line sharing end and start Point ## | 
 | #Param count  length of Point array ## | 
 | #Param close  true to add Line connecting Contour end and start ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setStrokeWidth(15); | 
 |     paint.setStrokeCap(SkPaint::kRound_Cap); | 
 |     const SkPoint points[] = {{20, 20}, {70, 20}, {40, 90}}; | 
 |     for (bool close : { false, true } ) { | 
 |         SkPath path; | 
 |         path.addPoly(points, SK_ARRAY_COUNT(points), close); | 
 |         for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style, | 
 |                 SkPaint::kStrokeAndFill_Style} ) { | 
 |             paint.setStyle(style); | 
 |             canvas->drawPath(path, paint); | 
 |             canvas->translate(85, 0); | 
 |         } | 
 |         canvas->translate(-255, 128); | 
 |     } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso SkCanvas::drawPoints | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Enum AddPathMode | 
 |  | 
 | #Code | 
 |     enum AddPathMode { | 
 |         kAppend_AddPathMode,  | 
 |         kExtend_AddPathMode,  | 
 |     }; | 
 | ## | 
 |  | 
 | AddPathMode chooses how addPath appends. Adding one Path to another can extend | 
 | the last Contour or start a new Contour. | 
 |  | 
 | #Const kAppend_AddPathMode | 
 |     Path Verbs, Points, and Conic_Weights are appended to destination unaltered. | 
 |     Since Path Verb_Array begins with kMove_Verb if src is not empty, this | 
 |     starts a new Contour. | 
 | ## | 
 | #Const kExtend_AddPathMode | 
 |     If destination is closed or empty, start a new Contour. If destination | 
 |     is not empty, add Line from Last_Point to added Path first Point. Skip added | 
 |     Path initial kMove_Verb, then append remining Verbs, Points, and Conic_Weights. | 
 | ## | 
 |  | 
 | #Example | 
 | #Description | 
 | test is built from path, open on the top row, and closed on the bottom row. | 
 | The left column uses kAppend_AddPathMode; the right uses kExtend_AddPathMode. | 
 | The top right composition is made up of one contour; the other three have two. | 
 | ## | 
 | #Height 180 | 
 |     SkPath path, path2; | 
 |     path.moveTo(20, 20); | 
 |     path.lineTo(20, 40); | 
 |     path.lineTo(40, 20); | 
 |     path2.moveTo(60, 60); | 
 |     path2.lineTo(80, 60); | 
 |     path2.lineTo(80, 40); | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     for (int i = 0; i < 2; i++) { | 
 |         for (auto addPathMode : { SkPath::kAppend_AddPathMode, SkPath::kExtend_AddPathMode } ) { | 
 |             SkPath test(path); | 
 |             test.addPath(path2, addPathMode); | 
 |             canvas->drawPath(test, paint); | 
 |             canvas->translate(100, 0); | 
 |         } | 
 |         canvas->translate(-200, 100); | 
 |         path.close(); | 
 |     } | 
 | ## | 
 |  | 
 | #SeeAlso addPath reverseAddPath | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addPath(const SkPath& src, SkScalar dx, SkScalar dy, | 
 |                  AddPathMode mode = kAppend_AddPathMode) | 
 |  | 
 | Append src to Path, offset by (dx, dy).  | 
 |  | 
 | If mode is kAppend_AddPathMode, src Verb_Array, Point_Array, and Conic_Weights are | 
 | added unaltered. If mode is kExtend_AddPathMode, add Line before appending | 
 | Verbs, Points, and Conic_Weights.  | 
 |  | 
 | #Param src  Path Verbs, Points, and Conic_Weights to add ## | 
 | #Param dx   offset added to src Point_Array x coordinates ## | 
 | #Param dy   offset added to src Point_Array y coordinates ## | 
 | #Param mode kAppend_AddPathMode or kExtend_AddPathMode ## | 
 |  | 
 | #Example | 
 | #Height 180 | 
 |     SkPaint paint; | 
 |     paint.setTextSize(128); | 
 |     paint.setFakeBoldText(true); | 
 |     SkPath dest, text; | 
 |     paint.getTextPath("O", 1, 50, 120, &text); | 
 |     for (int i = 0; i < 3; i++) { | 
 |         dest.addPath(text, i * 20, i * 20); | 
 |     } | 
 |     Simplify(dest, &dest); | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     paint.setStrokeWidth(3); | 
 |     canvas->drawPath(dest, paint); | 
 | ## | 
 |  | 
 | #SeeAlso AddPathMode offset() reverseAddPath | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode)  | 
 |  | 
 | Append src to Path. | 
 |  | 
 | If mode is kAppend_AddPathMode, src Verb_Array, Point_Array, and Conic_Weights are | 
 | added unaltered. If mode is kExtend_AddPathMode, add Line before appending | 
 | Verbs, Points, and Conic_Weights.  | 
 |  | 
 | #Param src  Path Verbs, Points, and Conic_Weights to add ## | 
 | #Param mode kAppend_AddPathMode or kExtend_AddPathMode ## | 
 |  | 
 | #Example | 
 | #Height 80 | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPath dest, path; | 
 |     path.addOval({-80, 20, 0, 60}, SkPath::kCW_Direction, 1); | 
 |     for (int i = 0; i < 2; i++) { | 
 |         dest.addPath(path, SkPath::kExtend_AddPathMode); | 
 |         dest.offset(100, 0); | 
 |     } | 
 |     canvas->drawPath(dest, paint); | 
 | ## | 
 |  | 
 | #SeeAlso AddPathMode reverseAddPath | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode) | 
 |  | 
 | Append src to Path, transformed by matrix. Transformed curves may have different | 
 | Verbs, Points, and Conic_Weights. | 
 |  | 
 | If mode is kAppend_AddPathMode, src Verb_Array, Point_Array, and Conic_Weights are | 
 | added unaltered. If mode is kExtend_AddPathMode, add Line before appending | 
 | Verbs, Points, and Conic_Weights.  | 
 |  | 
 | #Param src     Path Verbs, Points, and Conic_Weights to add ## | 
 | #Param matrix  transform applied to src ## | 
 | #Param mode    kAppend_AddPathMode or kExtend_AddPathMode ## | 
 |  | 
 | #Example | 
 | #Height 160 | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     SkPath dest, path; | 
 |     path.addOval({20, 20, 200, 120}, SkPath::kCW_Direction, 1); | 
 |     for (int i = 0; i < 6; i++) { | 
 |         SkMatrix matrix; | 
 |         matrix.reset(); | 
 |         matrix.setPerspX(i / 400.f); | 
 |         dest.addPath(path, matrix); | 
 |     } | 
 |     canvas->drawPath(dest, paint); | 
 | ## | 
 |  | 
 | #SeeAlso AddPathMode transform() offset() reverseAddPath | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void reverseAddPath(const SkPath& src) | 
 |  | 
 | Append src to Path, from back to front.  | 
 | Reversed src always appends a new Contour to Path. | 
 |  | 
 | #Param src     Path Verbs, Points, and Conic_Weights to add ## | 
 |  | 
 | #Example | 
 | #Height 200 | 
 |     SkPath path; | 
 |     path.moveTo(20, 20); | 
 |     path.lineTo(20, 40); | 
 |     path.lineTo(40, 20); | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     for (int i = 0; i < 2; i++) { | 
 |         SkPath path2; | 
 |         path2.moveTo(60, 60); | 
 |         path2.lineTo(80, 60); | 
 |         path2.lineTo(80, 40); | 
 |         for (int j = 0; j < 2; j++) { | 
 |             SkPath test(path); | 
 |             test.reverseAddPath(path2); | 
 |             canvas->drawPath(test, paint); | 
 |             canvas->translate(100, 0); | 
 |             path2.close(); | 
 |         } | 
 |         canvas->translate(-200, 100); | 
 |         path.close(); | 
 |     } | 
 | ## | 
 |  | 
 | #SeeAlso AddPathMode transform() offset() addPath | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void offset(SkScalar dx, SkScalar dy, SkPath* dst) const | 
 |  | 
 | Offset Point_Array by (dx, dy). Offset Path replaces dst. | 
 | If dst is nullptr, Path is replaced by offset data. | 
 |  | 
 | #Param dx   offset added to Point_Array x coordinates ## | 
 | #Param dy   offset added to Point_Array y coordinates ## | 
 | #Param dst  overwritten, translated copy of Path; may be nullptr ## | 
 |  | 
 | #Example | 
 | #Height 60 | 
 |     SkPath pattern; | 
 |     pattern.moveTo(20, 20); | 
 |     pattern.lineTo(20, 40); | 
 |     pattern.lineTo(40, 20); | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     for (int i = 0; i < 10; i++) { | 
 |         SkPath path; | 
 |         pattern.offset(20 * i, 0, &path); | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 | ## | 
 |  | 
 | #SeeAlso addPath transform | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void offset(SkScalar dx, SkScalar dy)  | 
 |  | 
 | Offset Point_Array by (dx, dy). Path is replaced by offset data. | 
 |  | 
 | #Param dx  offset added to Point_Array x coordinates ## | 
 | #Param dy  offset added to Point_Array y coordinates ## | 
 |  | 
 | #Example | 
 | #Height 60 | 
 |     SkPath path; | 
 |     path.moveTo(20, 20); | 
 |     path.lineTo(20, 40); | 
 |     path.lineTo(40, 20); | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     for (int i = 0; i < 10; i++) { | 
 |         canvas->drawPath(path, paint); | 
 |         path.offset(20, 0); | 
 |     } | 
 | ## | 
 |  | 
 | #SeeAlso addPath transform SkCanvas::translate() | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void transform(const SkMatrix& matrix, SkPath* dst) const | 
 |  | 
 | Transform Verb_Array, Point_Array, and weight by matrix. | 
 | transform may change Verbs and increase their number. | 
 | Transformed Path replaces dst; if dst is nullptr, original data | 
 | is replaced.  | 
 |  | 
 | #Param matrix  Matrix to apply to Path ## | 
 | #Param dst     overwritten, transformed copy of Path; may be nullptr ## | 
 |  | 
 | #Example | 
 | #Height 200 | 
 |     SkPath pattern; | 
 |     pattern.moveTo(100, 100); | 
 |     pattern.lineTo(100, 20); | 
 |     pattern.lineTo(20, 100); | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     for (int i = 0; i < 10; i++) { | 
 |         SkPath path; | 
 |         SkMatrix matrix; | 
 |         matrix.setRotate(36 * i, 100, 100); | 
 |         pattern.transform(matrix, &path); | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 | ## | 
 |  | 
 | #SeeAlso addPath offset SkCanvas::concat() SkMatrix | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void transform(const SkMatrix& matrix)  | 
 |  | 
 | Transform Verb_Array, Point_Array, and weight by matrix. | 
 | transform may change Verbs and increase their number. | 
 | Path is replaced by transformed data. | 
 |  | 
 | #Param matrix  Matrix to apply to Path ## | 
 |  | 
 | #Example | 
 | #Height 200 | 
 |     SkPath path; | 
 |     path.moveTo(100, 100); | 
 |     path.quadTo(100, 20, 20, 100); | 
 |     SkPaint paint; | 
 |     paint.setStyle(SkPaint::kStroke_Style); | 
 |     for (int i = 0; i < 10; i++) { | 
 |         SkMatrix matrix; | 
 |         matrix.setRotate(36, 100, 100); | 
 |         path.transform(matrix); | 
 |         canvas->drawPath(path, paint); | 
 |     } | 
 | ## | 
 |  | 
 | #SeeAlso addPath offset SkCanvas::concat() SkMatrix | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Subtopic Last_Point | 
 |  | 
 | Path is defined cumulatively, often by adding a segment to the end of last | 
 | Contour. Last_Point of Contour is shared as first Point of added Line or Curve. | 
 | Last_Point can be read and written directly with getLastPt and setLastPt. | 
 |  | 
 | #Method bool getLastPt(SkPoint* lastPt) const | 
 |  | 
 |     Returns Last_Point on Path in lastPt. Returns false if Point_Array is empty,  | 
 |     storing (0, 0) if lastPt is not nullptr. | 
 |  | 
 |     #Param lastPt  storage for final Point in Point_Array; may be nullptr ## | 
 |  | 
 |     #Return  true if Point_Array contains one or more Points ## | 
 |  | 
 |     #Example | 
 |         SkPath path; | 
 |         path.moveTo(100, 100); | 
 |         path.quadTo(100, 20, 20, 100); | 
 |         SkMatrix matrix; | 
 |         matrix.setRotate(36, 100, 100); | 
 |         path.transform(matrix); | 
 |         SkPoint last; | 
 |         path.getLastPt(&last); | 
 |         SkDebugf("last point: %g, %g\n", last.fX, last.fY); | 
 |     #StdOut | 
 |     last point: 35.2786, 52.9772 | 
 |     ##     | 
 |     ## | 
 |  | 
 |     #SeeAlso setLastPt | 
 |  | 
 | ## | 
 |  | 
 | #Method void setLastPt(SkScalar x, SkScalar y) | 
 |  | 
 |     Set Last_Point to (x, y). If Point_Array is empty, append kMove_Verb to | 
 |     Verb_Array and (x, y) to Point_Array. | 
 |  | 
 |     #Param x  set x-coordinate of Last_Point ## | 
 |     #Param y  set y-coordinate of Last_Point ## | 
 |  | 
 |     #Example | 
 |     #Height 128 | 
 |         SkPaint paint; | 
 |         paint.setTextSize(128); | 
 |         SkPath path; | 
 |         paint.getTextPath("@", 1, 60, 100, &path); | 
 |         path.setLastPt(20, 120); | 
 |         canvas->drawPath(path, paint); | 
 |     ## | 
 |  | 
 |     #SeeAlso getLastPt | 
 |  | 
 | ## | 
 |  | 
 | #Method void setLastPt(const SkPoint& p)  | 
 |  | 
 |     Set the last point on the path. If no points have been added, moveTo(p) | 
 |     is automatically called. | 
 |  | 
 |     #Param p  set value of Last_Point ## | 
 |  | 
 |     #Example | 
 |     #Height 128 | 
 |         SkPaint paint; | 
 |         paint.setTextSize(128); | 
 |         SkPath path, path2; | 
 |         paint.getTextPath("A", 1, 60, 100, &path); | 
 |         paint.getTextPath("Z", 1, 60, 100, &path2); | 
 |         SkPoint pt, pt2; | 
 |         path.getLastPt(&pt); | 
 |         path2.getLastPt(&pt2); | 
 |         path.setLastPt(pt2); | 
 |         path2.setLastPt(pt); | 
 |         canvas->drawPath(path, paint); | 
 |         canvas->drawPath(path2, paint); | 
 |     ## | 
 |  | 
 |     #SeeAlso getLastPt | 
 |  | 
 | ## | 
 |  | 
 | #Subtopic Last_Point ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Enum SegmentMask | 
 |  | 
 | #Code | 
 |     enum SegmentMask { | 
 |         kLine_SegmentMask = 1 << 0, | 
 |         kQuad_SegmentMask = 1 << 1, | 
 |         kConic_SegmentMask = 1 << 2, | 
 |         kCubic_SegmentMask = 1 << 3, | 
 |     }; | 
 | ## | 
 |  | 
 | SegmentMask constants correspond to each drawing Verb type in Path; for | 
 | instance, if Path only contains Lines, only the kLine_SegmentMask bit is set. | 
 |  | 
 | #Bug 6785 ## | 
 | #Const kLine_SegmentMask 1 | 
 | Set if Verb_Array contains kLine_Verb. | 
 | ## | 
 | #Const kQuad_SegmentMask 2 | 
 | Set if Verb_Array contains kQuad_Verb. Note that conicTo may add a Quad. | 
 | ## | 
 | #Const kConic_SegmentMask 4 | 
 | Set if Verb_Array contains kConic_Verb. | 
 | ## | 
 | #Const kCubic_SegmentMask 8 | 
 | Set if Verb_Array contains kCubic_Verb. | 
 | ## | 
 |  | 
 | #Example | 
 | #Description | 
 | When conicTo has a weight of one, Quad is added to Path. | 
 | ## | 
 |     SkPath path; | 
 |     path.conicTo(10, 10, 20, 30, 1); | 
 |     SkDebugf("Path kConic_SegmentMask is %s\n", path.getSegmentMasks() &  | 
 |           SkPath::kConic_SegmentMask ? "set" : "clear"); | 
 |     SkDebugf("Path kQuad_SegmentMask is %s\n", path.getSegmentMasks() &  | 
 |           SkPath::kQuad_SegmentMask ? "set" : "clear"); | 
 | #StdOut | 
 | Path kConic_SegmentMask is clear | 
 | Path kQuad_SegmentMask is set | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso getSegmentMasks Verb | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method uint32_t getSegmentMasks() const  | 
 |  | 
 | Returns a mask, where each set bit corresponds to a SegmentMask constant | 
 | if Path contains one or more Verbs of that type. | 
 | Returns zero if Path contains no Lines, or Curves: Quads, Conics, or Cubics. | 
 |  | 
 | getSegmentMasks() returns a cached result; it is very fast. | 
 |  | 
 | #Return  SegmentMask bits or zero ## | 
 |  | 
 | #Example | 
 | SkPath path; | 
 | path.quadTo(20, 30, 40, 50); | 
 | path.close(); | 
 | const char* masks[] = { "line", "quad", "conic", "cubic" }; | 
 | int index = 0; | 
 | for (auto mask : { SkPath::kLine_SegmentMask, SkPath::kQuad_SegmentMask, | 
 |         SkPath::kConic_SegmentMask, SkPath::kCubic_SegmentMask } ) { | 
 |     if (mask & path.getSegmentMasks()) { | 
 |        SkDebugf("mask %s set\n", masks[index]); | 
 |     }        | 
 |     ++index; | 
 | } | 
 | #StdOut | 
 | mask quad set | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso getSegmentMasks Verb | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool contains(SkScalar x, SkScalar y) const | 
 |  | 
 | Returns true if the point (x, y) is contained by Path, taking into | 
 | account FillType.  | 
 |  | 
 | #Table | 
 | #Legend | 
 | # FillType                 # contains() returns true if Point is enclosed by ## | 
 | ## | 
 | # kWinding_FillType        # a non-zero sum of Contour Directions. ## | 
 | # kEvenOdd_FillType        # an odd number of Contours.            ## | 
 | # kInverseWinding_FillType # a zero sum of Contour Directions.     ## | 
 | # kInverseEvenOdd_FillType # and even number of Contours.          ## | 
 | ##  | 
 |  | 
 | #Param x  x-coordinate of containment test ## | 
 | #Param y  y-coordinate of containment test ## | 
 |  | 
 | #Return  true if Point is in Path ## | 
 |  | 
 | #Example | 
 | SkPath path; | 
 | SkPaint paint; | 
 | paint.setTextSize(256); | 
 | paint.getTextPath("&", 1, 30, 220, &path); | 
 | for (int y = 2; y < 256; y += 9) { | 
 |    for (int x = 2; x < 256; x += 9) { | 
 |        int coverage = 0; | 
 |        for (int iy = -4; iy <= 4; iy += 2) { | 
 |            for (int ix = -4; ix <= 4; ix += 2) { | 
 |                coverage += path.contains(x + ix, y + iy); | 
 |            } | 
 |        } | 
 |        paint.setColor(SkColorSetARGB(0x5f, 0xff * coverage / 25, 0, 0xff * (25 - coverage) / 25)); | 
 |        canvas->drawCircle(x, y, 8, paint); | 
 |    } | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso conservativelyContainsRect Fill_Type Op | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void dump(SkWStream* stream, bool forceClose, bool dumpAsHex) const | 
 |  | 
 | Writes text representation of Path to stream. If stream is nullptr, writes to | 
 | standard output. Set forceClose to true to get edges used to fill Path. | 
 | Set dumpAsHex true to generate exact binary representations | 
 | of floating point numbers used in Point_Array and Conic_Weights. | 
 |  | 
 | #Param stream      writable Stream receiving Path text representation; may be nullptr ## | 
 | #Param forceClose  true if missing kClose_Verb is output ## | 
 | #Param dumpAsHex   true if SkScalar values are written as hexadecimal ## | 
 |  | 
 | #Example | 
 |    SkPath path; | 
 |    path.quadTo(20, 30, 40, 50); | 
 |    for (bool forceClose : { false, true } ) { | 
 |        for (bool dumpAsHex : { false, true } ) { | 
 |            path.dump(nullptr, forceClose, dumpAsHex); | 
 |            SkDebugf("\n"); | 
 |        } | 
 |    } | 
 | #StdOut | 
 | path.setFillType(SkPath::kWinding_FillType); | 
 | path.moveTo(0, 0); | 
 | path.quadTo(20, 30, 40, 50); | 
 |  | 
 | path.setFillType(SkPath::kWinding_FillType); | 
 | path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0 | 
 | path.quadTo(SkBits2Float(0x41a00000), SkBits2Float(0x41f00000), SkBits2Float(0x42200000), SkBits2Float(0x42480000));  // 20, 30, 40, 50 | 
 |  | 
 | path.setFillType(SkPath::kWinding_FillType); | 
 | path.moveTo(0, 0); | 
 | path.quadTo(20, 30, 40, 50); | 
 | path.lineTo(0, 0); | 
 | path.close(); | 
 |  | 
 | path.setFillType(SkPath::kWinding_FillType); | 
 | path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0 | 
 | path.quadTo(SkBits2Float(0x41a00000), SkBits2Float(0x41f00000), SkBits2Float(0x42200000), SkBits2Float(0x42480000));  // 20, 30, 40, 50 | 
 | path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0 | 
 | path.close(); | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso SkRect::dump() SkRRect::dump() SkPathMeasure::dump() | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void dump() const | 
 |  | 
 | Writes text representation of Path to standard output. The representation may be | 
 | directly compiled as C++ code. Floating point values are written | 
 | with limited precision; it may not be possible to reconstruct original Path | 
 | from output. | 
 |  | 
 | #Example | 
 | SkPath path, copy; | 
 | path.lineTo(6.f / 7, 2.f / 3); | 
 | path.dump(); | 
 | copy.setFillType(SkPath::kWinding_FillType); | 
 | copy.moveTo(0, 0); | 
 | copy.lineTo(0.857143f, 0.666667f); | 
 | SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not "); | 
 | #StdOut | 
 | path.setFillType(SkPath::kWinding_FillType); | 
 | path.moveTo(0, 0); | 
 | path.lineTo(0.857143f, 0.666667f); | 
 | path is not equal to copy | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso dumpHex SkRect::dump() SkRRect::dump() SkPathMeasure::dump() writeToMemory | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method void dumpHex() const | 
 |  | 
 | Writes text representation of Path to standard output. The representation may be | 
 | directly compiled as C++ code. Floating point values are written | 
 | in hexadecimal to preserve their exact bit pattern. The output reconstructs the | 
 | original Path. | 
 |  | 
 | Use instead of dump() when submitting  | 
 | #A bug reports against Skia # http://bug.skia.org ## | 
 | . | 
 |  | 
 | #Example | 
 | SkPath path, copy; | 
 | path.lineTo(6.f / 7, 2.f / 3); | 
 | path.dumpHex(); | 
 | copy.setFillType(SkPath::kWinding_FillType); | 
 | copy.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0 | 
 | copy.lineTo(SkBits2Float(0x3f5b6db7), SkBits2Float(0x3f2aaaab));  // 0.857143f, 0.666667f | 
 | SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not "); | 
 | #StdOut | 
 | path.setFillType(SkPath::kWinding_FillType); | 
 | path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0 | 
 | path.lineTo(SkBits2Float(0x3f5b6db7), SkBits2Float(0x3f2aaaab));  // 0.857143f, 0.666667f | 
 | path is equal to copy | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso dump SkRect::dumpHex() SkRRect::dumpHex() writeToMemory | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method size_t writeToMemory(void* buffer) const | 
 |  | 
 | Writes Path to buffer, returning the number of bytes written. | 
 | Pass nullptr to obtain the storage size. | 
 |  | 
 | Writes Fill_Type, Verb_Array, Point_Array, Conic_Weight, and | 
 | additionally writes computed information like Convexity and bounds. | 
 |  | 
 | Use only be used in concert with readFromMemory; | 
 | the format used for Path in memory is not guaranteed. | 
 |  | 
 | #Param buffer  storage for Path; may be nullptr ## | 
 |  | 
 | #Return  size of storage required for Path; always a multiple of 4 ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPath path, copy; | 
 |     path.lineTo(6.f / 7, 2.f / 3); | 
 |     size_t size = path.writeToMemory(nullptr); | 
 |     SkTDArray<char> storage; | 
 |     storage.setCount(size); | 
 |     path.writeToMemory(storage.begin()); | 
 |     copy.readFromMemory(storage.begin(), size); | 
 |     SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not "); | 
 | } | 
 | #StdOut | 
 | path is equal to copy | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso serialize readFromMemory dump dumpHex | 
 |  | 
 | ## | 
 |  | 
 | #Method sk_sp<SkData> serialize() const | 
 |  | 
 | Write Path to buffer, returning the buffer written to, wrapped in Data. | 
 |  | 
 | serialize() writes Fill_Type, Verb_Array, Point_Array, Conic_Weight, and | 
 | additionally writes computed information like Convexity and bounds. | 
 |  | 
 | serialize() should only be used in concert with readFromMemory. | 
 | The format used for Path in memory is not guaranteed. | 
 |  | 
 | #Return  Path data wrapped in Data buffer ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPath path, copy; | 
 |     path.lineTo(6.f / 7, 2.f / 3); | 
 |     sk_sp<SkData> data = path.serialize(); | 
 |     copy.readFromMemory(data->data(), data->size()); | 
 |     SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not "); | 
 | } | 
 | #StdOut | 
 | path is equal to copy | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso writeToMemory readFromMemory dump dumpHex | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method size_t readFromMemory(const void* buffer, size_t length) | 
 |  | 
 | Initializes Path from buffer of size length. Returns zero if the buffer is | 
 | data is inconsistent, or the length is too small.  | 
 |  | 
 | Reads Fill_Type, Verb_Array, Point_Array, Conic_Weight, and | 
 | additionally reads computed information like Convexity and bounds. | 
 |  | 
 | Used only in concert with writeToMemory; | 
 | the format used for Path in memory is not guaranteed. | 
 |  | 
 | #Param buffer  storage for Path ## | 
 | #Param length  buffer size in bytes; must be multiple of 4 ## | 
 |  | 
 | #Return  number of bytes read, or zero on failure ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPath path, copy; | 
 |     path.lineTo(6.f / 7, 2.f / 3); | 
 |     size_t size = path.writeToMemory(nullptr); | 
 |     SkTDArray<char> storage; | 
 |     storage.setCount(size); | 
 |     path.writeToMemory(storage.begin()); | 
 |     size_t wrongSize = size - 4; | 
 |     size_t bytesRead = copy.readFromMemory(storage.begin(), wrongSize); | 
 |     SkDebugf("length = %u; returned by readFromMemory = %u\n", wrongSize, bytesRead); | 
 |     size_t largerSize = size + 4; | 
 |     bytesRead = copy.readFromMemory(storage.begin(), largerSize); | 
 |     SkDebugf("length = %u; returned by readFromMemory = %u\n", largerSize, bytesRead); | 
 | } | 
 | #StdOut | 
 | length = 60; returned by readFromMemory = 0 | 
 | length = 68; returned by readFromMemory = 64 | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso writeToMemory | 
 |  | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 | #Topic Generation_ID | 
 | #Alias Generation_IDs | 
 |  | 
 | Generation_ID provides a quick way to check if Verb_Array, Point_Array, or | 
 | Conic_Weight has changed. Generation_ID is not a hash; identical Paths will | 
 | not necessarily have matching Generation_IDs. | 
 |  | 
 | Empty Paths have a Generation_ID of one. | 
 |  | 
 | #Method uint32_t getGenerationID() const | 
 |  | 
 | Returns a non-zero, globally unique value. A different value is returned  | 
 | if Verb_Array, Point_Array, or Conic_Weight changes. | 
 |  | 
 | Setting Fill_Type does not change Generation_ID. | 
 |  | 
 | Each time the path is modified, a different Generation_ID will be returned. | 
 |  | 
 | #Bug 1762 | 
 | Fill_Type does affect Generation_ID on Android framework. | 
 | ## | 
 |  | 
 | #Return  non-zero, globally unique value ## | 
 |  | 
 | #Example | 
 | SkPath path; | 
 | SkDebugf("empty genID = %u\n", path.getGenerationID()); | 
 | path.lineTo(1, 2); | 
 | SkDebugf("1st lineTo genID = %u\n", path.getGenerationID()); | 
 | path.rewind(); | 
 | SkDebugf("empty genID = %u\n", path.getGenerationID()); | 
 | path.lineTo(1, 2); | 
 | SkDebugf("2nd lineTo genID = %u\n", path.getGenerationID()); | 
 | #StdOut | 
 | empty genID = 1 | 
 | 1st lineTo genID = 2 | 
 | empty genID = 1 | 
 | 2nd lineTo genID = 3 | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso operator==(const SkPath& a, const SkPath& b) | 
 |  | 
 | ## | 
 |  | 
 | #Topic ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Method bool isValid() const | 
 |  | 
 |     Returns if Path data is consistent. Corrupt Path data is detected if | 
 |     internal values are out of range or internal storage does not match | 
 |     array dimensions. | 
 |  | 
 |     #Return  true if Path data is consistent ## | 
 |  | 
 |     #NoExample | 
 |     ## | 
 |  | 
 | ## | 
 |  | 
 | #Method bool pathRefIsValid() const | 
 |  | 
 |     Returns if Path data is consistent. | 
 |  | 
 |     #Deprecated | 
 |     To be deprecated soon. | 
 |     ## | 
 |  | 
 |     #Return  true if Path data is consistent ## | 
 |  | 
 |     #NoExample | 
 |     ## | 
 | ## | 
 |  | 
 | # ------------------------------------------------------------------------------ | 
 |  | 
 | #Class Iter | 
 |  | 
 | Iterates through Verb_Array, and associated Point_Array and Conic_Weight. | 
 | Provides options to treat open Contours as closed, and to ignore | 
 | degenerate data. | 
 |  | 
 | #Code | 
 | class Iter { | 
 | public: | 
 |     Iter(); | 
 |     Iter(const SkPath& path, bool forceClose); | 
 |     void setPath(const SkPath& path, bool forceClose); | 
 |     Verb next(SkPoint pts[4], bool doConsumeDegenerates = true, bool exact = false); | 
 |     SkScalar conicWeight() const; | 
 |     bool isCloseLine() const; | 
 |     bool isClosedContour() const; | 
 | }; | 
 | ## | 
 |  | 
 | #Example | 
 | #Height 128 | 
 | #Description | 
 | Ignoring the actual Verbs and replacing them with Quads rounds the | 
 | path of the glyph. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setTextSize(256); | 
 |     SkPath asterisk, path; | 
 |     paint.getTextPath("*", 1, 50, 192, &asterisk); | 
 |     SkPath::Iter iter(asterisk, true);  | 
 |     SkPoint start[4], pts[4]; | 
 |     iter.next(start);  // skip moveTo | 
 |     iter.next(start);  // first quadTo | 
 |     path.moveTo((start[0] + start[1]) * 0.5f); | 
 |     while (SkPath::kClose_Verb != iter.next(pts)) { | 
 |         path.quadTo(pts[0], (pts[0] + pts[1]) * 0.5f); | 
 |     } | 
 |     path.quadTo(start[0], (start[0] + start[1]) * 0.5f); | 
 |     canvas->drawPath(path, paint); | 
 | } | 
 | ## | 
 |  | 
 | #SeeAlso RawIter | 
 |  | 
 | #Method Iter() | 
 |  | 
 | Initializes Iter with an empty Path. next() on Iter returns kDone_Verb. | 
 | Call setPath to initialize Iter at a later time. | 
 |  | 
 | #Return  Iter of empty Path ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     SkPath::Iter iter; | 
 |     SkPoint points[4]; | 
 |     SkDebugf("iter is " "%s" "done\n", SkPath::kDone_Verb == iter.next(points) ? "" : "not "); | 
 |     SkPath path; | 
 |     iter.setPath(path, false); | 
 |     SkDebugf("iter is " "%s" "done\n", SkPath::kDone_Verb == iter.next(points) ? "" : "not "); | 
 | } | 
 | #StdOut | 
 | iter is done | 
 | iter is done | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso setPath | 
 |  | 
 | ## | 
 |  | 
 | #Method Iter(const SkPath& path, bool forceClose) | 
 |  | 
 | Sets Iter to return elements of Verb_Array, Point_Array, and Conic_Weight in path. | 
 | If forceClose is true, Iter will add kLine_Verb and kClose_Verb after each | 
 | open Contour. path is not altered. | 
 |  | 
 | #Param path  Path to iterate ## | 
 | #Param forceClose true if open Contours generate kClose_Verb ## | 
 |  | 
 | #Return Iter of path ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, SkPath::Iter& iter) -> void { | 
 |         SkDebugf("%s:\n", prefix); | 
 |         const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; | 
 |         const int pointCount[] = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  }; | 
 |         SkPath::Verb verb; | 
 |         do { | 
 |            SkPoint points[4]; | 
 |            verb = iter.next(points); | 
 |            SkDebugf("k%s_Verb ", verbStr[(int) verb]); | 
 |            for (int i = 0; i < pointCount[(int) verb]; ++i) { | 
 |                 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); | 
 |            } | 
 |            if (SkPath::kConic_Verb == verb) { | 
 |                SkDebugf("weight = %g", iter.conicWeight()); | 
 |            } | 
 |            SkDebugf("\n"); | 
 |         } while (SkPath::kDone_Verb != verb); | 
 |         SkDebugf("\n"); | 
 |     }; | 
 |  | 
 |     SkPath path; | 
 |     path.quadTo(10, 20, 30, 40); | 
 |     SkPath::Iter openIter(path, false); | 
 |     debugster("open", openIter); | 
 |     SkPath::Iter closedIter(path, true); | 
 |     debugster("closed", closedIter); | 
 | } | 
 | #StdOut | 
 | open: | 
 | kMove_Verb {0, 0},  | 
 | kQuad_Verb {0, 0}, {10, 20}, {30, 40},  | 
 | kDone_Verb  | 
 |  | 
 | closed: | 
 | kMove_Verb {0, 0},  | 
 | kQuad_Verb {0, 0}, {10, 20}, {30, 40},  | 
 | kLine_Verb {30, 40}, {0, 0},  | 
 | kClose_Verb {0, 0},  | 
 | kDone_Verb  | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso setPath | 
 |  | 
 | ## | 
 |  | 
 | #Method void setPath(const SkPath& path, bool forceClose) | 
 |  | 
 | Sets Iter to return elements of Verb_Array, Point_Array, and Conic_Weight in path. | 
 | If forceClose is true, Iter will add kLine_Verb and kClose_Verb after each | 
 | open Contour. path is not altered. | 
 |  | 
 | #Param path  Path to iterate ## | 
 | #Param forceClose true if open Contours generate kClose_Verb ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, SkPath::Iter& iter) -> void { | 
 |         SkDebugf("%s:\n", prefix); | 
 |         const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; | 
 |         const int pointCount[] = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  }; | 
 |         SkPath::Verb verb; | 
 |         do { | 
 |            SkPoint points[4]; | 
 |            verb = iter.next(points); | 
 |            SkDebugf("k%s_Verb ", verbStr[(int) verb]); | 
 |            for (int i = 0; i < pointCount[(int) verb]; ++i) { | 
 |                 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); | 
 |            } | 
 |            if (SkPath::kConic_Verb == verb) { | 
 |                SkDebugf("weight = %g", iter.conicWeight()); | 
 |            } | 
 |            SkDebugf("\n"); | 
 |         } while (SkPath::kDone_Verb != verb); | 
 |         SkDebugf("\n"); | 
 |     }; | 
 |  | 
 |     SkPath path; | 
 |     path.quadTo(10, 20, 30, 40); | 
 |     SkPath::Iter iter(path, false); | 
 |     debugster("quad open", iter); | 
 |     SkPath path2; | 
 |     path2.conicTo(1, 2, 3, 4, .5f); | 
 |     iter.setPath(path2, true); | 
 |     debugster("conic closed", iter); | 
 | } | 
 | #StdOut | 
 | quad open: | 
 | kMove_Verb {0, 0},  | 
 | kQuad_Verb {0, 0}, {10, 20}, {30, 40},  | 
 | kDone_Verb  | 
 |  | 
 | conic closed: | 
 | kMove_Verb {0, 0},  | 
 | kConic_Verb {0, 0}, {1, 2}, {3, 4}, weight = 0.5 | 
 | kLine_Verb {3, 4}, {0, 0},  | 
 | kClose_Verb {0, 0},  | 
 | kDone_Verb  | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso Iter(const SkPath& path, bool forceClose) | 
 |  | 
 | ## | 
 |  | 
 | #Method Verb next(SkPoint pts[4], bool doConsumeDegenerates = true, bool exact = false)  | 
 |  | 
 | Returns next Verb in Verb_Array, and advances Iter. | 
 | When Verb_Array is exhausted, returns kDone_Verb. | 
 |  | 
 | Zero to four Points are stored in pts, depending on the returned Verb. | 
 |  | 
 | If doConsumeDegenerates is true, skip consecutive kMove_Verb entries, returning | 
 | only the last in the series; and skip very small Lines, Quads, and Conics; and | 
 | skip kClose_Verb following kMove_Verb. | 
 | if doConsumeDegenerates is true and exact is true, only skip Lines, Quads, and | 
 | Conics with zero lengths. | 
 |  | 
 |     #Param  pts storage for Point data describing returned Verb ## | 
 |     #Param doConsumeDegenerates if true, skip degenerate Verbs ## | 
 |     #Param exact skip zero length curves ## | 
 |  | 
 |     #Return next Verb from Verb_Array  ## | 
 |  | 
 | #Example | 
 | #Description  | 
 | skip degenerate skips the first in a kMove_Verb pair, the kMove_Verb | 
 | followed by the kClose_Verb, the zero length Line and the very small Line. | 
 |  | 
 | skip degenerate if exact skips the same as skip degenerate, but shows | 
 | the very small Line. | 
 |  | 
 | skip none shows all of the Verbs and Points in Path. | 
 | ## | 
 | void draw(SkCanvas* canvas) { | 
 |     auto debugster = [](const char* prefix, const SkPath& path, bool degen, bool exact) -> void { | 
 |         SkPath::Iter iter(path, false); | 
 |         SkDebugf("%s:\n", prefix); | 
 |         const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; | 
 |         const int pointCount[] = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  }; | 
 |         SkPath::Verb verb; | 
 |         do { | 
 |            SkPoint points[4]; | 
 |            verb = iter.next(points, degen, exact); | 
 |            SkDebugf("k%s_Verb ", verbStr[(int) verb]); | 
 |            for (int i = 0; i < pointCount[(int) verb]; ++i) { | 
 |                 SkDebugf("{%1.8g, %1.8g}, ", points[i].fX, points[i].fY); | 
 |            } | 
 |            SkDebugf("\n"); | 
 |         } while (SkPath::kDone_Verb != verb); | 
 |         SkDebugf("\n"); | 
 |     }; | 
 |  | 
 |     SkPath path; | 
 |     path.moveTo(10, 10); | 
 |     path.moveTo(20, 20); | 
 |     path.quadTo(10, 20, 30, 40); | 
 |     path.moveTo(1, 1); | 
 |     path.close(); | 
 |     path.moveTo(30, 30); | 
 |     path.lineTo(30, 30); | 
 |     path.moveTo(30, 30); | 
 |     path.lineTo(30.00001f, 30); | 
 |     debugster("skip degenerate", path, true, false); | 
 |     debugster("skip degenerate if exact", path, true, true); | 
 |     debugster("skip none", path, false, false); | 
 | } | 
 | #StdOut | 
 | skip degenerate: | 
 | kMove_Verb {20, 20},  | 
 | kQuad_Verb {20, 20}, {10, 20}, {30, 40},  | 
 | kDone_Verb  | 
 |  | 
 | skip degenerate if exact: | 
 | kMove_Verb {20, 20},  | 
 | kQuad_Verb {20, 20}, {10, 20}, {30, 40},  | 
 | kMove_Verb {30, 30},  | 
 | kLine_Verb {30, 30}, {30.00001, 30},  | 
 | kDone_Verb  | 
 |  | 
 | skip none: | 
 | kMove_Verb {10, 10},  | 
 | kMove_Verb {20, 20},  | 
 | kQuad_Verb {20, 20}, {10, 20}, {30, 40},  | 
 | kMove_Verb {1, 1},  | 
 | kClose_Verb {1, 1},  | 
 | kMove_Verb {30, 30},  | 
 | kLine_Verb {30, 30}, {30, 30},  | 
 | kMove_Verb {30, 30},  | 
 | kLine_Verb {30, 30}, {30.00001, 30},  | 
 | kDone_Verb  | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso Verb IsLineDegenerate IsCubicDegenerate IsQuadDegenerate  | 
 |  | 
 | ## | 
 |  | 
 | #Method SkScalar conicWeight() const | 
 |  | 
 |     Returns Conic_Weight if next() returned kConic_Verb. | 
 |  | 
 |     If next() has not been called, or next() did not return kConic_Verb, | 
 |     result is undefined. | 
 |  | 
 |     #Return  Conic_Weight for Conic Points returned by next() ## | 
 |  | 
 |     #Example | 
 |     void draw(SkCanvas* canvas) { | 
 |        SkPath path; | 
 |        path.conicTo(1, 2, 3, 4, .5f); | 
 |        SkPath::Iter iter(path, false);  | 
 |        SkPoint p[4]; | 
 |        SkDebugf("first verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not "); | 
 |        SkDebugf("next verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not "); | 
 |        SkDebugf("conic points: {%g,%g}, {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY, | 
 |                     p[2].fX, p[2].fY); | 
 |        SkDebugf("conic weight: %g\n", iter.conicWeight()); | 
 |     } | 
 |     #StdOut | 
 | first verb is move | 
 | next verb is conic | 
 | conic points: {0,0}, {1,2}, {3,4} | 
 | conic weight: 0.5 | 
 |     ## | 
 |     ## | 
 |  | 
 |     #SeeAlso Conic_Weight | 
 |  | 
 | ## | 
 |  | 
 | #Method bool isCloseLine() const | 
 |  | 
 |     Returns true if last kLine_Verb returned by next() was generated | 
 |     by kClose_Verb. When true, the end point returned by next() is | 
 |     also the start point of Contour. | 
 |  | 
 |     If next() has not been called, or next() did not return kLine_Verb, | 
 |     result is undefined. | 
 |  | 
 |     #Return  true if last kLine_Verb was generated by kClose_Verb ## | 
 |  | 
 |     #Example | 
 | void draw(SkCanvas* canvas) { | 
 |    SkPath path; | 
 |    path.moveTo(6, 7); | 
 |    path.conicTo(1, 2, 3, 4, .5f); | 
 |    path.close(); | 
 |    SkPath::Iter iter(path, false);  | 
 |    SkPoint p[4]; | 
 |    SkDebugf("1st verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not "); | 
 |    SkDebugf("moveTo point: {%g,%g}\n", p[0].fX, p[0].fY); | 
 |    SkDebugf("2nd verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not "); | 
 |    SkDebugf("3rd verb is " "%s" "line\n", SkPath::kLine_Verb == iter.next(p) ? "" : "not "); | 
 |    SkDebugf("line points: {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY); | 
 |    SkDebugf("line " "%s" "generated by close\n", iter.isCloseLine() ? "" : "not "); | 
 |    SkDebugf("4th verb is " "%s" "close\n", SkPath::kClose_Verb == iter.next(p) ? "" : "not "); | 
 | } | 
 |     #StdOut | 
 | 1st verb is move | 
 | moveTo point: {6,7} | 
 | 2nd verb is conic | 
 | 3rd verb is line | 
 | line points: {3,4}, {6,7} | 
 | line generated by close | 
 | 4th verb is close | 
 |     ## | 
 |     ## | 
 |  | 
 |     #SeeAlso close() | 
 | ## | 
 |  | 
 | #Method bool isClosedContour() const | 
 |  | 
 | Returns true if subsequent calls to next() return kClose_Verb before returning | 
 | kMove_Verb. if true, Contour Iter is processing may end with kClose_Verb, or | 
 | Iter may have been initialized with force close set to true. | 
 |  | 
 | #Return true if Contour is closed ## | 
 |  | 
 | #Example | 
 | void draw(SkCanvas* canvas) { | 
 |    for (bool forceClose : { false, true } ) { | 
 |        SkPath path; | 
 |        path.conicTo(1, 2, 3, 4, .5f); | 
 |        SkPath::Iter iter(path, forceClose);  | 
 |        SkDebugf("without close(), forceClose is %s: isClosedContour returns %s\n", | 
 |            forceClose ? "true " : "false", iter.isClosedContour() ? "true" : "false"); | 
 |        path.close(); | 
 |        iter.setPath(path, forceClose); | 
 |        SkDebugf("with close(),    forceClose is %s: isClosedContour returns %s\n", | 
 |            forceClose ? "true " : "false", iter.isClosedContour() ? "true" : "false"); | 
 |     } | 
 | } | 
 | #StdOut | 
 | without close(), forceClose is false: isClosedContour returns false | 
 | with close(),    forceClose is false: isClosedContour returns true | 
 | without close(), forceClose is true : isClosedContour returns true | 
 | with close(),    forceClose is true : isClosedContour returns true | 
 | ## | 
 | ## | 
 |  | 
 | #SeeAlso Iter(const SkPath& path, bool forceClose) | 
 |  | 
 | ## | 
 |  | 
 | #Class Iter ## | 
 |  | 
 | #Class RawIter | 
 |  | 
 | Iterates through Verb_Array, and associated Point_Array and Conic_Weight. | 
 | Verb_Array, Point_Array, and Conic_Weight are returned unaltered.  | 
 |  | 
 | #Code | 
 |     class RawIter { | 
 |     public: | 
 |         RawIter(); | 
 |         RawIter(const SkPath& path); | 
 |         void setPath(const SkPath& path); | 
 |         Verb next(SkPoint pts[4]); | 
 |         Verb peek() const; | 
 |         SkScalar conicWeight() const; | 
 |     } | 
 | ## | 
 |  | 
 |     #Method RawIter() | 
 |  | 
 |         Initializes RawIter with an empty Path. next() on RawIter returns kDone_Verb. | 
 |         Call setPath to initialize Iter at a later time. | 
 |  | 
 |         #Return RawIter of empty Path ## | 
 |  | 
 |         #NoExample | 
 |         ## | 
 |     ## | 
 |  | 
 |     #Method RawIter(const SkPath& path) | 
 |  | 
 |  | 
 |         Sets RawIter to return elements of Verb_Array, Point_Array, and Conic_Weight in path. | 
 |  | 
 |         #Param path  Path to iterate ## | 
 |  | 
 |         #Return RawIter of path ## | 
 |  | 
 |         #NoExample | 
 |         ## | 
 |     ## | 
 |  | 
 |     #Method void setPath(const SkPath& path) | 
 |  | 
 |         Sets Iter to return elements of Verb_Array, Point_Array, and Conic_Weight in path. | 
 |  | 
 |         #Param path  Path to iterate ## | 
 |  | 
 |         #NoExample | 
 |         ## | 
 |    ## | 
 |  | 
 |     #Method Verb next(SkPoint pts[4]) | 
 |  | 
 |         Returns next Verb in Verb_Array, and advances RawIter. | 
 |         When Verb_Array is exhausted, returns kDone_Verb. | 
 |         Zero to four Points are stored in pts, depending on the returned Verb. | 
 |  | 
 |         #Param  pts storage for Point data describing returned Verb ## | 
 |  | 
 |         #Return next Verb from Verb_Array  ## | 
 |  | 
 |         #Example | 
 |         void draw(SkCanvas* canvas) { | 
 |             SkPath path; | 
 |             path.moveTo(50, 60); | 
 |             path.quadTo(10, 20, 30, 40); | 
 |             path.close(); | 
 |             path.lineTo(30, 30); | 
 |             path.conicTo(1, 2, 3, 4, .5f); | 
 |             path.cubicTo(-1, -2, -3, -4, -5, -6); | 
 |             SkPath::RawIter iter(path); | 
 |             const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; | 
 |             const int pointCount[] = {     1 ,     2 ,     3 ,      3 ,      4 ,      1 ,     0  }; | 
 |             SkPath::Verb verb; | 
 |             do { | 
 |                 SkPoint points[4]; | 
 |                 verb = iter.next(points); | 
 |                 SkDebugf("k%s_Verb ", verbStr[(int) verb]); | 
 |                 for (int i = 0; i < pointCount[(int) verb]; ++i) { | 
 |                     SkDebugf("{%1.8g, %1.8g}, ", points[i].fX, points[i].fY); | 
 |                 } | 
 |                 if (SkPath::kConic_Verb == verb) { | 
 |                     SkDebugf("weight = %g", iter.conicWeight()); | 
 |                 } | 
 |                 SkDebugf("\n"); | 
 |             } while (SkPath::kDone_Verb != verb); | 
 |         } | 
 |     #StdOut | 
 |         kMove_Verb {50, 60},  | 
 |         kQuad_Verb {50, 60}, {10, 20}, {30, 40},  | 
 |         kClose_Verb {50, 60},  | 
 |         kMove_Verb {50, 60},  | 
 |         kLine_Verb {50, 60}, {30, 30},  | 
 |         kConic_Verb {30, 30}, {1, 2}, {3, 4}, weight = 0.5 | 
 |         kCubic_Verb {3, 4}, {-1, -2}, {-3, -4}, {-5, -6},  | 
 |         kDone_Verb  | 
 |     ## | 
 |     ## | 
 |  | 
 |     #SeeAlso peek() | 
 |  | 
 |     ## | 
 |  | 
 |     #Method Verb peek() const | 
 |  | 
 |         Returns next Verb, but does not advance RawIter. | 
 |  | 
 |         #Return next Verb from Verb_Array  ## | 
 |  | 
 |         #Example | 
 |             SkPath path; | 
 |             path.quadTo(10, 20, 30, 40); | 
 |             path.conicTo(1, 2, 3, 4, .5f); | 
 |             path.cubicTo(1, 2, 3, 4, .5, 6); | 
 |             SkPath::RawIter iter(path); | 
 |             SkPath::Verb verb, peek = iter.peek(); | 
 |             const char* verbStr[] =  { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; | 
 |             do { | 
 |                 SkPoint points[4]; | 
 |                 verb = iter.next(points); | 
 |                 SkDebugf("peek %s %c= verb %s\n", verbStr[peek], peek == verb ? '=' : '!', verbStr[verb]); | 
 |                 peek = iter.peek(); | 
 |             } while (SkPath::kDone_Verb != verb); | 
 |             SkDebugf("peek %s %c= verb %s\n", verbStr[peek], peek == verb ? '=' : '!', verbStr[verb]); | 
 |             #StdOut | 
 |                 #Volatile | 
 |                 peek Move == verb Move | 
 |                 peek Quad == verb Quad | 
 |                 peek Conic == verb Conic | 
 |                 peek Cubic == verb Cubic | 
 |                 peek Done == verb Done | 
 |                 peek Done == verb Done | 
 |             ## | 
 |         ## | 
 |  | 
 |         #Bug 6832 | 
 |         StdOut isn't really volatile, it just produces the wrong result. | 
 |         A simple fix changes the output of hairlines and needs to be | 
 |         investigated to see if the change is correct or not. | 
 |         https://skia-review.googlesource.com/c/21340/ | 
 |         ## | 
 |  | 
 |         #SeeAlso next() | 
 |  | 
 |     ## | 
 |  | 
 |     #Method SkScalar conicWeight() const | 
 |  | 
 |         Returns Conic_Weight if next() returned kConic_Verb. | 
 |  | 
 |         If next() has not been called, or next() did not return kConic_Verb, | 
 |         result is undefined. | 
 |  | 
 |         #Return  Conic_Weight for Conic Points returned by next() ## | 
 |  | 
 |     #Example | 
 |     void draw(SkCanvas* canvas) { | 
 |        SkPath path; | 
 |        path.conicTo(1, 2, 3, 4, .5f); | 
 |        SkPath::RawIter iter(path);  | 
 |        SkPoint p[4]; | 
 |        SkDebugf("first verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not "); | 
 |        SkDebugf("next verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not "); | 
 |        SkDebugf("conic points: {%g,%g}, {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY, | 
 |                     p[2].fX, p[2].fY); | 
 |        SkDebugf("conic weight: %g\n", iter.conicWeight()); | 
 |     } | 
 |     #StdOut | 
 |         first verb is move | 
 |         next verb is conic | 
 |         conic points: {0,0}, {1,2}, {3,4} | 
 |         conic weight: 0.5 | 
 |     ## | 
 |     ## | 
 |  | 
 |     #SeeAlso Conic_Weight | 
 |  | 
 |     ## | 
 |  | 
 | #Class RawIter ## | 
 |  | 
 | #Class SkPath ## | 
 |  | 
 | #Topic Path ## |