From 73fa972d0bd9de7cb801323495b5d1fabd31b24f Mon Sep 17 00:00:00 2001 From: Cary Clark Date: Tue, 29 Aug 2017 17:36:51 -0400 Subject: work on path Work on SkPath.h documentation; fixed self-consistency bugs identified by bookmaker. Fixed a couple of minor typos in SkPath.h itself. Also brought SkPaint and SkCanvas docs up to date. TBR=reed@google.com Docs-Preview: https://skia.org/?cl=39040 Bug: skia: 6898 Change-Id: Id89d4e2fa7fb6ee2e3cbec7ea762e06308b67d8b Reviewed-on: https://skia-review.googlesource.com/39040 Commit-Queue: Cary Clark Reviewed-by: Cary Clark Reviewed-by: Cary Clark --- docs/SkCanvas_Reference.bmh | 36 +- docs/SkPaint_Reference.bmh | 4 +- docs/SkPath_Reference.bmh | 10622 +++++++++++++++++++++--------------------- docs/undocumented.bmh | 3 +- 4 files changed, 5385 insertions(+), 5280 deletions(-) (limited to 'docs') diff --git a/docs/SkCanvas_Reference.bmh b/docs/SkCanvas_Reference.bmh index ca4a129a47..0168afae77 100644 --- a/docs/SkCanvas_Reference.bmh +++ b/docs/SkCanvas_Reference.bmh @@ -67,7 +67,7 @@ when no Surface is required, and some helpers implicitly create Raster_Surface. # # description ## #Legend ## # SkCanvas() # No Surface, no dimensions. ## -# SkCanvas(int width, int height, const SkSurfaceProps* props = NULL) # No Surface, set dimensions, Surface_Properties. ## +# SkCanvas(int width, int height, const SkSurfaceProps* props = nullptr) # No Surface, set dimensions, Surface_Properties. ## # SkCanvas(SkBaseDevice* device) # Existing Device. (SkBaseDevice is private.) ## # SkCanvas(const SkBitmap& bitmap) # Uses existing Bitmap. ## # SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props) # Uses existing Bitmap and Surface_Properties. ## @@ -364,7 +364,7 @@ void draw(SkCanvas* canvas) { # ------------------------------------------------------------------------------ -#Method SkCanvas(int width, int height, const SkSurfaceProps* props = NULL) +#Method SkCanvas(int width, int height, const SkSurfaceProps* props = nullptr) Creates Canvas of the specified dimensions without a Surface. Used by subclasses with custom implementations for draw methods. @@ -465,16 +465,16 @@ The actual output depends on the installed fonts. } #StdOut - ----- - --x-- - --x-- - --x-- - --x-- - --x-- - --x-- - ----- - --x-- - --x-- + ----- + ---x- + ---x- + ---x- + ---x- + ---x- + ---x- + ----- + ---x- + ---x- ----- #StdOut ## ## @@ -803,7 +803,7 @@ void draw(SkCanvas* canvas) { # ------------------------------------------------------------------------------ -#Method void* accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin = NULL) +#Method void* accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin = nullptr) Returns the pixel base address, Image_Info, rowBytes, and origin if the pixels can be read directly. The returned address is only valid @@ -3774,7 +3774,7 @@ void draw(SkCanvas* canvas) { drawImage, drawImageRect, and drawImageNine can be called with a bare pointer or a smart pointer as a convenience. The pairs of calls are otherwise identical. -#Method void drawImage(const SkImage* image, SkScalar left, SkScalar top, const SkPaint* paint = NULL) +#Method void drawImage(const SkImage* image, SkScalar left, SkScalar top, const SkPaint* paint = nullptr) Draw Image image, with its top-left corner at (left, top), using Clip, Matrix, and optional Paint paint. @@ -3814,7 +3814,7 @@ void draw(SkCanvas* canvas) { # ------------------------------------------------------------------------------ #Method void drawImage(const sk_sp& image, SkScalar left, SkScalar top, - const SkPaint* paint = NULL) + const SkPaint* paint = nullptr) Draw Image image, with its top-left corner at (left, top), using Clip, Matrix, and optional Paint paint. @@ -4362,7 +4362,7 @@ void draw(SkCanvas* canvas) { # ------------------------------------------------------------------------------ #Method void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, - const SkPaint* paint = NULL) + const SkPaint* paint = nullptr) Draw Bitmap bitmap, with its top-left corner at (left, top), using Clip, Matrix, and optional Paint paint. @@ -4580,7 +4580,7 @@ void draw(SkCanvas* canvas) { # ------------------------------------------------------------------------------ #Method void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, - const SkPaint* paint = NULL) + const SkPaint* paint = nullptr) Draw Bitmap bitmap stretched differentially to fit into Rect dst. IRect center divides the bitmap into nine sections: four sides, four corners, @@ -5843,7 +5843,7 @@ void draw(SkCanvas* canvas) { # ------------------------------------------------------------------------------ -#Method void drawDrawable(SkDrawable* drawable, const SkMatrix* matrix = NULL) +#Method void drawDrawable(SkDrawable* drawable, const SkMatrix* matrix = nullptr) Draw Drawable drawable using Clip and Matrix, concatenated with optional matrix. diff --git a/docs/SkPaint_Reference.bmh b/docs/SkPaint_Reference.bmh index cfb432a1dd..cc9ae93fbe 100644 --- a/docs/SkPaint_Reference.bmh +++ b/docs/SkPaint_Reference.bmh @@ -4713,7 +4713,7 @@ text contains an invalid UTF-8 sequence, zero is returned. ## #Method size_t breakText(const void* text, size_t length, SkScalar maxWidth, - SkScalar* measuredWidth = NULL) const + SkScalar* measuredWidth = nullptr) const Returns the bytes of text that fit within maxWidth. If kVerticalText_Flag is clear, the text fragment fits if its advance width is less than or @@ -4757,7 +4757,7 @@ text contains an invalid UTF-8 sequence, zero is returned. ## #Method int getTextWidths(const void* text, size_t byteLength, SkScalar widths[], - SkRect bounds[] = NULL) const + SkRect bounds[] = nullptr) const Retrieves the advance and bounds for each glyph in text, and returns the glyph count in text. diff --git a/docs/SkPath_Reference.bmh b/docs/SkPath_Reference.bmh index 95067c71b5..a0508dff29 100644 --- a/docs/SkPath_Reference.bmh +++ b/docs/SkPath_Reference.bmh @@ -1,80 +1,80 @@ -#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 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 decribes 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. - +#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 decribes 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 Subtopics #ToDo not all methods are in topics ## #ToDo subtopics are not in topics ## @@ -90,10 +90,10 @@ its contents until it is edited, to reduce memory use and improve performance. # Verb # How Points and Contours are defined. ## # Verb_Array # All Verbs in Path. ## # Verb # How Points and Contours are defined. ## -# Weight # Strength of control Point in Conic. ## +# Conic_Weight # Strength of control Point in Conic. ## #Subtopic ## - - + + #Subtopic Contour #Alias Contours Contour contains one or more Verbs, and as many Points as @@ -101,27 +101,27 @@ 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 wasn't 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); +#Description +Each SkPath::moveTo starts a new Contour, and content after SkPath::close() +also starts a new Contour. Since SkPath::conicTo wasn't 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 @@ -132,23 +132,23 @@ 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); -} +#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 @@ -159,33 +159,50 @@ 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(); +#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 beginnning 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 -#Class SkPath - -#Topic Overview - #Subtopic Constants #ToDo incomplete ## #Table @@ -201,7 +218,7 @@ makes them visible. # Verb # Controls how Path Points are interpreted. ## #Table ## #Subtopic ## - + #Subtopic Classes_and_Structs #Table #Legend @@ -211,7 +228,7 @@ makes them visible. # RawIter # Iterates through lines and curves, including degenerates. ## #Table ## #Subtopic ## - + #Subtopic Constructors #Table #Legend @@ -233,140 +250,141 @@ makes them visible. # operator!=(const SkPath& a, const SkPath& b) # Compares paths for inequality. ## #Table ## #Subtopic ## - -#Subtopic Member_Functions -#Table -#Legend -# function # 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 stdout. ## -# dumpHex # Sends text representation using hexadecimal to stdout. ## -# experimentalValidateRef # Experimental; debugging only. ## -# 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 # Hint to reserve 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. ## -# isVolatile # Returns if Device should not cache. ## -# lineTo # Appends Line. ## -# moveTo # Starts Contour. ## -# offset() # Translates Point_Array. ## -# 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 # Initialize 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; leaves memory allocated. ## -# 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 # Refresh result of getBounds. ## -# writeToMemory # Copy data to buffer. ## -#Table ## -#Subtopic Path_Member_Functions ## -#Topic Overview ## - + +#Subtopic Member_Functions +#Table +#Legend +# function # 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 stdout. ## +# dumpHex # Sends text representation using hexadecimal to stdout. ## +# 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 # Hint to reserve 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. ## +# 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; leaves memory allocated. ## +# 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 Weight; +#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 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 Weight. 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 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. +#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 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. @@ -385,4123 +403,4147 @@ for consecutive entries. # 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 - Contour travels in a clockwise direction. -## -#Const kCCW_Direction - 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, amd 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 friend SK_API 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 friend 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 -trival 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 equal sized Point_Arrays. -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 -(this->points * weight) + ending->points * (1 - weight) -## - -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 Most useful when between zero (ending Point_Array) and - one (this Point_Array); will work with values outside of this - range. -## -#Param out ## - -#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 - }; -## +#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. +## -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(); - } +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); + } } ## - -#Const kWinding_FillType -Specifies fill as area is enclosed by a non-zero sum of Contour Directions. -## -#Const kEvenOdd_FillType -Specifies fill as area enclosed by an odd number of Contours. -## -#Const kInverseWinding_FillType -Specifies fill as area is enclosed by a zero sum of Contour Directions. -## -#Const kInverseEvenOdd_FillType -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 setFillType does not 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 { - 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. +#SeeAlso arcTo rArcTo isRect isNestedFillRects addRect addOval -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 - Indicates Convexity has not been determined. -## -#Const kConvex_Convexity - Path has one Contour made of a simple geometry without indentations. -## -#Const kConcave_Convexity - 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. -## - -## - -#Subtopic Convexity ## - -# ------------------------------------------------------------------------------ - -#Method bool isOval(SkRect* rect, Direction* dir = nullptr, - unsigned* start = nullptr) const - -Path is Oval 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. - -isOval triggers performance optimizations on some GPU_Surface implementations. - -#Param rect storage for bounding Rect of Oval. Oval is Circle if rect width -equals rect height. Unwritten if Path is not Oval. May be nullptr. -## -#Param dir storage for Direction; kCW_Direction if clockwise, kCCW_Direction if -counterclockwise. Unwritten if Path is not Oval. May be nullptr. -## -#Param start storage for start of Oval: 0 for top, -1 for right, 2 for bottom, 3 for left. Unwritten if Path is not 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 - -Path is Round_Rect if constructed by addRoundRect, addRRect; and if construction -is not empty, not Rect, and not Oval. Path constructed with other other calls -will not return true though Path draws Round_Rect. - -isRRect triggers performance optimizations on some GPU_Surface implementations. - -#Param rrect storage for bounding Rect of Round_Rect. -Unwritten if Path is not Round_Rect. May be nullptr. -## -#Param dir storage for Direction; kCW_Direction if clockwise, kCCW_Direction if -counterclockwise. Unwritten if Path is not Round_Rect. May be nullptr. -## -#Param start storage for start of Round_Rect: 0 for top, -1 for right, 2 for bottom, 3 for left. Unwritten if Path is not Round_Rect. May be nullptr. -## - -#Return true for Round_Rect Path constructed by addRoundRect or addRRect. ## - -#Example -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 intial 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 intial 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 - -Finite Point array values are between negative SK_ScalarMax and -positive SK_ScalarMax. Any Point array value of -SK_ScalarInfinity, SK_ScalarNegativeInfinity, or SK_ScalarNaN -cause isFinite to return false. - -#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-volative 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. - -#Param p1 Line start point. ## -#Param p2 Line end point. ## -#Param exact If true, returns true only if p1 equals p2. If false, returns true - if p1 equals or nearly equals p2. -## - -#Return true if Line is degenerate; its length is effectively zero. ## - -#Example -#Description -As single precision floats, 100 and 100.000001f have the same bit representation, -and are exactly equal. 100 and 100.0001f 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 SkPoint::equalsWithinTolerance -## - -# ------------------------------------------------------------------------------ - -#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.00001f, and 100.00002f 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't 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 SkPoint::equalsWithinTolerance -## - -# ------------------------------------------------------------------------------ - -#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 -#Alias Point_Arrays +#Enum Direction ## +#Subtopic Direction ## -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: move to, line to, quad to; 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 Number of points alloted in points storage; 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. -If the index is out of range, getPoint returns (0, 0). - -#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 +#Method SkPath() -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. +By default, Path has no Verbs, no Points, and no Weights. +Fill_Type is set to kWinding_FillType. -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 If not null, receives up to max verbs ## -#Param max The maximum number of verbs 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 -## +#Return empty Path ## -#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 util 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. If Path contains -no points, getBounds returns (0, 0, 0, 0). Returned bounds width and height may -be larger or smaller than area affected when Path is drawn. - -getBounds 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, updateBoundsCache is identical to getBounds, where the -returned value is ignored. - -updateBoundsCache prepares a 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. -If Path contains no points, computeTightBounds returns (0, 0, 0, 0). -Returned bounds width and height may be larger or smaller than area affected -when Path is drawn. - -computeTightBounds behaves identically to getBounds when Path contains -only lines. If Path contains curves, compute computeTightBounds includes -the maximum extent of the Quad, Conic, or Cubic; is slower, -and does not cache the result. - -Like getBounds, computeTightBounds includes Points associated with -kMove_Verb that define empty Contours. - -#Return ## - -#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. -If rect is empty, that is, it has zero width or height; conservativelyContainsRect -returns true if 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. -incReserve 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 preallocate. ## - -#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). -rMoveTo 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. - -rLineTo first appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. -rLineTo then appends kLine_Verb to Verb_Array and Line end to Point_Array. -Line end is Last_Point plus Vector (dx, dy). -rLineTo 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 +#Example + SkPath path; + SkDebugf("path is " "%s" "empty", path.isEmpty() ? "" : "not "); +#StdOut +path is empty +## +## -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. +#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 -#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); -} +#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 +## ## -Quad is a special case of Conic where Conic_Weight is set to one. +#SeeAlso operator=(const SkPath& path) -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. +## + +# ------------------------------------------------------------------------------ + +#Method ~SkPath() + +Releases ownership of any shared data and deletes data if Path is sole owner. #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; - } +#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 "); } ## - -#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. - - quadTo appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. - quadTo 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. - - quadTo appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. - quadTo 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. - - rQuadTo first appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, - if needed. rQuadTo 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). - rQuadTo 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. +#SeeAlso SkPath() SkPath(const SkPath& path) operator=(const SkPath& path) -Each Conic in Path adds two Points and one Weight. Weights in Path may be -inspected with Iter, or with RawIter. +## -#Subtopic Weight -#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. +#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, amd Fill_Type to copy ## + +#Return Path copied by value ## #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); -} +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 -move {0, 0}, -quad {0, 0}, {20, 30}, {50, 60}, -done +path1 bounds = 10, 20, 30, 40 +path2 bounds = 10, 20, 30, 40 +#StdOut ## ## + +#SeeAlso swap() SkPath(const SkPath& path) + ## -If weight is less than one, Conic is an elliptical segment. +# ------------------------------------------------------------------------------ -#Example +#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 -A 90 degree circular arc has the weight -#Formula -1 / sqrt(2) +Rewind removes Verb_Array but leaves storage; since storage is not compared, +Path pair are equivalent. ## - . -## -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); +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 -move {0, 0}, -conic {0, 0}, {20, 0}, {20, 20}, weight = 0.707107 -done +empty one == two +moveTo one != two +rewind one == two +reset one == two ## ## -If weight is greater than one, Conic is a hyperbolic segment. As w gets large, -a hyperbolic segment can be approximated by straight lines connecting the -control Point with the end Points. +## + +# ------------------------------------------------------------------------------ + +#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 -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); +#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 -move {0, 0}, -line {0, 0}, {20, 0}, -line {20, 0}, {20, 20}, -done +empty one == two +addRect one == two +setConvexity one == two +convexity != ## ## -#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. - - conicTo appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. - - If w is finite and not one, conicTo then appends kConic_Verb to Verb_Array; - and (x1, y1), (x2, y2) to Point_Array; and w to Weights. - - If w is one, conicTo appends kQuad_Verb to Verb_Array, and - (x1, y1), (x2, y2) to Point_Array. - - If w is not finite, conicTo 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. - - conicTo appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. - - If w is finite and not one, conicTo then appends kConic_Verb to Verb_Array; - and Points p1, p2 to Point_Array; and w to Weights. - - If w is one, conicTo appends kQuad_Verb to Verb_Array, and Points p1, p2 - to Point_Array. - - If w is not finite, conicTo 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. - - rConicTo first appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, - if needed. - - If w is finite and not one, rConicTo then appends kConic_Verb to Verb_Array, - and w is recorded as Conic_Weight; otherwise, if w is one, rConicTo appends - kQuad_Verb to Verb_Array; or if w is not finite, rConicTo appends kLine_Verb - twice to Verb_Array. - - In all cases rConicTo then appends Points control and end to Point_Array. - control is Last_Point plus Vector (dx1, dy1). - end is Last_Point plus Vector (dx2, dy2). - - rConicTo 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 Cubics +## -Cubic describes a cubic Bezier, a third-order curve. -Cubic begins at a start Point, curving towards the first control Point; -and curves from the end Point towards the second control Point. +# ------------------------------------------------------------------------------ + +#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 +trival 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 -#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; - } -} + 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 equal sized Point_Arrays. +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 +(this->points * weight) + ending->points * (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 ending Point_Array, and + one minus contribution of this Point_Array ## - -#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. - -cubicTo appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. -cubicTo 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. - -cubicTo appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, if needed. -cubicTo 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. - - rCubicTo first appends kMove_Verb to Verb_Array and (0, 0) to Point_Array, - if needed. rCubicTo 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). - rCubicTo 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 -# 1 arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) ## -# 2 parameter sets force MoveTo ## -# 3 start angle must be multiple of 90 degrees. ## -# 4 arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) ## -# 5 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 Conic 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 functionatlity 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; positve 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. - -arcTo appends up to four Conic curves. -arcTo implements the functionatlity 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; positve 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 (r.fX, r.fY) rotated by -xAxisRotate degrees. Arc curves from last Path Point (x0, y0) to -(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 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. - -arcTo appends up to four Conic curves. -arcTo implements the functionatlity 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; positve 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 continous 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) -## -ConvertConicToQuads 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 = NULL, Direction* direction = NULL) const - -Returns true if Path is eqivalent to Rect when filled. -If isRect returns false: rect, isClosed, and direction are unchanged. -If isRect returns 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] = NULL) const - -Returns true if Path is equivalent to nested Rect pair when filled. -If isNestedFillRects returns false, rect and dirs are unchanged. -If isNestedFillRects returns 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 (x + radius, y) and -continues clockwise if dir is kCW_Direction, counterclockwise if dir is -kCCW_Direction. - -addCircle 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 RRect. - -#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 RRect. - -#Param rrect bounds and radii of rounded rectangle. ## -#Param dir Direction to wind RRect. ## -#Param start Index of initial point of RRect. ## - -#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. Given count pts, addPoly adds -count - 1 Line segments. Contour added starts at pt[0], then adds a line -for every additional Point in pts array. If close is true, addPoly -appends kClose_Verb to Path, connecting pts[count - 1] and pts[0]. - -If count is zero, append kMove_Verb to path. -addPoly 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 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 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 +#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); - 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); + 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 { + 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 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 for Round_Rect Path constructed by addRoundRect or addRRect ## + +#Example +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 intial 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 intial 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-volative 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. + +#Param p1 line start point ## +#Param p2 line end point ## +#Param exact If true, returns true only if p1 equals p2. If false, returns true + if p1 equals or nearly equals p2 +## + +#Return true if Line is degenerate; its length is effectively zero ## + +#Example +#Description +As single precision floats, 100 and 100.000001f have the same bit representation, +and are exactly equal. 100 and 100.0001f 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 SkPoint::equalsWithinTolerance +## + +# ------------------------------------------------------------------------------ + +#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.00001f, and 100.00002f 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't 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 SkPoint::equalsWithinTolerance +## + +# ------------------------------------------------------------------------------ + +#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 +#Alias Point_Arrays + +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: move to, line to, quad to; 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 util 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 preallocate ## + +#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 +#Substitute quads +#Alias Quads + +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 +#Substitute conics +#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 +#Substitute weights +#Alias Weights +#Alias Conic_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 w 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 +#Substitute cubics +#Alias Cubics + +Cubic describes a cubic Bezier, a third-order curve. +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 +# 1 arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) ## +# 2 parameter sets force MoveTo ## +# 3 start angle must be multiple of 90 degrees. ## +# 4 arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) ## +# 5 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 Conic 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 functionatlity 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; positve 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. + +arcTo appends up to four Conic curves. +arcTo implements the functionatlity 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; positve 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 (r.fX, r.fY) rotated by +xAxisRotate degrees. Arc curves from last Path Point (x0, y0) to +#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 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. + +arcTo appends up to four Conic curves. +arcTo implements the functionatlity 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; positve 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 continous 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) +## +ConvertConicToQuads 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 eqivalent 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) +## +and continues clockwise if dir is kCW_Direction, counterclockwise if dir is +kCCW_Direction. + +addCircle 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 +#Formula +count - 1 +## +Line segments. Contour added starts at pt[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); @@ -4509,30 +4551,30 @@ The top right composition is made up of one contour; the other three have two. 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 Weights are -added unaltered. If mode is kExtend_AddPathMode, add Line before appending -Verbs, Points, and Weights. - -#Param src Path Verbs, Points, and 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 +## + +#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); @@ -4545,27 +4587,27 @@ Verbs, Points, and Weights. 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 Weights are -added unaltered. If mode is kExtend_AddPathMode, add Line before appending -Verbs, Points, and Weights. - -#Param src Path Verbs, Points, and Weights to add. ## -#Param mode kAppend_AddPathMode or kExtend_AddPathMode. ## - -#Example -#Height 80 +## + +#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; @@ -4574,30 +4616,30 @@ Verbs, Points, and Weights. 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 Weights. - -If mode is kAppend_AddPathMode, src Verb_Array, Point_Array, and Weights are -added unaltered. If mode is kExtend_AddPathMode, add Line before appending -Verbs, Points, and Weights. - -#Param src Path Verbs, Points, and Weights to add. ## -#Param matrix Transform applied to src. ## -#Param mode kAppend_AddPathMode or kExtend_AddPathMode. ## - -#Example -#Height 160 + 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; @@ -4609,23 +4651,23 @@ Verbs, Points, and Weights. 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 Weights to add. ## - -#Example -#Height 200 +## + +#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); @@ -4647,25 +4689,25 @@ Reversed src always appends a new Contour to Path. 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 +## + +#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); @@ -4677,23 +4719,23 @@ If dst is nullptr, Path is replaced by offset data. 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 +## + +#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); @@ -4704,25 +4746,25 @@ Offset Point_Array by (dx, dy). Path is replaced by offset data. 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 +## + +#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); @@ -4736,24 +4778,24 @@ is replaced. 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 + } +## + +#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); @@ -4765,31 +4807,31 @@ Path is replaced by transformed data. matrix.setRotate(36, 100, 100); path.transform(matrix); canvas->drawPath(path, paint); - } -## - -#SeeAlso addPath offset SkCanvas::concat() SkMatrix - -## - -# ------------------------------------------------------------------------------ - + } +## + +#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 +#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); @@ -4798,47 +4840,47 @@ Last_Point can be read and written directly with getLastPt and setLastPt. 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 + 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 + 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; @@ -4851,431 +4893,481 @@ Last_Point can be read and written directly with getLastPt and setLastPt. 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, 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, dump() writes to -stdout. Set forceClose to true to get -edges used to fill Path. Set dumpAsHex true to get exact binary representations -of floating point numbers used in Point_Array and 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 hexidecimal. ## - -#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 stdout. 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 stdout. 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 dumpHex when submitting #A bug reports against Skia # http://bug.skia.org ##. -Slight value changes in Point_Array may cause the bug to disappear. - -#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 - -Write Path to buffer, returning the number of bytes written. -Pass nullptr to obtain the storage size. - -writeToMemory writes Fill_Type, Verb_Array, Point_Array, Conic_Weight, and -additionally writes computed information like Convexity and bounds. - -writeToMemory should 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 storage; - storage.setCount(size); - path.writeToMemory(storage.begin()); - copy.readFromMemory(storage.begin(), size); - SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not "); -} -## - -#SeeAlso 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. - -readFromMemory reads Fill_Type, Verb_Array, Point_Array, Conic_Weight, and -additionally reads computed information like Convexity and bounds. - -readFromMemory should only be used 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 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 void validate() const - -Debugging check to see if Path data is consistent. -Not currently maintained. - -#NoExample -## - -## - -# ------------------------------------------------------------------------------ - -#Method void experimentalValidateRef() const - -#Private -Debugging check to see if Path data is consistent. -Not ready for public use. -## - -## - -# ------------------------------------------------------------------------------ - + ## + + #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, dump() writes to +stdout. Set forceClose to true to get +edges used to fill Path. Set dumpAsHex true to get 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 hexidecimal ## + +#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 stdout. 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 stdout. 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 ##. +Slight value changes in Point_Array may cause the bug to disappear. + +#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 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 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 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 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 +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); +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); } ## @@ -5286,21 +5378,21 @@ void draw(SkCanvas* canvas) { 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. ## +#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 "); +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 -## +#StdOut +iter is done +iter is done +## ## #SeeAlso setPath @@ -5313,51 +5405,51 @@ Sets Iter to return elements of Verb_Array, Point_Array, and Conic_Weight in pat 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. ## +#Param path Path to iterate ## +#Param forceClose true if open Contours generate kClose_Verb ## -#Return Iter of path. ## +#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); +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}, +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 ## ## @@ -5372,51 +5464,51 @@ Sets Iter to return elements of Verb_Array, Point_Array, and Conic_Weight in pat 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. ## +#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); +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}, +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 ## ## @@ -5436,13 +5528,13 @@ 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 pts Storage for Point data describing returned Verb ## + #Param doConsumeDegenerates If true, skip degenerate Verbs ## #Param exact If true, skip zero length curves. Has no effect if doConsumeDegenerates - is false. + is false ## - #Return next Verb from Verb_Array. ## + #Return next Verb from Verb_Array ## #Example #Description @@ -5454,63 +5546,63 @@ 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); +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 +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 ## ## @@ -5525,24 +5617,24 @@ kDone_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(). ## + #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()); + 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} +first verb is move +next verb is conic +conic points: {0,0}, {1,2}, {3,4} conic weight: 0.5 ## ## @@ -5560,31 +5652,31 @@ conic weight: 0.5 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. ## + #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 "); +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 +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 ## ## @@ -5598,26 +5690,26 @@ 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. ## +#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"); - } +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 +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 ## ## @@ -5625,47 +5717,59 @@ with close(), forceClose is true : isClosedContour returns true #SeeAlso Iter(const SkPath& path, bool forceClose) ## - -#Class Iter ## - + +#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 - ## - ## + #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. ## + #Param path Path to iterate ## + + #Return RawIter of path ## - #Return RawIter of path. ## - - #NoExample - ## - ## + #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 - ## - ## + #Param path Path to iterate ## + + #NoExample + ## + ## #Method Verb next(SkPoint pts[4]) @@ -5673,82 +5777,82 @@ Verb_Array, Point_Array, and Conic_Weight are returned unaltered. 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. ## + #Param pts Storage for Point data describing returned Verb ## - #Return next Verb from Verb_Array. ## + #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); + 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}, + 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. ## + #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(); + 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 - ## + 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 @@ -5760,43 +5864,43 @@ Verb_Array, Point_Array, and Conic_Weight are returned unaltered. #SeeAlso next() - ## + ## + + #Method SkScalar conicWeight() const - #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(). ## + + #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()); + 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} + 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 ## + + ## + +#Class RawIter ## + +#Class SkPath ## + +#Topic Path ## diff --git a/docs/undocumented.bmh b/docs/undocumented.bmh index 9e3ff056fd..e3c0bedb1b 100644 --- a/docs/undocumented.bmh +++ b/docs/undocumented.bmh @@ -13,7 +13,8 @@ GPU GPU-backed OpenGL Vulkan NULL RFC - Bezier Coons + Bezier Coons Cartesian + C C++ SaveLayerFlags # not external; need to add typedef support SkUserConfig.h # not external, but still thinking about how markup refers to this SkXXX.h # ditto -- cgit v1.2.3