SkPath.h revision e522ca5d5f249bd51a00cb68bb051f811d0a9e85
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
23class SkFlattenableReadBuffer;
24class SkFlattenableWriteBuffer;
25class SkAutoPathBoundsUpdate;
26class 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*/
33class SkPath {
34public:
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    */
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    */
75    void setFillType(FillType ft) { fFillType = SkToU8(ft); }
76
77    /** Returns true if the filltype is one of the Inverse variants */
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    */
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     */
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     */
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    */
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     */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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    */
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        */
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    void dump(bool forceClose, const char title[] = NULL) const;
551    void dump() const;
552
553    void flatten(SkFlattenableWriteBuffer&) const;
554    void unflatten(SkFlattenableReadBuffer&);
555
556    /** Subdivide the path so that no segment is longer that dist.
557        If bendLines is true, then turn all line segments into curves.
558        If dst == null, then the original path itself is modified (not const!)
559    */
560    void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const;
561
562    SkDEBUGCODE(void validate() const;)
563
564private:
565    SkTDArray<SkPoint>  fPts;
566    SkTDArray<uint8_t>  fVerbs;
567    mutable SkRect      fBounds;
568    mutable uint8_t     fBoundsIsDirty;
569    uint8_t             fFillType;
570    uint8_t             fIsConvex;
571
572    // called, if dirty, by getBounds()
573    void computeBounds() const;
574
575    friend class Iter;
576    void cons_moveto();
577
578    friend class SkPathStroker;
579    /*  Append the first contour of path, ignoring path's initial point. If no
580        moveTo() call has been made for this contour, the first point is
581        automatically set to (0,0).
582    */
583    void pathTo(const SkPath& path);
584
585    /*  Append, in reverse order, the first contour of path, ignoring path's
586        last point. If no moveTo() call has been made for this contour, the
587        first point is automatically set to (0,0).
588    */
589    void reversePathTo(const SkPath&);
590
591    friend const SkPoint* sk_get_path_points(const SkPath&, int index);
592    friend class SkAutoPathBoundsUpdate;
593};
594
595#endif
596
597