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 "SkPathRef.h"
13 #include "SkRefCnt.h"
14 
15 class SkReader32;
16 class SkWriter32;
17 class SkAutoPathBoundsUpdate;
18 class SkString;
19 class SkRRect;
20 class SkWStream;
21 
22 /** \class SkPath
23 
24     The SkPath class encapsulates compound (multiple contour) geometric paths
25     consisting of straight line segments, quadratic curves, and cubic curves.
26 
27     SkPath is not thread safe unless you've first called SkPath::updateBoundsCache().
28 */
29 class SK_API SkPath {
30 public:
31     enum Direction {
32         /** clockwise direction for adding closed contours */
33         kCW_Direction,
34         /** counter-clockwise direction for adding closed contours */
35         kCCW_Direction,
36     };
37 
38     SkPath();
39     SkPath(const SkPath&);
40     ~SkPath();
41 
42     SkPath& operator=(const SkPath&);
43     friend  SK_API bool operator==(const SkPath&, const SkPath&);
44     friend bool operator!=(const SkPath& a, const SkPath& b) {
45         return !(a == b);
46     }
47 
48     /** Return true if the paths contain an equal array of verbs and weights. Paths
49      *  with equal verb counts can be readily interpolated. If the paths contain one
50      *  or more conics, the conics' weights must also match.
51      *
52      *  @param compare  The path to compare.
53      *
54      *  @return true if the paths have the same verbs and weights.
55      */
56     bool isInterpolatable(const SkPath& compare) const;
57 
58     /** Interpolate between two paths with same-sized point arrays.
59      *  The out path contains the verbs and weights of this path.
60      *  The out points are a weighted average of this path and the ending path.
61      *
62      *  @param ending  The path to interpolate between.
63      *  @param weight  The weight, from 0 to 1. The output points are set to
64      *                 (this->points * weight) + ending->points * (1 - weight).
65      *  @return true if the paths could be interpolated.
66      */
67     bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const;
68 
69 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
70     /** Returns true if the caller is the only owner of the underlying path data */
unique()71     bool unique() const { return fPathRef->unique(); }
72 #endif
73 
74     enum FillType {
75         /** Specifies that "inside" is computed by a non-zero sum of signed
76             edge crossings
77         */
78         kWinding_FillType,
79         /** Specifies that "inside" is computed by an odd number of edge
80             crossings
81         */
82         kEvenOdd_FillType,
83         /** Same as Winding, but draws outside of the path, rather than inside
84         */
85         kInverseWinding_FillType,
86         /** Same as EvenOdd, but draws outside of the path, rather than inside
87          */
88         kInverseEvenOdd_FillType
89     };
90 
91     /** Return the path's fill type. This is used to define how "inside" is
92         computed. The default value is kWinding_FillType.
93 
94         @return the path's fill type
95     */
getFillType()96     FillType getFillType() const { return (FillType)fFillType; }
97 
98     /** Set the path's fill type. This is used to define how "inside" is
99         computed. The default value is kWinding_FillType.
100 
101         @param ft The new fill type for this path
102     */
setFillType(FillType ft)103     void setFillType(FillType ft) {
104         fFillType = SkToU8(ft);
105     }
106 
107     /** Returns true if the filltype is one of the Inverse variants */
isInverseFillType()108     bool isInverseFillType() const { return IsInverseFillType((FillType)fFillType); }
109 
110     /**
111      *  Toggle between inverse and normal filltypes. This reverse the return
112      *  value of isInverseFillType()
113      */
toggleInverseFillType()114     void toggleInverseFillType() {
115         fFillType ^= 2;
116     }
117 
118     enum Convexity {
119         kUnknown_Convexity,
120         kConvex_Convexity,
121         kConcave_Convexity
122     };
123 
124     /**
125      *  Return the path's convexity, as stored in the path. If it is currently unknown,
126      *  then this function will attempt to compute the convexity (and cache the result).
127      */
getConvexity()128     Convexity getConvexity() const {
129         if (kUnknown_Convexity != fConvexity) {
130             return static_cast<Convexity>(fConvexity);
131         } else {
132             return this->internalGetConvexity();
133         }
134     }
135 
136     /**
137      *  Return the currently cached value for convexity, even if that is set to
138      *  kUnknown_Convexity. Note: getConvexity() will automatically call
139      *  ComputeConvexity and cache its return value if the current setting is
140      *  kUnknown.
141      */
getConvexityOrUnknown()142     Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; }
143 
144     /**
145      *  Store a convexity setting in the path. There is no automatic check to
146      *  see if this value actually agrees with the return value that would be
147      *  computed by getConvexity().
148      *
149      *  Note: even if this is set to a "known" value, if the path is later
150      *  changed (e.g. lineTo(), addRect(), etc.) then the cached value will be
151      *  reset to kUnknown_Convexity.
152      */
153     void setConvexity(Convexity);
154 
155     /**
156      *  Returns true if the path is flagged as being convex. This is not a
157      *  confirmed by any analysis, it is just the value set earlier.
158      */
isConvex()159     bool isConvex() const {
160         return kConvex_Convexity == this->getConvexity();
161     }
162 
163     /**
164      *  Set the isConvex flag to true or false. Convex paths may draw faster if
165      *  this flag is set, though setting this to true on a path that is in fact
166      *  not convex can give undefined results when drawn. Paths default to
167      *  isConvex == false
168      */
169     SK_ATTR_DEPRECATED("use setConvexity")
setIsConvex(bool isConvex)170     void setIsConvex(bool isConvex) {
171         this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
172     }
173 
174     /** Returns true if the path is an oval.
175      *
176      * @param rect      returns the bounding rect of this oval. It's a circle
177      *                  if the height and width are the same.
178      * @param dir       is the oval CCW (or CW if false).
179      * @param start     indicates where the contour starts on the oval (see
180      *                  SkPath::addOval for intepretation of the index).
181      * @return true if this path is an oval.
182      *              Tracking whether a path is an oval is considered an
183      *              optimization for performance and so some paths that are in
184      *              fact ovals can report false.
185      */
186     bool isOval(SkRect* rect, Direction* dir = nullptr,
187                 unsigned* start = nullptr) const {
188         bool isCCW = false;
189         bool result = fPathRef->isOval(rect, &isCCW, start);
190         if (dir && result) {
191             *dir = isCCW ? kCCW_Direction : kCW_Direction;
192         }
193         return result;
194     }
195 
196     /** Returns true if the path is a round rect.
197      *
198      * @param rrect  Returns the bounding rect and radii of this round rect.
199      * @param dir    is the rrect CCW (or CW if false).
200      * @param start  indicates where the contour starts on the rrect (see
201      *               SkPath::addRRect for intepretation of the index).
202      *
203      * @return true if this path is a round rect.
204      *              Tracking whether a path is a round rect is considered an
205      *              optimization for performance and so some paths that are in
206      *              fact round rects can report false.
207      */
208     bool isRRect(SkRRect* rrect, Direction* dir = nullptr,
209                  unsigned* start = nullptr) const {
210         bool isCCW = false;
211         bool result = fPathRef->isRRect(rrect, &isCCW, start);
212         if (dir && result) {
213             *dir = isCCW ? kCCW_Direction : kCW_Direction;
214         }
215         return result;
216     }
217 
218     /** Clear any lines and curves from the path, making it empty. This frees up
219         internal storage associated with those segments.
220         On Android, does not change fSourcePath.
221     */
222     void reset();
223 
224     /** Similar to reset(), in that all lines and curves are removed from the
225         path. However, any internal storage for those lines/curves is retained,
226         making reuse of the path potentially faster.
227         On Android, does not change fSourcePath.
228     */
229     void rewind();
230 
231     /** Returns true if the path is empty (contains no lines or curves)
232 
233         @return true if the path is empty (contains no lines or curves)
234     */
isEmpty()235     bool isEmpty() const {
236         SkDEBUGCODE(this->validate();)
237         return 0 == fPathRef->countVerbs();
238     }
239 
240     /** Return true if the last contour of this path ends with a close verb.
241      */
242     bool isLastContourClosed() const;
243 
244     /**
245      *  Returns true if all of the points in this path are finite, meaning there
246      *  are no infinities and no NaNs.
247      */
isFinite()248     bool isFinite() const {
249         SkDEBUGCODE(this->validate();)
250         return fPathRef->isFinite();
251     }
252 
253     /** Returns true if the path is volatile (i.e. should not be cached by devices.)
254      */
isVolatile()255     bool isVolatile() const {
256         return SkToBool(fIsVolatile);
257     }
258 
259     /** Specify whether this path is volatile. Paths are not volatile by
260      default. Temporary paths that are discarded or modified after use should be
261      marked as volatile. This provides a hint to the device that the path
262      should not be cached. Providing this hint when appropriate can
263      improve performance by avoiding unnecessary overhead and resource
264      consumption on the device.
265      */
setIsVolatile(bool isVolatile)266     void setIsVolatile(bool isVolatile) {
267         fIsVolatile = isVolatile;
268     }
269 
270     /** Test a line for zero length
271 
272         @return true if the line is of zero length; otherwise false.
273     */
IsLineDegenerate(const SkPoint & p1,const SkPoint & p2,bool exact)274     static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) {
275         return exact ? p1 == p2 : p1.equalsWithinTolerance(p2);
276     }
277 
278     /** Test a quad for zero length
279 
280         @return true if the quad is of zero length; otherwise false.
281     */
IsQuadDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,bool exact)282     static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
283                                  const SkPoint& p3, bool exact) {
284         return exact ? p1 == p2 && p2 == p3 : p1.equalsWithinTolerance(p2) &&
285                p2.equalsWithinTolerance(p3);
286     }
287 
288     /** Test a cubic curve for zero length
289 
290         @return true if the cubic is of zero length; otherwise false.
291     */
IsCubicDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,const SkPoint & p4,bool exact)292     static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
293                                   const SkPoint& p3, const SkPoint& p4, bool exact) {
294         return exact ? p1 == p2 && p2 == p3 && p3 == p4 : p1.equalsWithinTolerance(p2) &&
295                p2.equalsWithinTolerance(p3) &&
296                p3.equalsWithinTolerance(p4);
297     }
298 
299     /**
300      *  Returns true if the path specifies a single line (i.e. it contains just
301      *  a moveTo and a lineTo). If so, and line[] is not null, it sets the 2
302      *  points in line[] to the end-points of the line. If the path is not a
303      *  line, returns false and ignores line[].
304      */
305     bool isLine(SkPoint line[2]) const;
306 
307     /** Return the number of points in the path
308      */
309     int countPoints() const;
310 
311     /** Return the point at the specified index. If the index is out of range
312          (i.e. is not 0 <= index < countPoints()) then the returned coordinates
313          will be (0,0)
314      */
315     SkPoint getPoint(int index) const;
316 
317     /** Returns the number of points in the path. Up to max points are copied.
318 
319         @param points If not null, receives up to max points
320         @param max The maximum number of points to copy into points
321         @return the actual number of points in the path
322     */
323     int getPoints(SkPoint points[], int max) const;
324 
325     /** Return the number of verbs in the path
326      */
327     int countVerbs() const;
328 
329     /** Returns the number of verbs in the path. Up to max verbs are copied. The
330         verbs are copied as one byte per verb.
331 
332         @param verbs If not null, receives up to max verbs
333         @param max The maximum number of verbs to copy into verbs
334         @return the actual number of verbs in the path
335     */
336     int getVerbs(uint8_t verbs[], int max) const;
337 
338     //! Swap contents of this and other. Guaranteed not to throw
339     void swap(SkPath& other);
340 
341     /**
342      *  Returns the bounds of the path's points. If the path contains zero points/verbs, this
343      *  will return the "empty" rect [0, 0, 0, 0].
344      *  Note: this bounds may be larger than the actual shape, since curves
345      *  do not extend as far as their control points. Additionally this bound encompases all points,
346      *  even isolated moveTos either preceeding or following the last non-degenerate contour.
347     */
getBounds()348     const SkRect& getBounds() const {
349         return fPathRef->getBounds();
350     }
351 
352     /** Calling this will, if the internal cache of the bounds is out of date,
353         update it so that subsequent calls to getBounds will be instantaneous.
354         This also means that any copies or simple transformations of the path
355         will inherit the cached bounds.
356      */
updateBoundsCache()357     void updateBoundsCache() const {
358         // for now, just calling getBounds() is sufficient
359         this->getBounds();
360     }
361 
362     /**
363      *  Computes a bounds that is conservatively "snug" around the path. This assumes that the
364      *  path will be filled. It does not attempt to collapse away contours that are logically
365      *  empty (e.g. moveTo(x, y) + lineTo(x, y)) but will include them in the calculation.
366      *
367      *  It differs from getBounds() in that it will look at the snug bounds of curves, whereas
368      *  getBounds() just returns the bounds of the control-points. Thus computing this may be
369      *  slower than just calling getBounds().
370      *
371      *  If the path is empty (i.e. no points or verbs), it will return SkRect::MakeEmpty().
372      */
373     SkRect computeTightBounds() const;
374 
375     /**
376      * Does a conservative test to see whether a rectangle is inside a path. Currently it only
377      * will ever return true for single convex contour paths. The empty-status of the rect is not
378      * considered (e.g. a rect that is a point can be inside a path). Points or line segments where
379      * the rect edge touches the path border are not considered containment violations.
380      */
381     bool conservativelyContainsRect(const SkRect& rect) const;
382 
383     //  Construction methods
384 
385     /** Hint to the path to prepare for adding more points. This can allow the
386         path to more efficiently grow its storage.
387 
388         @param extraPtCount The number of extra points the path should
389                             preallocate for.
390     */
391     void incReserve(unsigned extraPtCount);
392 
393     /** Set the beginning of the next contour to the point (x,y).
394 
395         @param x    The x-coordinate of the start of a new contour
396         @param y    The y-coordinate of the start of a new contour
397     */
398     void moveTo(SkScalar x, SkScalar y);
399 
400     /** Set the beginning of the next contour to the point
401 
402         @param p    The start of a new contour
403     */
moveTo(const SkPoint & p)404     void moveTo(const SkPoint& p) {
405         this->moveTo(p.fX, p.fY);
406     }
407 
408     /** Set the beginning of the next contour relative to the last point on the
409         previous contour. If there is no previous contour, this is treated the
410         same as moveTo().
411 
412         @param dx   The amount to add to the x-coordinate of the end of the
413                     previous contour, to specify the start of a new contour
414         @param dy   The amount to add to the y-coordinate of the end of the
415                     previous contour, to specify the start of a new contour
416     */
417     void rMoveTo(SkScalar dx, SkScalar dy);
418 
419     /** Add a line from the last point to the specified point (x,y). If no
420         moveTo() call has been made for this contour, the first point is
421         automatically set to (0,0).
422 
423         @param x    The x-coordinate of the end of a line
424         @param y    The y-coordinate of the end of a line
425     */
426     void lineTo(SkScalar x, SkScalar y);
427 
428     /** Add a line from the last point to the specified point. If no moveTo()
429         call has been made for this contour, the first point is automatically
430         set to (0,0).
431 
432         @param p    The end of a line
433     */
lineTo(const SkPoint & p)434     void lineTo(const SkPoint& p) {
435         this->lineTo(p.fX, p.fY);
436     }
437 
438     /** Same as lineTo, but the coordinates are considered relative to the last
439         point on this contour. If there is no previous point, then a moveTo(0,0)
440         is inserted automatically.
441 
442         @param dx   The amount to add to the x-coordinate of the previous point
443                     on this contour, to specify a line
444         @param dy   The amount to add to the y-coordinate of the previous point
445                     on this contour, to specify a line
446     */
447     void rLineTo(SkScalar dx, SkScalar dy);
448 
449     /** Add a quadratic bezier from the last point, approaching control point
450         (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
451         this contour, the first point is automatically set to (0,0).
452 
453         @param x1   The x-coordinate of the control point on a quadratic curve
454         @param y1   The y-coordinate of the control point on a quadratic curve
455         @param x2   The x-coordinate of the end point on a quadratic curve
456         @param y2   The y-coordinate of the end point on a quadratic curve
457     */
458     void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
459 
460     /** Add a quadratic bezier from the last point, approaching control point
461         p1, and ending at p2. If no moveTo() call has been made for this
462         contour, the first point is automatically set to (0,0).
463 
464         @param p1   The control point on a quadratic curve
465         @param p2   The end point on a quadratic curve
466     */
quadTo(const SkPoint & p1,const SkPoint & p2)467     void quadTo(const SkPoint& p1, const SkPoint& p2) {
468         this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
469     }
470 
471     /** Same as quadTo, but the coordinates are considered relative to the last
472         point on this contour. If there is no previous point, then a moveTo(0,0)
473         is inserted automatically.
474 
475         @param dx1   The amount to add to the x-coordinate of the last point on
476                 this contour, to specify the control point of a quadratic curve
477         @param dy1   The amount to add to the y-coordinate of the last point on
478                 this contour, to specify the control point of a quadratic curve
479         @param dx2   The amount to add to the x-coordinate of the last point on
480                      this contour, to specify the end point of a quadratic curve
481         @param dy2   The amount to add to the y-coordinate of the last point on
482                      this contour, to specify the end point of a quadratic curve
483     */
484     void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
485 
486     void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
487                  SkScalar w);
conicTo(const SkPoint & p1,const SkPoint & p2,SkScalar w)488     void conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) {
489         this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w);
490     }
491     void rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
492                   SkScalar w);
493 
494     /** Add a cubic bezier from the last point, approaching control points
495         (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
496         made for this contour, the first point is automatically set to (0,0).
497 
498         @param x1   The x-coordinate of the 1st control point on a cubic curve
499         @param y1   The y-coordinate of the 1st control point on a cubic curve
500         @param x2   The x-coordinate of the 2nd control point on a cubic curve
501         @param y2   The y-coordinate of the 2nd control point on a cubic curve
502         @param x3   The x-coordinate of the end point on a cubic curve
503         @param y3   The y-coordinate of the end point on a cubic curve
504     */
505     void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
506                  SkScalar x3, SkScalar y3);
507 
508     /** Add a cubic bezier from the last point, approaching control points p1
509         and p2, and ending at p3. If no moveTo() call has been made for this
510         contour, the first point is automatically set to (0,0).
511 
512         @param p1   The 1st control point on a cubic curve
513         @param p2   The 2nd control point on a cubic curve
514         @param p3   The end point on a cubic curve
515     */
cubicTo(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3)516     void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
517         this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
518     }
519 
520     /** Same as cubicTo, but the coordinates are considered relative to the
521         current point on this contour. If there is no previous point, then a
522         moveTo(0,0) is inserted automatically.
523 
524         @param dx1   The amount to add to the x-coordinate of the last point on
525                 this contour, to specify the 1st control point of a cubic curve
526         @param dy1   The amount to add to the y-coordinate of the last point on
527                 this contour, to specify the 1st control point of a cubic curve
528         @param dx2   The amount to add to the x-coordinate of the last point on
529                 this contour, to specify the 2nd control point of a cubic curve
530         @param dy2   The amount to add to the y-coordinate of the last point on
531                 this contour, to specify the 2nd control point of a cubic curve
532         @param dx3   The amount to add to the x-coordinate of the last point on
533                      this contour, to specify the end point of a cubic curve
534         @param dy3   The amount to add to the y-coordinate of the last point on
535                      this contour, to specify the end point of a cubic curve
536     */
537     void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
538                   SkScalar x3, SkScalar y3);
539 
540     /**
541      *  Append the specified arc to the path. If the start of the arc is different from the path's
542      *  current last point, then an automatic lineTo() is added to connect the current contour
543      *  to the start of the arc. However, if the path is empty, then we call moveTo() with
544      *  the first point of the arc. The sweep angle is treated mod 360.
545      *
546      *  @param oval The bounding oval defining the shape and size of the arc
547      *  @param startAngle Starting angle (in degrees) where the arc begins
548      *  @param sweepAngle Sweep angle (in degrees) measured clockwise. This is treated mod 360.
549      *  @param forceMoveTo If true, always begin a new contour with the arc
550      */
551     void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo);
552 
553     /**
554      *  Append a line and arc to the current path. This is the same as the PostScript call "arct".
555      */
556     void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius);
557 
558     /** Append a line and arc to the current path. This is the same as the
559         PostScript call "arct".
560     */
arcTo(const SkPoint p1,const SkPoint p2,SkScalar radius)561     void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
562         this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
563     }
564 
565     enum ArcSize {
566         /** the smaller of the two possible SVG arcs. */
567         kSmall_ArcSize,
568         /** the larger of the two possible SVG arcs. */
569         kLarge_ArcSize,
570     };
571 
572     /**
573      *  Append an elliptical arc from the current point in the format used by SVG.
574      *  The center of the ellipse is computed to satisfy the constraints below.
575      *
576      *  @param rx,ry The radii in the x and y directions respectively.
577      *  @param xAxisRotate The angle in degrees relative to the x-axis.
578      *  @param largeArc Determines whether the smallest or largest arc possible
579      *         is drawn.
580      *  @param sweep Determines if the arc should be swept in an anti-clockwise or
581      *         clockwise direction. Note that this enum value is opposite the SVG
582      *         arc sweep value.
583      *  @param x,y The destination coordinates.
584      */
585     void arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
586                Direction sweep, SkScalar x, SkScalar y);
587 
arcTo(const SkPoint r,SkScalar xAxisRotate,ArcSize largeArc,Direction sweep,const SkPoint xy)588     void arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
589                const SkPoint xy) {
590         this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, sweep, xy.fX, xy.fY);
591     }
592 
593     /** Same as arcTo format used by SVG, but the destination coordinate is relative to the
594      *  last point on this contour. If there is no previous point, then a
595      *  moveTo(0,0) is inserted automatically.
596      *
597      *  @param rx,ry The radii in the x and y directions respectively.
598      *  @param xAxisRotate The angle in degrees relative to the x-axis.
599      *  @param largeArc Determines whether the smallest or largest arc possible
600      *         is drawn.
601      *  @param sweep Determines if the arc should be swept in an anti-clockwise or
602      *         clockwise direction. Note that this enum value is opposite the SVG
603      *         arc sweep value.
604      *  @param dx,dy The destination coordinates relative to the last point.
605      */
606     void rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
607                 Direction sweep, SkScalar dx, SkScalar dy);
608 
609     /** Close the current contour. If the current point is not equal to the
610         first point of the contour, a line segment is automatically added.
611     */
612     void close();
613 
614     /**
615      *  Returns whether or not a fill type is inverted
616      *
617      *  kWinding_FillType        -> false
618      *  kEvenOdd_FillType        -> false
619      *  kInverseWinding_FillType -> true
620      *  kInverseEvenOdd_FillType -> true
621      */
IsInverseFillType(FillType fill)622     static bool IsInverseFillType(FillType fill) {
623         static_assert(0 == kWinding_FillType, "fill_type_mismatch");
624         static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
625         static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
626         static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
627         return (fill & 2) != 0;
628     }
629 
630     /**
631      *  Returns the equivalent non-inverted fill type to the given fill type
632      *
633      *  kWinding_FillType        -> kWinding_FillType
634      *  kEvenOdd_FillType        -> kEvenOdd_FillType
635      *  kInverseWinding_FillType -> kWinding_FillType
636      *  kInverseEvenOdd_FillType -> kEvenOdd_FillType
637      */
ConvertToNonInverseFillType(FillType fill)638     static FillType ConvertToNonInverseFillType(FillType fill) {
639         static_assert(0 == kWinding_FillType, "fill_type_mismatch");
640         static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
641         static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
642         static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
643         return (FillType)(fill & 1);
644     }
645 
646     /**
647      *  Chop a conic into N quads, stored continguously in pts[], where
648      *  N = 1 << pow2. The amount of storage needed is (1 + 2 * N)
649      */
650     static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
651                                    SkScalar w, SkPoint pts[], int pow2);
652 
653     /**
654      *  Returns true if the path specifies a rectangle.
655      *
656      *  If this returns false, then all output parameters are ignored, and left
657      *  unchanged. If this returns true, then each of the output parameters
658      *  are checked for NULL. If they are not, they return their value.
659      *
660      *  @param rect If not null, set to the bounds of the rectangle.
661      *              Note : this bounds may be smaller than the path's bounds, since it is just
662      *              the bounds of the "drawable" parts of the path. e.g. a trailing MoveTo would
663      *              be ignored in this rect, but not by the path's bounds
664      *  @param isClosed If not null, set to true if the path is closed
665      *  @param direction If not null, set to the rectangle's direction
666      *  @return true if the path specifies a rectangle
667      */
668     bool isRect(SkRect* rect, bool* isClosed = NULL, Direction* direction = NULL) const;
669 
670     /** Returns true if the path specifies a pair of nested rectangles, or would draw a
671         pair of nested rectangles when filled. If so, and if
672         rect is not null, set rect[0] to the outer rectangle and rect[1] to the inner
673         rectangle. If so, and dirs is not null, set dirs[0] to the direction of
674         the outer rectangle and dirs[1] to the direction of the inner rectangle. If
675         the path does not specify a pair of nested rectangles, return
676         false and ignore rect and dirs.
677 
678         @param rect If not null, returns the path as a pair of nested rectangles
679         @param dirs If not null, returns the direction of the rects
680         @return true if the path describes a pair of nested rectangles
681     */
682     bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = NULL) const;
683 
684     /**
685      *  Add a closed rectangle contour to the path
686      *  @param rect The rectangle to add as a closed contour to the path
687      *  @param dir  The direction to wind the rectangle's contour.
688      *
689      *  Note: the contour initial point index is 0 (as defined below).
690      */
691     void addRect(const SkRect& rect, Direction dir = kCW_Direction);
692 
693     /**
694      *  Add a closed rectangle contour to the path
695      *  @param rect  The rectangle to add as a closed contour to the path
696      *  @param dir   The direction to wind the rectangle's contour.
697      *  @param start Initial point of the contour (initial moveTo), expressed as
698      *               a corner index, starting in the upper-left position, clock-wise:
699      *
700      *  0         1
701      *   *-------*
702      *   |       |
703      *   *-------*
704      *  3         2
705      */
706     void addRect(const SkRect& rect, Direction dir, unsigned start);
707 
708     /**
709      *  Add a closed rectangle contour to the path
710      *
711      *  @param left     The left side of a rectangle to add as a closed contour
712      *                  to the path
713      *  @param top      The top of a rectangle to add as a closed contour to the
714      *                  path
715      *  @param right    The right side of a rectangle to add as a closed contour
716      *                  to the path
717      *  @param bottom   The bottom of a rectangle to add as a closed contour to
718      *                  the path
719      *  @param dir  The direction to wind the rectangle's contour.
720      *
721      *  Note: the contour initial point index is 0 (as defined above).
722      */
723     void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
724                  Direction dir = kCW_Direction);
725 
726     /**
727      *  Add a closed oval contour to the path
728      *
729      *  @param oval The bounding oval to add as a closed contour to the path
730      *  @param dir  The direction to wind the oval's contour.
731      *
732      *  Note: the contour initial point index is 1 (as defined below).
733      */
734     void addOval(const SkRect& oval, Direction dir = kCW_Direction);
735 
736     /**
737      *  Add a closed oval contour to the path
738      *
739      *  @param oval  The bounding oval to add as a closed contour to the path
740      *  @param dir   The direction to wind the oval's contour.
741      *  @param start Initial point of the contour (initial moveTo), expressed
742      *               as an ellipse vertex index, starting at the top, clock-wise
743      *               (90/0/270/180deg order):
744      *
745      *        0
746      *       -*-
747      *     |     |
748      *   3 *     * 1
749      *     |     |
750      *       -*-
751      *        2
752      */
753     void addOval(const SkRect& oval, Direction dir, unsigned start);
754 
755     /**
756      *  Add a closed circle contour to the path. The circle contour begins at
757      *  the right-most point (as though 1 were passed to addOval's 'start' param).
758      *
759      *  @param x        The x-coordinate of the center of a circle to add as a
760      *                  closed contour to the path
761      *  @param y        The y-coordinate of the center of a circle to add as a
762      *                  closed contour to the path
763      *  @param radius   The radius of a circle to add as a closed contour to the
764      *                  path
765      *  @param dir  The direction to wind the circle's contour.
766      */
767     void addCircle(SkScalar x, SkScalar y, SkScalar radius,
768                    Direction dir = kCW_Direction);
769 
770     /** Add the specified arc to the path as a new contour.
771 
772         @param oval The bounds of oval used to define the size of the arc
773         @param startAngle Starting angle (in degrees) where the arc begins
774         @param sweepAngle Sweep angle (in degrees) measured clockwise
775     */
776     void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
777 
778     /**
779      *  Add a closed round-rectangle contour to the path
780      *  @param rect The bounds of a round-rectangle to add as a closed contour
781      *  @param rx   The x-radius of the rounded corners on the round-rectangle
782      *  @param ry   The y-radius of the rounded corners on the round-rectangle
783      *  @param dir  The direction to wind the rectangle's contour.
784      */
785     void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
786                       Direction dir = kCW_Direction);
787 
788     /**
789      *  Add a closed round-rectangle contour to the path. Each corner receives
790      *  two radius values [X, Y]. The corners are ordered top-left, top-right,
791      *  bottom-right, bottom-left.
792      *  @param rect The bounds of a round-rectangle to add as a closed contour
793      *  @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
794      *  @param dir  The direction to wind the rectangle's contour.
795      * Note: The radii here now go through the same constraint handling as the
796      *       SkRRect radii (i.e., either radii at a corner being 0 implies a
797      *       sqaure corner and oversized radii are proportionally scaled down).
798      */
799     void addRoundRect(const SkRect& rect, const SkScalar radii[],
800                       Direction dir = kCW_Direction);
801 
802     /**
803      *  Add an SkRRect contour to the path
804      *  @param rrect The rounded rect to add as a closed contour
805      *  @param dir   The winding direction for the new contour.
806      *
807      *  Note: the contour initial point index is either 6 (for dir == kCW_Direction)
808      *        or 7 (for dir == kCCW_Direction), as defined below.
809      *
810      */
811     void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction);
812 
813     /**
814      *  Add an SkRRect contour to the path
815      *  @param rrect The rounded rect to add as a closed contour
816      *  @param dir   The winding direction for the new contour.
817      *  @param start Initial point of the contour (initial moveTo), expressed as
818      *               an index of the radii minor/major points, ordered clock-wise:
819      *
820      *      0    1
821      *      *----*
822      *   7 *      * 2
823      *     |      |
824      *   6 *      * 3
825      *      *----*
826      *      5    4
827      */
828     void addRRect(const SkRRect& rrect, Direction dir, unsigned start);
829 
830     /**
831      *  Add a new contour made of just lines. This is just a fast version of
832      *  the following:
833      *      this->moveTo(pts[0]);
834      *      for (int i = 1; i < count; ++i) {
835      *          this->lineTo(pts[i]);
836      *      }
837      *      if (close) {
838      *          this->close();
839      *      }
840      */
841     void addPoly(const SkPoint pts[], int count, bool close);
842 
843     enum AddPathMode {
844         /** Source path contours are added as new contours.
845         */
846         kAppend_AddPathMode,
847         /** Path is added by extending the last contour of the destination path
848             with the first contour of the source path. If the last contour of
849             the destination path is closed, then it will not be extended.
850             Instead, the start of source path will be extended by a straight
851             line to the end point of the destination path.
852         */
853         kExtend_AddPathMode
854     };
855 
856     /** Add a copy of src to the path, offset by (dx,dy)
857         @param src  The path to add as a new contour
858         @param dx   The amount to translate the path in X as it is added
859         @param dx   The amount to translate the path in Y as it is added
860     */
861     void addPath(const SkPath& src, SkScalar dx, SkScalar dy,
862                  AddPathMode mode = kAppend_AddPathMode);
863 
864     /** Add a copy of src to the path
865     */
866     void addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) {
867         SkMatrix m;
868         m.reset();
869         this->addPath(src, m, mode);
870     }
871 
872     /** Add a copy of src to the path, transformed by matrix
873         @param src  The path to add as a new contour
874         @param matrix  Transform applied to src
875         @param mode  Determines how path is added
876     */
877     void addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode);
878 
879     /**
880      *  Same as addPath(), but reverses the src input
881      */
882     void reverseAddPath(const SkPath& src);
883 
884     /** Offset the path by (dx,dy), returning true on success
885 
886         @param dx   The amount in the X direction to offset the entire path
887         @param dy   The amount in the Y direction to offset the entire path
888         @param dst  The translated path is written here
889     */
890     void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
891 
892     /** Offset the path by (dx,dy), returning true on success
893 
894         @param dx   The amount in the X direction to offset the entire path
895         @param dy   The amount in the Y direction to offset the entire path
896     */
offset(SkScalar dx,SkScalar dy)897     void offset(SkScalar dx, SkScalar dy) {
898         this->offset(dx, dy, this);
899     }
900 
901     /** Transform the points in this path by matrix, and write the answer into
902         dst.
903 
904         @param matrix   The matrix to apply to the path
905         @param dst      The transformed path is written here
906     */
907     void transform(const SkMatrix& matrix, SkPath* dst) const;
908 
909     /** Transform the points in this path by matrix
910 
911         @param matrix The matrix to apply to the path
912     */
transform(const SkMatrix & matrix)913     void transform(const SkMatrix& matrix) {
914         this->transform(matrix, this);
915     }
916 
917     /** Return the last point on the path. If no points have been added, (0,0)
918         is returned. If there are no points, this returns false, otherwise it
919         returns true.
920 
921         @param lastPt   The last point on the path is returned here
922     */
923     bool getLastPt(SkPoint* lastPt) const;
924 
925     /** Set the last point on the path. If no points have been added,
926         moveTo(x,y) is automatically called.
927 
928         @param x    The new x-coordinate for the last point
929         @param y    The new y-coordinate for the last point
930     */
931     void setLastPt(SkScalar x, SkScalar y);
932 
933     /** Set the last point on the path. If no points have been added, moveTo(p)
934         is automatically called.
935 
936         @param p    The new location for the last point
937     */
setLastPt(const SkPoint & p)938     void setLastPt(const SkPoint& p) {
939         this->setLastPt(p.fX, p.fY);
940     }
941 
942     enum SegmentMask {
943         kLine_SegmentMask   = 1 << 0,
944         kQuad_SegmentMask   = 1 << 1,
945         kConic_SegmentMask  = 1 << 2,
946         kCubic_SegmentMask  = 1 << 3,
947     };
948 
949     /**
950      *  Returns a mask, where each bit corresponding to a SegmentMask is
951      *  set if the path contains 1 or more segments of that type.
952      *  Returns 0 for an empty path (no segments).
953      */
getSegmentMasks()954     uint32_t getSegmentMasks() const { return fPathRef->getSegmentMasks(); }
955 
956     enum Verb {
957         kMove_Verb,     //!< iter.next returns 1 point
958         kLine_Verb,     //!< iter.next returns 2 points
959         kQuad_Verb,     //!< iter.next returns 3 points
960         kConic_Verb,    //!< iter.next returns 3 points + iter.conicWeight()
961         kCubic_Verb,    //!< iter.next returns 4 points
962         kClose_Verb,    //!< iter.next returns 0 points
963         kDone_Verb,     //!< iter.next returns 0 points
964     };
965 
966     /** Iterate through all of the segments (lines, quadratics, cubics) of
967         each contours in a path.
968 
969         The iterator cleans up the segments along the way, removing degenerate
970         segments and adding close verbs where necessary. When the forceClose
971         argument is provided, each contour (as defined by a new starting
972         move command) will be completed with a close verb regardless of the
973         contour's contents.
974     */
975     class SK_API Iter {
976     public:
977         Iter();
978         Iter(const SkPath&, bool forceClose);
979 
980         void setPath(const SkPath&, bool forceClose);
981 
982         /** Return the next verb in this iteration of the path. When all
983             segments have been visited, return kDone_Verb.
984 
985             @param  pts The points representing the current verb and/or segment
986             @param doConsumeDegerates If true, first scan for segments that are
987                    deemed degenerate (too short) and skip those.
988             @param exact if doConsumeDegenerates is true and exact is true, skip only
989                    degenerate elements with lengths exactly equal to zero. If exact
990                    is false, skip degenerate elements with lengths close to zero. If
991                    doConsumeDegenerates is false, exact has no effect.
992             @return The verb for the current segment
993         */
994         Verb next(SkPoint pts[4], bool doConsumeDegerates = true, bool exact = false) {
995             if (doConsumeDegerates) {
996                 this->consumeDegenerateSegments(exact);
997             }
998             return this->doNext(pts);
999         }
1000 
1001         /**
1002          *  Return the weight for the current conic. Only valid if the current
1003          *  segment return by next() was a conic.
1004          */
conicWeight()1005         SkScalar conicWeight() const { return *fConicWeights; }
1006 
1007         /** If next() returns kLine_Verb, then this query returns true if the
1008             line was the result of a close() command (i.e. the end point is the
1009             initial moveto for this contour). If next() returned a different
1010             verb, this returns an undefined value.
1011 
1012             @return If the last call to next() returned kLine_Verb, return true
1013                     if it was the result of an explicit close command.
1014         */
isCloseLine()1015         bool isCloseLine() const { return SkToBool(fCloseLine); }
1016 
1017         /** Returns true if the current contour is closed (has a kClose_Verb)
1018             @return true if the current contour is closed (has a kClose_Verb)
1019         */
1020         bool isClosedContour() const;
1021 
1022     private:
1023         const SkPoint*  fPts;
1024         const uint8_t*  fVerbs;
1025         const uint8_t*  fVerbStop;
1026         const SkScalar* fConicWeights;
1027         SkPoint         fMoveTo;
1028         SkPoint         fLastPt;
1029         SkBool8         fForceClose;
1030         SkBool8         fNeedClose;
1031         SkBool8         fCloseLine;
1032         SkBool8         fSegmentState;
1033 
1034         inline const SkPoint& cons_moveTo();
1035         Verb autoClose(SkPoint pts[2]);
1036         void consumeDegenerateSegments(bool exact);
1037         Verb doNext(SkPoint pts[4]);
1038     };
1039 
1040     /** Iterate through the verbs in the path, providing the associated points.
1041     */
1042     class SK_API RawIter {
1043     public:
RawIter()1044         RawIter() {}
RawIter(const SkPath & path)1045         RawIter(const SkPath& path) {
1046             setPath(path);
1047         }
1048 
setPath(const SkPath & path)1049         void setPath(const SkPath& path) {
1050             fRawIter.setPathRef(*path.fPathRef.get());
1051         }
1052 
1053         /** Return the next verb in this iteration of the path. When all
1054             segments have been visited, return kDone_Verb.
1055 
1056             @param  pts The points representing the current verb and/or segment
1057                         This must not be NULL.
1058             @return The verb for the current segment
1059         */
next(SkPoint pts[4])1060         Verb next(SkPoint pts[4]) {
1061             return (Verb) fRawIter.next(pts);
1062         }
1063 
1064         /** Return what the next verb will be, but do not visit the next segment.
1065 
1066             @return The verb for the next segment
1067         */
peek()1068         Verb peek() const {
1069             return (Verb) fRawIter.peek();
1070         }
1071 
conicWeight()1072         SkScalar conicWeight() const {
1073             return fRawIter.conicWeight();
1074         }
1075 
1076     private:
1077         SkPathRef::Iter fRawIter;
1078         friend class SkPath;
1079     };
1080 
1081     /**
1082      *  Returns true if the point { x, y } is contained by the path, taking into
1083      *  account the FillType.
1084      */
1085     bool contains(SkScalar x, SkScalar y) const;
1086 
1087     void dump(SkWStream* , bool forceClose, bool dumpAsHex) const;
1088     void dump() const;
1089     void dumpHex() const;
1090 
1091     /**
1092      *  Write the path to the buffer, and return the number of bytes written.
1093      *  If buffer is NULL, it still returns the number of bytes.
1094      */
1095     size_t writeToMemory(void* buffer) const;
1096     /**
1097      * Initializes the path from the buffer
1098      *
1099      * @param buffer Memory to read from
1100      * @param length Amount of memory available in the buffer
1101      * @return number of bytes read (must be a multiple of 4) or
1102      *         0 if there was not enough memory available
1103      */
1104     size_t readFromMemory(const void* buffer, size_t length);
1105 
1106     /** Returns a non-zero, globally unique value corresponding to the set of verbs
1107         and points in the path (but not the fill type [except on Android skbug.com/1762]).
1108         Each time the path is modified, a different generation ID will be returned.
1109     */
1110     uint32_t getGenerationID() const;
1111 
1112 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
1113     static const int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762)
1114 #else
1115     static const int kPathRefGenIDBitCnt = 32;
1116 #endif
1117 
1118     SkDEBUGCODE(void validate() const;)
1119     SkDEBUGCODE(void experimentalValidateRef() const { fPathRef->validate(); } )
1120 
1121 private:
1122     enum SerializationOffsets {
1123         // 1 free bit at 29
1124         kUnused1_SerializationShift = 28,    // 1 free bit
1125         kDirection_SerializationShift = 26, // requires 2 bits
1126         kIsVolatile_SerializationShift = 25, // requires 1 bit
1127         // 1 free bit at 24
1128         kConvexity_SerializationShift = 16, // requires 8 bits
1129         kFillType_SerializationShift = 8,   // requires 8 bits
1130         // low-8-bits are version
1131     };
1132 
1133     enum SerializationVersions {
1134         kPathPrivFirstDirection_Version = 1,
1135         kPathPrivLastMoveToIndex_Version = 2,
1136         kCurrent_Version = 2
1137     };
1138 
1139     sk_sp<SkPathRef>                                   fPathRef;
1140     int                                                fLastMoveToIndex;
1141     uint8_t                                            fFillType;
1142     mutable uint8_t                                    fConvexity;
1143     mutable SkAtomic<uint8_t, sk_memory_order_relaxed> fFirstDirection;// SkPathPriv::FirstDirection
1144     SkBool8                                            fIsVolatile;
1145 
1146     /** Resets all fields other than fPathRef to their initial 'empty' values.
1147      *  Assumes the caller has already emptied fPathRef.
1148      *  On Android increments fGenerationID without reseting it.
1149      */
1150     void resetFields();
1151 
1152     /** Sets all fields other than fPathRef to the values in 'that'.
1153      *  Assumes the caller has already set fPathRef.
1154      *  Doesn't change fGenerationID or fSourcePath on Android.
1155      */
1156     void copyFields(const SkPath& that);
1157 
1158     friend class Iter;
1159     friend class SkPathPriv;
1160     friend class SkPathStroker;
1161 
1162     /*  Append, in reverse order, the first contour of path, ignoring path's
1163         last point. If no moveTo() call has been made for this contour, the
1164         first point is automatically set to (0,0).
1165     */
1166     void reversePathTo(const SkPath&);
1167 
1168     // called before we add points for lineTo, quadTo, cubicTo, checking to see
1169     // if we need to inject a leading moveTo first
1170     //
1171     //  SkPath path; path.lineTo(...);   <--- need a leading moveTo(0, 0)
1172     // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo)
1173     //
1174     inline void injectMoveToIfNeeded();
1175 
1176     inline bool hasOnlyMoveTos() const;
1177 
1178     Convexity internalGetConvexity() const;
1179 
1180     bool isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts,
1181                        bool* isClosed, Direction* direction) const;
1182 
1183     // called by stroker to see if all points are equal and worthy of a cap
1184     // equivalent to a short-circuit version of getBounds().isEmpty()
1185     bool isZeroLength() const;
1186 
1187     /** Returns if the path can return a bound at no cost (true) or will have to
1188         perform some computation (false).
1189      */
hasComputedBounds()1190     bool hasComputedBounds() const {
1191         SkDEBUGCODE(this->validate();)
1192         return fPathRef->hasComputedBounds();
1193     }
1194 
1195 
1196     // 'rect' needs to be sorted
setBounds(const SkRect & rect)1197     void setBounds(const SkRect& rect) {
1198         SkPathRef::Editor ed(&fPathRef);
1199 
1200         ed.setBounds(rect);
1201     }
1202 
1203     void setPt(int index, SkScalar x, SkScalar y);
1204 
1205     friend class SkAutoPathBoundsUpdate;
1206     friend class SkAutoDisableOvalCheck;
1207     friend class SkAutoDisableDirectionCheck;
1208     friend class SkPathWriter;
1209     friend class SkOpBuilder;
1210     friend class SkBench_AddPathTest; // perf test reversePathTo
1211     friend class PathTest_Private; // unit test reversePathTo
1212     friend class ForceIsRRect_Private; // unit test isRRect
1213 };
1214 
1215 #endif
1216