• 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     /** Returns the number of points in the path. Up to max points are copied.
127 
128         @param points If not null, receives up to max points
129         @param max The maximum number of points to copy into points
130         @return the actual number of points in the path
131     */
132     int getPoints(SkPoint points[], int max) const;
133 
134     //! Swap contents of this and other. Guaranteed not to throw
135     void swap(SkPath& other);
136 
137     /** Returns the bounds of the path's points. If the path contains 0 or 1
138         points, the bounds is set to (0,0,0,0), and isEmpty() will return true.
139         Note: this bounds may be larger than the actual shape, since curves
140         do not extend as far as their control points.
141     */
getBounds()142     const SkRect& getBounds() const {
143         if (fBoundsIsDirty) {
144             this->computeBounds();
145         }
146         return fBounds;
147     }
148 
149     /** Calling this will, if the internal cache of the bounds is out of date,
150         update it so that subsequent calls to getBounds will be instanteous.
151         This also means that any copies or simple transformations of the path
152         will inherit the cached bounds.
153      */
updateBoundsCache()154     void updateBoundsCache() const {
155         // for now, just calling getBounds() is sufficient
156         this->getBounds();
157     }
158 
159     //  Construction methods
160 
161     /** Hint to the path to prepare for adding more points. This can allow the
162         path to more efficiently grow its storage.
163 
164         @param extraPtCount The number of extra points the path should
165                             preallocate for.
166     */
167     void incReserve(unsigned extraPtCount);
168 
169     /** Set the beginning of the next contour to the point (x,y).
170 
171         @param x    The x-coordinate of the start of a new contour
172         @param y    The y-coordinate of the start of a new contour
173     */
174     void moveTo(SkScalar x, SkScalar y);
175 
176     /** Set the beginning of the next contour to the point
177 
178         @param p    The start of a new contour
179     */
moveTo(const SkPoint & p)180     void moveTo(const SkPoint& p) {
181         this->moveTo(p.fX, p.fY);
182     }
183 
184     /** Set the beginning of the next contour relative to the last point on the
185         previous contour. If there is no previous contour, this is treated the
186         same as moveTo().
187 
188         @param dx   The amount to add to the x-coordinate of the end of the
189                     previous contour, to specify the start of a new contour
190         @param dy   The amount to add to the y-coordinate of the end of the
191                     previous contour, to specify the start of a new contour
192     */
193     void rMoveTo(SkScalar dx, SkScalar dy);
194 
195     /** Add a line from the last point to the specified point (x,y). If no
196         moveTo() call has been made for this contour, the first point is
197         automatically set to (0,0).
198 
199         @param x    The x-coordinate of the end of a line
200         @param y    The y-coordinate of the end of a line
201     */
202     void lineTo(SkScalar x, SkScalar y);
203 
204     /** Add a line from the last point to the specified point. If no moveTo()
205         call has been made for this contour, the first point is automatically
206         set to (0,0).
207 
208         @param p    The end of a line
209     */
lineTo(const SkPoint & p)210     void lineTo(const SkPoint& p) {
211         this->lineTo(p.fX, p.fY);
212     }
213 
214     /** Same as lineTo, but the coordinates are considered relative to the last
215         point on this contour. If there is no previous point, then a moveTo(0,0)
216         is inserted automatically.
217 
218         @param dx   The amount to add to the x-coordinate of the previous point
219                     on this contour, to specify a line
220         @param dy   The amount to add to the y-coordinate of the previous point
221                     on this contour, to specify a line
222     */
223     void rLineTo(SkScalar dx, SkScalar dy);
224 
225     /** Add a quadratic bezier from the last point, approaching control point
226         (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
227         this contour, the first point is automatically set to (0,0).
228 
229         @param x1   The x-coordinate of the control point on a quadratic curve
230         @param y1   The y-coordinate of the control point on a quadratic curve
231         @param x2   The x-coordinate of the end point on a quadratic curve
232         @param y2   The y-coordinate of the end point on a quadratic curve
233     */
234     void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
235 
236     /** Add a quadratic bezier from the last point, approaching control point
237         p1, and ending at p2. If no moveTo() call has been made for this
238         contour, the first point is automatically set to (0,0).
239 
240         @param p1   The control point on a quadratic curve
241         @param p2   The end point on a quadratic curve
242     */
quadTo(const SkPoint & p1,const SkPoint & p2)243     void quadTo(const SkPoint& p1, const SkPoint& p2) {
244         this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
245     }
246 
247     /** Same as quadTo, but the coordinates are considered relative to the last
248         point on this contour. If there is no previous point, then a moveTo(0,0)
249         is inserted automatically.
250 
251         @param dx1   The amount to add to the x-coordinate of the last point on
252                 this contour, to specify the control point of a quadratic curve
253         @param dy1   The amount to add to the y-coordinate of the last point on
254                 this contour, to specify the control point of a quadratic curve
255         @param dx2   The amount to add to the x-coordinate of the last point on
256                      this contour, to specify the end point of a quadratic curve
257         @param dy2   The amount to add to the y-coordinate of the last point on
258                      this contour, to specify the end point of a quadratic curve
259     */
260     void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
261 
262     /** Add a cubic bezier from the last point, approaching control points
263         (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
264         made for this contour, the first point is automatically set to (0,0).
265 
266         @param x1   The x-coordinate of the 1st control point on a cubic curve
267         @param y1   The y-coordinate of the 1st control point on a cubic curve
268         @param x2   The x-coordinate of the 2nd control point on a cubic curve
269         @param y2   The y-coordinate of the 2nd control point on a cubic curve
270         @param x3   The x-coordinate of the end point on a cubic curve
271         @param y3   The y-coordinate of the end point on a cubic curve
272     */
273     void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
274                  SkScalar x3, SkScalar y3);
275 
276     /** Add a cubic bezier from the last point, approaching control points p1
277         and p2, and ending at p3. If no moveTo() call has been made for this
278         contour, the first point is automatically set to (0,0).
279 
280         @param p1   The 1st control point on a cubic curve
281         @param p2   The 2nd control point on a cubic curve
282         @param p3   The end point on a cubic curve
283     */
cubicTo(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3)284     void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
285         this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
286     }
287 
288     /** Same as cubicTo, but the coordinates are considered relative to the
289         current point on this contour. If there is no previous point, then a
290         moveTo(0,0) is inserted automatically.
291 
292         @param dx1   The amount to add to the x-coordinate of the last point on
293                 this contour, to specify the 1st control point of a cubic curve
294         @param dy1   The amount to add to the y-coordinate of the last point on
295                 this contour, to specify the 1st control point of a cubic curve
296         @param dx2   The amount to add to the x-coordinate of the last point on
297                 this contour, to specify the 2nd control point of a cubic curve
298         @param dy2   The amount to add to the y-coordinate of the last point on
299                 this contour, to specify the 2nd control point of a cubic curve
300         @param dx3   The amount to add to the x-coordinate of the last point on
301                      this contour, to specify the end point of a cubic curve
302         @param dy3   The amount to add to the y-coordinate of the last point on
303                      this contour, to specify the end point of a cubic curve
304     */
305     void    rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
306                      SkScalar x3, SkScalar y3);
307 
308     /** Append the specified arc to the path as a new contour. If the start of
309         the path is different from the path's current last point, then an
310         automatic lineTo() is added to connect the current contour to the start
311         of the arc. However, if the path is empty, then we call moveTo() with
312         the first point of the arc. The sweep angle is treated mod 360.
313 
314         @param oval The bounding oval defining the shape and size of the arc
315         @param startAngle Starting angle (in degrees) where the arc begins
316         @param sweepAngle Sweep angle (in degrees) measured clockwise. This is
317                           treated mod 360.
318         @param forceMoveTo If true, always begin a new contour with the arc
319     */
320     void    arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
321                   bool forceMoveTo);
322 
323     /** Append a line and arc to the current path. This is the same as the
324         PostScript call "arct".
325     */
326     void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
327                SkScalar radius);
328 
329     /** Append a line and arc to the current path. This is the same as the
330         PostScript call "arct".
331     */
arcTo(const SkPoint p1,const SkPoint p2,SkScalar radius)332     void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
333         this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
334     }
335 
336     /** Close the current contour. If the current point is not equal to the
337         first point of the contour, a line segment is automatically added.
338     */
339     void close();
340 
341     enum Direction {
342         /** clockwise direction for adding closed contours */
343         kCW_Direction,
344         /** counter-clockwise direction for adding closed contours */
345         kCCW_Direction
346     };
347 
348     /** Add a closed rectangle contour to the path
349         @param rect The rectangle to add as a closed contour to the path
350         @param dir  The direction to wind the rectangle's contour
351     */
352     void    addRect(const SkRect& rect, Direction dir = kCW_Direction);
353 
354     /** Add a closed rectangle contour to the path
355 
356         @param left     The left side of a rectangle to add as a closed contour
357                         to the path
358         @param top      The top of a rectangle to add as a closed contour to the
359                         path
360         @param right    The right side of a rectangle to add as a closed contour
361                         to the path
362         @param bottom   The bottom of a rectangle to add as a closed contour to
363                         the path
364         @param dir      The direction to wind the rectangle's contour
365     */
366     void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
367                  Direction dir = kCW_Direction);
368 
369     /** Add a closed oval contour to the path
370 
371         @param oval The bounding oval to add as a closed contour to the path
372         @param dir  The direction to wind the oval's contour
373     */
374     void addOval(const SkRect& oval, Direction dir = kCW_Direction);
375 
376     /** Add a closed circle contour to the path
377 
378         @param x        The x-coordinate of the center of a circle to add as a
379                         closed contour to the path
380         @param y        The y-coordinate of the center of a circle to add as a
381                         closed contour to the path
382         @param radius   The radius of a circle to add as a closed contour to the
383                         path
384         @param dir      The direction to wind the circle's contour
385     */
386     void addCircle(SkScalar x, SkScalar y, SkScalar radius,
387                    Direction dir = kCW_Direction);
388 
389     /** Add the specified arc to the path as a new contour.
390 
391         @param oval The bounds of oval used to define the size of the arc
392         @param startAngle Starting angle (in degrees) where the arc begins
393         @param sweepAngle Sweep angle (in degrees) measured clockwise
394     */
395     void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
396 
397     /** Add a closed round-rectangle contour to the path
398         @param rect The bounds of a round-rectangle to add as a closed contour
399         @param rx   The x-radius of the rounded corners on the round-rectangle
400         @param ry   The y-radius of the rounded corners on the round-rectangle
401         @param dir  The direction to wind the round-rectangle's contour
402     */
403     void    addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
404                          Direction dir = kCW_Direction);
405 
406     /** Add a closed round-rectangle contour to the path. Each corner receives
407         two radius values [X, Y]. The corners are ordered top-left, top-right,
408         bottom-right, bottom-left.
409         @param rect The bounds of a round-rectangle to add as a closed contour
410         @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
411         @param dir  The direction to wind the round-rectangle's contour
412         */
413     void addRoundRect(const SkRect& rect, const SkScalar radii[],
414                       Direction dir = kCW_Direction);
415 
416     /** Add a copy of src to the path, offset by (dx,dy)
417         @param src  The path to add as a new contour
418         @param dx   The amount to translate the path in X as it is added
419         @param dx   The amount to translate the path in Y as it is added
420     */
421     void    addPath(const SkPath& src, SkScalar dx, SkScalar dy);
422 
423     /** Add a copy of src to the path
424     */
addPath(const SkPath & src)425     void addPath(const SkPath& src) {
426         SkMatrix m;
427         m.reset();
428         this->addPath(src, m);
429     }
430 
431     /** Add a copy of src to the path, transformed by matrix
432         @param src  The path to add as a new contour
433     */
434     void addPath(const SkPath& src, const SkMatrix& matrix);
435 
436     /** Offset the path by (dx,dy), returning true on success
437 
438         @param dx   The amount in the X direction to offset the entire path
439         @param dy   The amount in the Y direction to offset the entire path
440         @param dst  The translated path is written here
441     */
442     void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
443 
444     /** Offset the path by (dx,dy), returning true on success
445 
446         @param dx   The amount in the X direction to offset the entire path
447         @param dy   The amount in the Y direction to offset the entire path
448     */
offset(SkScalar dx,SkScalar dy)449     void offset(SkScalar dx, SkScalar dy) {
450         this->offset(dx, dy, this);
451     }
452 
453     /** Transform the points in this path by matrix, and write the answer into
454         dst.
455 
456         @param matrix   The matrix to apply to the path
457         @param dst      The transformed path is written here
458     */
459     void transform(const SkMatrix& matrix, SkPath* dst) const;
460 
461     /** Transform the points in this path by matrix
462 
463         @param matrix The matrix to apply to the path
464     */
transform(const SkMatrix & matrix)465     void transform(const SkMatrix& matrix) {
466         this->transform(matrix, this);
467     }
468 
469     /** Return the last point on the path. If no points have been added, (0,0)
470         is returned.
471 
472         @param lastPt   The last point on the path is returned here
473     */
474     void getLastPt(SkPoint* lastPt) const;
475 
476     /** Set the last point on the path. If no points have been added,
477         moveTo(x,y) is automatically called.
478 
479         @param x    The new x-coordinate for the last point
480         @param y    The new y-coordinate for the last point
481     */
482     void setLastPt(SkScalar x, SkScalar y);
483 
484     /** Set the last point on the path. If no points have been added, moveTo(p)
485         is automatically called.
486 
487         @param p    The new location for the last point
488     */
setLastPt(const SkPoint & p)489     void setLastPt(const SkPoint& p) {
490         this->setLastPt(p.fX, p.fY);
491     }
492 
493     enum Verb {
494         kMove_Verb,     //!< iter.next returns 1 point
495         kLine_Verb,     //!< iter.next returns 2 points
496         kQuad_Verb,     //!< iter.next returns 3 points
497         kCubic_Verb,    //!< iter.next returns 4 points
498         kClose_Verb,    //!< iter.next returns 1 point (the last point)
499         kDone_Verb      //!< iter.next returns 0 points
500     };
501 
502     /** Iterate through all of the segments (lines, quadratics, cubics) of
503         each contours in a path.
504     */
505     class Iter {
506     public:
507                 Iter();
508                 Iter(const SkPath&, bool forceClose);
509 
510         void setPath(const SkPath&, bool forceClose);
511 
512         /** Return the next verb in this iteration of the path. When all
513             segments have been visited, return kDone_Verb.
514 
515             @param  pts The points representing the current verb and/or segment
516             @return The verb for the current segment
517         */
518         Verb next(SkPoint pts[4]);
519 
520         /** If next() returns kLine_Verb, then this query returns true if the
521             line was the result of a close() command (i.e. the end point is the
522             initial moveto for this contour). If next() returned a different
523             verb, this returns an undefined value.
524 
525             @return If the last call to next() returned kLine_Verb, return true
526                     if it was the result of an explicit close command.
527         */
isCloseLine()528         bool isCloseLine() const { return SkToBool(fCloseLine); }
529 
530         /** Returns true if the current contour is closed (has a kClose_Verb)
531             @return true if the current contour is closed (has a kClose_Verb)
532         */
533         bool isClosedContour() const;
534 
535     private:
536         const SkPoint*  fPts;
537         const uint8_t*  fVerbs;
538         const uint8_t*  fVerbStop;
539         SkPoint         fMoveTo;
540         SkPoint         fLastPt;
541         SkBool8         fForceClose;
542         SkBool8         fNeedClose;
543         SkBool8         fNeedMoveTo;
544         SkBool8         fCloseLine;
545 
546         bool cons_moveTo(SkPoint pts[1]);
547         Verb autoClose(SkPoint pts[2]);
548     };
549 
550 #ifdef SK_DEBUG
551   /** @cond UNIT_TEST */
552     void dump(bool forceClose, const char title[] = NULL) const;
553   /** @endcond */
554 #endif
555 
556     void flatten(SkFlattenableWriteBuffer&) const;
557     void unflatten(SkFlattenableReadBuffer&);
558 
559     /** Subdivide the path so that no segment is longer that dist.
560         If bendLines is true, then turn all line segments into curves.
561         If dst == null, then the original path itself is modified (not const!)
562     */
563     void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const;
564 
565     SkDEBUGCODE(void validate() const;)
566 
567 private:
568     SkTDArray<SkPoint>  fPts;
569     SkTDArray<uint8_t>  fVerbs;
570     mutable SkRect      fBounds;
571     mutable uint8_t     fBoundsIsDirty;
572     uint8_t             fFillType;
573     uint8_t             fIsConvex;
574 
575     // called, if dirty, by getBounds()
576     void computeBounds() const;
577 
578     friend class Iter;
579     void cons_moveto();
580 
581     friend class SkPathStroker;
582     /*  Append the first contour of path, ignoring path's initial point. If no
583         moveTo() call has been made for this contour, the first point is
584         automatically set to (0,0).
585     */
586     void pathTo(const SkPath& path);
587 
588     /*  Append, in reverse order, the first contour of path, ignoring path's
589         last point. If no moveTo() call has been made for this contour, the
590         first point is automatically set to (0,0).
591     */
592     void reversePathTo(const SkPath&);
593 
594     friend const SkPoint* sk_get_path_points(const SkPath&, int index);
595     friend class SkAutoPathBoundsUpdate;
596 };
597 
598 #endif
599 
600