blob: 8aecf01728395bb31f67a78a6b795569c0cfb4a0 [file] [log] [blame]
#Topic Path
#Alias Path_Reference
#Alias Paths
#Subtopic Overview
#Subtopic Subtopic
#Populate
##
##
Path contains Lines and Curves which can be stroked or filled. Contour is
composed of a series of connected Lines and Curves. Path may contain zero,
one, or more Contours.
Each Line and Curve are described by Verb, Points, and optional Conic_Weight.
Each pair of connected Lines and Curves share common Point; for instance, Path
containing two connected Lines are described the Verb sequence:
SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb; and a Point sequence
with three entries, sharing
the middle entry as the end of the first Line and the start of the second Line.
Path components Arc, Rect, Round_Rect, Circle, and Oval are composed of
Lines and Curves with as many Verbs and Points required
for an exact description. Once added to Path, these components may lose their
identity; although Path can be inspected to determine if it describes a single
Rect, Oval, Round_Rect, and so on.
#Example
#Height 192
#Description
Path contains three Contours: Line, Circle, and Quad. Line is stroked but
not filled. Circle is stroked and filled; Circle stroke forms a loop. Quad
is stroked and filled, but since it is not closed, Quad does not stroke a loop.
##
void draw(SkCanvas* canvas) {
SkPaint paint;
paint.setAntiAlias(true);
SkPath path;
path.moveTo(124, 108);
path.lineTo(172, 24);
path.addCircle(50, 50, 30);
path.moveTo(36, 148);
path.quadTo(66, 188, 120, 136);
canvas->drawPath(path, paint);
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorBLUE);
paint.setStrokeWidth(3);
canvas->drawPath(path, paint);
}
##
Path contains a Fill_Type which determines whether overlapping Contours
form fills or holes. Fill_Type also determines whether area inside or outside
Lines and Curves is filled.
#Example
#Height 192
#Description
Path is drawn filled, then stroked, then stroked and filled.
##
void draw(SkCanvas* canvas) {
SkPaint paint;
paint.setAntiAlias(true);
SkPath path;
path.moveTo(36, 48);
path.quadTo(66, 88, 120, 36);
canvas->drawPath(path, paint);
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorBLUE);
paint.setStrokeWidth(8);
canvas->translate(0, 50);
canvas->drawPath(path, paint);
paint.setStyle(SkPaint::kStrokeAndFill_Style);
paint.setColor(SK_ColorRED);
canvas->translate(0, 50);
canvas->drawPath(path, paint);
}
##
Path contents are never shared. Copying Path by value effectively creates
a new Path independent of the original. Internally, the copy does not duplicate
its contents until it is edited, to reduce memory use and improve performance.
#Subtopic Contour
#Alias Contours
#Line # loop of lines and curves ##
Contour contains one or more Verbs, and as many Points as
are required to satisfy Verb_Array. First Verb in Path is always
SkPath::kMove_Verb; each SkPath::kMove_Verb that follows starts a new Contour.
#Example
#Description
Each SkPath::moveTo starts a new Contour, and content after SkPath::close()
also starts a new Contour. Since SkPath::conicTo is not preceded by
SkPath::moveTo, the first Point of the third Contour starts at the last Point
of the second Contour.
##
#Height 192
SkPaint paint;
paint.setAntiAlias(true);
canvas->drawString("1st contour", 150, 100, paint);
canvas->drawString("2nd contour", 130, 160, paint);
canvas->drawString("3rd contour", 40, 30, paint);
paint.setStyle(SkPaint::kStroke_Style);
SkPath path;
path.moveTo(124, 108);
path.lineTo(172, 24);
path.moveTo(36, 148);
path.quadTo(66, 188, 120, 136);
path.close();
path.conicTo(70, 20, 110, 40, 0.6f);
canvas->drawPath(path, paint);
##
If final Verb in Contour is SkPath::kClose_Verb, Line connects Last_Point in
Contour with first Point. A closed Contour, stroked, draws
Paint_Stroke_Join at Last_Point and first Point. Without SkPath::kClose_Verb
as final Verb, Last_Point and first Point are not connected; Contour
remains open. An open Contour, stroked, draws Paint_Stroke_Cap at
Last_Point and first Point.
#Example
#Height 160
#Description
Path is drawn stroked, with an open Contour and a closed Contour.
##
void draw(SkCanvas* canvas) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(8);
SkPath path;
path.moveTo(36, 48);
path.quadTo(66, 88, 120, 36);
canvas->drawPath(path, paint);
path.close();
canvas->translate(0, 50);
canvas->drawPath(path, paint);
}
##
#Subtopic Zero_Length
#Alias Zero_Length_Contour
#Line # consideration when contour has no length ##
Contour length is distance traveled from first Point to Last_Point,
plus, if Contour is closed, distance from Last_Point to first Point.
Even if Contour length is zero, stroked Lines are drawn if Paint_Stroke_Cap
makes them visible.
#Example
#Height 64
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(8);
paint.setStrokeCap(SkPaint::kRound_Cap);
SkPath path;
path.moveTo(36, 48);
path.lineTo(36, 48);
canvas->drawPath(path, paint);
path.reset();
paint.setStrokeCap(SkPaint::kSquare_Cap);
path.moveTo(56, 48);
path.close();
canvas->drawPath(path, paint);
##
#Subtopic Zero_Length ##
#Subtopic Contour ##
# ------------------------------------------------------------------------------
#Class SkPath
Paths contain geometry. Paths may be empty, or contain one or more Verbs that
outline a figure. Path always starts with a move verb to a Cartesian_Coordinate,
and may be followed by additional verbs that add lines or curves.
Adding a close verb makes the geometry into a continuous loop, a closed contour.
Paths may contain any number of contours, each beginning with a move verb.
Path contours may contain only a move verb, or may also contain lines,
Quadratic_Beziers, Conics, and Cubic_Beziers. Path contours may be open or
closed.
When used to draw a filled area, Path describes whether the fill is inside or
outside the geometry. Path also describes the winding rule used to fill
overlapping contours.
Internally, Path lazily computes metrics likes bounds and convexity. Call
SkPath::updateBoundsCache to make Path thread safe.
#Subtopic Related_Function
#Populate
##
#Subtopic Constant
#Populate
##
#Subtopic Class_or_Struct
#Populate
##
#Subtopic Constructor
#Populate
##
#Subtopic Operator
#Populate
##
#Subtopic Member_Function
#Populate
##
#Subtopic Verb
#Alias Verbs
#Line # line and curve type ##
#Enum Verb
#Line # controls how Path Points are interpreted ##
#Code
enum Verb {
kMove_Verb,
kLine_Verb,
kQuad_Verb,
kConic_Verb,
kCubic_Verb,
kClose_Verb,
kDone_Verb,
};
##
Verb instructs Path how to interpret one or more Point and optional Conic_Weight;
manage Contour, and terminate Path.
#Const kMove_Verb 0
Starts new Contour at next Point.
##
#Const kLine_Verb 1
Adds Line from Last_Point to next Point.
Line is a straight segment from Point to Point.
##
#Const kQuad_Verb 2
Adds Quad from Last_Point, using control Point, and end Point.
Quad is a parabolic section within tangents from Last_Point to control Point,
and control Point to end Point.
##
#Const kConic_Verb 3
Adds Conic from Last_Point, using control Point, end Point, and Conic_Weight.
Conic is a elliptical, parabolic, or hyperbolic section within tangents
from Last_Point to control Point, and control Point to end Point, constrained
by Conic_Weight. Conic_Weight less than one is elliptical; equal to one is
parabolic (and identical to Quad); greater than one hyperbolic.
##
#Const kCubic_Verb 4
Adds Cubic from Last_Point, using two control Points, and end Point.
Cubic is a third-order Bezier_Curve section within tangents from Last_Point
to first control Point, and from second control Point to end Point.
##
#Const kClose_Verb 5
Closes Contour, connecting Last_Point to kMove_Verb Point.
##
#Const kDone_Verb 6
Terminates Path. Not in Verb_Array, but returned by Path iterator.
##
Each Verb has zero or more Points stored in Path.
Path iterator returns complete curve descriptions, duplicating shared Points
for consecutive entries.
#Table
#Legend
# Verb # Allocated Points # Iterated Points # Weights ##
##
# kMove_Verb # 1 # 1 # 0 ##
# kLine_Verb # 1 # 2 # 0 ##
# kQuad_Verb # 2 # 3 # 0 ##
# kConic_Verb # 2 # 3 # 1 ##
# kCubic_Verb # 3 # 4 # 0 ##
# kClose_Verb # 0 # 1 # 0 ##
# kDone_Verb # -- # 0 # 0 ##
##
#Example
void draw(SkCanvas* canvas) {
SkPath path;
path.lineTo(20, 20);
path.quadTo(-10, -10, 30, 30);
path.close();
path.cubicTo(1, 2, 3, 4, 5, 6);
path.conicTo(0, 0, 0, 0, 2);
uint8_t verbs[7];
int count = path.getVerbs(verbs, (int) SK_ARRAY_COUNT(verbs));
const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close" };
SkDebugf("verb count: %d\nverbs: ", count);
for (int i = 0; i < count; ++i) {
SkDebugf("k%s_Verb ", verbStr[verbs[i]]);
}
SkDebugf("\n");
}
#StdOut
verb count: 7
verbs: kMove_Verb kLine_Verb kQuad_Verb kClose_Verb kMove_Verb kCubic_Verb kConic_Verb
##
##
#Enum Verb ##
#Subtopic Verb ##
# ------------------------------------------------------------------------------
#Subtopic Direction
#Line # Path contour orientation ##
#Alias Directions
#Enum Direction
#Line # sets Contour clockwise or counterclockwise ##
#Code
enum Direction {
kCW_Direction,
kCCW_Direction,
};
##
Direction describes whether Contour is clockwise or counterclockwise.
When Path contains multiple overlapping Contours, Direction together with
Fill_Type determines whether overlaps are filled or form holes.
Direction also determines how Contour is measured. For instance, dashing
measures along Path to determine where to start and stop stroke; Direction
will change dashed results as it steps clockwise or counterclockwise.
Closed Contours like Rect, Round_Rect, Circle, and Oval added with
kCW_Direction travel clockwise; the same added with kCCW_Direction
travel counterclockwise.
#Const kCW_Direction 0
Contour travels in a clockwise direction
##
#Const kCCW_Direction 1
Contour travels in a counterclockwise direction
##
#Example
#Height 100
void draw(SkCanvas* canvas) {
const SkPoint arrow[] = { {40, -5}, {45, 0}, {40, 5} };
const SkRect rect = {10, 10, 90, 90};
SkPaint rectPaint;
rectPaint.setAntiAlias(true);
SkPaint textPaint(rectPaint);
textPaint.setTextAlign(SkPaint::kCenter_Align);
rectPaint.setStyle(SkPaint::kStroke_Style);
SkPaint arrowPaint(rectPaint);
SkPath arrowPath;
arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 320, 0,
SkPath1DPathEffect::kRotate_Style));
for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
canvas->drawRect(rect, rectPaint);
for (unsigned start : { 0, 1, 2, 3 } ) {
SkPath path;
path.addRect(rect, direction, start);
canvas->drawPath(path, arrowPaint);
}
canvas->drawString(SkPath::kCW_Direction == direction ? "CW" : "CCW", rect.centerX(),
rect.centerY(), textPaint);
canvas->translate(120, 0);
}
}
##
#SeeAlso arcTo rArcTo isRect isNestedFillRects addRect addOval
#Enum Direction ##
#Subtopic Direction ##
# ------------------------------------------------------------------------------
#Method SkPath()
#Line # constructs with default values ##
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)
#Line # makes a shallow copy ##
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()
#Line # decreases Reference_Count of owned objects ##
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)
#Line # makes a shallow copy ##
Path assignment makes two paths identical by value. Internally, assignment
shares pointer values. The underlying Verb_Array, Point_Array and Weights
are copied when modified.
Copying Paths by assignment is very efficient and never allocates memory.
Paths are always copied by value from the interface; the underlying shared
pointers are not exposed.
#Param path Verb_Array, Point_Array, Weights, and Fill_Type to copy ##
#Return Path copied by value ##
#Example
SkPath path1;
path1.addRect({10, 20, 30, 40});
SkPath path2 = path1;
const SkRect& b1 = path1.getBounds();
SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom);
const SkRect& b2 = path2.getBounds();
SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom);
#StdOut
path1 bounds = 10, 20, 30, 40
path2 bounds = 10, 20, 30, 40
#StdOut ##
##
#SeeAlso swap() SkPath(const SkPath& path)
##
# ------------------------------------------------------------------------------
#Method bool operator==(const SkPath& a, const SkPath& b)
#Line # compares paths for equality ##
Compares a and b; returns true if Fill_Type, Verb_Array, Point_Array, and Weights
are equivalent.
#Param a Path to compare ##
#Param b Path to compare ##
#Return true if Path pair are equivalent ##
#Example
#Description
Rewind removes Verb_Array but leaves storage; since storage is not compared,
Path pair are equivalent.
##
void draw(SkCanvas* canvas) {
auto debugster = [](const char* prefix, const SkPath& a, const SkPath& b) -> void {
SkDebugf("%s one %c= two\n", prefix, a == b ? '=' : '!');
};
SkPath one;
SkPath two;
debugster("empty", one, two);
one.moveTo(0, 0);
debugster("moveTo", one, two);
one.rewind();
debugster("rewind", one, two);
one.moveTo(0, 0);
one.reset();
debugster("reset", one, two);
}
#StdOut
empty one == two
moveTo one != two
rewind one == two
reset one == two
##
##
##
# ------------------------------------------------------------------------------
#Method bool operator!=(const SkPath& a, const SkPath& b)
#Line # compares paths for inequality ##
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 !=
##
##
##
# ------------------------------------------------------------------------------
#Subtopic Property
#Populate
#Line # metrics and attributes ##
##
#Method bool isInterpolatable(const SkPath& compare) const
#In Property
#In Interpolate
#Line # returns if pair contains equal counts of Verb_Array and Weights ##
Return true if Paths contain equal Verbs and equal Weights.
If Paths contain one or more Conics, the Weights must match.
conicTo may add different Verbs depending on Conic_Weight, so it is not
trivial to interpolate a pair of Paths containing Conics with different
Conic_Weight values.
#Param compare Path to compare ##
#Return true if Paths Verb_Array and Weights are equivalent ##
#Example
SkPath path, path2;
path.moveTo(20, 20);
path.lineTo(40, 40);
path.lineTo(20, 20);
path.lineTo(40, 40);
path.close();
path2.addRect({20, 20, 40, 40});
SkDebugf("paths are " "%s" "interpolatable", path.isInterpolatable(path2) ? "" : "not ");
#StdOut
paths are interpolatable
##
##
#SeeAlso isInterpolatable
##
# ------------------------------------------------------------------------------
#Subtopic Interpolate
#Populate
#Line # weighted average of Path pair ##
##
#Method bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const
#In Interpolate
#Line # interpolates between Path pair ##
Interpolate between Paths with Point_Array of equal size.
Copy Verb_Array and Weights to out, and set out Point_Array to a weighted
average of this Point_Array and ending Point_Array, using the formula:
#Formula
(Path Point * weight) + ending Point * (1 - weight)
##
.
weight is most useful when between zero (ending Point_Array) and
one (this Point_Array); will work with values outside of this
range.
interpolate() returns false and leaves out unchanged if Point_Array is not
the same size as ending Point_Array. Call isInterpolatable to check Path
compatibility prior to calling interpolate().
#Param ending Point_Array averaged with this Point_Array ##
#Param weight contribution of this Point_Array, and
one minus contribution of ending Point_Array
##
#Param out Path replaced by interpolated averages ##
#Return true if Paths contain same number of Points ##
#Example
#Height 60
void draw(SkCanvas* canvas) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
SkPath path, path2;
path.moveTo(20, 20);
path.lineTo(40, 40);
path.lineTo(20, 40);
path.lineTo(40, 20);
path.close();
path2.addRect({20, 20, 40, 40});
for (SkScalar i = 0; i <= 1; i += 1.f / 6) {
SkPath interp;
path.interpolate(path2, i, &interp);
canvas->drawPath(interp, paint);
canvas->translate(30, 0);
}
}
##
#SeeAlso isInterpolatable
##
# ------------------------------------------------------------------------------
#Method bool unique() const
#Deprecated soon
Only valid for Android framework.
##
# ------------------------------------------------------------------------------
#Subtopic Fill_Type
#Line # Path fill rule, normal and inverted ##
#Enum FillType
#Line # sets winding rule and inverse fill ##
#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
#In Fill_Type
#Line # returns Fill_Type: winding, even-odd, inverse ##
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)
#In Fill_Type
#Line # sets Fill_Type: winding, even-odd, inverse ##
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
#In Fill_Type
#Line # returns if Fill_Type fills outside geometry ##
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()
#In Fill_Type
#Line # toggles Fill_Type between inside and outside geometry ##
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
#Line # if Path is concave or convex ##
#Enum Convexity
#Line # returns if Path is convex or concave ##
#Code
enum Convexity : uint8_t {
kUnknown_Convexity,
kConvex_Convexity,
kConcave_Convexity,
};
##
Path is convex if it contains one Contour and Contour loops no more than
360 degrees, and Contour angles all have same Direction. Convex Path
may have better performance and require fewer resources on GPU_Surface.
Path is concave when either at least one Direction change is clockwise and
another is counterclockwise, or the sum of the changes in Direction is not 360
degrees.
Initially Path Convexity is kUnknown_Convexity. Path Convexity is computed
if needed by destination Surface.
#Const kUnknown_Convexity 0
Indicates Convexity has not been determined.
##
#Const kConvex_Convexity 1
Path has one Contour made of a simple geometry without indentations.
##
#Const kConcave_Convexity 2
Path has more than one Contour, or a geometry with indentations.
##
#Example
void draw(SkCanvas* canvas) {
SkPaint paint;
SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};
const char* labels[] = { "unknown", "convex", "concave" };
for (SkScalar x : { 40, 100 } ) {
SkPath path;
quad[0].fX = x;
path.addPoly(quad, SK_ARRAY_COUNT(quad), true);
canvas->drawPath(path, paint);
canvas->drawString(labels[(int) path.getConvexity()], 30, 100, paint);
canvas->translate(100, 100);
}
}
##
#SeeAlso Contour Direction getConvexity getConvexityOrUnknown setConvexity isConvex
#Enum Convexity ##
#Method Convexity getConvexity() const
#In Convexity
#Line # returns geometry convexity, computing if necessary ##
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
#In Convexity
#Line # returns geometry convexity if known ##
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)
#In Convexity
#Line # sets if geometry is convex to avoid future computation ##
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
#In Convexity
#Line # returns if geometry is convex ##
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
##
#Subtopic Convexity ##
# ------------------------------------------------------------------------------
#Method bool isOval(SkRect* bounds) const
#In Property
#Line # returns if describes Oval ##
Returns true if this path is recognized as an oval or circle.
bounds receives bounds of Oval.
bounds is unmodified if Oval is not found.
#Param bounds storage for bounding Rect of Oval; may be nullptr ##
#Return true if Path is recognized as an oval or circle ##
#Example
void draw(SkCanvas* canvas) {
SkPaint paint;
SkPath path;
path.addOval({20, 20, 220, 220});
SkRect bounds;
if (path.isOval(&bounds)) {
paint.setColor(0xFF9FBFFF);
canvas->drawRect(bounds, paint);
}
paint.setColor(0x3f000000);
canvas->drawPath(path, paint);
}
##
#SeeAlso Oval addCircle addOval
##
# ------------------------------------------------------------------------------
#Method bool isRRect(SkRRect* rrect) const
#In Property
#Line # returns if describes Round_Rect ##
Returns true if this path is recognized as a SkRRect (but not an oval/circle or rect).
rrect receives bounds of Round_Rect.
rrect is unmodified if Round_Rect is not found.
#Param rrect storage for bounding Rect of Round_Rect; may be nullptr ##
#Return true if Path contains only Round_Rect ##
#Example
#Description
Draw rounded rectangle and its bounds.
##
void draw(SkCanvas* canvas) {
SkPaint paint;
SkPath path;
path.addRRect(SkRRect::MakeRectXY({20, 20, 220, 220}, 30, 50));
SkRRect rrect;
if (path.isRRect(&rrect)) {
const SkRect& bounds = rrect.rect();
paint.setColor(0xFF9FBFFF);
canvas->drawRect(bounds, paint);
}
paint.setColor(0x3f000000);
canvas->drawPath(path, paint);
}
##
#SeeAlso Round_Rect addRoundRect addRRect
##
# ------------------------------------------------------------------------------
#Method void reset()
#In Constructor
#Line # removes Verb_Array, Point_Array, and Weights; frees memory ##
Sets Path to its initial state.
Removes Verb_Array, Point_Array, and Weights, and sets FillType to kWinding_FillType.
Internal storage associated with Path is released.
#Example
SkPath path1, path2;
path1.setFillType(SkPath::kInverseWinding_FillType);
path1.addRect({10, 20, 30, 40});
SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
path1.reset();
SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
##
#SeeAlso rewind()
##
# ------------------------------------------------------------------------------
#Method void rewind()
#In Constructor
#Line # removes Verb_Array, Point_Array, and Weights, keeping memory ##
Sets Path to its initial state, preserving internal storage.
Removes Verb_Array, Point_Array, and Weights, and sets FillType to kWinding_FillType.
Internal storage associated with Path is retained.
Use rewind() instead of reset() if Path storage will be reused and performance
is critical.
#Example
#Description
Although path1 retains its internal storage, it is indistinguishable from
a newly initialized path.
##
SkPath path1, path2;
path1.setFillType(SkPath::kInverseWinding_FillType);
path1.addRect({10, 20, 30, 40});
SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
path1.rewind();
SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
##
#SeeAlso reset()
##
# ------------------------------------------------------------------------------
#Method bool isEmpty() const
#In Property
#Line # returns if verb count is zero ##
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
#In Property
#Line # returns if final Contour forms a loop ##
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
#In Property
#Line # returns if all Point values are finite ##
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
#In Property
#In Volatile
#Line # returns if Device should not cache ##
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
##
# ------------------------------------------------------------------------------
#Subtopic Volatile
#Populate
#Line # caching attribute ##
##
#Method void setIsVolatile(bool isVolatile)
#In Volatile
#Line # sets if Device should not cache ##
Specify whether Path is volatile; whether it will be altered or discarded
by the caller after it is drawn. Paths by default have volatile set false, allowing
Device to attach a cache of data which speeds repeated drawing.
Mark temporary paths, discarded or modified after use, as volatile
to inform Device that the path need not be cached.
Mark animating Path volatile to improve performance.
Mark unchanging Path non-volatile to improve repeated rendering.
Raster_Surface Path draws are affected by volatile for some shadows.
GPU_Surface Path draws are affected by volatile for some shadows and concave geometries.
#Param isVolatile true if caller will alter Path after drawing ##
#Example
#Height 50
#Width 50
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
SkPath path;
path.setIsVolatile(true);
path.lineTo(40, 40);
canvas->drawPath(path, paint);
path.rewind();
path.moveTo(0, 40);
path.lineTo(40, 0);
canvas->drawPath(path, paint);
##
#ToDo tie example to bench to show how volatile affects speed or dm to show resource usage ##
#SeeAlso isVolatile
##
# ------------------------------------------------------------------------------
#Method static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact)
#In Property
#Line # returns if Line is very small ##
Test if Line between Point pair is degenerate.
Line with no length or that moves a very short distance is degenerate; it is
treated as a point.
exact changes the equality test. If true, returns true only if p1 equals p2.
If false, returns true if p1 equals or nearly equals p2.
#Param p1 line start point ##
#Param p2 line end point ##
#Param exact if false, allow nearly equals ##
#Return true if Line is degenerate; its length is effectively zero ##
#Example
#Description
As single precision floats, 100 and 100.000001 have the same bit representation,
and are exactly equal. 100 and 100.0001 have different bit representations, and
are not exactly equal, but are nearly equal.
##
void draw(SkCanvas* canvas) {
SkPoint points[] = { {100, 100}, {100.000001f, 100.000001f}, {100.0001f, 100.0001f} };
for (size_t i = 0; i < SK_ARRAY_COUNT(points) - 1; ++i) {
for (bool exact : { false, true } ) {
SkDebugf("line from (%1.8g,%1.8g) to (%1.8g,%1.8g) is %s" "degenerate, %s\n",
points[i].fX, points[i].fY, points[i + 1].fX, points[i + 1].fY,
SkPath::IsLineDegenerate(points[i], points[i + 1], exact)
? "" : "not ", exact ? "exactly" : "nearly");
}
}
}
#StdOut
line from (100,100) to (100,100) is degenerate, nearly
line from (100,100) to (100,100) is degenerate, exactly
line from (100,100) to (100.0001,100.0001) is degenerate, nearly
line from (100,100) to (100.0001,100.0001) is not degenerate, exactly
#StdOut ##
##
#SeeAlso IsQuadDegenerate IsCubicDegenerate
##
# ------------------------------------------------------------------------------
#Method static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
const SkPoint& p3, bool exact)
#In Property
#Line # returns if Quad is very small ##
Test if Quad is degenerate.
Quad with no length or that moves a very short distance is degenerate; it is
treated as a point.
#Param p1 Quad start point ##
#Param p2 Quad control point ##
#Param p3 Quad end point ##
#Param exact if true, returns true only if p1, p2, and p3 are equal;
if false, returns true if p1, p2, and p3 are equal or nearly equal
##
#Return true if Quad is degenerate; its length is effectively zero ##
#Example
#Description
As single precision floats: 100, 100.00001, and 100.00002 have different bit representations
but nearly the same value. Translating all three by 1000 gives them the same bit representation;
the fractional portion of the number can not be represented by the float and is lost.
##
void draw(SkCanvas* canvas) {
auto debugster = [](const SkPath& path, bool exact) -> void {
SkDebugf("quad (%1.8g,%1.8g), (%1.8g,%1.8g), (%1.8g,%1.8g) is %s" "degenerate, %s\n",
path.getPoint(0).fX, path.getPoint(0).fY, path.getPoint(1).fX,
path.getPoint(1).fY, path.getPoint(2).fX, path.getPoint(2).fY,
SkPath::IsQuadDegenerate(path.getPoint(0), path.getPoint(1), path.getPoint(2), exact) ?
"" : "not ", exact ? "exactly" : "nearly");
};
SkPath path, offset;
path.moveTo({100, 100});
path.quadTo({100.00001f, 100.00001f}, {100.00002f, 100.00002f});
offset.addPath(path, 1000, 1000);
for (bool exact : { false, true } ) {
debugster(path, exact);
debugster(offset, exact);
}
}
#StdOut
quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is degenerate, nearly
quad (1100,1100), (1100,1100), (1100,1100) is degenerate, nearly
quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is not degenerate, exactly
quad (1100,1100), (1100,1100), (1100,1100) is degenerate, exactly
#StdOut ##
##
#SeeAlso IsLineDegenerate IsCubicDegenerate
##
# ------------------------------------------------------------------------------
#Method static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
const SkPoint& p3, const SkPoint& p4, bool exact)
#In Property
#Line # returns if Cubic is very small ##
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
#In Property
#Line # returns if describes Line ##
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
#Line # end points and control points for lines and curves ##
#Substitute SkPoint array
Point_Array contains Points satisfying the allocated Points for
each Verb in Verb_Array. For instance, Path containing one Contour with Line
and Quad is described by Verb_Array: Verb::kMoveTo, Verb::kLineTo, Verb::kQuadTo; and
one Point for move, one Point for Line, two Points for Quad; totaling four Points.
Point_Array may be read directly from Path with getPoints, or inspected with
getPoint, with Iter, or with RawIter.
#Method int getPoints(SkPoint points[], int max) const
#In Point_Array
#Line # returns Point_Array ##
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
#In Point_Array
#Line # returns Point_Array length ##
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
#In Point_Array
#Line # returns entry from Point_Array ##
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) {
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
#Line # line and curve type for points ##
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
#In Verb_Array
#Line # returns Verb_Array length ##
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
#In Verb_Array
#Line # returns Verb_Array ##
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)
#In Operator
#Line # exchanges Path pair ##
Exchanges the Verb_Array, Point_Array, Weights, and Fill_Type with other.
Cached state is also exchanged. swap() internally exchanges pointers, so
it is lightweight and does not allocate memory.
swap() usage has largely been replaced by operator=(const SkPath& path).
Paths do not copy their content on assignment until they are written to,
making assignment as efficient as swap().
#Param other Path exchanged by value ##
#Example
SkPath path1, path2;
path1.addRect({10, 20, 30, 40});
path1.swap(path2);
const SkRect& b1 = path1.getBounds();
SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom);
const SkRect& b2 = path2.getBounds();
SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom);
#StdOut
path1 bounds = 0, 0, 0, 0
path2 bounds = 10, 20, 30, 40
#StdOut ##
##
#SeeAlso operator=(const SkPath& path)
##
# ------------------------------------------------------------------------------
#Method const SkRect& getBounds() const
#In Property
#Line # returns maximum and minimum of Point_Array ##
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
##
# ------------------------------------------------------------------------------
#Subtopic Utility
#Populate
#Line # rarely called management functions ##
##
#Method void updateBoundsCache() const
#In Utility
#Line # refreshes result of getBounds ##
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
#In Property
#Line # returns extent of geometry ##
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
#In Property
#Line # returns true if Rect may be inside ##
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)
#In Utility
#Line # reserves space for additional data ##
grows Path Verb_Array and Point_Array to contain extraPtCount additional Points.
May improve performance and use less memory by
reducing the number and size of allocations when creating Path.
#Param extraPtCount number of additional Points to allocate ##
#Example
#Height 192
void draw(SkCanvas* canvas) {
auto addPoly = [](int sides, SkScalar size, SkPath* path) -> void {
path->moveTo(size, 0);
for (int i = 1; i < sides; i++) {
SkScalar c, s = SkScalarSinCos(SK_ScalarPI * 2 * i / sides, &c);
path->lineTo(c * size, s * size);
}
path->close();
};
SkPath path;
path.incReserve(3 + 4 + 5 + 6 + 7 + 8 + 9);
for (int sides = 3; sides < 10; ++sides) {
addPoly(sides, sides, &path);
}
SkMatrix matrix;
matrix.setScale(10, 10, -10, -10);
path.transform(matrix);
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
canvas->drawPath(path, paint);
}
##
#SeeAlso Point_Array
##
# ------------------------------------------------------------------------------
#Subtopic Build
#Populate
#Line # adds points and verbs to path ##
##
#Method void moveTo(SkScalar x, SkScalar y)
#In Build
#Line # starts Contour ##
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)
#In Build
#Line # starts Contour relative to Last_Point ##
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)
#In Build
#Line # appends Line ##
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)
#In Build
#Line # appends Line relative to Last_Point ##
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
##
# ------------------------------------------------------------------------------
#Subtopic Quad
#Alias Quad
#Alias Quads
#Alias Quadratic_Bezier
#Alias Quadratic_Beziers
#Line # Bezier_Curve described by second-order polynomial ##
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)
#In Quad
#Line # appends Quad ##
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)
#In Build
#In Quad
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)
#In Build
#In Quad
#Line # appends Quad relative to Last_Point ##
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
##
#Subtopic Quad ##
# ------------------------------------------------------------------------------
#Subtopic Conic
#Line # conic section defined by three points and a weight ##
#Alias Conics
Conic describes a conical section: a piece of an ellipse, or a piece of a
parabola, or a piece of a hyperbola. Conic begins at a start Point,
curves towards a control Point, and then curves to an end Point. The influence
of the control Point is determined by Conic_Weight.
Each Conic in Path adds two Points and one Conic_Weight. Conic_Weights in Path
may be inspected with Iter, or with RawIter.
#Subtopic Weight
#Alias Conic_Weights
#Alias Weights
#Line # strength of Conic control Point ##
Weight determines both the strength of the control Point and the type of Conic.
If Weight is exactly one, then Conic is identical to Quad; it is always a
parabolic segment.
#Example
#Description
When Conic_Weight is one, Quad is added to path; the two are identical.
##
void draw(SkCanvas* canvas) {
const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
SkPath path;
path.conicTo(20, 30, 50, 60, 1);
SkPath::Iter iter(path, false);
SkPath::Verb verb;
do {
SkPoint points[4];
verb = iter.next(points);
SkDebugf("%s ", verbNames[(int) verb]);
for (int i = 0; i < pointCount[(int) verb]; ++i) {
SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
}
if (SkPath::kConic_Verb == verb) {
SkDebugf("weight = %g", iter.conicWeight());
}
SkDebugf("\n");
} while (SkPath::kDone_Verb != verb);
}
#StdOut
move {0, 0},
quad {0, 0}, {20, 30}, {50, 60},
done
##
##
If weight is less than one, Conic is an elliptical segment.
#Example
#Description
A 90 degree circular arc has the weight
#Formula
1 / sqrt(2)
##
.
##
void draw(SkCanvas* canvas) {
const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
SkPath path;
path.arcTo(20, 0, 20, 20, 20);
SkPath::Iter iter(path, false);
SkPath::Verb verb;
do {
SkPoint points[4];
verb = iter.next(points);
SkDebugf("%s ", verbNames[(int) verb]);
for (int i = 0; i < pointCount[(int) verb]; ++i) {
SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
}
if (SkPath::kConic_Verb == verb) {
SkDebugf("weight = %g", iter.conicWeight());
}
SkDebugf("\n");
} while (SkPath::kDone_Verb != verb);
}
#StdOut
move {0, 0},
conic {0, 0}, {20, 0}, {20, 20}, weight = 0.707107
done
##
##
If weight is greater than one, Conic is a hyperbolic segment. As weight gets large,
a hyperbolic segment can be approximated by straight lines connecting the
control Point with the end Points.
#Example
void draw(SkCanvas* canvas) {
const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
SkPath path;
path.conicTo(20, 0, 20, 20, SK_ScalarInfinity);
SkPath::Iter iter(path, false);
SkPath::Verb verb;
do {
SkPoint points[4];
verb = iter.next(points);
SkDebugf("%s ", verbNames[(int) verb]);
for (int i = 0; i < pointCount[(int) verb]; ++i) {
SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
}
if (SkPath::kConic_Verb == verb) {
SkDebugf("weight = %g", iter.conicWeight());
}
SkDebugf("\n");
} while (SkPath::kDone_Verb != verb);
}
#StdOut
move {0, 0},
line {0, 0}, {20, 0},
line {20, 0}, {20, 20},
done
##
##
#Subtopic Weight ##
#Method void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
SkScalar w)
#In Conic
#In Build
#Line # appends Conic ##
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}};