• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2006 The Android Open Source Project
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkPath_DEFINED
9 #define SkPath_DEFINED
10 
11 #include "SkMatrix.h"
12 #include "../private/SkPathRef.h"
13 
14 class SkAutoPathBoundsUpdate;
15 class SkRRect;
16 class SkWStream;
17 
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.
22 
23     SkPath is not thread safe unless you've first called SkPath::updateBoundsCache().
24 */
25 class SK_API SkPath {
26 public:
27     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 
34     SkPath();
35     SkPath(const SkPath& path);
36     ~SkPath();
37 
38     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);
41     friend bool operator!=(const SkPath& a, const SkPath& b) {
42         return !(a == b);
43     }
44 
45     /** 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 
66 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
67     /** Returns true if the caller is the only owner of the underlying path data */
unique()68     bool unique() const { return fPathRef->unique(); }
69 #endif
70 
71     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     */
getFillType()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.
97 
98         @param ft The new fill type for this path
99     */
setFillType(FillType ft)100     void setFillType(FillType ft) {
101         fFillType = SkToU8(ft);
102     }
103 
104     /** Returns true if the filltype is one of the Inverse variants */
isInverseFillType()105     bool isInverseFillType() const { return IsInverseFillType((FillType)fFillType); }
106 
107     /**
108      *  Toggle between inverse and normal filltypes. This reverse the return
109      *  value of isInverseFillType()
110      */
toggleInverseFillType()111     void toggleInverseFillType() {
112         fFillType ^= 2;
113     }
114 
115     enum Convexity {
116         kUnknown_Convexity,
117         kConvex_Convexity,
118         kConcave_Convexity
119     };
120 
121     /**
122      *  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).
124      */
getConvexity()125     Convexity getConvexity() const {
126         if (kUnknown_Convexity != fConvexity) {
127             return static_cast<Convexity>(fConvexity);
128         } else {
129             return this->internalGetConvexity();
130         }
131     }
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      */
getConvexityOrUnknown()139     Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; }
140 
141     /**
142      *  Store a convexity setting in the path. There is no automatic check to
143      *  see if this value actually agrees with the return value that would be
144      *  computed by getConvexity().
145      *
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.
149      */
150     void setConvexity(Convexity convexity);
151 
152     /**
153      *  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      */
isConvex()156     bool isConvex() const {
157         return kConvex_Convexity == this->getConvexity();
158     }
159 
160     /**
161      *  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
165      */
166     SK_ATTR_DEPRECATED("use setConvexity")
setIsConvex(bool isConvex)167     void setIsConvex(bool isConvex) {
168         this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
169     }
170 
171     /** 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.
175      * @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).
178      * @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      */
183     bool isOval(SkRect* rect, Direction* dir = nullptr,
184                 unsigned* start = nullptr) const {
185         bool isCCW = false;
186         bool result = fPathRef->isOval(rect, &isCCW, start);
187         if (dir && result) {
188             *dir = isCCW ? kCCW_Direction : kCW_Direction;
189         }
190         return result;
191     }
192 
193     /** Returns true if the path is a round rect.
194      *
195      * @param rrect  Returns the bounding rect and radii of this round rect.
196      * @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).
199      *
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      */
205     bool isRRect(SkRRect* rrect, Direction* dir = nullptr,
206                  unsigned* start = nullptr) const {
207         bool isCCW = false;
208         bool result = fPathRef->isRRect(rrect, &isCCW, start);
209         if (dir && result) {
210             *dir = isCCW ? kCCW_Direction : kCW_Direction;
211         }
212         return result;
213     }
214 
215     /** Clear any lines and curves from the path, making it empty. This frees up
216         internal storage associated with those segments.
217         On Android, does not change fSourcePath.
218     */
219     void reset();
220 
221     /** 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.
224         On Android, does not change fSourcePath.
225     */
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     */
isEmpty()232     bool isEmpty() const {
233         SkDEBUGCODE(this->validate();)
234         return 0 == fPathRef->countVerbs();
235     }
236 
237     /** Return true if the last contour of this path ends with a close verb.
238      */
239     bool isLastContourClosed() const;
240 
241     /**
242      *  Returns true if all of the points in this path are finite, meaning there
243      *  are no infinities and no NaNs.
244      */
isFinite()245     bool isFinite() const {
246         SkDEBUGCODE(this->validate();)
247         return fPathRef->isFinite();
248     }
249 
250     /** Returns true if the path is volatile (i.e. should not be cached by devices.)
251      */
isVolatile()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      */
setIsVolatile(bool isVolatile)263     void setIsVolatile(bool isVolatile) {
264         fIsVolatile = isVolatile;
265     }
266 
267     /** Test a line for zero length
268 
269         @return true if the line is of zero length; otherwise false.
270     */
IsLineDegenerate(const SkPoint & p1,const SkPoint & p2,bool exact)271     static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) {
272         return exact ? p1 == p2 : p1.equalsWithinTolerance(p2);
273     }
274 
275     /** Test a quad for zero length
276 
277         @return true if the quad is of zero length; otherwise false.
278     */
IsQuadDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,bool exact)279     static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
280                                  const SkPoint& p3, bool exact) {
281         return exact ? p1 == p2 && p2 == p3 : p1.equalsWithinTolerance(p2) &&
282                p2.equalsWithinTolerance(p3);
283     }
284 
285     /** Test a cubic curve for zero length
286 
287         @return true if the cubic is of zero length; otherwise false.
288     */
IsCubicDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,const SkPoint & p4,bool exact)289     static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
290                                   const SkPoint& p3, const SkPoint& p4, bool exact) {
291         return exact ? p1 == p2 && p2 == p3 && p3 == p4 : p1.equalsWithinTolerance(p2) &&
292                p2.equalsWithinTolerance(p3) &&
293                p3.equalsWithinTolerance(p4);
294     }
295 
296     /**
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 
304     /** Return the number of points in the path
305      */
306     int countPoints() const;
307 
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 
314     /** Returns the number of points in the path. Up to max points are copied.
315 
316         @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 
322     /** Return the number of verbs in the path
323      */
324     int countVerbs() const;
325 
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.
328 
329         @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 
335     //! Swap contents of this and other. Guaranteed not to throw
336     void swap(SkPath& other);
337 
338     /**
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.
344     */
getBounds()345     const SkRect& getBounds() const {
346         return fPathRef->getBounds();
347     }
348 
349     /** Calling this will, if the internal cache of the bounds is out of date,
350         update it so that subsequent calls to getBounds will be instantaneous.
351         This also means that any copies or simple transformations of the path
352         will inherit the cached bounds.
353      */
updateBoundsCache()354     void updateBoundsCache() const {
355         // for now, just calling getBounds() is sufficient
356         this->getBounds();
357     }
358 
359     /**
360      *  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     /**
373      * 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 
380     //  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.
384 
385         @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).
391 
392         @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
398 
399         @param p    The start of a new contour
400     */
moveTo(const SkPoint & p)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().
408 
409         @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     */
lineTo(const SkPoint & p)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.
438 
439         @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).
449 
450         @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).
460 
461         @param p1   The control point on a quadratic curve
462         @param p2   The end point on a quadratic curve
463     */
quadTo(const SkPoint & p1,const SkPoint & p2)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 
483     void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
484                  SkScalar w);
conicTo(const SkPoint & p1,const SkPoint & p2,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);
490 
491     /** 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).
494 
495         @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).
508 
509         @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     */
cubicTo(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3)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.
520 
521         @param x1   The amount to add to the x-coordinate of the last point on
522                 this contour, to specify the 1st control point of a cubic curve
523         @param y1   The amount to add to the y-coordinate of the last point on
524                 this contour, to specify the 1st control point of a cubic curve
525         @param x2   The amount to add to the x-coordinate of the last point on
526                 this contour, to specify the 2nd control point of a cubic curve
527         @param y2   The amount to add to the y-coordinate of the last point on
528                 this contour, to specify the 2nd control point of a cubic curve
529         @param x3   The amount to add to the x-coordinate of the last point on
530                      this contour, to specify the end point of a cubic curve
531         @param y3   The amount to add to the y-coordinate of the last point on
532                      this contour, to specify the end point of a cubic curve
533     */
534     void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
535                   SkScalar x3, SkScalar y3);
536 
537     /**
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);
549 
550     /**
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);
554 
555     /** Append a line and arc to the current path. This is the same as the
556         PostScript call "arct".
557     */
arcTo(const SkPoint p1,const SkPoint p2,SkScalar radius)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 
562     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     };
568 
569     /**
570      *  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 
arcTo(const SkPoint r,SkScalar xAxisRotate,ArcSize largeArc,Direction sweep,const SkPoint xy)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     /**
612      *  Returns whether or not a fill type is inverted
613      *
614      *  kWinding_FillType        -> false
615      *  kEvenOdd_FillType        -> false
616      *  kInverseWinding_FillType -> true
617      *  kInverseEvenOdd_FillType -> true
618      */
IsInverseFillType(FillType fill)619     static bool IsInverseFillType(FillType fill) {
620         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");
624         return (fill & 2) != 0;
625     }
626 
627     /**
628      *  Returns the equivalent non-inverted fill type to the given fill type
629      *
630      *  kWinding_FillType        -> kWinding_FillType
631      *  kEvenOdd_FillType        -> kEvenOdd_FillType
632      *  kInverseWinding_FillType -> kWinding_FillType
633      *  kInverseEvenOdd_FillType -> kEvenOdd_FillType
634      */
ConvertToNonInverseFillType(FillType fill)635     static FillType ConvertToNonInverseFillType(FillType fill) {
636         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");
640         return (FillType)(fill & 1);
641     }
642 
643     /**
644      *  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     /**
651      *  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      *
657      *  @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
661      *  @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      */
665     bool isRect(SkRect* rect, bool* isClosed = NULL, Direction* direction = NULL) const;
666 
667     /** 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
669         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     */
679     bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = NULL) const;
680 
681     /**
682      *  Add a closed rectangle contour to the path
683      *  @param rect The rectangle to add as a closed contour to the path
684      *  @param dir  The direction to wind the rectangle's contour.
685      *
686      *  Note: the contour initial point index is 0 (as defined below).
687      */
688     void addRect(const SkRect& rect, Direction dir = kCW_Direction);
689 
690     /**
691      *  Add a closed rectangle contour to the path
692      *  @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
707      *
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
716      *  @param dir  The direction to wind the rectangle's contour.
717      *
718      *  Note: the contour initial point index is 0 (as defined above).
719      */
720     void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
721                  Direction dir = kCW_Direction);
722 
723     /**
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
727      *  @param dir  The direction to wind the oval's contour.
728      *
729      *  Note: the contour initial point index is 1 (as defined below).
730      */
731     void addOval(const SkRect& oval, Direction dir = kCW_Direction);
732 
733     /**
734      *  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     /**
753      *  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).
755      *
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
762      *  @param dir  The direction to wind the circle's contour.
763      */
764     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.
768 
769         @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 
775     /**
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
780      *  @param dir  The direction to wind the rectangle's contour.
781      */
782     void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
783                       Direction dir = kCW_Direction);
784 
785     /**
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
791      *  @param dir  The direction to wind the rectangle's contour.
792      * 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).
795      */
796     void addRoundRect(const SkRect& rect, const SkScalar radii[],
797                       Direction dir = kCW_Direction);
798 
799     /**
800      *  Add an SkRRect contour to the path
801      *  @param rrect The rounded rect to add as a closed contour
802      *  @param dir   The winding direction for the new contour.
803      *
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      *
807      */
808     void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction);
809 
810     /**
811      *  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     /**
828      *  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 
840     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
846             the destination path is closed, then it will not be extended.
847             Instead, the start of source path will be extended by a straight
848             line to the end point of the destination path.
849         */
850         kExtend_AddPathMode
851     };
852 
853     /** 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     */
858     void addPath(const SkPath& src, SkScalar dx, SkScalar dy,
859                  AddPathMode mode = kAppend_AddPathMode);
860 
861     /** Add a copy of src to the path
862     */
863     void addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) {
864         SkMatrix m;
865         m.reset();
866         this->addPath(src, m, mode);
867     }
868 
869     /** Add a copy of src to the path, transformed by matrix
870         @param src  The path to add as a new contour
871         @param matrix  Transform applied to src
872         @param mode  Determines how path is added
873     */
874     void addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode);
875 
876     /**
877      *  Same as addPath(), but reverses the src input
878      */
879     void reverseAddPath(const SkPath& src);
880 
881     /** Offset the path by (dx,dy), returning true on success
882 
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
885         @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
890 
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
893     */
offset(SkScalar dx,SkScalar dy)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.
900 
901         @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     */
transform(const SkMatrix & matrix)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)
915         is returned. If there are no points, this returns false, otherwise it
916         returns true.
917 
918         @param lastPt   The last point on the path is returned here
919     */
920     bool getLastPt(SkPoint* lastPt) const;
921 
922     /** Set the last point on the path. If no points have been added,
923         moveTo(x,y) is automatically called.
924 
925         @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     */
setLastPt(const SkPoint & p)935     void setLastPt(const SkPoint& p) {
936         this->setLastPt(p.fX, p.fY);
937     }
938 
939     enum SegmentMask {
940         kLine_SegmentMask   = 1 << 0,
941         kQuad_SegmentMask   = 1 << 1,
942         kConic_SegmentMask  = 1 << 2,
943         kCubic_SegmentMask  = 1 << 3,
944     };
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      */
getSegmentMasks()951     uint32_t getSegmentMasks() const { return fPathRef->getSegmentMasks(); }
952 
953     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
957         kConic_Verb,    //!< iter.next returns 3 points + iter.conicWeight()
958         kCubic_Verb,    //!< iter.next returns 4 points
959         kClose_Verb,    //!< iter.next returns 0 points
960         kDone_Verb,     //!< iter.next returns 0 points
961     };
962 
963     /** Iterate through all of the segments (lines, quadratics, cubics) of
964         each contours in a path.
965 
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.
971     */
972     class SK_API Iter {
973     public:
974         Iter();
975         Iter(const SkPath&, bool forceClose);
976 
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.
981 
982             @param  pts The points representing the current verb and/or segment
983             @param doConsumeDegerates If true, first scan for segments that are
984                    deemed degenerate (too short) and skip those.
985             @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.
989             @return The verb for the current segment
990         */
991         Verb next(SkPoint pts[4], bool doConsumeDegerates = true, bool exact = false) {
992             if (doConsumeDegerates) {
993                 this->consumeDegenerateSegments(exact);
994             }
995             return this->doNext(pts);
996         }
997 
998         /**
999          *  Return the weight for the current conic. Only valid if the current
1000          *  segment return by next() was a conic.
1001          */
conicWeight()1002         SkScalar conicWeight() const { return *fConicWeights; }
1003 
1004         /** 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.
1008 
1009             @return If the last call to next() returned kLine_Verb, return true
1010                     if it was the result of an explicit close command.
1011         */
isCloseLine()1012         bool isCloseLine() const { return SkToBool(fCloseLine); }
1013 
1014         /** 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;
1023         const SkScalar* fConicWeights;
1024         SkPoint         fMoveTo;
1025         SkPoint         fLastPt;
1026         SkBool8         fForceClose;
1027         SkBool8         fNeedClose;
1028         SkBool8         fCloseLine;
1029         SkBool8         fSegmentState;
1030 
1031         inline const SkPoint& cons_moveTo();
1032         Verb autoClose(SkPoint pts[2]);
1033         void consumeDegenerateSegments(bool exact);
1034         Verb doNext(SkPoint pts[4]);
1035     };
1036 
1037     /** Iterate through the verbs in the path, providing the associated points.
1038     */
1039     class SK_API RawIter {
1040     public:
RawIter()1041         RawIter() {}
RawIter(const SkPath & path)1042         RawIter(const SkPath& path) {
1043             setPath(path);
1044         }
1045 
setPath(const SkPath & path)1046         void setPath(const SkPath& path) {
1047             fRawIter.setPathRef(*path.fPathRef.get());
1048         }
1049 
1050         /** Return the next verb in this iteration of the path. When all
1051             segments have been visited, return kDone_Verb.
1052 
1053             @param  pts The points representing the current verb and/or segment
1054                         This must not be NULL.
1055             @return The verb for the current segment
1056         */
next(SkPoint pts[4])1057         Verb next(SkPoint pts[4]) {
1058             return (Verb) fRawIter.next(pts);
1059         }
1060 
1061         /** Return what the next verb will be, but do not visit the next segment.
1062 
1063             @return The verb for the next segment
1064         */
peek()1065         Verb peek() const {
1066             return (Verb) fRawIter.peek();
1067         }
1068 
conicWeight()1069         SkScalar conicWeight() const {
1070             return fRawIter.conicWeight();
1071         }
1072 
1073     private:
1074         SkPathRef::Iter fRawIter;
1075         friend class SkPath;
1076     };
1077 
1078     /**
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 
1084     void dump(SkWStream* stream, bool forceClose, bool dumpAsHex) const;
1085     void dump() const;
1086     void dumpHex() const;
1087 
1088     /**
1089      *  Write the path to the buffer, and return the number of bytes written.
1090      *  If buffer is NULL, it still returns the number of bytes.
1091      */
1092     size_t writeToMemory(void* buffer) const;
1093     /**
1094      * 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
1100      */
1101     size_t readFromMemory(const void* buffer, size_t length);
1102 
1103     /** 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]).
1105         Each time the path is modified, a different generation ID will be returned.
1106     */
1107     uint32_t getGenerationID() const;
1108 
1109 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
1110     static const int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762)
1111 #else
1112     static const int kPathRefGenIDBitCnt = 32;
1113 #endif
1114 
1115     SkDEBUGCODE(void validate() const;)
1116     SkDEBUGCODE(void experimentalValidateRef() const { fPathRef->validate(); } )
1117 
1118 private:
1119     enum SerializationOffsets {
1120         // 1 free bit at 29
1121         kUnused1_SerializationShift = 28,    // 1 free bit
1122         kDirection_SerializationShift = 26, // requires 2 bits
1123         kIsVolatile_SerializationShift = 25, // requires 1 bit
1124         // 1 free bit at 24
1125         kConvexity_SerializationShift = 16, // requires 8 bits
1126         kFillType_SerializationShift = 8,   // requires 8 bits
1127         // low-8-bits are version
1128     };
1129 
1130     enum SerializationVersions {
1131         kPathPrivFirstDirection_Version = 1,
1132         kPathPrivLastMoveToIndex_Version = 2,
1133         kCurrent_Version = 2
1134     };
1135 
1136     sk_sp<SkPathRef>                                   fPathRef;
1137     int                                                fLastMoveToIndex;
1138     uint8_t                                            fFillType;
1139     mutable uint8_t                                    fConvexity;
1140     mutable SkAtomic<uint8_t, sk_memory_order_relaxed> fFirstDirection;// SkPathPriv::FirstDirection
1141     SkBool8                                            fIsVolatile;
1142 
1143     /** 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.
1151      *  Doesn't change fGenerationID or fSourcePath on Android.
1152      */
1153     void copyFields(const SkPath& that);
1154 
1155     friend class Iter;
1156     friend class SkPathPriv;
1157     friend class SkPathStroker;
1158 
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 
1165     // 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     //
1171     inline void injectMoveToIfNeeded();
1172 
1173     inline bool hasOnlyMoveTos() const;
1174 
1175     Convexity internalGetConvexity() const;
1176 
1177     bool isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts,
1178                        bool* isClosed, Direction* direction) const;
1179 
1180     // 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;
1182 
1183     /** Returns if the path can return a bound at no cost (true) or will have to
1184         perform some computation (false).
1185      */
hasComputedBounds()1186     bool hasComputedBounds() const {
1187         SkDEBUGCODE(this->validate();)
1188         return fPathRef->hasComputedBounds();
1189     }
1190 
1191 
1192     // 'rect' needs to be sorted
setBounds(const SkRect & rect)1193     void setBounds(const SkRect& rect) {
1194         SkPathRef::Editor ed(&fPathRef);
1195 
1196         ed.setBounds(rect);
1197     }
1198 
1199     void setPt(int index, SkScalar x, SkScalar y);
1200 
1201     friend class SkAutoPathBoundsUpdate;
1202     friend class SkAutoDisableOvalCheck;
1203     friend class SkAutoDisableDirectionCheck;
1204     friend class SkPathWriter;
1205     friend class SkOpBuilder;
1206     friend class SkBench_AddPathTest; // perf test reversePathTo
1207     friend class PathTest_Private; // unit test reversePathTo
1208     friend class ForceIsRRect_Private; // unit test isRRect
1209 };
1210 
1211 #endif
1212