• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SkPath_DEFINED
18 #define SkPath_DEFINED
19 
20 #include "SkMatrix.h"
21 #include "SkTDArray.h"
22 
23 class SkFlattenableReadBuffer;
24 class SkFlattenableWriteBuffer;
25 class SkAutoPathBoundsUpdate;
26 class SkString;
27 
28 /** \class SkPath
29 
30     The SkPath class encapsulates compound (multiple contour) geometric paths
31     consisting of straight line segments, quadratic curves, and cubic curves.
32 */
33 class SkPath {
34 public:
35     SkPath();
36     SkPath(const SkPath&);
37     ~SkPath();
38 
39     SkPath& operator=(const SkPath&);
40 
41     friend bool operator==(const SkPath&, const SkPath&);
42     friend bool operator!=(const SkPath& a, const SkPath& b) {
43         return !(a == b);
44     }
45 
46     enum FillType {
47         /** Specifies that "inside" is computed by a non-zero sum of signed
48             edge crossings
49         */
50         kWinding_FillType,
51         /** Specifies that "inside" is computed by an odd number of edge
52             crossings
53         */
54         kEvenOdd_FillType,
55         /** Same as Winding, but draws outside of the path, rather than inside
56         */
57         kInverseWinding_FillType,
58         /** Same as EvenOdd, but draws outside of the path, rather than inside
59          */
60         kInverseEvenOdd_FillType
61     };
62 
63     /** Return the path's fill type. This is used to define how "inside" is
64         computed. The default value is kWinding_FillType.
65 
66         @return the path's fill type
67     */
getFillType()68     FillType getFillType() const { return (FillType)fFillType; }
69 
70     /** Set the path's fill type. This is used to define how "inside" is
71         computed. The default value is kWinding_FillType.
72 
73         @param ft The new fill type for this path
74     */
setFillType(FillType ft)75     void setFillType(FillType ft) { fFillType = SkToU8(ft); }
76 
77     /** Returns true if the filltype is one of the Inverse variants */
isInverseFillType()78     bool isInverseFillType() const { return (fFillType & 2) != 0; }
79 
80     /** Toggle between inverse and normal filltypes. This reverse the return
81         value of isInverseFillType()
82     */
toggleInverseFillType()83     void toggleInverseFillType() { fFillType ^= 2; }
84 
85     /** Returns true if the path is flagged as being convex. This is not a
86         confirmed by any analysis, it is just the value set earlier.
87      */
isConvex()88     bool isConvex() const { return fIsConvex != 0; }
89 
90     /** Set the isConvex flag to true or false. Convex paths may draw faster if
91         this flag is set, though setting this to true on a path that is in fact
92         not convex can give undefined results when drawn. Paths default to
93         isConvex == false
94      */
setIsConvex(bool isConvex)95     void setIsConvex(bool isConvex) { fIsConvex = (isConvex != 0); }
96 
97     /** Clear any lines and curves from the path, making it empty. This frees up
98         internal storage associated with those segments.
99         This does NOT change the fill-type setting nor isConvex
100     */
101     void reset();
102 
103     /** Similar to reset(), in that all lines and curves are removed from the
104         path. However, any internal storage for those lines/curves is retained,
105         making reuse of the path potentially faster.
106         This does NOT change the fill-type setting nor isConvex
107     */
108     void rewind();
109 
110     /** Returns true if the path is empty (contains no lines or curves)
111 
112         @return true if the path is empty (contains no lines or curves)
113     */
114     bool isEmpty() const;
115 
116     /** Returns true if the path specifies a rectangle. If so, and if rect is
117         not null, set rect to the bounds of the path. If the path does not
118         specify a rectangle, return false and ignore rect.
119 
120         @param rect If not null, returns the bounds of the path if it specifies
121                     a rectangle
122         @return true if the path specifies a rectangle
123     */
124     bool isRect(SkRect* rect) const;
125 
126     /** Return the number of points in the path
127      */
countPoints()128     int countPoints() const {
129         return this->getPoints(NULL, 0);
130     }
131 
132     /** Return the point at the specified index. If the index is out of range
133          (i.e. is not 0 <= index < countPoints()) then the returned coordinates
134          will be (0,0)
135      */
136     SkPoint getPoint(int index) const;
137 
138     /** Returns the number of points in the path. Up to max points are copied.
139 
140         @param points If not null, receives up to max points
141         @param max The maximum number of points to copy into points
142         @return the actual number of points in the path
143     */
144     int getPoints(SkPoint points[], int max) const;
145 
146     //! Swap contents of this and other. Guaranteed not to throw
147     void swap(SkPath& other);
148 
149     /** Returns the bounds of the path's points. If the path contains 0 or 1
150         points, the bounds is set to (0,0,0,0), and isEmpty() will return true.
151         Note: this bounds may be larger than the actual shape, since curves
152         do not extend as far as their control points.
153     */
getBounds()154     const SkRect& getBounds() const {
155         if (fBoundsIsDirty) {
156             this->computeBounds();
157         }
158         return fBounds;
159     }
160 
161     /** Calling this will, if the internal cache of the bounds is out of date,
162         update it so that subsequent calls to getBounds will be instanteous.
163         This also means that any copies or simple transformations of the path
164         will inherit the cached bounds.
165      */
updateBoundsCache()166     void updateBoundsCache() const {
167         // for now, just calling getBounds() is sufficient
168         this->getBounds();
169     }
170 
171     //  Construction methods
172 
173     /** Hint to the path to prepare for adding more points. This can allow the
174         path to more efficiently grow its storage.
175 
176         @param extraPtCount The number of extra points the path should
177                             preallocate for.
178     */
179     void incReserve(unsigned extraPtCount);
180 
181     /** Set the beginning of the next contour to the point (x,y).
182 
183         @param x    The x-coordinate of the start of a new contour
184         @param y    The y-coordinate of the start of a new contour
185     */
186     void moveTo(SkScalar x, SkScalar y);
187 
188     /** Set the beginning of the next contour to the point
189 
190         @param p    The start of a new contour
191     */
moveTo(const SkPoint & p)192     void moveTo(const SkPoint& p) {
193         this->moveTo(p.fX, p.fY);
194     }
195 
196     /** Set the beginning of the next contour relative to the last point on the
197         previous contour. If there is no previous contour, this is treated the
198         same as moveTo().
199 
200         @param dx   The amount to add to the x-coordinate of the end of the
201                     previous contour, to specify the start of a new contour
202         @param dy   The amount to add to the y-coordinate of the end of the
203                     previous contour, to specify the start of a new contour
204     */
205     void rMoveTo(SkScalar dx, SkScalar dy);
206 
207     /** Add a line from the last point to the specified point (x,y). If no
208         moveTo() call has been made for this contour, the first point is
209         automatically set to (0,0).
210 
211         @param x    The x-coordinate of the end of a line
212         @param y    The y-coordinate of the end of a line
213     */
214     void lineTo(SkScalar x, SkScalar y);
215 
216     /** Add a line from the last point to the specified point. If no moveTo()
217         call has been made for this contour, the first point is automatically
218         set to (0,0).
219 
220         @param p    The end of a line
221     */
lineTo(const SkPoint & p)222     void lineTo(const SkPoint& p) {
223         this->lineTo(p.fX, p.fY);
224     }
225 
226     /** Same as lineTo, but the coordinates are considered relative to the last
227         point on this contour. If there is no previous point, then a moveTo(0,0)
228         is inserted automatically.
229 
230         @param dx   The amount to add to the x-coordinate of the previous point
231                     on this contour, to specify a line
232         @param dy   The amount to add to the y-coordinate of the previous point
233                     on this contour, to specify a line
234     */
235     void rLineTo(SkScalar dx, SkScalar dy);
236 
237     /** Add a quadratic bezier from the last point, approaching control point
238         (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
239         this contour, the first point is automatically set to (0,0).
240 
241         @param x1   The x-coordinate of the control point on a quadratic curve
242         @param y1   The y-coordinate of the control point on a quadratic curve
243         @param x2   The x-coordinate of the end point on a quadratic curve
244         @param y2   The y-coordinate of the end point on a quadratic curve
245     */
246     void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
247 
248     /** Add a quadratic bezier from the last point, approaching control point
249         p1, and ending at p2. If no moveTo() call has been made for this
250         contour, the first point is automatically set to (0,0).
251 
252         @param p1   The control point on a quadratic curve
253         @param p2   The end point on a quadratic curve
254     */
quadTo(const SkPoint & p1,const SkPoint & p2)255     void quadTo(const SkPoint& p1, const SkPoint& p2) {
256         this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
257     }
258 
259     /** Same as quadTo, but the coordinates are considered relative to the last
260         point on this contour. If there is no previous point, then a moveTo(0,0)
261         is inserted automatically.
262 
263         @param dx1   The amount to add to the x-coordinate of the last point on
264                 this contour, to specify the control point of a quadratic curve
265         @param dy1   The amount to add to the y-coordinate of the last point on
266                 this contour, to specify the control point of a quadratic curve
267         @param dx2   The amount to add to the x-coordinate of the last point on
268                      this contour, to specify the end point of a quadratic curve
269         @param dy2   The amount to add to the y-coordinate of the last point on
270                      this contour, to specify the end point of a quadratic curve
271     */
272     void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
273 
274     /** Add a cubic bezier from the last point, approaching control points
275         (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
276         made for this contour, the first point is automatically set to (0,0).
277 
278         @param x1   The x-coordinate of the 1st control point on a cubic curve
279         @param y1   The y-coordinate of the 1st control point on a cubic curve
280         @param x2   The x-coordinate of the 2nd control point on a cubic curve
281         @param y2   The y-coordinate of the 2nd control point on a cubic curve
282         @param x3   The x-coordinate of the end point on a cubic curve
283         @param y3   The y-coordinate of the end point on a cubic curve
284     */
285     void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
286                  SkScalar x3, SkScalar y3);
287 
288     /** Add a cubic bezier from the last point, approaching control points p1
289         and p2, and ending at p3. If no moveTo() call has been made for this
290         contour, the first point is automatically set to (0,0).
291 
292         @param p1   The 1st control point on a cubic curve
293         @param p2   The 2nd control point on a cubic curve
294         @param p3   The end point on a cubic curve
295     */
cubicTo(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3)296     void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
297         this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
298     }
299 
300     /** Same as cubicTo, but the coordinates are considered relative to the
301         current point on this contour. If there is no previous point, then a
302         moveTo(0,0) is inserted automatically.
303 
304         @param dx1   The amount to add to the x-coordinate of the last point on
305                 this contour, to specify the 1st control point of a cubic curve
306         @param dy1   The amount to add to the y-coordinate of the last point on
307                 this contour, to specify the 1st control point of a cubic curve
308         @param dx2   The amount to add to the x-coordinate of the last point on
309                 this contour, to specify the 2nd control point of a cubic curve
310         @param dy2   The amount to add to the y-coordinate of the last point on
311                 this contour, to specify the 2nd control point of a cubic curve
312         @param dx3   The amount to add to the x-coordinate of the last point on
313                      this contour, to specify the end point of a cubic curve
314         @param dy3   The amount to add to the y-coordinate of the last point on
315                      this contour, to specify the end point of a cubic curve
316     */
317     void    rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
318                      SkScalar x3, SkScalar y3);
319 
320     /** Append the specified arc to the path as a new contour. If the start of
321         the path is different from the path's current last point, then an
322         automatic lineTo() is added to connect the current contour to the start
323         of the arc. However, if the path is empty, then we call moveTo() with
324         the first point of the arc. The sweep angle is treated mod 360.
325 
326         @param oval The bounding oval defining the shape and size of the arc
327         @param startAngle Starting angle (in degrees) where the arc begins
328         @param sweepAngle Sweep angle (in degrees) measured clockwise. This is
329                           treated mod 360.
330         @param forceMoveTo If true, always begin a new contour with the arc
331     */
332     void    arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
333                   bool forceMoveTo);
334 
335     /** Append a line and arc to the current path. This is the same as the
336         PostScript call "arct".
337     */
338     void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
339                SkScalar radius);
340 
341     /** Append a line and arc to the current path. This is the same as the
342         PostScript call "arct".
343     */
arcTo(const SkPoint p1,const SkPoint p2,SkScalar radius)344     void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
345         this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
346     }
347 
348     /** Close the current contour. If the current point is not equal to the
349         first point of the contour, a line segment is automatically added.
350     */
351     void close();
352 
353     enum Direction {
354         /** clockwise direction for adding closed contours */
355         kCW_Direction,
356         /** counter-clockwise direction for adding closed contours */
357         kCCW_Direction
358     };
359 
360     /** Add a closed rectangle contour to the path
361         @param rect The rectangle to add as a closed contour to the path
362         @param dir  The direction to wind the rectangle's contour
363     */
364     void    addRect(const SkRect& rect, Direction dir = kCW_Direction);
365 
366     /** Add a closed rectangle contour to the path
367 
368         @param left     The left side of a rectangle to add as a closed contour
369                         to the path
370         @param top      The top of a rectangle to add as a closed contour to the
371                         path
372         @param right    The right side of a rectangle to add as a closed contour
373                         to the path
374         @param bottom   The bottom of a rectangle to add as a closed contour to
375                         the path
376         @param dir      The direction to wind the rectangle's contour
377     */
378     void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
379                  Direction dir = kCW_Direction);
380 
381     /** Add a closed oval contour to the path
382 
383         @param oval The bounding oval to add as a closed contour to the path
384         @param dir  The direction to wind the oval's contour
385     */
386     void addOval(const SkRect& oval, Direction dir = kCW_Direction);
387 
388     /** Add a closed circle contour to the path
389 
390         @param x        The x-coordinate of the center of a circle to add as a
391                         closed contour to the path
392         @param y        The y-coordinate of the center of a circle to add as a
393                         closed contour to the path
394         @param radius   The radius of a circle to add as a closed contour to the
395                         path
396         @param dir      The direction to wind the circle's contour
397     */
398     void addCircle(SkScalar x, SkScalar y, SkScalar radius,
399                    Direction dir = kCW_Direction);
400 
401     /** Add the specified arc to the path as a new contour.
402 
403         @param oval The bounds of oval used to define the size of the arc
404         @param startAngle Starting angle (in degrees) where the arc begins
405         @param sweepAngle Sweep angle (in degrees) measured clockwise
406     */
407     void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
408 
409     /** Add a closed round-rectangle contour to the path
410         @param rect The bounds of a round-rectangle to add as a closed contour
411         @param rx   The x-radius of the rounded corners on the round-rectangle
412         @param ry   The y-radius of the rounded corners on the round-rectangle
413         @param dir  The direction to wind the round-rectangle's contour
414     */
415     void    addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
416                          Direction dir = kCW_Direction);
417 
418     /** Add a closed round-rectangle contour to the path. Each corner receives
419         two radius values [X, Y]. The corners are ordered top-left, top-right,
420         bottom-right, bottom-left.
421         @param rect The bounds of a round-rectangle to add as a closed contour
422         @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
423         @param dir  The direction to wind the round-rectangle's contour
424         */
425     void addRoundRect(const SkRect& rect, const SkScalar radii[],
426                       Direction dir = kCW_Direction);
427 
428     /** Add a copy of src to the path, offset by (dx,dy)
429         @param src  The path to add as a new contour
430         @param dx   The amount to translate the path in X as it is added
431         @param dx   The amount to translate the path in Y as it is added
432     */
433     void    addPath(const SkPath& src, SkScalar dx, SkScalar dy);
434 
435     /** Add a copy of src to the path
436     */
addPath(const SkPath & src)437     void addPath(const SkPath& src) {
438         SkMatrix m;
439         m.reset();
440         this->addPath(src, m);
441     }
442 
443     /** Add a copy of src to the path, transformed by matrix
444         @param src  The path to add as a new contour
445     */
446     void addPath(const SkPath& src, const SkMatrix& matrix);
447 
448     /** Offset the path by (dx,dy), returning true on success
449 
450         @param dx   The amount in the X direction to offset the entire path
451         @param dy   The amount in the Y direction to offset the entire path
452         @param dst  The translated path is written here
453     */
454     void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
455 
456     /** Offset the path by (dx,dy), returning true on success
457 
458         @param dx   The amount in the X direction to offset the entire path
459         @param dy   The amount in the Y direction to offset the entire path
460     */
offset(SkScalar dx,SkScalar dy)461     void offset(SkScalar dx, SkScalar dy) {
462         this->offset(dx, dy, this);
463     }
464 
465     /** Transform the points in this path by matrix, and write the answer into
466         dst.
467 
468         @param matrix   The matrix to apply to the path
469         @param dst      The transformed path is written here
470     */
471     void transform(const SkMatrix& matrix, SkPath* dst) const;
472 
473     /** Transform the points in this path by matrix
474 
475         @param matrix The matrix to apply to the path
476     */
transform(const SkMatrix & matrix)477     void transform(const SkMatrix& matrix) {
478         this->transform(matrix, this);
479     }
480 
481     /** Return the last point on the path. If no points have been added, (0,0)
482         is returned.
483 
484         @param lastPt   The last point on the path is returned here
485     */
486     void getLastPt(SkPoint* lastPt) const;
487 
488     /** Set the last point on the path. If no points have been added,
489         moveTo(x,y) is automatically called.
490 
491         @param x    The new x-coordinate for the last point
492         @param y    The new y-coordinate for the last point
493     */
494     void setLastPt(SkScalar x, SkScalar y);
495 
496     /** Set the last point on the path. If no points have been added, moveTo(p)
497         is automatically called.
498 
499         @param p    The new location for the last point
500     */
setLastPt(const SkPoint & p)501     void setLastPt(const SkPoint& p) {
502         this->setLastPt(p.fX, p.fY);
503     }
504 
505     enum Verb {
506         kMove_Verb,     //!< iter.next returns 1 point
507         kLine_Verb,     //!< iter.next returns 2 points
508         kQuad_Verb,     //!< iter.next returns 3 points
509         kCubic_Verb,    //!< iter.next returns 4 points
510         kClose_Verb,    //!< iter.next returns 1 point (the last point)
511         kDone_Verb      //!< iter.next returns 0 points
512     };
513 
514     /** Iterate through all of the segments (lines, quadratics, cubics) of
515         each contours in a path.
516     */
517     class Iter {
518     public:
519                 Iter();
520                 Iter(const SkPath&, bool forceClose);
521 
522         void setPath(const SkPath&, bool forceClose);
523 
524         /** Return the next verb in this iteration of the path. When all
525             segments have been visited, return kDone_Verb.
526 
527             @param  pts The points representing the current verb and/or segment
528             @return The verb for the current segment
529         */
530         Verb next(SkPoint pts[4]);
531 
532         /** If next() returns kLine_Verb, then this query returns true if the
533             line was the result of a close() command (i.e. the end point is the
534             initial moveto for this contour). If next() returned a different
535             verb, this returns an undefined value.
536 
537             @return If the last call to next() returned kLine_Verb, return true
538                     if it was the result of an explicit close command.
539         */
isCloseLine()540         bool isCloseLine() const { return SkToBool(fCloseLine); }
541 
542         /** Returns true if the current contour is closed (has a kClose_Verb)
543             @return true if the current contour is closed (has a kClose_Verb)
544         */
545         bool isClosedContour() const;
546 
547     private:
548         const SkPoint*  fPts;
549         const uint8_t*  fVerbs;
550         const uint8_t*  fVerbStop;
551         SkPoint         fMoveTo;
552         SkPoint         fLastPt;
553         SkBool8         fForceClose;
554         SkBool8         fNeedClose;
555         SkBool8         fNeedMoveTo;
556         SkBool8         fCloseLine;
557 
558         bool cons_moveTo(SkPoint pts[1]);
559         Verb autoClose(SkPoint pts[2]);
560     };
561 
562     void dump(bool forceClose, const char title[] = NULL) const;
563     void dump() const;
564 
565     void flatten(SkFlattenableWriteBuffer&) const;
566     void unflatten(SkFlattenableReadBuffer&);
567 
568     /** Subdivide the path so that no segment is longer that dist.
569         If bendLines is true, then turn all line segments into curves.
570         If dst == null, then the original path itself is modified (not const!)
571     */
572     void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const;
573 
574     SkDEBUGCODE(void validate() const;)
575 
576 private:
577     SkTDArray<SkPoint>  fPts;
578     SkTDArray<uint8_t>  fVerbs;
579     mutable SkRect      fBounds;
580     mutable uint8_t     fBoundsIsDirty;
581     uint8_t             fFillType;
582     uint8_t             fIsConvex;
583 
584     // called, if dirty, by getBounds()
585     void computeBounds() const;
586 
587     friend class Iter;
588     void cons_moveto();
589 
590     friend class SkPathStroker;
591     /*  Append the first contour of path, ignoring path's initial point. If no
592         moveTo() call has been made for this contour, the first point is
593         automatically set to (0,0).
594     */
595     void pathTo(const SkPath& path);
596 
597     /*  Append, in reverse order, the first contour of path, ignoring path's
598         last point. If no moveTo() call has been made for this contour, the
599         first point is automatically set to (0,0).
600     */
601     void reversePathTo(const SkPath&);
602 
603     friend const SkPoint* sk_get_path_points(const SkPath&, int index);
604     friend class SkAutoPathBoundsUpdate;
605 };
606 
607 #endif
608 
609