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