SkPath.h revision 0c3137c4f45ffbf09a41526e5eb96e12cc6f35ae
1/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SkPath_DEFINED
9#define SkPath_DEFINED
10
11#include "SkMatrix.h"
12#include "../private/SkPathRef.h"
13
14class SkAutoPathBoundsUpdate;
15class SkData;
16class SkRRect;
17class SkWStream;
18
19/** \class SkPath
20    Paths contain geometry. Paths may be empty, or contain one or more verbs that
21    outline a figure. SkPath always starts with a move verb to a Cartesian_Coordinate,
22    and may be followed by additional verbs that add lines or curves.
23    Adding a close verb makes the geometry into a continuous loop, a closed contour.
24    Paths may contain any number of contours, each beginning with a move verb.
25
26    SkPath contours may contain only a move verb, or may also contain lines,
27    quadratic beziers, conics, and cubic beziers. SkPath contours may be open or
28    closed.
29
30    When used to draw a filled area, SkPath describes whether the fill is inside or
31    outside the geometry. SkPath also describes the winding rule used to fill
32    overlapping contours.
33
34    Internally, SkPath lazily computes metrics likes bounds and convexity. Call
35    SkPath::updateBoundsCache to make SkPath thread safe.
36*/
37class SK_API SkPath {
38public:
39
40    /** \enum SkPath::Direction
41        Direction describes whether contour is clockwise or counterclockwise.
42        When SkPath contains multiple overlapping contours, Direction together with
43        FillType determines whether overlaps are filled or form holes.
44
45        Direction also determines how contour is measured. For instance, dashing
46        measures along SkPath to determine where to start and stop stroke; Direction
47        will change dashed results as it steps clockwise or counterclockwise.
48
49        Closed contours like SkRect, SkRRect, circle, and oval added with
50        kCW_Direction travel clockwise; the same added with kCCW_Direction
51        travel counterclockwise.
52    */
53    enum Direction {
54        kCW_Direction,  //!< Contour travels in a clockwise direction
55        kCCW_Direction, //!< Contour travels in a counterclockwise direction
56    };
57
58    /** By default, SkPath has no verbs, no points, and no weights.
59        SkPath::FillType is set to kWinding_FillType.
60
61        @return  empty SkPath
62    */
63    SkPath();
64
65    /** Copy constructor makes two paths identical by value. Internally, path and
66        the returned result share pointer values. The underlying verb array, SkPoint array
67        and weights are copied when modified.
68
69        Creating a SkPath copy is very efficient and never allocates memory.
70        Paths are always copied by value from the interface; the underlying shared
71        pointers are not exposed.
72
73        @param path  SkPath to copy by value
74        @return      copy of SkPath
75    */
76    SkPath(const SkPath& path);
77
78    /** Releases ownership of any shared data and deletes data if SkPath is sole owner.
79    */
80    ~SkPath();
81
82    /** SkPath assignment makes two paths identical by value. Internally, assignment
83        shares pointer values. The underlying verb array, SkPoint array and weights
84        are copied when modified.
85
86        Copying paths by assignment is very efficient and never allocates memory.
87        Paths are always copied by value from the interface; the underlying shared
88        pointers are not exposed.
89
90        @param path  verb array, SkPoint array, weights, and SkPath::FillType to copy
91        @return      SkPath copied by value
92    */
93    SkPath& operator=(const SkPath& path);
94
95    /** Compares a and b; returns true if SkPath::FillType, verb array, SkPoint array, and weights
96        are equivalent.
97
98        @param a  SkPath to compare
99        @param b  SkPath to compare
100        @return   true if SkPath pair are equivalent
101    */
102    friend SK_API bool operator==(const SkPath& a, const SkPath& b);
103
104    /** Compares a and b; returns true if SkPath::FillType, verb array, SkPoint array, and weights
105        are not equivalent.
106
107        @param a  SkPath to compare
108        @param b  SkPath to compare
109        @return   true if SkPath pair are not equivalent
110    */
111    friend bool operator!=(const SkPath& a, const SkPath& b) {
112        return !(a == b);
113    }
114
115    /** Return true if paths contain equal verbs and equal weights.
116        If paths contain one or more conics, the weights must match.
117
118        conicTo() may add different verbs depending on conic weight, so it is not
119        trivial to interpolate a pair of paths containing conics with different
120        conic weight values.
121
122        @param compare  SkPath to compare
123        @return         true if paths verb array and weights are equivalent
124    */
125    bool isInterpolatable(const SkPath& compare) const;
126
127    /** Interpolate between paths with SkPoint array of equal size.
128        Copy verb array and weights to out, and set out SkPoint array to a weighted
129        average of this SkPoint array and ending SkPoint array, using the formula: (this->points * weight) + ending->points * (1 - weight).
130
131        weight is most useful when between zero (ending SkPoint array) and
132        one (this Point_Array); will work with values outside of this
133        range.
134
135        interpolate() returns false and leaves out unchanged if SkPoint array is not
136        the same size as ending SkPoint array. Call isInterpolatable() to check SkPath
137        compatibility prior to calling interpolate().
138
139        @param ending  SkPoint array averaged with this SkPoint array
140        @param weight  contribution of this SkPoint array, and
141                       one minus contribution of ending SkPoint array
142        @param out     SkPath replaced by interpolated averages
143        @return        true if paths contain same number of points
144    */
145    bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const;
146
147#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
148    /** To be deprecated; only valid for Android framework.
149
150        @return  true if SkPath has one owner
151    */
152    bool unique() const { return fPathRef->unique(); }
153#endif
154
155    /** \enum SkPath::FillType
156        FillType selects the rule used to fill SkPath. SkPath set to kWinding_FillType
157        fills if the sum of contour edges is not zero, where clockwise edges add one, and
158        counterclockwise edges subtract one. SkPath set to kEvenOdd_FillType fills if the
159        number of contour edges is odd. Each FillType has an inverse variant that
160        reverses the rule:
161        kInverseWinding_FillType fills where the sum of contour edges is zero;
162        kInverseEvenOdd_FillType fills where the number of contour edges is even.
163    */
164    enum FillType {
165        /** Specifies fill as area is enclosed by a non-zero sum of contour directions. */
166        kWinding_FillType,
167
168        /** Specifies fill as area enclosed by an odd number of contours. */
169        kEvenOdd_FillType,
170
171        /** Specifies fill as area is enclosed by a zero sum of contour directions. */
172        kInverseWinding_FillType,
173
174        /** Specifies fill as area enclosed by an even number of contours. */
175        kInverseEvenOdd_FillType,
176    };
177
178    /** Returns FillType, the rule used to fill SkPath. FillType of a new SkPath is
179        kWinding_FillType.
180
181        @return  one of: kWinding_FillType, kEvenOdd_FillType,  kInverseWinding_FillType,
182                 kInverseEvenOdd_FillType
183    */
184    FillType getFillType() const { return (FillType)fFillType; }
185
186    /** Sets FillType, the rule used to fill SkPath. While there is no check
187        that ft is legal, values outside of FillType are not supported.
188
189        @param ft  one of: kWinding_FillType, kEvenOdd_FillType,  kInverseWinding_FillType,
190                   kInverseEvenOdd_FillType
191    */
192    void setFillType(FillType ft) {
193        fFillType = SkToU8(ft);
194    }
195
196    /** Returns if FillType describes area outside SkPath geometry. The inverse fill area
197        extends indefinitely.
198
199        @return  true if FillType is kInverseWinding_FillType or kInverseEvenOdd_FillType
200    */
201    bool isInverseFillType() const { return IsInverseFillType((FillType)fFillType); }
202
203    /** Replace FillType with its inverse. The inverse of FillType describes the area
204        unmodified by the original FillType.
205    */
206    void toggleInverseFillType() {
207        fFillType ^= 2;
208    }
209
210    /** \enum SkPath::Convexity
211        SkPath is convex if it contains one contour and contour loops no more than
212        360 degrees, and contour angles all have same Direction. Convex SkPath
213        may have better performance and require fewer resources on GPU surface.
214
215        SkPath is concave when either at least one Direction change is clockwise and
216        another is counterclockwise, or the sum of the changes in Direction is not 360
217        degrees.
218
219        Initially SkPath Convexity is kUnknown_Convexity. SkPath Convexity is computed
220        if needed by destination SkSurface.
221    */
222    enum Convexity : uint8_t {
223        kUnknown_Convexity, //!< Indicates Convexity has not been determined.
224
225        /** SkPath has one contour made of a simple geometry without indentations. */
226        kConvex_Convexity,
227        kConcave_Convexity, //!< SkPath has more than one contour, or a geometry with indentations.
228    };
229
230    /** Computes SkPath::Convexity if required, and returns stored value.
231        SkPath::Convexity is computed if stored value is kUnknown_Convexity,
232        or if SkPath has been altered since SkPath::Convexity was computed or set.
233
234        @return  computed or stored SkPath::Convexity
235    */
236    Convexity getConvexity() const {
237        for (Convexity convexity = fConvexity.load(); kUnknown_Convexity != convexity; ) {
238            return convexity;
239        }
240        return this->internalGetConvexity();
241    }
242
243    /** Returns last computed SkPath::Convexity, or kUnknown_Convexity if
244        SkPath has been altered since SkPath::Convexity was computed or set.
245
246        @return  stored SkPath::Convexity
247    */
248    Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; }
249
250    /** Stores convexity so that it is later returned by getConvexity() or getConvexityOrUnknown().
251        convexity may differ from getConvexity(), although setting an incorrect value may
252        cause incorrect or inefficient drawing.
253
254        If convexity is kUnknown_Convexity: getConvexity() will
255        compute SkPath::Convexity, and getConvexityOrUnknown() will return kUnknown_Convexity.
256
257        If convexity is kConvex_Convexity or kConcave_Convexity, getConvexity()
258        and getConvexityOrUnknown() will return convexity until the path is
259        altered.
260
261        @param convexity  one of: kUnknown_Convexity, kConvex_Convexity, or kConcave_Convexity
262    */
263    void setConvexity(Convexity convexity);
264
265    /** Computes SkPath::Convexity if required, and returns true if value is kConvex_Convexity.
266        If setConvexity() was called with kConvex_Convexity or kConcave_Convexity, and
267        the path has not been altered, SkPath::Convexity is not recomputed.
268
269        @return  true if SkPath::Convexity stored or computed is kConvex_Convexity
270    */
271    bool isConvex() const {
272        return kConvex_Convexity == this->getConvexity();
273    }
274
275    /** Deprecated. Use setConvexity().
276    */
277    SK_ATTR_DEPRECATED("use setConvexity")
278    void setIsConvex(bool isConvex) {
279        this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
280    }
281
282    /**
283     *  Returns true if this path is recognized as an oval or circle.
284     *
285     *  @param bounds   If this returns true and bounds is not null, then it is set to the
286     *                  bounds of the oval that matches this path. If this returns false,
287     *                  then this parameter is ignored.
288     */
289    bool isOval(SkRect* bounds) const;
290
291    /**
292     *  Returns true if this path is recognized as a SkRRect (but not an oval/circle or rect).
293     *
294     *  @param rrect    If this returns true and rrect is not null, then it is set to the
295     *                  SkRRect that matches this path. If this returns false, then this parameter
296     *                  is ignored.
297     */
298    bool isRRect(SkRRect* rrect) const;
299
300    /** Sets SkPath to its initial state.
301        Removes verb array, SkPoint array, and weights, and sets FillType to kWinding_FillType.
302        Internal storage associated with SkPath is released.
303    */
304    void reset();
305
306    /** Sets SkPath to its initial state, preserving internal storage.
307        Removes verb array, SkPoint array, and weights, and sets FillType to kWinding_FillType.
308        Internal storage associated with SkPath is retained.
309
310        Use rewind() instead of reset() if SkPath storage will be reused and performance
311        is critical.
312    */
313    void rewind();
314
315    /** Empty SkPath may have FillType but has no SkPoint, SkPath::Verb, or conic weight.
316        SkPath() constructs empty SkPath; reset() and (rewind) make SkPath empty.
317
318        @return  true if the path contains no SkPath::Verb array
319    */
320    bool isEmpty() const {
321        SkDEBUGCODE(this->validate();)
322        return 0 == fPathRef->countVerbs();
323    }
324
325    /** Contour is closed if SkPath SkPath::Verb array was last modified by close(). When stroked,
326        closed contour draws SkPaint::Join instead of SkPaint::Cap at first and last SkPoint.
327
328        @return  true if the last contour ends with a kClose_Verb
329    */
330    bool isLastContourClosed() const;
331
332    /** Returns true for finite SkPoint array values between negative SK_ScalarMax and
333        positive SK_ScalarMax. Returns false for any SkPoint array value of
334        SK_ScalarInfinity, SK_ScalarNegativeInfinity, or SK_ScalarNaN.
335
336        @return  true if all SkPoint values are finite
337    */
338    bool isFinite() const {
339        SkDEBUGCODE(this->validate();)
340        return fPathRef->isFinite();
341    }
342
343    /** Returns true if the path is volatile; it will not be altered or discarded
344        by the caller after it is drawn. Paths by default have volatile set false, allowing
345        SkSurface to attach a cache of data which speeds repeated drawing. If true, SkSurface
346        may not speed repeated drawing.
347
348        @return  true if caller will alter SkPath after drawing
349    */
350    bool isVolatile() const {
351        return SkToBool(fIsVolatile);
352    }
353
354    /** Specify whether SkPath is volatile; whether it will be altered or discarded
355        by the caller after it is drawn. Paths by default have volatile set false, allowing
356        SkBaseDevice to attach a cache of data which speeds repeated drawing.
357
358        Mark temporary paths, discarded or modified after use, as volatile
359        to inform SkBaseDevice that the path need not be cached.
360
361        Mark animating SkPath volatile to improve performance.
362        Mark unchanging SkPath non-volatile to improve repeated rendering.
363
364        raster surface SkPath draws are affected by volatile for some shadows.
365        GPU surface SkPath draws are affected by volatile for some shadows and concave geometries.
366
367        @param isVolatile  true if caller will alter SkPath after drawing
368    */
369    void setIsVolatile(bool isVolatile) {
370        fIsVolatile = isVolatile;
371    }
372
373    /** Test if line between SkPoint pair is degenerate.
374        Line with no length or that moves a very short distance is degenerate; it is
375        treated as a point.
376
377        exact changes the equality test. If true, returns true only if p1 equals p2.
378        If false, returns true if p1 equals or nearly equals p2.
379
380        @param p1     line start point
381        @param p2     line end point
382        @param exact  if false, allow nearly equals
383        @return       true if line is degenerate; its length is effectively zero
384    */
385    static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact);
386
387    /** Test if quad is degenerate.
388        Quad with no length or that moves a very short distance is degenerate; it is
389        treated as a point.
390
391        @param p1     quad start point
392        @param p2     quad control point
393        @param p3     quad end point
394        @param exact  if true, returns true only if p1, p2, and p3 are equal;
395                      if false, returns true if p1, p2, and p3 are equal or nearly equal
396        @return       true if quad is degenerate; its length is effectively zero
397    */
398    static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
399                                 const SkPoint& p3, bool exact);
400
401    /** Test if cubic is degenerate.
402        Cubic with no length or that moves a very short distance is degenerate; it is
403        treated as a point.
404
405        @param p1     cubic start point
406        @param p2     cubic control point 1
407        @param p3     cubic control point 2
408        @param p4     cubic end point
409        @param exact  if true, returns true only if p1, p2, p3, and p4 are equal;
410                      if false, returns true if p1, p2, p3, and p4 are equal or nearly equal
411        @return       true if cubic is degenerate; its length is effectively zero
412    */
413    static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
414                                  const SkPoint& p3, const SkPoint& p4, bool exact);
415
416    /** Returns true if SkPath contains only one line;
417        SkPath::Verb array has two entries: kMove_Verb, kLine_Verb.
418        If SkPath contains one line and line is not nullptr, line is set to
419        line start point and line end point.
420        Returns false if SkPath is not one line; line is unaltered.
421
422        @param line  storage for line. May be nullptr
423        @return      true if SkPath contains exactly one line
424    */
425    bool isLine(SkPoint line[2]) const;
426
427    /** Returns the number of points in SkPath.
428        SkPoint count is initially zero.
429
430        @return  SkPath SkPoint array length
431    */
432    int countPoints() const;
433
434    /** Returns SkPoint at index in SkPoint array. Valid range for index is
435        0 to countPoints() - 1.
436        Returns (0, 0) if index is out of range.
437
438        @param index  SkPoint array element selector
439        @return       SkPoint array value or (0, 0)
440    */
441    SkPoint getPoint(int index) const;
442
443    /** Returns number of points in SkPath. Up to max points are copied.
444        points may be nullptr; then, max must be zero.
445        If max is greater than number of points, excess points storage is unaltered.
446
447        @param points  storage for SkPath SkPoint array. May be nullptr
448        @param max     maximum to copy; must be greater than or equal to zero
449        @return        SkPath SkPoint array length
450    */
451    int getPoints(SkPoint points[], int max) const;
452
453    /** Returns the number of verbs: kMove_Verb, kLine_Verb, kQuad_Verb, kConic_Verb,
454        kCubic_Verb, and kClose_Verb; added to SkPath.
455
456        @return  length of verb array
457    */
458    int countVerbs() const;
459
460    /** Returns the number of verbs in the path. Up to max verbs are copied. The
461        verbs are copied as one byte per verb.
462
463        @param verbs  storage for verbs, may be nullptr
464        @param max    maximum number to copy into verbs
465        @return       the actual number of verbs in the path
466    */
467    int getVerbs(uint8_t verbs[], int max) const;
468
469    /** Exchanges the verb array, SkPoint array, weights, and SkPath::FillType with other.
470        Cached state is also exchanged. swap() internally exchanges pointers, so
471        it is lightweight and does not allocate memory.
472
473        swap() usage has largely been replaced by operator=(const SkPath& path).
474        Paths do not copy their content on assignment until they are written to,
475        making assignment as efficient as swap().
476
477        @param other  SkPath exchanged by value
478    */
479    void swap(SkPath& other);
480
481    /** Returns minimum and maximum x and y values of SkPoint array.
482        Returns (0, 0, 0, 0) if SkPath contains no points. Returned bounds width and height may
483        be larger or smaller than area affected when SkPath is drawn.
484
485        SkRect returned includes all points added to SkPath, including points associated with
486        kMove_Verb that define empty contours.
487
488        @return  bounds of all points in SkPoint array
489    */
490    const SkRect& getBounds() const {
491        return fPathRef->getBounds();
492    }
493
494    /** Update internal bounds so that subsequent calls to getBounds() are instantaneous.
495        Unaltered copies of SkPath may also access cached bounds through getBounds().
496
497        For now, identical to calling getBounds() and ignoring the returned value.
498
499        Call to prepare SkPath subsequently drawn from multiple threads,
500        to avoid a race condition where each draw separately computes the bounds.
501    */
502    void updateBoundsCache() const {
503        // for now, just calling getBounds() is sufficient
504        this->getBounds();
505    }
506
507    /** Returns minimum and maximum x and y values of the lines and curves in SkPath.
508        Returns (0, 0, 0, 0) if SkPath contains no points.
509        Returned bounds width and height may be larger or smaller than area affected
510        when SkPath is drawn.
511
512        Includes points associated with kMove_Verb that define empty
513        contours.
514
515        Behaves identically to getBounds() when SkPath contains
516        only lines. If SkPath contains curves, computed bounds includes
517        the maximum extent of the quad, conic, or cubic; is slower than getBounds();
518        and unlike getBounds(), does not cache the result.
519
520        @return  tight bounds of curves in SkPath
521    */
522    SkRect computeTightBounds() const;
523
524    /** Returns true if rect is contained by SkPath.
525        May return false when rect is contained by SkPath.
526
527        For now, only returns true if SkPath has one contour and is convex.
528        rect may share points and edges with SkPath and be contained.
529        Returns true if rect is empty, that is, it has zero width or height; and
530        the SkPoint or line described by rect is contained by SkPath.
531
532        @param rect  SkRect, line, or SkPoint checked for containment
533        @return      true if rect is contained
534    */
535    bool conservativelyContainsRect(const SkRect& rect) const;
536
537    /** grows SkPath verb array and SkPoint array to contain extraPtCount additional points.
538        May improve performance and use less memory by
539        reducing the number and size of allocations when creating SkPath.
540
541        @param extraPtCount  number of additional points to allocate
542    */
543    void incReserve(unsigned extraPtCount);
544
545    /** Adds beginning of contour at SkPoint (x, y).
546
547        @param x  x-coordinate of contour start
548        @param y  y-coordinate of contour start
549    */
550    void moveTo(SkScalar x, SkScalar y);
551
552    /** Adds beginning of contour at SkPoint p.
553
554        @param p  contour start
555    */
556    void moveTo(const SkPoint& p) {
557        this->moveTo(p.fX, p.fY);
558    }
559
560    /** Adds beginning of contour relative to last point.
561        If SkPath is empty, starts contour at (dx, dy).
562        Otherwise, start contour at last point offset by (dx, dy).
563        Function name stands for "relative move to".
564
565        @param dx  offset from last point x to contour start x
566        @param dy  offset from last point y to contour start y
567    */
568    void rMoveTo(SkScalar dx, SkScalar dy);
569
570    /** Adds line from last point to (x, y). If SkPath is empty, or last SkPath::Verb is
571        kClose_Verb, last point is set to (0, 0) before adding line.
572
573        lineTo() appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed.
574        lineTo() then appends kLine_Verb to verb array and (x, y) to SkPoint array.
575
576        @param x  end of added line in x
577        @param y  end of added line in y
578    */
579    void lineTo(SkScalar x, SkScalar y);
580
581    /** Adds line from last point to SkPoint p. If SkPath is empty, or last SkPath::Verb is
582        kClose_Verb, last point is set to (0, 0) before adding line.
583
584        lineTo() first appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed.
585        lineTo() then appends kLine_Verb to verb array and SkPoint p to SkPoint array.
586
587        @param p  end SkPoint of added line
588    */
589    void lineTo(const SkPoint& p) {
590        this->lineTo(p.fX, p.fY);
591    }
592
593    /** Adds line from last point to vector (dx, dy). If SkPath is empty, or last SkPath::Verb is
594        kClose_Verb, last point is set to (0, 0) before adding line.
595
596        Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
597        then appends kLine_Verb to verb array and line end to SkPoint array.
598        Line end is last point plus vector (dx, dy).
599        Function name stands for "relative line to".
600
601        @param dx  offset from last point x to line end x
602        @param dy  offset from last point y to line end y
603    */
604    void rLineTo(SkScalar dx, SkScalar dy);
605
606    /** Adds quad from last point towards (x1, y1), to (x2, y2).
607        If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0)
608        before adding quad.
609
610        Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
611        then appends kQuad_Verb to verb array; and (x1, y1), (x2, y2)
612        to SkPoint array.
613
614        @param x1  control SkPoint of quad in x
615        @param y1  control SkPoint of quad in y
616        @param x2  end SkPoint of quad in x
617        @param y2  end SkPoint of quad in y
618    */
619    void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
620
621    /** Adds quad from last point towards SkPoint p1, to SkPoint p2.
622        If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0)
623        before adding quad.
624
625        Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
626        then appends kQuad_Verb to verb array; and points p1, p2
627        to SkPoint array.
628
629        @param p1  control SkPoint of added quad
630        @param p2  end SkPoint of added quad
631    */
632    void quadTo(const SkPoint& p1, const SkPoint& p2) {
633        this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
634    }
635
636    /** Adds quad from last point towards vector (dx1, dy1), to vector (dx2, dy2).
637        If SkPath is empty, or last SkPath::Verb
638        is kClose_Verb, last point is set to (0, 0) before adding quad.
639
640        Appends kMove_Verb to verb array and (0, 0) to SkPoint array,
641        if needed; then appends kQuad_Verb to verb array; and appends quad
642        control and quad end to SkPoint array.
643        Quad control is last point plus vector (dx1, dy1).
644        Quad end is last point plus vector (dx2, dy2).
645        Function name stands for "relative quad to".
646
647        @param dx1  offset from last point x to quad control x
648        @param dy1  offset from last point x to quad control y
649        @param dx2  offset from last point x to quad end x
650        @param dy2  offset from last point x to quad end y
651    */
652    void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
653
654    /** Adds conic from last point towards (x1, y1), to (x2, y2), weighted by w.
655        If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0)
656        before adding conic.
657
658        Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed.
659
660        If w is finite and not one, appends kConic_Verb to verb array;
661        and (x1, y1), (x2, y2) to SkPoint array; and w to conic weights.
662
663        If w is one, appends kQuad_Verb to verb array, and
664        (x1, y1), (x2, y2) to SkPoint array.
665
666        If w is not finite, appends kLine_Verb twice to verb array, and
667        (x1, y1), (x2, y2) to SkPoint array.
668
669        @param x1  control SkPoint of conic in x
670        @param y1  control SkPoint of conic in y
671        @param x2  end SkPoint of conic in x
672        @param y2  end SkPoint of conic in y
673        @param w   weight of added conic
674    */
675    void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
676                 SkScalar w);
677
678    /** Adds conic from last point towards SkPoint p1, to SkPoint p2, weighted by w.
679        If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0)
680        before adding conic.
681
682        Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed.
683
684        If w is finite and not one, appends kConic_Verb to verb array;
685        and points p1, p2 to SkPoint array; and w to conic weights.
686
687        If w is one, appends kQuad_Verb to verb array, and points p1, p2
688        to SkPoint array.
689
690        If w is not finite, appends kLine_Verb twice to verb array, and
691        points p1, p2 to SkPoint array.
692
693        @param p1  control SkPoint of added conic
694        @param p2  end SkPoint of added conic
695        @param w   weight of added conic
696    */
697    void conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) {
698        this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w);
699    }
700
701    /** Adds conic from last point towards vector (dx1, dy1), to vector (dx2, dy2),
702        weighted by w. If SkPath is empty, or last SkPath::Verb
703        is kClose_Verb, last point is set to (0, 0) before adding conic.
704
705        Appends kMove_Verb to verb array and (0, 0) to SkPoint array,
706        if needed.
707
708        If w is finite and not one, next appends kConic_Verb to verb array,
709        and w is recorded as conic weight; otherwise, if w is one, appends
710        kQuad_Verb to verb array; or if w is not finite, appends kLine_Verb
711        twice to verb array.
712
713        In all cases appends points control and end to SkPoint array.
714        control is last point plus vector (dx1, dy1).
715        end is last point plus vector (dx2, dy2).
716
717        Function name stands for "relative conic to".
718
719        @param dx1  offset from last point x to conic control x
720        @param dy1  offset from last point x to conic control y
721        @param dx2  offset from last point x to conic end x
722        @param dy2  offset from last point x to conic end y
723        @param w    weight of added conic
724    */
725    void rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
726                  SkScalar w);
727
728    /** Adds cubic from last point towards (x1, y1), then towards (x2, y2), ending at
729        (x3, y3). If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to
730        (0, 0) before adding cubic.
731
732        Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
733        then appends kCubic_Verb to verb array; and (x1, y1), (x2, y2), (x3, y3)
734        to SkPoint array.
735
736        @param x1  first control SkPoint of cubic in x
737        @param y1  first control SkPoint of cubic in y
738        @param x2  second control SkPoint of cubic in x
739        @param y2  second control SkPoint of cubic in y
740        @param x3  end SkPoint of cubic in x
741        @param y3  end SkPoint of cubic in y
742    */
743    void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
744                 SkScalar x3, SkScalar y3);
745
746    /** Adds cubic from last point towards SkPoint p1, then towards SkPoint p2, ending at
747        SkPoint p3. If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to
748        (0, 0) before adding cubic.
749
750        Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed;
751        then appends kCubic_Verb to verb array; and points p1, p2, p3
752        to SkPoint array.
753
754        @param p1  first control SkPoint of cubic
755        @param p2  second control SkPoint of cubic
756        @param p3  end SkPoint of cubic
757    */
758    void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
759        this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
760    }
761
762    /** Adds cubic from last point towards vector (dx1, dy1), then towards
763        vector (dx2, dy2), to vector (dx3, dy3).
764        If SkPath is empty, or last SkPath::Verb
765        is kClose_Verb, last point is set to (0, 0) before adding cubic.
766
767        Appends kMove_Verb to verb array and (0, 0) to SkPoint array,
768        if needed; then appends kCubic_Verb to verb array; and appends cubic
769        control and cubic end to SkPoint array.
770        Cubic control is last point plus vector (dx1, dy1).
771        Cubic end is last point plus vector (dx2, dy2).
772        Function name stands for "relative cubic to".
773
774        @param x1  offset from last point x to first cubic control x
775        @param y1  offset from last point x to first cubic control y
776        @param x2  offset from last point x to second cubic control x
777        @param y2  offset from last point x to second cubic control y
778        @param x3  offset from last point x to cubic end x
779        @param y3  offset from last point x to cubic end y
780    */
781    void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
782                  SkScalar x3, SkScalar y3);
783
784    /** Append arc to SkPath. Arc added is part of ellipse
785        bounded by oval, from startAngle through sweepAngle. Both startAngle and
786        sweepAngle are measured in degrees, where zero degrees is aligned with the
787        positive x-axis, and positive sweeps extends arc clockwise.
788
789        arcTo() adds line connecting SkPath last SkPoint to initial arc SkPoint if forceMoveTo
790        is false and SkPath is not empty. Otherwise, added contour begins with first point
791        of arc. Angles greater than -360 and less than 360 are treated modulo 360.
792
793        @param oval         bounds of ellipse containing arc
794        @param startAngle   starting angle of arc in degrees
795        @param sweepAngle   sweep, in degrees. Positive is clockwise; treated modulo 360
796        @param forceMoveTo  true to start a new contour with arc
797    */
798    void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo);
799
800    /** Append arc to SkPath, after appending line if needed. Arc is implemented by conic
801        weighted to describe part of circle. Arc is contained by tangent from
802        last SkPath point (x0, y0) to (x1, y1), and tangent from (x1, y1) to (x2, y2). Arc
803        is part of circle sized to radius, positioned so it touches both tangent lines.
804
805        @param x1      x common to pair of tangents
806        @param y1      y common to pair of tangents
807        @param x2      x end of second tangent
808        @param y2      y end of second tangent
809        @param radius  distance from arc to circle center
810    */
811    void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius);
812
813    /** Append arc to SkPath, after appending line if needed. Arc is implemented by conic
814        weighted to describe part of circle. Arc is contained by tangent from
815        last SkPath point to p1, and tangent from p1 to p2. Arc
816        is part of circle sized to radius, positioned so it touches both tangent lines.
817
818        If last SkPath SkPoint does not start arc, arcTo() appends connecting line to SkPath.
819        The length of vector from p1 to p2 does not affect arc.
820
821        Arc sweep is always less than 180 degrees. If radius is zero, or if
822        tangents are nearly parallel, arcTo() appends line from last SkPath SkPoint to p1.
823
824        arcTo() appends at most one line and one conic.
825        arcTo() implements the functionality of PostScript_Arct and HTML_Canvas_ArcTo.
826
827        @param p1      SkPoint common to pair of tangents
828        @param p2      end of second tangent
829        @param radius  distance from arc to circle center
830    */
831    void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
832        this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
833    }
834
835    /** \enum SkPath::ArcSize
836        Four oval parts with radii (rx, ry) start at last SkPath SkPoint and ends at (x, y).
837        ArcSize and Direction select one of the four oval parts.
838    */
839    enum ArcSize {
840        kSmall_ArcSize, //!< smaller of arc pair
841        kLarge_ArcSize, //!< larger of arc pair
842    };
843
844    /** Append arc to SkPath. Arc is implemented by one or more conics weighted to
845        describe part of oval with radii (rx, ry) rotated by xAxisRotate degrees. Arc
846        curves from last SkPath SkPoint to (x, y), choosing one of four possible routes:
847        clockwise or counterclockwise, and smaller or larger.
848
849        Arc sweep is always less than 360 degrees. arcTo() appends line to (x, y) if
850        either radii are zero, or if last SkPath SkPoint equals (x, y). arcTo() scales radii
851        (rx, ry) to fit last SkPath SkPoint and (x, y) if both are greater than zero but
852        too small.
853
854        arcTo() appends up to four conic curves.
855        arcTo() implements the functionality of svg arc, although SVG "sweep-flag" value
856        is opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise,
857        while kCW_Direction  cast to int is zero.
858
859        @param rx           radius in x before x-axis rotation
860        @param ry           radius in y before x-axis rotation
861        @param xAxisRotate  x-axis rotation in degrees; positive values are clockwise
862        @param largeArc     chooses smaller or larger arc
863        @param sweep        chooses clockwise or counterclockwise arc
864        @param x            end of arc
865        @param y            end of arc
866    */
867    void arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
868               Direction sweep, SkScalar x, SkScalar y);
869
870    /** Append arc to SkPath. Arc is implemented by one or more conic weighted to describe part of oval
871        with radii (r.fX, r.fY) rotated by xAxisRotate degrees. Arc curves from last SkPath SkPoint to
872        (xy.fX, xy.fY), choosing one of four possible routes: clockwise or counterclockwise,
873        and smaller or larger.
874
875        Arc sweep is always less than 360 degrees. arcTo() appends line to xy if either radii are zero,
876        or if last SkPath SkPoint equals (x, y). arcTo() scales radii r to fit last SkPath SkPoint and
877        xy if both are greater than zero but too small to describe an arc.
878
879        arcTo() appends up to four conic curves.
880        arcTo() implements the functionality of svg arc, although SVG "sweep-flag" value is
881        opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while
882        kCW_Direction cast to int is zero.
883
884        @param r            radii in x and y before x-axis rotation
885        @param xAxisRotate  x-axis rotation in degrees; positive values are clockwise
886        @param largeArc     chooses smaller or larger arc
887        @param sweep        chooses clockwise or counterclockwise arc
888        @param xy           end of arc
889    */
890    void arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
891               const SkPoint xy) {
892        this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, sweep, xy.fX, xy.fY);
893    }
894
895    /** Append arc to SkPath, relative to last SkPath SkPoint. Arc is implemented by one or
896        more conic, weighted to describe part of oval with radii (rx, ry) rotated by
897        xAxisRotate degrees. Arc curves from last SkPath SkPoint (x0, y0) to end SkPoint:
898        (x0 + dx, y0 + dy), choosing one of four possible routes: clockwise or
899        counterclockwise, and smaller or larger. If SkPath is empty, the start arc SkPoint
900        is (0, 0).
901
902        Arc sweep is always less than 360 degrees. arcTo() appends line to end SkPoint
903        if either radii are zero, or if last SkPath SkPoint equals end SkPoint.
904        arcTo() scales radii (rx, ry) to fit last SkPath SkPoint and end SkPoint if both are
905        greater than zero but too small to describe an arc.
906
907        arcTo() appends up to four conic curves.
908        arcTo() implements the functionality of svg arc, although SVG "sweep-flag" value is
909        opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while
910        kCW_Direction cast to int is zero.
911
912        @param rx           radius in x before x-axis rotation
913        @param ry           radius in y before x-axis rotation
914        @param xAxisRotate  x-axis rotation in degrees; positive values are clockwise
915        @param largeArc     chooses smaller or larger arc
916        @param sweep        chooses clockwise or counterclockwise arc
917        @param dx           x offset end of arc from last SkPath SkPoint
918        @param dy           y offset end of arc from last SkPath SkPoint
919    */
920    void rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
921                Direction sweep, SkScalar dx, SkScalar dy);
922
923    /** Append kClose_Verb to SkPath. A closed contour connects the first and last SkPoint
924        with line, forming a continuous loop. Open and closed contour draw the same
925        with SkPaint::kFill_Style. With SkPaint::kStroke_Style, open contour draws
926        SkPaint::Cap at contour start and end; closed contour draws
927        SkPaint::Join at contour start and end.
928
929        close() has no effect if SkPath is empty or last SkPath SkPath::Verb is kClose_Verb.
930    */
931    void close();
932
933    /** Returns true if fill is inverted and SkPath with fill represents area outside
934        of its geometric bounds.
935
936        @param fill  one of: kWinding_FillType, kEvenOdd_FillType,
937                     kInverseWinding_FillType, kInverseEvenOdd_FillType
938        @return      true if SkPath fills outside its bounds
939    */
940    static bool IsInverseFillType(FillType fill) {
941        static_assert(0 == kWinding_FillType, "fill_type_mismatch");
942        static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
943        static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
944        static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
945        return (fill & 2) != 0;
946    }
947
948    /** Returns equivalent SkPath::FillType representing SkPath fill inside its bounds.
949        .
950
951        @param fill  one of: kWinding_FillType, kEvenOdd_FillType,
952                     kInverseWinding_FillType, kInverseEvenOdd_FillType
953        @return      fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted
954    */
955    static FillType ConvertToNonInverseFillType(FillType fill) {
956        static_assert(0 == kWinding_FillType, "fill_type_mismatch");
957        static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
958        static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
959        static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
960        return (FillType)(fill & 1);
961    }
962
963    /** Approximates conic with quad array. Conic is constructed from start SkPoint p0,
964        control SkPoint p1, end SkPoint p2, and weight w.
965        Quad array is stored in pts; this storage is supplied by caller.
966        Maximum quad count is 2 to the pow2.
967        Every third point in array shares last SkPoint of previous quad and first SkPoint of
968        next quad. Maximum pts storage size is given by: (1 + 2 * (1 << pow2)) * sizeof(SkPoint).
969
970        Returns quad count used the approximation, which may be smaller
971        than the number requested.
972
973        conic weight determines the amount of influence conic control point has on the curve.
974        w less than one represents an elliptical section. w greater than one represents
975        a hyperbolic section. w equal to one represents a parabolic section.
976
977        Two quad curves are sufficient to approximate an elliptical conic with a sweep
978        of up to 90 degrees; in this case, set pow2 to one.
979
980        @param p0    conic start SkPoint
981        @param p1    conic control SkPoint
982        @param p2    conic end SkPoint
983        @param w     conic weight
984        @param pts   storage for quad array
985        @param pow2  quad count, as power of two, normally 0 to 5 (1 to 32 quad curves)
986        @return      number of quad curves written to pts
987    */
988    static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
989                                   SkScalar w, SkPoint pts[], int pow2);
990
991    /** Returns true if SkPath is equivalent to SkRect when filled.
992        If false: rect, isClosed, and direction are unchanged.
993        If true: rect, isClosed, and direction are written to if not nullptr.
994
995        rect may be smaller than the SkPath bounds. SkPath bounds may include kMove_Verb points
996        that do not alter the area drawn by the returned rect.
997
998        @param rect       storage for bounds of SkRect; may be nullptr
999        @param isClosed   storage set to true if SkPath is closed; may be nullptr
1000        @param direction  storage set to SkRect direction; may be nullptr
1001        @return           true if SkPath contains SkRect
1002    */
1003    bool isRect(SkRect* rect, bool* isClosed = nullptr, Direction* direction = nullptr) const;
1004
1005    /** Returns true if SkPath is equivalent to nested SkRect pair when filled.
1006        If false, rect and dirs are unchanged.
1007        If true, rect and dirs are written to if not nullptr:
1008        setting rect[0] to outer SkRect, and rect[1] to inner SkRect;
1009        setting dirs[0] to SkPath::Direction of outer SkRect, and dirs[1] to SkPath::Direction of inner
1010        SkRect.
1011
1012        @param rect  storage for SkRect pair; may be nullptr
1013        @param dirs  storage for SkPath::Direction pair; may be nullptr
1014        @return      true if SkPath contains nested SkRect pair
1015    */
1016    bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = nullptr) const;
1017
1018    /** Add SkRect to SkPath, appending kMove_Verb, three kLine_Verb, and kClose_Verb,
1019        starting with top-left corner of SkRect; followed by top-right, bottom-right,
1020        and bottom-left if dir is kCW_Direction; or followed by bottom-left,
1021        bottom-right, and top-right if dir is kCCW_Direction.
1022
1023        @param rect  SkRect to add as a closed contour
1024        @param dir   SkPath::Direction to wind added contour
1025    */
1026    void addRect(const SkRect& rect, Direction dir = kCW_Direction);
1027
1028    /** Add SkRect to SkPath, appending kMove_Verb, three kLine_Verb, and kClose_Verb.
1029        If dir is kCW_Direction, SkRect corners are added clockwise; if dir is
1030        kCCW_Direction, SkRect corners are added counterclockwise.
1031        start determines the first corner added.
1032
1033        @param rect   SkRect to add as a closed contour
1034        @param dir    SkPath::Direction to wind added contour
1035        @param start  initial corner of SkRect to add
1036    */
1037    void addRect(const SkRect& rect, Direction dir, unsigned start);
1038
1039    /** Add SkRect (left, top, right, bottom) to SkPath,
1040        appending kMove_Verb, three kLine_Verb, and kClose_Verb,
1041        starting with top-left corner of SkRect; followed by top-right, bottom-right,
1042        and bottom-left if dir is kCW_Direction; or followed by bottom-left,
1043        bottom-right, and top-right if dir is kCCW_Direction.
1044
1045        @param left    smaller x of SkRect
1046        @param top     smaller y of SkRect
1047        @param right   larger x of SkRect
1048        @param bottom  larger y of SkRect
1049        @param dir     SkPath::Direction to wind added contour
1050    */
1051    void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
1052                 Direction dir = kCW_Direction);
1053
1054    /** Add oval to path, appending kMove_Verb, four kConic_Verb, and kClose_Verb.
1055        Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width
1056        and half oval height. Oval begins at (oval.fRight, oval.centerY()) and continues
1057        clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction.
1058
1059        This form is identical to addOval(oval, dir, 1).
1060
1061        @param oval  bounds of ellipse added
1062        @param dir   SkPath::Direction to wind ellipse
1063    */
1064    void addOval(const SkRect& oval, Direction dir = kCW_Direction);
1065
1066    /** Add oval to SkPath, appending kMove_Verb, four kConic_Verb, and kClose_Verb.
1067        Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width
1068        and half oval height. Oval begins at start and continues
1069        clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction.
1070
1071        @param oval   bounds of ellipse added
1072        @param dir    SkPath::Direction to wind ellipse
1073        @param start  index of initial point of ellipse
1074    */
1075    void addOval(const SkRect& oval, Direction dir, unsigned start);
1076
1077    /** Add circle centered at (x, y) of size radius to SkPath, appending kMove_Verb,
1078        four kConic_Verb, and kClose_Verb. Circle begins at: (x + radius, y), continuing
1079        clockwise if dir is kCW_Direction, and counterclockwise if dir is kCCW_Direction.
1080
1081        Has no effect if radius is zero or negative.
1082
1083        @param x       center of circle
1084        @param y       center of circle
1085        @param radius  distance from center to edge
1086        @param dir     SkPath::Direction to wind circle
1087    */
1088    void addCircle(SkScalar x, SkScalar y, SkScalar radius,
1089                   Direction dir = kCW_Direction);
1090
1091    /** Append arc to SkPath, as the start of new contour. Arc added is part of ellipse
1092        bounded by oval, from startAngle through sweepAngle. Both startAngle and
1093        sweepAngle are measured in degrees, where zero degrees is aligned with the
1094        positive x-axis, and positive sweeps extends arc clockwise.
1095
1096        If sweepAngle <= -360, or sweepAngle >= 360; and startAngle modulo 90 is nearly
1097        zero, append oval instead of arc. Otherwise, sweepAngle values are treated
1098        modulo 360, and arc may or may not draw depending on numeric rounding.
1099
1100        @param oval        bounds of ellipse containing arc
1101        @param startAngle  starting angle of arc in degrees
1102        @param sweepAngle  sweep, in degrees. Positive is clockwise; treated modulo 360
1103    */
1104    void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
1105
1106    /** Append SkRRect to SkPath, creating a new closed contour. SkRRect has bounds
1107        equal to rect; each corner is 90 degrees of an ellipse with radii (rx, ry). If
1108        dir is kCW_Direction, SkRRect starts at top-left of the lower-left corner and
1109        winds clockwise. If dir is kCCW_Direction, SkRRect starts at the bottom-left
1110        of the upper-left corner and winds counterclockwise.
1111
1112        If either rx or ry is too large, rx and ry are scaled uniformly until the
1113        corners fit. If rx or ry is less than or equal to zero, addRoundRect() appends
1114        SkRect rect to SkPath.
1115
1116        After appending, SkPath may be empty, or may contain: SkRect, oval, or RoundRect.
1117
1118        @param rect  bounds of SkRRect
1119        @param rx    x-radius of rounded corners on the SkRRect
1120        @param ry    y-radius of rounded corners on the SkRRect
1121        @param dir   SkPath::Direction to wind SkRRect
1122    */
1123    void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
1124                      Direction dir = kCW_Direction);
1125
1126    /** Append SkRRect to SkPath, creating a new closed contour. SkRRect has bounds
1127        equal to rect; each corner is 90 degrees of an ellipse with radii from the
1128        array.
1129
1130        @param rect   bounds of SkRRect
1131        @param radii  array of 8 SkScalar values, a radius pair for each corner
1132        @param dir    SkPath::Direction to wind SkRRect
1133    */
1134    void addRoundRect(const SkRect& rect, const SkScalar radii[],
1135                      Direction dir = kCW_Direction);
1136
1137    /** Add rrect to SkPath, creating a new closed contour. If
1138        dir is kCW_Direction, rrect starts at top-left of the lower-left corner and
1139        winds clockwise. If dir is kCCW_Direction, rrect starts at the bottom-left
1140        of the upper-left corner and winds counterclockwise.
1141
1142        After appending, SkPath may be empty, or may contain: SkRect, oval, or SkRRect.
1143
1144        @param rrect  bounds and radii of rounded rectangle
1145        @param dir    SkPath::Direction to wind SkRRect
1146    */
1147    void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction);
1148
1149    /** Add rrect to SkPath, creating a new closed contour. If dir is kCW_Direction, rrect
1150        winds clockwise; if dir is kCCW_Direction, rrect winds counterclockwise.
1151        start determines the first point of rrect to add.
1152
1153        @param rrect  bounds and radii of rounded rectangle
1154        @param dir    SkPath::Direction to wind SkRRect
1155        @param start  index of initial point of SkRRect
1156    */
1157    void addRRect(const SkRRect& rrect, Direction dir, unsigned start);
1158
1159    /** Add contour created from line array, adding (count - 1) line segments.
1160        Contour added starts at pts[0], then adds a line for every additional SkPoint
1161        in pts array. If close is true,appends kClose_Verb to SkPath, connecting
1162        pts[count - 1] and pts[0].
1163
1164        If count is zero, append kMove_Verb to path.
1165        Has no effect if count is less than one.
1166
1167        @param pts    array of line sharing end and start SkPoint
1168        @param count  length of SkPoint array
1169        @param close  true to add line connecting contour end and start
1170    */
1171    void addPoly(const SkPoint pts[], int count, bool close);
1172
1173    /** \enum SkPath::AddPathMode
1174        AddPathMode chooses how addPath() appends. Adding one SkPath to another can extend
1175        the last contour or start a new contour.
1176    */
1177    enum AddPathMode {
1178        /** Since SkPath verb array begins with kMove_Verb if src is not empty, this
1179            starts a new contour.
1180        */
1181        kAppend_AddPathMode,
1182
1183        /** is not empty, add line from last point to added SkPath first SkPoint. Skip added
1184            SkPath initial kMove_Verb, then append remining verbs, points, and conic weights.
1185        */
1186        kExtend_AddPathMode,
1187    };
1188
1189    /** Append src to SkPath, offset by (dx, dy).
1190
1191        If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are
1192        added unaltered. If mode is kExtend_AddPathMode, add line before appending
1193        verbs, points, and conic weights.
1194
1195        @param src   SkPath verbs, points, and conic weights to add
1196        @param dx    offset added to src SkPoint array x coordinates
1197        @param dy    offset added to src SkPoint array y coordinates
1198        @param mode  kAppend_AddPathMode or kExtend_AddPathMode
1199    */
1200    void addPath(const SkPath& src, SkScalar dx, SkScalar dy,
1201                 AddPathMode mode = kAppend_AddPathMode);
1202
1203    /** Append src to SkPath.
1204
1205        If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are
1206        added unaltered. If mode is kExtend_AddPathMode, add line before appending
1207        verbs, points, and conic weights.
1208
1209        @param src   SkPath verbs, points, and conic weights to add
1210        @param mode  kAppend_AddPathMode or kExtend_AddPathMode
1211    */
1212    void addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) {
1213        SkMatrix m;
1214        m.reset();
1215        this->addPath(src, m, mode);
1216    }
1217
1218    /** Append src to SkPath, transformed by matrix. Transformed curves may have different
1219        verbs, points, and conic weights.
1220
1221        If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are
1222        added unaltered. If mode is kExtend_AddPathMode, add line before appending
1223        verbs, points, and conic weights.
1224
1225        @param src     SkPath verbs, points, and conic weights to add
1226        @param matrix  transform applied to src
1227        @param mode    kAppend_AddPathMode or kExtend_AddPathMode
1228    */
1229    void addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode);
1230
1231    /** Append src to SkPath, from back to front.
1232        Reversed src always appends a new contour to SkPath.
1233
1234        @param src  SkPath verbs, points, and conic weights to add
1235    */
1236    void reverseAddPath(const SkPath& src);
1237
1238    /** Offset SkPoint array by (dx, dy). Offset SkPath replaces dst.
1239        If dst is nullptr, SkPath is replaced by offset data.
1240
1241        @param dx   offset added to SkPoint array x coordinates
1242        @param dy   offset added to SkPoint array y coordinates
1243        @param dst  overwritten, translated copy of SkPath; may be nullptr
1244    */
1245    void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
1246
1247    /** Offset SkPoint array by (dx, dy). SkPath is replaced by offset data.
1248
1249        @param dx  offset added to SkPoint array x coordinates
1250        @param dy  offset added to SkPoint array y coordinates
1251    */
1252    void offset(SkScalar dx, SkScalar dy) {
1253        this->offset(dx, dy, this);
1254    }
1255
1256    /** Transform verb array, SkPoint array, and weight by matrix.
1257        transform may change verbs and increase their number.
1258        Transformed SkPath replaces dst; if dst is nullptr, original data
1259        is replaced.
1260
1261        @param matrix  SkMatrix to apply to SkPath
1262        @param dst     overwritten, transformed copy of SkPath; may be nullptr
1263    */
1264    void transform(const SkMatrix& matrix, SkPath* dst) const;
1265
1266    /** Transform verb array, SkPoint array, and weight by matrix.
1267        transform may change verbs and increase their number.
1268        SkPath is replaced by transformed data.
1269
1270        @param matrix  SkMatrix to apply to SkPath
1271    */
1272    void transform(const SkMatrix& matrix) {
1273        this->transform(matrix, this);
1274    }
1275
1276    /** Returns last point on SkPath in lastPt. Returns false if SkPoint array is empty,
1277        storing (0, 0) if lastPt is not nullptr.
1278
1279        @param lastPt  storage for final SkPoint in SkPoint array; may be nullptr
1280        @return        true if SkPoint array contains one or more points
1281    */
1282    bool getLastPt(SkPoint* lastPt) const;
1283
1284    /** Set last point to (x, y). If SkPoint array is empty, append kMove_Verb to
1285        verb array and (x, y) to SkPoint array.
1286
1287        @param x  set x-coordinate of last point
1288        @param y  set y-coordinate of last point
1289    */
1290    void setLastPt(SkScalar x, SkScalar y);
1291
1292    /** Set the last point on the path. If no points have been added, moveTo(p)
1293        is automatically called.
1294
1295        @param p  set value of last point
1296    */
1297    void setLastPt(const SkPoint& p) {
1298        this->setLastPt(p.fX, p.fY);
1299    }
1300
1301    /** \enum SkPath::SegmentMask
1302        SegmentMask constants correspond to each drawing Verb type in SkPath; for
1303        instance, if SkPath only contains lines, only the kLine_SegmentMask bit is set.
1304    */
1305    enum SegmentMask {
1306        kLine_SegmentMask  = 1 << 0, //!< Set if verb array contains kLine_Verb.
1307
1308        /** Set if verb array contains kQuad_Verb. Note that conicTo() may add a quad. */
1309        kQuad_SegmentMask  = 1 << 1,
1310        kConic_SegmentMask = 1 << 2, //!< Set if verb array contains kConic_Verb.
1311        kCubic_SegmentMask = 1 << 3, //!< Set if verb array contains kCubic_Verb.
1312    };
1313
1314    /** Returns a mask, where each set bit corresponds to a SegmentMask constant
1315        if SkPath contains one or more verbs of that type.
1316        Returns zero if SkPath contains no lines, or curves: quads, conics, or cubics.
1317
1318        getSegmentMasks() returns a cached result; it is very fast.
1319
1320        @return  SegmentMask bits or zero
1321    */
1322    uint32_t getSegmentMasks() const { return fPathRef->getSegmentMasks(); }
1323
1324    /** \enum SkPath::Verb
1325        Verb instructs SkPath how to interpret one or more SkPoint and optional conic weight;
1326        manage contour, and terminate SkPath.
1327    */
1328    enum Verb {
1329        kMove_Verb,  //!< Starts new contour at next SkPoint.
1330
1331        /** Adds line from last point to next SkPoint.
1332            Line is a straight segment from SkPoint to SkPoint.
1333        */
1334        kLine_Verb,
1335
1336        /** Adds quad from last point, using control SkPoint, and end SkPoint.
1337            Quad is a parabolic section within tangents from last point to control SkPoint,
1338            and control SkPoint to end SkPoint.
1339        */
1340        kQuad_Verb,
1341
1342        /** Adds conic from last point, using control SkPoint, end SkPoint, and conic weight.
1343            Conic is a elliptical, parabolic, or hyperbolic section within tangents
1344            from last point to control SkPoint, and control SkPoint to end SkPoint, constrained
1345            by conic weight. conic weight less than one is elliptical; equal to one is
1346            parabolic (and identical to Quad); greater than one hyperbolic.
1347        */
1348        kConic_Verb,
1349
1350        /** Adds cubic from last point, using two control points, and end SkPoint.
1351            Cubic is a third-order Bezier_Curve section within tangents from last point
1352            to first control SkPoint, and from second control SkPoint to end SkPoint.
1353        */
1354        kCubic_Verb,
1355        kClose_Verb, //!< Closes contour, connecting last point to kMove_Verb SkPoint.
1356        kDone_Verb,  //!< Terminates SkPath. Not in verb array, but returned by SkPath iterator.
1357    };
1358
1359    /** \class SkPath::Iter
1360    */
1361    class SK_API Iter {
1362    public:
1363
1364        /** Initializes iter with an empty SkPath. next() on iter returns kDone_Verb.
1365            Call setPath to initialize iter at a later time.
1366
1367            @return  iter of empty SkPath
1368        */
1369        Iter();
1370
1371        /** Sets iter to return elements of verb array, SkPoint array, and conic weight in path.
1372            If forceClose is true, iter will add kLine_Verb and kClose_Verb after each
1373            open contour. path is not altered.
1374
1375            @param path        SkPath to iterate
1376            @param forceClose  true if open contours generate kClose_Verb
1377            @return            iter of path
1378        */
1379        Iter(const SkPath& path, bool forceClose);
1380
1381        /** Sets iter to return elements of verb array, SkPoint array, and conic weight in path.
1382            If forceClose is true, iter will add kLine_Verb and kClose_Verb after each
1383            open contour. path is not altered.
1384
1385            @param path        SkPath to iterate
1386            @param forceClose  true if open contours generate kClose_Verb
1387        */
1388        void setPath(const SkPath& path, bool forceClose);
1389
1390        /** Returns next SkPath::Verb in verb array, and advances iter.
1391            When verb array is exhausted, returns kDone_Verb.
1392
1393            Zero to four points are stored in pts, depending on the returned SkPath::Verb.
1394
1395            If doConsumeDegenerates is true, skip consecutive kMove_Verb entries, returning
1396            only the last in the series; and skip very small lines, quads, and conics; and
1397            skip kClose_Verb following kMove_Verb.
1398            if doConsumeDegenerates is true and exact is true, only skip lines, quads, and
1399            conics with zero lengths.
1400
1401            @param pts                   storage for SkPoint data describing returned SkPath::Verb
1402            @param doConsumeDegenerates  if true, skip degenerate verbs
1403            @param exact                 skip zero length curves
1404            @return                      next SkPath::Verb from verb array
1405        */
1406        Verb next(SkPoint pts[4], bool doConsumeDegenerates = true, bool exact = false) {
1407            if (doConsumeDegenerates) {
1408                this->consumeDegenerateSegments(exact);
1409            }
1410            return this->doNext(pts);
1411        }
1412
1413        /** Returns conic weight if next() returned kConic_Verb.
1414
1415            If next() has not been called, or next() did not return kConic_Verb,
1416            result is undefined.
1417
1418            @return  conic weight for conic points returned by next()
1419        */
1420        SkScalar conicWeight() const { return *fConicWeights; }
1421
1422        /** Returns true if last kLine_Verb returned by next() was generated
1423            by kClose_Verb. When true, the end point returned by next() is
1424            also the start point of contour.
1425
1426            If next() has not been called, or next() did not return kLine_Verb,
1427            result is undefined.
1428
1429            @return  true if last kLine_Verb was generated by kClose_Verb
1430        */
1431        bool isCloseLine() const { return SkToBool(fCloseLine); }
1432
1433        /** Returns true if subsequent calls to next() return kClose_Verb before returning
1434            kMove_Verb. if true, contour iter is processing may end with kClose_Verb, or
1435            iter may have been initialized with force close set to true.
1436
1437            @return  true if contour is closed
1438        */
1439        bool isClosedContour() const;
1440
1441    private:
1442        const SkPoint*  fPts;
1443        const uint8_t*  fVerbs;
1444        const uint8_t*  fVerbStop;
1445        const SkScalar* fConicWeights;
1446        SkPoint         fMoveTo;
1447        SkPoint         fLastPt;
1448        SkBool8         fForceClose;
1449        SkBool8         fNeedClose;
1450        SkBool8         fCloseLine;
1451        SkBool8         fSegmentState;
1452
1453        inline const SkPoint& cons_moveTo();
1454        Verb autoClose(SkPoint pts[2]);
1455        void consumeDegenerateSegments(bool exact);
1456        Verb doNext(SkPoint pts[4]);
1457
1458    };
1459
1460    /** \class SkPath::RawIter
1461    */
1462    class SK_API RawIter {
1463    public:
1464
1465        /** Initializes RawIter with an empty SkPath. next() on RawIter returns kDone_Verb.
1466            Call setPath to initialize iter at a later time.
1467
1468            @return  RawIter of empty SkPath
1469        */
1470        RawIter() {}
1471
1472        /** Sets RawIter to return elements of verb array, SkPoint array, and conic weight in path.
1473
1474            @param path  SkPath to iterate
1475            @return      RawIter of path
1476        */
1477        RawIter(const SkPath& path) {
1478            setPath(path);
1479        }
1480
1481        /** Sets iter to return elements of verb array, SkPoint array, and conic weight in path.
1482
1483            @param path  SkPath to iterate
1484        */
1485        void setPath(const SkPath& path) {
1486            fRawIter.setPathRef(*path.fPathRef.get());
1487        }
1488
1489        /** Returns next SkPath::Verb in verb array, and advances RawIter.
1490            When verb array is exhausted, returns kDone_Verb.
1491            Zero to four points are stored in pts, depending on the returned SkPath::Verb.
1492
1493            @param pts  storage for SkPoint data describing returned SkPath::Verb
1494            @return     next SkPath::Verb from verb array
1495        */
1496        Verb next(SkPoint pts[4]) {
1497            return (Verb) fRawIter.next(pts);
1498        }
1499
1500        /** Returns next SkPath::Verb, but does not advance RawIter.
1501
1502            @return  next SkPath::Verb from verb array
1503        */
1504        Verb peek() const {
1505            return (Verb) fRawIter.peek();
1506        }
1507
1508        /** Returns conic weight if next() returned kConic_Verb.
1509
1510            If next() has not been called, or next() did not return kConic_Verb,
1511            result is undefined.
1512
1513            @return  conic weight for conic points returned by next()
1514        */
1515        SkScalar conicWeight() const {
1516            return fRawIter.conicWeight();
1517        }
1518
1519    private:
1520        SkPathRef::Iter fRawIter;
1521        friend class SkPath;
1522
1523    };
1524
1525    /** Returns true if the point (x, y) is contained by SkPath, taking into
1526        account FillType.
1527
1528        @param x  x-coordinate of containment test
1529        @param y  y-coordinate of containment test
1530        @return   true if SkPoint is in SkPath
1531    */
1532    bool contains(SkScalar x, SkScalar y) const;
1533
1534    /** Writes text representation of SkPath to stream. If stream is nullptr, writes to
1535        standard output. Set forceClose to true to get edges used to fill SkPath.
1536        Set dumpAsHex true to generate exact binary representations
1537        of floating point numbers used in SkPoint array and conic weights.
1538
1539        @param stream      writable SkStream receiving SkPath text representation; may be nullptr
1540        @param forceClose  true if missing kClose_Verb is output
1541        @param dumpAsHex   true if SkScalar values are written as hexadecimal
1542    */
1543    void dump(SkWStream* stream, bool forceClose, bool dumpAsHex) const;
1544
1545    /** Writes text representation of SkPath to standard output. The representation may be
1546        directly compiled as C++ code. Floating point values are written
1547        with limited precision; it may not be possible to reconstruct original SkPath
1548        from output.
1549    */
1550    void dump() const;
1551
1552    /** Writes text representation of SkPath to standard output. The representation may be
1553        directly compiled as C++ code. Floating point values are written
1554        in hexadecimal to preserve their exact bit pattern. The output reconstructs the
1555        original SkPath.
1556
1557        Use instead of dump() when submitting
1558    */
1559    void dumpHex() const;
1560
1561    /** Writes SkPath to buffer, returning the number of bytes written.
1562        Pass nullptr to obtain the storage size.
1563
1564        Writes SkPath::FillType, verb array, SkPoint array, conic weight, and
1565        additionally writes computed information like SkPath::Convexity and bounds.
1566
1567        Use only be used in concert with readFromMemory();
1568        the format used for SkPath in memory is not guaranteed.
1569
1570        @param buffer  storage for SkPath; may be nullptr
1571        @return        size of storage required for SkPath; always a multiple of 4
1572    */
1573    size_t writeToMemory(void* buffer) const;
1574
1575    /** Write SkPath to buffer, returning the buffer written to, wrapped in SkData.
1576
1577        serialize() writes SkPath::FillType, verb array, SkPoint array, conic weight, and
1578        additionally writes computed information like SkPath::Convexity and bounds.
1579
1580        serialize() should only be used in concert with readFromMemory().
1581        The format used for SkPath in memory is not guaranteed.
1582
1583        @return  SkPath data wrapped in SkData buffer
1584    */
1585    sk_sp<SkData> serialize() const;
1586
1587    /** Initializes SkPath from buffer of size length. Returns zero if the buffer is
1588        data is inconsistent, or the length is too small.
1589
1590        Reads SkPath::FillType, verb array, SkPoint array, conic weight, and
1591        additionally reads computed information like SkPath::Convexity and bounds.
1592
1593        Used only in concert with writeToMemory();
1594        the format used for SkPath in memory is not guaranteed.
1595
1596        @param buffer  storage for SkPath
1597        @param length  buffer size in bytes; must be multiple of 4
1598        @return        number of bytes read, or zero on failure
1599    */
1600    size_t readFromMemory(const void* buffer, size_t length);
1601
1602    /** Returns a non-zero, globally unique value. A different value is returned
1603        if verb array, SkPoint array, or conic weight changes.
1604
1605        Setting SkPath::FillType does not change generation id.
1606
1607        Each time the path is modified, a different generation id will be returned.
1608
1609        @return  non-zero, globally unique value
1610    */
1611    uint32_t getGenerationID() const;
1612
1613#ifdef SK_SUPPORT_DIRECT_PATHREF_VALIDATION
1614    /** Returns if SkPath data is consistent. Corrupt SkPath data is detected if
1615        internal values are out of range or internal storage does not match
1616        array dimensions.
1617
1618        @return  true if SkPath data is consistent
1619    */
1620    bool isValid() const { return this->isValidImpl() && fPathRef->isValid(); }
1621#else
1622    bool isValid() const { return this->isValidImpl(); }
1623    bool pathRefIsValid() const { return fPathRef->isValid(); }
1624#endif
1625
1626private:
1627    enum SerializationOffsets {
1628        kType_SerializationShift = 28,       // requires 4 bits
1629        kDirection_SerializationShift = 26,  // requires 2 bits, could be reused - ignored on read.
1630        kIsVolatile_SerializationShift = 25, // requires 1 bit
1631        // 1 free bit at 24
1632        kConvexity_SerializationShift = 16,  // requires 8 bits, could be reused - ignored on read.
1633        kFillType_SerializationShift = 8,    // requires 8 bits
1634        // low-8-bits are version
1635    };
1636
1637    enum SerializationVersions {
1638        // kPathPrivFirstDirection_Version = 1,
1639        kPathPrivLastMoveToIndex_Version = 2,
1640        kPathPrivTypeEnumVersion = 3,
1641        kCurrent_Version = 3
1642    };
1643
1644    enum SerializationType {
1645        kGeneral = 0,
1646        kRRect = 1
1647    };
1648
1649    sk_sp<SkPathRef>                                     fPathRef;
1650    int                                                  fLastMoveToIndex;
1651    uint8_t                                              fFillType;
1652    mutable SkAtomic<Convexity, sk_memory_order_relaxed> fConvexity;
1653    mutable SkAtomic<uint8_t, sk_memory_order_relaxed>   fFirstDirection;// SkPathPriv::FirstDirection
1654    SkBool8                                              fIsVolatile;
1655
1656    /** Resets all fields other than fPathRef to their initial 'empty' values.
1657     *  Assumes the caller has already emptied fPathRef.
1658     *  On Android increments fGenerationID without reseting it.
1659     */
1660    void resetFields();
1661
1662    /** Sets all fields other than fPathRef to the values in 'that'.
1663     *  Assumes the caller has already set fPathRef.
1664     *  Doesn't change fGenerationID or fSourcePath on Android.
1665     */
1666    void copyFields(const SkPath& that);
1667
1668    size_t writeToMemoryAsRRect(int32_t packedHeader, void* buffer) const;
1669    size_t readFromMemoryAsRRect(const void* buffer) const;
1670
1671    friend class Iter;
1672    friend class SkPathPriv;
1673    friend class SkPathStroker;
1674
1675    /*  Append, in reverse order, the first contour of path, ignoring path's
1676        last point. If no moveTo() call has been made for this contour, the
1677        first point is automatically set to (0,0).
1678    */
1679    void reversePathTo(const SkPath&);
1680
1681    // called before we add points for lineTo, quadTo, cubicTo, checking to see
1682    // if we need to inject a leading moveTo first
1683    //
1684    //  SkPath path; path.lineTo(...);   <--- need a leading moveTo(0, 0)
1685    // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo)
1686    //
1687    inline void injectMoveToIfNeeded();
1688
1689    inline bool hasOnlyMoveTos() const;
1690
1691    Convexity internalGetConvexity() const;
1692
1693    /** Asserts if SkPath data is inconsistent.
1694        Debugging check intended for internal use only.
1695     */
1696    SkDEBUGCODE(void validate() const { SkASSERT(this->isValidImpl()); } )
1697    bool isValidImpl() const;
1698    SkDEBUGCODE(void validateRef() const { fPathRef->validate(); } )
1699
1700    bool isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts,
1701                       bool* isClosed, Direction* direction) const;
1702
1703    // called by stroker to see if all points (in the last contour) are equal and worthy of a cap
1704    bool isZeroLengthSincePoint(int startPtIndex) const;
1705
1706    /** Returns if the path can return a bound at no cost (true) or will have to
1707        perform some computation (false).
1708     */
1709    bool hasComputedBounds() const {
1710        SkDEBUGCODE(this->validate();)
1711        return fPathRef->hasComputedBounds();
1712    }
1713
1714
1715    // 'rect' needs to be sorted
1716    void setBounds(const SkRect& rect) {
1717        SkPathRef::Editor ed(&fPathRef);
1718
1719        ed.setBounds(rect);
1720    }
1721
1722    void setPt(int index, SkScalar x, SkScalar y);
1723
1724    friend class SkAutoPathBoundsUpdate;
1725    friend class SkAutoDisableOvalCheck;
1726    friend class SkAutoDisableDirectionCheck;
1727    friend class SkPathWriter;
1728    friend class SkOpBuilder;
1729    friend class SkBench_AddPathTest; // perf test reversePathTo
1730    friend class PathTest_Private; // unit test reversePathTo
1731    friend class ForceIsRRect_Private; // unit test isRRect
1732    friend class FuzzPath; // for legacy access to validateRef
1733};
1734
1735#endif
1736