blob: b36fa65a6dbf7255ca86405e9dcfb22a86520dc9 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2006 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
8#ifndef SkPath_DEFINED
9#define SkPath_DEFINED
10
11#include "SkMatrix.h"
Mike Reed89c8fec2017-06-07 10:57:22 -040012#include "../private/SkPathRef.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013
reed@android.com8a1c16f2008-12-17 15:59:43 +000014class SkAutoPathBoundsUpdate;
reed@google.com4ed0fb72012-12-12 20:48:18 +000015class SkRRect;
caryclark66a5d8b2014-06-24 08:30:15 -070016class SkWStream;
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
18/** \class SkPath
19
20 The SkPath class encapsulates compound (multiple contour) geometric paths
21 consisting of straight line segments, quadratic curves, and cubic curves.
Mike Kleinafe7a5f2017-03-01 11:16:17 -050022
23 SkPath is not thread safe unless you've first called SkPath::updateBoundsCache().
reed@android.com8a1c16f2008-12-17 15:59:43 +000024*/
ctguil@chromium.org7ffb1b22011-03-15 21:27:08 +000025class SK_API SkPath {
reed@android.com8a1c16f2008-12-17 15:59:43 +000026public:
bsalomon78d58d12016-05-27 09:17:04 -070027 enum Direction {
28 /** clockwise direction for adding closed contours */
29 kCW_Direction,
30 /** counter-clockwise direction for adding closed contours */
31 kCCW_Direction,
32 };
33
reed@android.com8a1c16f2008-12-17 15:59:43 +000034 SkPath();
Cary Clark0e616cf2017-05-18 15:27:57 -040035 SkPath(const SkPath& path);
reed@android.com8a1c16f2008-12-17 15:59:43 +000036 ~SkPath();
37
Cary Clark0e616cf2017-05-18 15:27:57 -040038 SkPath& operator=(const SkPath& path);
39 // mac chromium dbg requires SK_API to make operator== visible
40 friend SK_API bool operator==(const SkPath& a, const SkPath& b);
reed@android.com3abec1d2009-03-02 05:36:20 +000041 friend bool operator!=(const SkPath& a, const SkPath& b) {
42 return !(a == b);
43 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000044
caryclark8e7b19d2016-02-18 04:11:48 -080045 /** Return true if the paths contain an equal array of verbs and weights. Paths
46 * with equal verb counts can be readily interpolated. If the paths contain one
47 * or more conics, the conics' weights must also match.
48 *
49 * @param compare The path to compare.
50 *
51 * @return true if the paths have the same verbs and weights.
52 */
53 bool isInterpolatable(const SkPath& compare) const;
54
55 /** Interpolate between two paths with same-sized point arrays.
56 * The out path contains the verbs and weights of this path.
57 * The out points are a weighted average of this path and the ending path.
58 *
59 * @param ending The path to interpolate between.
60 * @param weight The weight, from 0 to 1. The output points are set to
61 * (this->points * weight) + ending->points * (1 - weight).
62 * @return true if the paths could be interpolated.
63 */
64 bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const;
65
djsollen90b5c0c2015-02-12 12:08:40 -080066#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
67 /** Returns true if the caller is the only owner of the underlying path data */
68 bool unique() const { return fPathRef->unique(); }
69#endif
70
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 enum FillType {
72 /** Specifies that "inside" is computed by a non-zero sum of signed
73 edge crossings
74 */
75 kWinding_FillType,
76 /** Specifies that "inside" is computed by an odd number of edge
77 crossings
78 */
79 kEvenOdd_FillType,
80 /** Same as Winding, but draws outside of the path, rather than inside
81 */
82 kInverseWinding_FillType,
83 /** Same as EvenOdd, but draws outside of the path, rather than inside
84 */
85 kInverseEvenOdd_FillType
86 };
87
88 /** Return the path's fill type. This is used to define how "inside" is
89 computed. The default value is kWinding_FillType.
90
91 @return the path's fill type
92 */
93 FillType getFillType() const { return (FillType)fFillType; }
94
95 /** Set the path's fill type. This is used to define how "inside" is
96 computed. The default value is kWinding_FillType.
schenney@chromium.orgb0af6da2011-12-21 20:43:13 +000097
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 @param ft The new fill type for this path
99 */
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000100 void setFillType(FillType ft) {
101 fFillType = SkToU8(ft);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000102 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103
104 /** Returns true if the filltype is one of the Inverse variants */
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000105 bool isInverseFillType() const { return IsInverseFillType((FillType)fFillType); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106
reed@google.comb54455e2011-05-16 14:16:04 +0000107 /**
108 * Toggle between inverse and normal filltypes. This reverse the return
109 * value of isInverseFillType()
110 */
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000111 void toggleInverseFillType() {
112 fFillType ^= 2;
robertphillips@google.com6b8dbb62013-12-12 23:03:51 +0000113 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114
reed@google.com04863fa2011-05-15 04:08:24 +0000115 enum Convexity {
116 kUnknown_Convexity,
117 kConvex_Convexity,
118 kConcave_Convexity
119 };
reed@android.com6b82d1a2009-06-03 02:35:01 +0000120
reed@google.com04863fa2011-05-15 04:08:24 +0000121 /**
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000122 * Return the path's convexity, as stored in the path. If it is currently unknown,
123 * then this function will attempt to compute the convexity (and cache the result).
reed@google.com04863fa2011-05-15 04:08:24 +0000124 */
reed@google.comb54455e2011-05-16 14:16:04 +0000125 Convexity getConvexity() const {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000126 if (kUnknown_Convexity != fConvexity) {
127 return static_cast<Convexity>(fConvexity);
128 } else {
129 return this->internalGetConvexity();
reed@google.comb54455e2011-05-16 14:16:04 +0000130 }
reed@google.comb54455e2011-05-16 14:16:04 +0000131 }
132
133 /**
134 * Return the currently cached value for convexity, even if that is set to
135 * kUnknown_Convexity. Note: getConvexity() will automatically call
136 * ComputeConvexity and cache its return value if the current setting is
137 * kUnknown.
138 */
139 Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; }
reed@google.com04863fa2011-05-15 04:08:24 +0000140
141 /**
142 * Store a convexity setting in the path. There is no automatic check to
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000143 * see if this value actually agrees with the return value that would be
144 * computed by getConvexity().
reed@google.comb54455e2011-05-16 14:16:04 +0000145 *
146 * Note: even if this is set to a "known" value, if the path is later
147 * changed (e.g. lineTo(), addRect(), etc.) then the cached value will be
148 * reset to kUnknown_Convexity.
reed@google.com04863fa2011-05-15 04:08:24 +0000149 */
Cary Clark0e616cf2017-05-18 15:27:57 -0400150 void setConvexity(Convexity convexity);
reed@google.com04863fa2011-05-15 04:08:24 +0000151
152 /**
reed@google.com04863fa2011-05-15 04:08:24 +0000153 * Returns true if the path is flagged as being convex. This is not a
154 * confirmed by any analysis, it is just the value set earlier.
155 */
156 bool isConvex() const {
157 return kConvex_Convexity == this->getConvexity();
158 }
159
160 /**
reed@google.com04863fa2011-05-15 04:08:24 +0000161 * Set the isConvex flag to true or false. Convex paths may draw faster if
162 * this flag is set, though setting this to true on a path that is in fact
163 * not convex can give undefined results when drawn. Paths default to
164 * isConvex == false
reed@android.com6b82d1a2009-06-03 02:35:01 +0000165 */
reed@google.com44699382013-10-31 17:28:30 +0000166 SK_ATTR_DEPRECATED("use setConvexity")
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000167 void setIsConvex(bool isConvex) {
reed@google.com04863fa2011-05-15 04:08:24 +0000168 this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +0000169 }
reed@android.com6b82d1a2009-06-03 02:35:01 +0000170
bsalomon@google.com6aa29652012-04-18 13:29:52 +0000171 /** Returns true if the path is an oval.
172 *
173 * @param rect returns the bounding rect of this oval. It's a circle
174 * if the height and width are the same.
bsalomon78d58d12016-05-27 09:17:04 -0700175 * @param dir is the oval CCW (or CW if false).
176 * @param start indicates where the contour starts on the oval (see
177 * SkPath::addOval for intepretation of the index).
bsalomon@google.com6aa29652012-04-18 13:29:52 +0000178 * @return true if this path is an oval.
179 * Tracking whether a path is an oval is considered an
180 * optimization for performance and so some paths that are in
181 * fact ovals can report false.
182 */
bsalomon78d58d12016-05-27 09:17:04 -0700183 bool isOval(SkRect* rect, Direction* dir = nullptr,
184 unsigned* start = nullptr) const {
jcgregorioaef94d12016-07-26 14:10:17 -0700185 bool isCCW = false;
bsalomon78d58d12016-05-27 09:17:04 -0700186 bool result = fPathRef->isOval(rect, &isCCW, start);
187 if (dir && result) {
188 *dir = isCCW ? kCCW_Direction : kCW_Direction;
189 }
190 return result;
191 }
bsalomon@google.com6aa29652012-04-18 13:29:52 +0000192
caryclarkda707bf2015-11-19 14:47:43 -0800193 /** Returns true if the path is a round rect.
194 *
195 * @param rrect Returns the bounding rect and radii of this round rect.
bsalomon78d58d12016-05-27 09:17:04 -0700196 * @param dir is the rrect CCW (or CW if false).
197 * @param start indicates where the contour starts on the rrect (see
198 * SkPath::addRRect for intepretation of the index).
caryclarkda707bf2015-11-19 14:47:43 -0800199 *
200 * @return true if this path is a round rect.
201 * Tracking whether a path is a round rect is considered an
202 * optimization for performance and so some paths that are in
203 * fact round rects can report false.
204 */
bsalomon78d58d12016-05-27 09:17:04 -0700205 bool isRRect(SkRRect* rrect, Direction* dir = nullptr,
206 unsigned* start = nullptr) const {
jcgregorioaef94d12016-07-26 14:10:17 -0700207 bool isCCW = false;
bsalomon78d58d12016-05-27 09:17:04 -0700208 bool result = fPathRef->isRRect(rrect, &isCCW, start);
209 if (dir && result) {
210 *dir = isCCW ? kCCW_Direction : kCW_Direction;
211 }
212 return result;
213 }
caryclarkda707bf2015-11-19 14:47:43 -0800214
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 /** Clear any lines and curves from the path, making it empty. This frees up
216 internal storage associated with those segments.
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +0000217 On Android, does not change fSourcePath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 */
219 void reset();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 /** Similar to reset(), in that all lines and curves are removed from the
222 path. However, any internal storage for those lines/curves is retained,
223 making reuse of the path potentially faster.
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +0000224 On Android, does not change fSourcePath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 */
226 void rewind();
227
228 /** Returns true if the path is empty (contains no lines or curves)
229
230 @return true if the path is empty (contains no lines or curves)
231 */
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000232 bool isEmpty() const {
233 SkDEBUGCODE(this->validate();)
234 return 0 == fPathRef->countVerbs();
235 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236
fsb1475b02016-01-20 09:51:07 -0800237 /** Return true if the last contour of this path ends with a close verb.
238 */
239 bool isLastContourClosed() const;
240
reed@google.com0bb18bb2012-07-26 15:20:36 +0000241 /**
242 * Returns true if all of the points in this path are finite, meaning there
243 * are no infinities and no NaNs.
244 */
245 bool isFinite() const {
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000246 SkDEBUGCODE(this->validate();)
247 return fPathRef->isFinite();
reed@google.com0bb18bb2012-07-26 15:20:36 +0000248 }
249
jvanverthb3eb6872014-10-24 07:12:51 -0700250 /** Returns true if the path is volatile (i.e. should not be cached by devices.)
251 */
252 bool isVolatile() const {
253 return SkToBool(fIsVolatile);
254 }
255
256 /** Specify whether this path is volatile. Paths are not volatile by
257 default. Temporary paths that are discarded or modified after use should be
258 marked as volatile. This provides a hint to the device that the path
259 should not be cached. Providing this hint when appropriate can
260 improve performance by avoiding unnecessary overhead and resource
261 consumption on the device.
262 */
263 void setIsVolatile(bool isVolatile) {
264 fIsVolatile = isVolatile;
265 }
266
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000267 /** Test a line for zero length
268
269 @return true if the line is of zero length; otherwise false.
270 */
caryclarke8c56662015-07-14 11:19:26 -0700271 static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) {
272 return exact ? p1 == p2 : p1.equalsWithinTolerance(p2);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000273 }
274
275 /** Test a quad for zero length
276
277 @return true if the quad is of zero length; otherwise false.
278 */
279 static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
caryclarke8c56662015-07-14 11:19:26 -0700280 const SkPoint& p3, bool exact) {
281 return exact ? p1 == p2 && p2 == p3 : p1.equalsWithinTolerance(p2) &&
epoger@google.com94fa43c2012-04-11 17:51:01 +0000282 p2.equalsWithinTolerance(p3);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000283 }
284
285 /** Test a cubic curve for zero length
286
287 @return true if the cubic is of zero length; otherwise false.
288 */
289 static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
caryclarke8c56662015-07-14 11:19:26 -0700290 const SkPoint& p3, const SkPoint& p4, bool exact) {
291 return exact ? p1 == p2 && p2 == p3 && p3 == p4 : p1.equalsWithinTolerance(p2) &&
epoger@google.com94fa43c2012-04-11 17:51:01 +0000292 p2.equalsWithinTolerance(p3) &&
293 p3.equalsWithinTolerance(p4);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +0000294 }
295
reed@google.com7e6c4d12012-05-10 14:05:43 +0000296 /**
297 * Returns true if the path specifies a single line (i.e. it contains just
298 * a moveTo and a lineTo). If so, and line[] is not null, it sets the 2
299 * points in line[] to the end-points of the line. If the path is not a
300 * line, returns false and ignores line[].
301 */
302 bool isLine(SkPoint line[2]) const;
303
reed@android.comd3aa4ff2010-02-09 16:38:45 +0000304 /** Return the number of points in the path
305 */
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000306 int countPoints() const;
reed@android.comd3aa4ff2010-02-09 16:38:45 +0000307
308 /** Return the point at the specified index. If the index is out of range
309 (i.e. is not 0 <= index < countPoints()) then the returned coordinates
310 will be (0,0)
311 */
312 SkPoint getPoint(int index) const;
313
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 /** Returns the number of points in the path. Up to max points are copied.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000315
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 @param points If not null, receives up to max points
317 @param max The maximum number of points to copy into points
318 @return the actual number of points in the path
319 */
320 int getPoints(SkPoint points[], int max) const;
321
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000322 /** Return the number of verbs in the path
323 */
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000324 int countVerbs() const;
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000325
326 /** Returns the number of verbs in the path. Up to max verbs are copied. The
327 verbs are copied as one byte per verb.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000328
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000329 @param verbs If not null, receives up to max verbs
330 @param max The maximum number of verbs to copy into verbs
331 @return the actual number of verbs in the path
332 */
333 int getVerbs(uint8_t verbs[], int max) const;
334
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 //! Swap contents of this and other. Guaranteed not to throw
336 void swap(SkPath& other);
337
reed91f283b2015-07-28 06:00:50 -0700338 /**
339 * Returns the bounds of the path's points. If the path contains zero points/verbs, this
340 * will return the "empty" rect [0, 0, 0, 0].
341 * Note: this bounds may be larger than the actual shape, since curves
342 * do not extend as far as their control points. Additionally this bound encompases all points,
343 * even isolated moveTos either preceeding or following the last non-degenerate contour.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 */
reed@android.comd252db02009-04-01 18:31:44 +0000345 const SkRect& getBounds() const {
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000346 return fPathRef->getBounds();
reed@android.comd252db02009-04-01 18:31:44 +0000347 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348
349 /** Calling this will, if the internal cache of the bounds is out of date,
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000350 update it so that subsequent calls to getBounds will be instantaneous.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 This also means that any copies or simple transformations of the path
352 will inherit the cached bounds.
reed@android.comd252db02009-04-01 18:31:44 +0000353 */
354 void updateBoundsCache() const {
355 // for now, just calling getBounds() is sufficient
356 this->getBounds();
357 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000359 /**
Mike Reed8d3196b2017-02-03 11:34:13 -0500360 * Computes a bounds that is conservatively "snug" around the path. This assumes that the
361 * path will be filled. It does not attempt to collapse away contours that are logically
362 * empty (e.g. moveTo(x, y) + lineTo(x, y)) but will include them in the calculation.
363 *
364 * It differs from getBounds() in that it will look at the snug bounds of curves, whereas
365 * getBounds() just returns the bounds of the control-points. Thus computing this may be
366 * slower than just calling getBounds().
367 *
368 * If the path is empty (i.e. no points or verbs), it will return SkRect::MakeEmpty().
369 */
370 SkRect computeTightBounds() const;
371
372 /**
bsalomon@google.com9bee33a2012-11-13 21:51:38 +0000373 * Does a conservative test to see whether a rectangle is inside a path. Currently it only
374 * will ever return true for single convex contour paths. The empty-status of the rect is not
375 * considered (e.g. a rect that is a point can be inside a path). Points or line segments where
376 * the rect edge touches the path border are not considered containment violations.
377 */
378 bool conservativelyContainsRect(const SkRect& rect) const;
379
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 // Construction methods
381
382 /** Hint to the path to prepare for adding more points. This can allow the
383 path to more efficiently grow its storage.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000384
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 @param extraPtCount The number of extra points the path should
386 preallocate for.
387 */
388 void incReserve(unsigned extraPtCount);
389
390 /** Set the beginning of the next contour to the point (x,y).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000391
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 @param x The x-coordinate of the start of a new contour
393 @param y The y-coordinate of the start of a new contour
394 */
395 void moveTo(SkScalar x, SkScalar y);
396
397 /** Set the beginning of the next contour to the point
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000398
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 @param p The start of a new contour
400 */
401 void moveTo(const SkPoint& p) {
402 this->moveTo(p.fX, p.fY);
403 }
404
405 /** Set the beginning of the next contour relative to the last point on the
406 previous contour. If there is no previous contour, this is treated the
407 same as moveTo().
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000408
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 @param dx The amount to add to the x-coordinate of the end of the
410 previous contour, to specify the start of a new contour
411 @param dy The amount to add to the y-coordinate of the end of the
412 previous contour, to specify the start of a new contour
413 */
414 void rMoveTo(SkScalar dx, SkScalar dy);
415
416 /** Add a line from the last point to the specified point (x,y). If no
417 moveTo() call has been made for this contour, the first point is
418 automatically set to (0,0).
419
420 @param x The x-coordinate of the end of a line
421 @param y The y-coordinate of the end of a line
422 */
423 void lineTo(SkScalar x, SkScalar y);
424
425 /** Add a line from the last point to the specified point. If no moveTo()
426 call has been made for this contour, the first point is automatically
427 set to (0,0).
428
429 @param p The end of a line
430 */
431 void lineTo(const SkPoint& p) {
432 this->lineTo(p.fX, p.fY);
433 }
434
435 /** Same as lineTo, but the coordinates are considered relative to the last
436 point on this contour. If there is no previous point, then a moveTo(0,0)
437 is inserted automatically.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 @param dx The amount to add to the x-coordinate of the previous point
440 on this contour, to specify a line
441 @param dy The amount to add to the y-coordinate of the previous point
442 on this contour, to specify a line
443 */
444 void rLineTo(SkScalar dx, SkScalar dy);
445
446 /** Add a quadratic bezier from the last point, approaching control point
447 (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
448 this contour, the first point is automatically set to (0,0).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 @param x1 The x-coordinate of the control point on a quadratic curve
451 @param y1 The y-coordinate of the control point on a quadratic curve
452 @param x2 The x-coordinate of the end point on a quadratic curve
453 @param y2 The y-coordinate of the end point on a quadratic curve
454 */
455 void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
456
457 /** Add a quadratic bezier from the last point, approaching control point
458 p1, and ending at p2. If no moveTo() call has been made for this
459 contour, the first point is automatically set to (0,0).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000460
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 @param p1 The control point on a quadratic curve
462 @param p2 The end point on a quadratic curve
463 */
464 void quadTo(const SkPoint& p1, const SkPoint& p2) {
465 this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
466 }
467
468 /** Same as quadTo, but the coordinates are considered relative to the last
469 point on this contour. If there is no previous point, then a moveTo(0,0)
470 is inserted automatically.
471
472 @param dx1 The amount to add to the x-coordinate of the last point on
473 this contour, to specify the control point of a quadratic curve
474 @param dy1 The amount to add to the y-coordinate of the last point on
475 this contour, to specify the control point of a quadratic curve
476 @param dx2 The amount to add to the x-coordinate of the last point on
477 this contour, to specify the end point of a quadratic curve
478 @param dy2 The amount to add to the y-coordinate of the last point on
479 this contour, to specify the end point of a quadratic curve
480 */
481 void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
482
reed@google.com277c3f82013-05-31 15:17:50 +0000483 void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
484 SkScalar w);
485 void conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) {
486 this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w);
487 }
488 void rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
489 SkScalar w);
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000490
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 /** Add a cubic bezier from the last point, approaching control points
492 (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
493 made for this contour, the first point is automatically set to (0,0).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000494
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 @param x1 The x-coordinate of the 1st control point on a cubic curve
496 @param y1 The y-coordinate of the 1st control point on a cubic curve
497 @param x2 The x-coordinate of the 2nd control point on a cubic curve
498 @param y2 The y-coordinate of the 2nd control point on a cubic curve
499 @param x3 The x-coordinate of the end point on a cubic curve
500 @param y3 The y-coordinate of the end point on a cubic curve
501 */
502 void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
503 SkScalar x3, SkScalar y3);
504
505 /** Add a cubic bezier from the last point, approaching control points p1
506 and p2, and ending at p3. If no moveTo() call has been made for this
507 contour, the first point is automatically set to (0,0).
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 @param p1 The 1st control point on a cubic curve
510 @param p2 The 2nd control point on a cubic curve
511 @param p3 The end point on a cubic curve
512 */
513 void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
514 this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
515 }
516
517 /** Same as cubicTo, but the coordinates are considered relative to the
518 current point on this contour. If there is no previous point, then a
519 moveTo(0,0) is inserted automatically.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000520
Cary Clark0e616cf2017-05-18 15:27:57 -0400521 @param x1 The amount to add to the x-coordinate of the last point on
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 this contour, to specify the 1st control point of a cubic curve
Cary Clark0e616cf2017-05-18 15:27:57 -0400523 @param y1 The amount to add to the y-coordinate of the last point on
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 this contour, to specify the 1st control point of a cubic curve
Cary Clark0e616cf2017-05-18 15:27:57 -0400525 @param x2 The amount to add to the x-coordinate of the last point on
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 this contour, to specify the 2nd control point of a cubic curve
Cary Clark0e616cf2017-05-18 15:27:57 -0400527 @param y2 The amount to add to the y-coordinate of the last point on
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 this contour, to specify the 2nd control point of a cubic curve
Cary Clark0e616cf2017-05-18 15:27:57 -0400529 @param x3 The amount to add to the x-coordinate of the last point on
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 this contour, to specify the end point of a cubic curve
Cary Clark0e616cf2017-05-18 15:27:57 -0400531 @param y3 The amount to add to the y-coordinate of the last point on
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 this contour, to specify the end point of a cubic curve
533 */
robertphillips@google.com6b8dbb62013-12-12 23:03:51 +0000534 void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
535 SkScalar x3, SkScalar y3);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536
reedc7789042015-01-29 12:59:11 -0800537 /**
538 * Append the specified arc to the path. If the start of the arc is different from the path's
539 * current last point, then an automatic lineTo() is added to connect the current contour
540 * to the start of the arc. However, if the path is empty, then we call moveTo() with
541 * the first point of the arc. The sweep angle is treated mod 360.
542 *
543 * @param oval The bounding oval defining the shape and size of the arc
544 * @param startAngle Starting angle (in degrees) where the arc begins
545 * @param sweepAngle Sweep angle (in degrees) measured clockwise. This is treated mod 360.
546 * @param forceMoveTo If true, always begin a new contour with the arc
547 */
548 void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000549
reedc7789042015-01-29 12:59:11 -0800550 /**
551 * Append a line and arc to the current path. This is the same as the PostScript call "arct".
552 */
553 void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554
555 /** Append a line and arc to the current path. This is the same as the
556 PostScript call "arct".
557 */
558 void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
559 this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
560 }
561
caryclark55d49052016-01-23 05:07:04 -0800562 enum ArcSize {
563 /** the smaller of the two possible SVG arcs. */
564 kSmall_ArcSize,
565 /** the larger of the two possible SVG arcs. */
566 kLarge_ArcSize,
567 };
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000569 /**
caryclark55d49052016-01-23 05:07:04 -0800570 * Append an elliptical arc from the current point in the format used by SVG.
571 * The center of the ellipse is computed to satisfy the constraints below.
572 *
573 * @param rx,ry The radii in the x and y directions respectively.
574 * @param xAxisRotate The angle in degrees relative to the x-axis.
575 * @param largeArc Determines whether the smallest or largest arc possible
576 * is drawn.
577 * @param sweep Determines if the arc should be swept in an anti-clockwise or
578 * clockwise direction. Note that this enum value is opposite the SVG
579 * arc sweep value.
580 * @param x,y The destination coordinates.
581 */
582 void arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
583 Direction sweep, SkScalar x, SkScalar y);
584
585 void arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
586 const SkPoint xy) {
587 this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, sweep, xy.fX, xy.fY);
588 }
589
590 /** Same as arcTo format used by SVG, but the destination coordinate is relative to the
591 * last point on this contour. If there is no previous point, then a
592 * moveTo(0,0) is inserted automatically.
593 *
594 * @param rx,ry The radii in the x and y directions respectively.
595 * @param xAxisRotate The angle in degrees relative to the x-axis.
596 * @param largeArc Determines whether the smallest or largest arc possible
597 * is drawn.
598 * @param sweep Determines if the arc should be swept in an anti-clockwise or
599 * clockwise direction. Note that this enum value is opposite the SVG
600 * arc sweep value.
601 * @param dx,dy The destination coordinates relative to the last point.
602 */
603 void rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
604 Direction sweep, SkScalar dx, SkScalar dy);
605
606 /** Close the current contour. If the current point is not equal to the
607 first point of the contour, a line segment is automatically added.
608 */
609 void close();
610
611 /**
sugoi@google.com12b4e272012-12-06 20:13:11 +0000612 * Returns whether or not a fill type is inverted
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000613 *
614 * kWinding_FillType -> false
615 * kEvenOdd_FillType -> false
616 * kInverseWinding_FillType -> true
617 * kInverseEvenOdd_FillType -> true
sugoi@google.com12b4e272012-12-06 20:13:11 +0000618 */
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000619 static bool IsInverseFillType(FillType fill) {
bungeman99fe8222015-08-20 07:57:51 -0700620 static_assert(0 == kWinding_FillType, "fill_type_mismatch");
621 static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
622 static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
623 static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
sugoi@google.com12b4e272012-12-06 20:13:11 +0000624 return (fill & 2) != 0;
625 }
626
627 /**
628 * Returns the equivalent non-inverted fill type to the given fill type
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000629 *
630 * kWinding_FillType -> kWinding_FillType
631 * kEvenOdd_FillType -> kEvenOdd_FillType
632 * kInverseWinding_FillType -> kWinding_FillType
633 * kInverseEvenOdd_FillType -> kEvenOdd_FillType
sugoi@google.com12b4e272012-12-06 20:13:11 +0000634 */
sugoi@google.com5f74cf82012-12-17 21:16:45 +0000635 static FillType ConvertToNonInverseFillType(FillType fill) {
bungeman99fe8222015-08-20 07:57:51 -0700636 static_assert(0 == kWinding_FillType, "fill_type_mismatch");
637 static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
638 static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
639 static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
sugoi@google.com12b4e272012-12-06 20:13:11 +0000640 return (FillType)(fill & 1);
641 }
642
643 /**
fmalitaaa0df4e2015-12-01 09:13:23 -0800644 * Chop a conic into N quads, stored continguously in pts[], where
645 * N = 1 << pow2. The amount of storage needed is (1 + 2 * N)
646 */
647 static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
648 SkScalar w, SkPoint pts[], int pow2);
649
650 /**
robertphillips4f662e62014-12-29 14:06:51 -0800651 * Returns true if the path specifies a rectangle.
652 *
653 * If this returns false, then all output parameters are ignored, and left
654 * unchanged. If this returns true, then each of the output parameters
655 * are checked for NULL. If they are not, they return their value.
656 *
robertphillips91b0a352015-01-05 10:13:46 -0800657 * @param rect If not null, set to the bounds of the rectangle.
658 * Note : this bounds may be smaller than the path's bounds, since it is just
659 * the bounds of the "drawable" parts of the path. e.g. a trailing MoveTo would
660 * be ignored in this rect, but not by the path's bounds
robertphillips4f662e62014-12-29 14:06:51 -0800661 * @param isClosed If not null, set to true if the path is closed
662 * @param direction If not null, set to the rectangle's direction
663 * @return true if the path specifies a rectangle
664 */
robertphillips91b0a352015-01-05 10:13:46 -0800665 bool isRect(SkRect* rect, bool* isClosed = NULL, Direction* direction = NULL) const;
robertphillips@google.com8fd16032013-06-25 15:39:58 +0000666
caryclark95bc5f32015-04-08 08:34:15 -0700667 /** Returns true if the path specifies a pair of nested rectangles, or would draw a
668 pair of nested rectangles when filled. If so, and if
robertphillips@google.com8fd16032013-06-25 15:39:58 +0000669 rect is not null, set rect[0] to the outer rectangle and rect[1] to the inner
670 rectangle. If so, and dirs is not null, set dirs[0] to the direction of
671 the outer rectangle and dirs[1] to the direction of the inner rectangle. If
672 the path does not specify a pair of nested rectangles, return
673 false and ignore rect and dirs.
674
675 @param rect If not null, returns the path as a pair of nested rectangles
676 @param dirs If not null, returns the direction of the rects
677 @return true if the path describes a pair of nested rectangles
678 */
caryclark95bc5f32015-04-08 08:34:15 -0700679 bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = NULL) const;
robertphillips@google.com8fd16032013-06-25 15:39:58 +0000680
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000681 /**
682 * Add a closed rectangle contour to the path
683 * @param rect The rectangle to add as a closed contour to the path
reed026beb52015-06-10 14:23:15 -0700684 * @param dir The direction to wind the rectangle's contour.
fmalitac08d53e2015-11-17 09:53:29 -0800685 *
686 * Note: the contour initial point index is 0 (as defined below).
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000687 */
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000688 void addRect(const SkRect& rect, Direction dir = kCW_Direction);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000690 /**
691 * Add a closed rectangle contour to the path
fmalitac08d53e2015-11-17 09:53:29 -0800692 * @param rect The rectangle to add as a closed contour to the path
693 * @param dir The direction to wind the rectangle's contour.
694 * @param start Initial point of the contour (initial moveTo), expressed as
695 * a corner index, starting in the upper-left position, clock-wise:
696 *
697 * 0 1
698 * *-------*
699 * | |
700 * *-------*
701 * 3 2
702 */
703 void addRect(const SkRect& rect, Direction dir, unsigned start);
704
705 /**
706 * Add a closed rectangle contour to the path
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000707 *
708 * @param left The left side of a rectangle to add as a closed contour
709 * to the path
710 * @param top The top of a rectangle to add as a closed contour to the
711 * path
712 * @param right The right side of a rectangle to add as a closed contour
713 * to the path
714 * @param bottom The bottom of a rectangle to add as a closed contour to
715 * the path
reed026beb52015-06-10 14:23:15 -0700716 * @param dir The direction to wind the rectangle's contour.
fmalitac08d53e2015-11-17 09:53:29 -0800717 *
718 * Note: the contour initial point index is 0 (as defined above).
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000719 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
721 Direction dir = kCW_Direction);
722
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000723 /**
724 * Add a closed oval contour to the path
725 *
726 * @param oval The bounding oval to add as a closed contour to the path
reed026beb52015-06-10 14:23:15 -0700727 * @param dir The direction to wind the oval's contour.
fmalitac08d53e2015-11-17 09:53:29 -0800728 *
729 * Note: the contour initial point index is 1 (as defined below).
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000730 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 void addOval(const SkRect& oval, Direction dir = kCW_Direction);
732
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000733 /**
fmalitac08d53e2015-11-17 09:53:29 -0800734 * Add a closed oval contour to the path
735 *
736 * @param oval The bounding oval to add as a closed contour to the path
737 * @param dir The direction to wind the oval's contour.
738 * @param start Initial point of the contour (initial moveTo), expressed
739 * as an ellipse vertex index, starting at the top, clock-wise
740 * (90/0/270/180deg order):
741 *
742 * 0
743 * -*-
744 * | |
745 * 3 * * 1
746 * | |
747 * -*-
748 * 2
749 */
750 void addOval(const SkRect& oval, Direction dir, unsigned start);
751
752 /**
bsalomon78d58d12016-05-27 09:17:04 -0700753 * Add a closed circle contour to the path. The circle contour begins at
754 * the right-most point (as though 1 were passed to addOval's 'start' param).
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000755 *
756 * @param x The x-coordinate of the center of a circle to add as a
757 * closed contour to the path
758 * @param y The y-coordinate of the center of a circle to add as a
759 * closed contour to the path
760 * @param radius The radius of a circle to add as a closed contour to the
761 * path
reed026beb52015-06-10 14:23:15 -0700762 * @param dir The direction to wind the circle's contour.
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000763 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 void addCircle(SkScalar x, SkScalar y, SkScalar radius,
765 Direction dir = kCW_Direction);
766
767 /** Add the specified arc to the path as a new contour.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000768
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 @param oval The bounds of oval used to define the size of the arc
770 @param startAngle Starting angle (in degrees) where the arc begins
771 @param sweepAngle Sweep angle (in degrees) measured clockwise
772 */
773 void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
774
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000775 /**
776 * Add a closed round-rectangle contour to the path
777 * @param rect The bounds of a round-rectangle to add as a closed contour
778 * @param rx The x-radius of the rounded corners on the round-rectangle
779 * @param ry The y-radius of the rounded corners on the round-rectangle
reed026beb52015-06-10 14:23:15 -0700780 * @param dir The direction to wind the rectangle's contour.
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000781 */
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000782 void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
783 Direction dir = kCW_Direction);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000785 /**
786 * Add a closed round-rectangle contour to the path. Each corner receives
787 * two radius values [X, Y]. The corners are ordered top-left, top-right,
788 * bottom-right, bottom-left.
789 * @param rect The bounds of a round-rectangle to add as a closed contour
790 * @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
reed026beb52015-06-10 14:23:15 -0700791 * @param dir The direction to wind the rectangle's contour.
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000792 * Note: The radii here now go through the same constraint handling as the
793 * SkRRect radii (i.e., either radii at a corner being 0 implies a
794 * sqaure corner and oversized radii are proportionally scaled down).
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000795 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 void addRoundRect(const SkRect& rect, const SkScalar radii[],
797 Direction dir = kCW_Direction);
798
reed@google.com744faba2012-05-29 19:54:52 +0000799 /**
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000800 * Add an SkRRect contour to the path
801 * @param rrect The rounded rect to add as a closed contour
reed026beb52015-06-10 14:23:15 -0700802 * @param dir The winding direction for the new contour.
fmalitac08d53e2015-11-17 09:53:29 -0800803 *
804 * Note: the contour initial point index is either 6 (for dir == kCW_Direction)
805 * or 7 (for dir == kCCW_Direction), as defined below.
806 *
reed@google.com4ed0fb72012-12-12 20:48:18 +0000807 */
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000808 void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000809
810 /**
fmalitac08d53e2015-11-17 09:53:29 -0800811 * Add an SkRRect contour to the path
812 * @param rrect The rounded rect to add as a closed contour
813 * @param dir The winding direction for the new contour.
814 * @param start Initial point of the contour (initial moveTo), expressed as
815 * an index of the radii minor/major points, ordered clock-wise:
816 *
817 * 0 1
818 * *----*
819 * 7 * * 2
820 * | |
821 * 6 * * 3
822 * *----*
823 * 5 4
824 */
825 void addRRect(const SkRRect& rrect, Direction dir, unsigned start);
826
827 /**
reed@google.com744faba2012-05-29 19:54:52 +0000828 * Add a new contour made of just lines. This is just a fast version of
829 * the following:
830 * this->moveTo(pts[0]);
831 * for (int i = 1; i < count; ++i) {
832 * this->lineTo(pts[i]);
833 * }
834 * if (close) {
835 * this->close();
836 * }
837 */
838 void addPoly(const SkPoint pts[], int count, bool close);
839
commit-bot@chromium.org14747e52014-02-11 21:16:29 +0000840 enum AddPathMode {
841 /** Source path contours are added as new contours.
842 */
843 kAppend_AddPathMode,
844 /** Path is added by extending the last contour of the destination path
845 with the first contour of the source path. If the last contour of
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000846 the destination path is closed, then it will not be extended.
commit-bot@chromium.org14747e52014-02-11 21:16:29 +0000847 Instead, the start of source path will be extended by a straight
848 line to the end point of the destination path.
849 */
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000850 kExtend_AddPathMode
commit-bot@chromium.org14747e52014-02-11 21:16:29 +0000851 };
852
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 /** Add a copy of src to the path, offset by (dx,dy)
854 @param src The path to add as a new contour
855 @param dx The amount to translate the path in X as it is added
856 @param dx The amount to translate the path in Y as it is added
857 */
commit-bot@chromium.org14747e52014-02-11 21:16:29 +0000858 void addPath(const SkPath& src, SkScalar dx, SkScalar dy,
859 AddPathMode mode = kAppend_AddPathMode);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860
861 /** Add a copy of src to the path
862 */
commit-bot@chromium.org14747e52014-02-11 21:16:29 +0000863 void addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 SkMatrix m;
865 m.reset();
commit-bot@chromium.org14747e52014-02-11 21:16:29 +0000866 this->addPath(src, m, mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 }
868
869 /** Add a copy of src to the path, transformed by matrix
870 @param src The path to add as a new contour
commit-bot@chromium.org14747e52014-02-11 21:16:29 +0000871 @param matrix Transform applied to src
872 @param mode Determines how path is added
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 */
commit-bot@chromium.org14747e52014-02-11 21:16:29 +0000874 void addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875
reed@google.com63d73742012-01-10 15:33:12 +0000876 /**
877 * Same as addPath(), but reverses the src input
878 */
879 void reverseAddPath(const SkPath& src);
880
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 /** Offset the path by (dx,dy), returning true on success
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000882
883 @param dx The amount in the X direction to offset the entire path
884 @param dy The amount in the Y direction to offset the entire path
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 @param dst The translated path is written here
886 */
887 void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
888
889 /** Offset the path by (dx,dy), returning true on success
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000890
891 @param dx The amount in the X direction to offset the entire path
892 @param dy The amount in the Y direction to offset the entire path
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 */
894 void offset(SkScalar dx, SkScalar dy) {
895 this->offset(dx, dy, this);
896 }
897
898 /** Transform the points in this path by matrix, and write the answer into
899 dst.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000900
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 @param matrix The matrix to apply to the path
902 @param dst The transformed path is written here
903 */
904 void transform(const SkMatrix& matrix, SkPath* dst) const;
905
906 /** Transform the points in this path by matrix
907
908 @param matrix The matrix to apply to the path
909 */
910 void transform(const SkMatrix& matrix) {
911 this->transform(matrix, this);
912 }
913
914 /** Return the last point on the path. If no points have been added, (0,0)
reed@google.com294dd7b2011-10-11 11:58:32 +0000915 is returned. If there are no points, this returns false, otherwise it
916 returns true.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000917
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 @param lastPt The last point on the path is returned here
919 */
reed@google.com294dd7b2011-10-11 11:58:32 +0000920 bool getLastPt(SkPoint* lastPt) const;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921
922 /** Set the last point on the path. If no points have been added,
923 moveTo(x,y) is automatically called.
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000924
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 @param x The new x-coordinate for the last point
926 @param y The new y-coordinate for the last point
927 */
928 void setLastPt(SkScalar x, SkScalar y);
929
930 /** Set the last point on the path. If no points have been added, moveTo(p)
931 is automatically called.
932
933 @param p The new location for the last point
934 */
935 void setLastPt(const SkPoint& p) {
936 this->setLastPt(p.fX, p.fY);
937 }
938
reed@google.com10296cc2011-09-21 12:29:05 +0000939 enum SegmentMask {
940 kLine_SegmentMask = 1 << 0,
941 kQuad_SegmentMask = 1 << 1,
reed@google.com277c3f82013-05-31 15:17:50 +0000942 kConic_SegmentMask = 1 << 2,
943 kCubic_SegmentMask = 1 << 3,
reed@google.com10296cc2011-09-21 12:29:05 +0000944 };
945
946 /**
947 * Returns a mask, where each bit corresponding to a SegmentMask is
948 * set if the path contains 1 or more segments of that type.
949 * Returns 0 for an empty path (no segments).
950 */
robertphillips@google.com6b8dbb62013-12-12 23:03:51 +0000951 uint32_t getSegmentMasks() const { return fPathRef->getSegmentMasks(); }
reed@google.com10296cc2011-09-21 12:29:05 +0000952
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953 enum Verb {
954 kMove_Verb, //!< iter.next returns 1 point
955 kLine_Verb, //!< iter.next returns 2 points
956 kQuad_Verb, //!< iter.next returns 3 points
reed@google.com277c3f82013-05-31 15:17:50 +0000957 kConic_Verb, //!< iter.next returns 3 points + iter.conicWeight()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 kCubic_Verb, //!< iter.next returns 4 points
caryclark449f7fc2016-02-19 07:40:34 -0800959 kClose_Verb, //!< iter.next returns 0 points
reed@google.com277c3f82013-05-31 15:17:50 +0000960 kDone_Verb, //!< iter.next returns 0 points
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961 };
962
963 /** Iterate through all of the segments (lines, quadratics, cubics) of
964 each contours in a path.
schenney@chromium.org72785c42011-12-29 21:03:28 +0000965
966 The iterator cleans up the segments along the way, removing degenerate
967 segments and adding close verbs where necessary. When the forceClose
968 argument is provided, each contour (as defined by a new starting
969 move command) will be completed with a close verb regardless of the
970 contour's contents.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 */
ctguil@chromium.org7ffb1b22011-03-15 21:27:08 +0000972 class SK_API Iter {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 public:
schenney@chromium.org72785c42011-12-29 21:03:28 +0000974 Iter();
975 Iter(const SkPath&, bool forceClose);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976
977 void setPath(const SkPath&, bool forceClose);
978
979 /** Return the next verb in this iteration of the path. When all
980 segments have been visited, return kDone_Verb.
schenney@chromium.orgb0af6da2011-12-21 20:43:13 +0000981
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 @param pts The points representing the current verb and/or segment
reed@google.com4a3b7142012-05-16 17:16:46 +0000983 @param doConsumeDegerates If true, first scan for segments that are
984 deemed degenerate (too short) and skip those.
caryclarke8c56662015-07-14 11:19:26 -0700985 @param exact if doConsumeDegenerates is true and exact is true, skip only
986 degenerate elements with lengths exactly equal to zero. If exact
987 is false, skip degenerate elements with lengths close to zero. If
988 doConsumeDegenerates is false, exact has no effect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 @return The verb for the current segment
990 */
caryclarke8c56662015-07-14 11:19:26 -0700991 Verb next(SkPoint pts[4], bool doConsumeDegerates = true, bool exact = false) {
reed@google.com4a3b7142012-05-16 17:16:46 +0000992 if (doConsumeDegerates) {
caryclarke8c56662015-07-14 11:19:26 -0700993 this->consumeDegenerateSegments(exact);
reed@google.com4a3b7142012-05-16 17:16:46 +0000994 }
995 return this->doNext(pts);
996 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997
reed@google.com277c3f82013-05-31 15:17:50 +0000998 /**
999 * Return the weight for the current conic. Only valid if the current
1000 * segment return by next() was a conic.
1001 */
1002 SkScalar conicWeight() const { return *fConicWeights; }
1003
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 /** If next() returns kLine_Verb, then this query returns true if the
1005 line was the result of a close() command (i.e. the end point is the
1006 initial moveto for this contour). If next() returned a different
1007 verb, this returns an undefined value.
schenney@chromium.orgb0af6da2011-12-21 20:43:13 +00001008
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 @return If the last call to next() returned kLine_Verb, return true
1010 if it was the result of an explicit close command.
1011 */
1012 bool isCloseLine() const { return SkToBool(fCloseLine); }
schenney@chromium.orgb0af6da2011-12-21 20:43:13 +00001013
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 /** Returns true if the current contour is closed (has a kClose_Verb)
1015 @return true if the current contour is closed (has a kClose_Verb)
1016 */
1017 bool isClosedContour() const;
1018
1019 private:
1020 const SkPoint* fPts;
1021 const uint8_t* fVerbs;
1022 const uint8_t* fVerbStop;
reed@google.com277c3f82013-05-31 15:17:50 +00001023 const SkScalar* fConicWeights;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 SkPoint fMoveTo;
1025 SkPoint fLastPt;
1026 SkBool8 fForceClose;
1027 SkBool8 fNeedClose;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 SkBool8 fCloseLine;
schenney@chromium.orgb0af6da2011-12-21 20:43:13 +00001029 SkBool8 fSegmentState;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030
reed@google.com9e25dbf2012-05-15 17:05:38 +00001031 inline const SkPoint& cons_moveTo();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 Verb autoClose(SkPoint pts[2]);
caryclarke8c56662015-07-14 11:19:26 -07001033 void consumeDegenerateSegments(bool exact);
reed@google.com4a3b7142012-05-16 17:16:46 +00001034 Verb doNext(SkPoint pts[4]);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 };
1036
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001037 /** Iterate through the verbs in the path, providing the associated points.
1038 */
1039 class SK_API RawIter {
1040 public:
caryclarkda707bf2015-11-19 14:47:43 -08001041 RawIter() {}
1042 RawIter(const SkPath& path) {
1043 setPath(path);
1044 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001045
caryclarkda707bf2015-11-19 14:47:43 -08001046 void setPath(const SkPath& path) {
1047 fRawIter.setPathRef(*path.fPathRef.get());
1048 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001049
1050 /** Return the next verb in this iteration of the path. When all
1051 segments have been visited, return kDone_Verb.
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001052
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001053 @param pts The points representing the current verb and/or segment
bsalomon@google.comf6d3c5a2012-06-07 17:47:33 +00001054 This must not be NULL.
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001055 @return The verb for the current segment
1056 */
caryclarkda707bf2015-11-19 14:47:43 -08001057 Verb next(SkPoint pts[4]) {
1058 return (Verb) fRawIter.next(pts);
1059 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001060
caryclark2028d7f2015-12-09 14:04:46 -08001061 /** Return what the next verb will be, but do not visit the next segment.
1062
1063 @return The verb for the next segment
1064 */
1065 Verb peek() const {
1066 return (Verb) fRawIter.peek();
1067 }
1068
caryclarkda707bf2015-11-19 14:47:43 -08001069 SkScalar conicWeight() const {
1070 return fRawIter.conicWeight();
1071 }
reed@google.com277c3f82013-05-31 15:17:50 +00001072
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001073 private:
caryclarkda707bf2015-11-19 14:47:43 -08001074 SkPathRef::Iter fRawIter;
1075 friend class SkPath;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001076 };
1077
mike@reedtribe.orgbad1b2f2012-07-11 01:51:33 +00001078 /**
1079 * Returns true if the point { x, y } is contained by the path, taking into
1080 * account the FillType.
1081 */
1082 bool contains(SkScalar x, SkScalar y) const;
1083
Cary Clark0e616cf2017-05-18 15:27:57 -04001084 void dump(SkWStream* stream, bool forceClose, bool dumpAsHex) const;
reed@android.come522ca52009-11-23 20:10:41 +00001085 void dump() const;
caryclarke9562592014-09-15 09:26:09 -07001086 void dumpHex() const;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001088 /**
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001089 * Write the path to the buffer, and return the number of bytes written.
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001090 * If buffer is NULL, it still returns the number of bytes.
1091 */
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001092 size_t writeToMemory(void* buffer) const;
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001093 /**
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001094 * Initializes the path from the buffer
1095 *
1096 * @param buffer Memory to read from
1097 * @param length Amount of memory available in the buffer
1098 * @return number of bytes read (must be a multiple of 4) or
1099 * 0 if there was not enough memory available
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001100 */
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +00001101 size_t readFromMemory(const void* buffer, size_t length);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102
commit-bot@chromium.org1ab9f732013-10-30 18:57:55 +00001103 /** Returns a non-zero, globally unique value corresponding to the set of verbs
1104 and points in the path (but not the fill type [except on Android skbug.com/1762]).
skia.committer@gmail.com7ed98df2013-10-31 07:01:53 +00001105 Each time the path is modified, a different generation ID will be returned.
commit-bot@chromium.org1ab9f732013-10-30 18:57:55 +00001106 */
robertphillips@google.com7101abe2013-10-29 22:45:37 +00001107 uint32_t getGenerationID() const;
commit-bot@chromium.org1ab9f732013-10-30 18:57:55 +00001108
djsollen523cda32015-02-17 08:06:32 -08001109#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
commit-bot@chromium.org1ab9f732013-10-30 18:57:55 +00001110 static const int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762)
commit-bot@chromium.org1ab9f732013-10-30 18:57:55 +00001111#else
1112 static const int kPathRefGenIDBitCnt = 32;
djsollen@google.comf5dbe2f2011-04-15 13:41:26 +00001113#endif
1114
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 SkDEBUGCODE(void validate() const;)
caryclark63c684a2015-02-25 09:04:04 -08001116 SkDEBUGCODE(void experimentalValidateRef() const { fPathRef->validate(); } )
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117
1118private:
robertphillips@google.com01ec2eb2012-08-17 10:58:49 +00001119 enum SerializationOffsets {
commit-bot@chromium.orgfed2ab62014-01-23 15:16:05 +00001120 // 1 free bit at 29
robertphillips@google.com11e05552013-12-03 19:46:58 +00001121 kUnused1_SerializationShift = 28, // 1 free bit
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001122 kDirection_SerializationShift = 26, // requires 2 bits
jvanverthb3eb6872014-10-24 07:12:51 -07001123 kIsVolatile_SerializationShift = 25, // requires 1 bit
commit-bot@chromium.orgfed2ab62014-01-23 15:16:05 +00001124 // 1 free bit at 24
robertphillips@google.comca0c8382013-09-26 12:18:23 +00001125 kConvexity_SerializationShift = 16, // requires 8 bits
1126 kFillType_SerializationShift = 8, // requires 8 bits
reed8f086022015-06-11 14:22:19 -07001127 // low-8-bits are version
1128 };
1129
1130 enum SerializationVersions {
1131 kPathPrivFirstDirection_Version = 1,
caryclark69006412016-02-17 12:16:27 -08001132 kPathPrivLastMoveToIndex_Version = 2,
1133 kCurrent_Version = 2
robertphillips@google.com01ec2eb2012-08-17 10:58:49 +00001134 };
1135
bungeman6bd52842016-10-27 09:30:08 -07001136 sk_sp<SkPathRef> fPathRef;
mtkleinbf905202015-10-07 12:46:43 -07001137 int fLastMoveToIndex;
1138 uint8_t fFillType;
1139 mutable uint8_t fConvexity;
1140 mutable SkAtomic<uint8_t, sk_memory_order_relaxed> fFirstDirection;// SkPathPriv::FirstDirection
Mike Kleinad611922016-11-11 10:43:44 -05001141 SkBool8 fIsVolatile;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142
bungeman@google.coma5809a32013-06-21 15:13:34 +00001143 /** Resets all fields other than fPathRef to their initial 'empty' values.
1144 * Assumes the caller has already emptied fPathRef.
1145 * On Android increments fGenerationID without reseting it.
1146 */
1147 void resetFields();
1148
1149 /** Sets all fields other than fPathRef to the values in 'that'.
1150 * Assumes the caller has already set fPathRef.
mtklein@google.com9c9d4a72013-08-07 19:17:53 +00001151 * Doesn't change fGenerationID or fSourcePath on Android.
bungeman@google.coma5809a32013-06-21 15:13:34 +00001152 */
1153 void copyFields(const SkPath& that);
1154
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 friend class Iter;
reed026beb52015-06-10 14:23:15 -07001156 friend class SkPathPriv;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 friend class SkPathStroker;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158
1159 /* Append, in reverse order, the first contour of path, ignoring path's
1160 last point. If no moveTo() call has been made for this contour, the
1161 first point is automatically set to (0,0).
1162 */
1163 void reversePathTo(const SkPath&);
1164
reed@google.comd335d1d2012-01-12 18:17:11 +00001165 // called before we add points for lineTo, quadTo, cubicTo, checking to see
1166 // if we need to inject a leading moveTo first
1167 //
1168 // SkPath path; path.lineTo(...); <--- need a leading moveTo(0, 0)
1169 // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo)
1170 //
commit-bot@chromium.org5e1a7f22014-02-12 17:44:30 +00001171 inline void injectMoveToIfNeeded();
reed@google.comd335d1d2012-01-12 18:17:11 +00001172
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001173 inline bool hasOnlyMoveTos() const;
1174
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001175 Convexity internalGetConvexity() const;
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001176
caryclark@google.comf68154a2012-11-21 15:18:06 +00001177 bool isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts,
1178 bool* isClosed, Direction* direction) const;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001179
Brian Osmana2318572017-07-10 15:09:26 -04001180 // called by stroker to see if all points (in the last contour) are equal and worthy of a cap
1181 bool isZeroLengthSincePoint(int startPtIndex) const;
caryclarkd49a86a2016-02-22 12:44:54 -08001182
robertphillips@google.comca0c8382013-09-26 12:18:23 +00001183 /** Returns if the path can return a bound at no cost (true) or will have to
1184 perform some computation (false).
1185 */
1186 bool hasComputedBounds() const {
1187 SkDEBUGCODE(this->validate();)
1188 return fPathRef->hasComputedBounds();
1189 }
1190
1191
1192 // 'rect' needs to be sorted
1193 void setBounds(const SkRect& rect) {
robertphillips@google.com0efb21b2013-12-13 19:36:25 +00001194 SkPathRef::Editor ed(&fPathRef);
1195
1196 ed.setBounds(rect);
robertphillips@google.comca0c8382013-09-26 12:18:23 +00001197 }
1198
caryclarkaec25102015-04-29 08:28:30 -07001199 void setPt(int index, SkScalar x, SkScalar y);
1200
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 friend class SkAutoPathBoundsUpdate;
bsalomon@google.com6aa29652012-04-18 13:29:52 +00001202 friend class SkAutoDisableOvalCheck;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001203 friend class SkAutoDisableDirectionCheck;
caryclark51c56782016-11-07 05:09:28 -08001204 friend class SkPathWriter;
1205 friend class SkOpBuilder;
commit-bot@chromium.orga1a097e2013-11-14 16:53:22 +00001206 friend class SkBench_AddPathTest; // perf test reversePathTo
1207 friend class PathTest_Private; // unit test reversePathTo
caryclarkda707bf2015-11-19 14:47:43 -08001208 friend class ForceIsRRect_Private; // unit test isRRect
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209};
1210
1211#endif