• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import android.annotation.FloatRange;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.Size;
23 
24 import dalvik.annotation.optimization.CriticalNative;
25 import dalvik.annotation.optimization.FastNative;
26 
27 /**
28  * The Path class encapsulates compound (multiple contour) geometric paths
29  * consisting of straight line segments, quadratic curves, and cubic curves.
30  * It can be drawn with canvas.drawPath(path, paint), either filled or stroked
31  * (based on the paint's Style), or it can be used for clipping or to draw
32  * text on a path.
33  */
34 public class Path {
35     /**
36      * @hide
37      */
38     public long mNativePath;
39 
40     /**
41      * @hide
42      */
43     public boolean isSimplePath = true;
44     /**
45      * @hide
46      */
47     public Region rects;
48     private Direction mLastDirection = null;
49 
50     /**
51      * Create an empty path
52      */
Path()53     public Path() {
54         mNativePath = nInit();
55     }
56 
57     /**
58      * Create a new path, copying the contents from the src path.
59      *
60      * @param src The path to copy from when initializing the new path
61      */
Path(Path src)62     public Path(Path src) {
63         long valNative = 0;
64         if (src != null) {
65             valNative = src.mNativePath;
66             isSimplePath = src.isSimplePath;
67             if (src.rects != null) {
68                 rects = new Region(src.rects);
69             }
70         }
71         mNativePath = nInit(valNative);
72     }
73 
74     /**
75      * Clear any lines and curves from the path, making it empty.
76      * This does NOT change the fill-type setting.
77      */
reset()78     public void reset() {
79         isSimplePath = true;
80         mLastDirection = null;
81         if (rects != null) rects.setEmpty();
82         // We promised not to change this, so preserve it around the native
83         // call, which does now reset fill type.
84         final FillType fillType = getFillType();
85         nReset(mNativePath);
86         setFillType(fillType);
87     }
88 
89     /**
90      * Rewinds the path: clears any lines and curves from the path but
91      * keeps the internal data structure for faster reuse.
92      */
rewind()93     public void rewind() {
94         isSimplePath = true;
95         mLastDirection = null;
96         if (rects != null) rects.setEmpty();
97         nRewind(mNativePath);
98     }
99 
100     /** Replace the contents of this with the contents of src.
101     */
set(@onNull Path src)102     public void set(@NonNull Path src) {
103         if (this == src) {
104             return;
105         }
106         isSimplePath = src.isSimplePath;
107         nSet(mNativePath, src.mNativePath);
108         if (!isSimplePath) {
109             return;
110         }
111 
112         if (rects != null && src.rects != null) {
113             rects.set(src.rects);
114         } else if (rects != null && src.rects == null) {
115             rects.setEmpty();
116         } else if (src.rects != null) {
117             rects = new Region(src.rects);
118         }
119     }
120 
121     /**
122      * The logical operations that can be performed when combining two paths.
123      *
124      * @see #op(Path, android.graphics.Path.Op)
125      * @see #op(Path, Path, android.graphics.Path.Op)
126      */
127     public enum Op {
128         /**
129          * Subtract the second path from the first path.
130          */
131         DIFFERENCE,
132         /**
133          * Intersect the two paths.
134          */
135         INTERSECT,
136         /**
137          * Union (inclusive-or) the two paths.
138          */
139         UNION,
140         /**
141          * Exclusive-or the two paths.
142          */
143         XOR,
144         /**
145          * Subtract the first path from the second path.
146          */
147         REVERSE_DIFFERENCE
148     }
149 
150     /**
151      * Set this path to the result of applying the Op to this path and the specified path.
152      * The resulting path will be constructed from non-overlapping contours.
153      * The curve order is reduced where possible so that cubics may be turned
154      * into quadratics, and quadratics maybe turned into lines.
155      *
156      * @param path The second operand (for difference, the subtrahend)
157      *
158      * @return True if operation succeeded, false otherwise and this path remains unmodified.
159      *
160      * @see Op
161      * @see #op(Path, Path, android.graphics.Path.Op)
162      */
op(Path path, Op op)163     public boolean op(Path path, Op op) {
164         return op(this, path, op);
165     }
166 
167     /**
168      * Set this path to the result of applying the Op to the two specified paths.
169      * The resulting path will be constructed from non-overlapping contours.
170      * The curve order is reduced where possible so that cubics may be turned
171      * into quadratics, and quadratics maybe turned into lines.
172      *
173      * @param path1 The first operand (for difference, the minuend)
174      * @param path2 The second operand (for difference, the subtrahend)
175      *
176      * @return True if operation succeeded, false otherwise and this path remains unmodified.
177      *
178      * @see Op
179      * @see #op(Path, android.graphics.Path.Op)
180      */
op(Path path1, Path path2, Op op)181     public boolean op(Path path1, Path path2, Op op) {
182         if (nOp(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
183             isSimplePath = false;
184             rects = null;
185             return true;
186         }
187         return false;
188     }
189 
190     /**
191      * Returns the path's convexity, as defined by the content of the path.
192      * <p>
193      * A path is convex if it has a single contour, and only ever curves in a
194      * single direction.
195      * <p>
196      * This function will calculate the convexity of the path from its control
197      * points, and cache the result.
198      *
199      * @return True if the path is convex.
200      */
isConvex()201     public boolean isConvex() {
202         return nIsConvex(mNativePath);
203     }
204 
205     /**
206      * Enum for the ways a path may be filled.
207      */
208     public enum FillType {
209         // these must match the values in SkPath.h
210         /**
211          * Specifies that "inside" is computed by a non-zero sum of signed
212          * edge crossings.
213          */
214         WINDING         (0),
215         /**
216          * Specifies that "inside" is computed by an odd number of edge
217          * crossings.
218          */
219         EVEN_ODD        (1),
220         /**
221          * Same as {@link #WINDING}, but draws outside of the path, rather than inside.
222          */
223         INVERSE_WINDING (2),
224         /**
225          * Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside.
226          */
227         INVERSE_EVEN_ODD(3);
228 
FillType(int ni)229         FillType(int ni) {
230             nativeInt = ni;
231         }
232 
233         final int nativeInt;
234     }
235 
236     // these must be in the same order as their native values
237     static final FillType[] sFillTypeArray = {
238         FillType.WINDING,
239         FillType.EVEN_ODD,
240         FillType.INVERSE_WINDING,
241         FillType.INVERSE_EVEN_ODD
242     };
243 
244     /**
245      * Return the path's fill type. This defines how "inside" is
246      * computed. The default value is WINDING.
247      *
248      * @return the path's fill type
249      */
getFillType()250     public FillType getFillType() {
251         return sFillTypeArray[nGetFillType(mNativePath)];
252     }
253 
254     /**
255      * Set the path's fill type. This defines how "inside" is computed.
256      *
257      * @param ft The new fill type for this path
258      */
setFillType(FillType ft)259     public void setFillType(FillType ft) {
260         nSetFillType(mNativePath, ft.nativeInt);
261     }
262 
263     /**
264      * Returns true if the filltype is one of the INVERSE variants
265      *
266      * @return true if the filltype is one of the INVERSE variants
267      */
isInverseFillType()268     public boolean isInverseFillType() {
269         final int ft = nGetFillType(mNativePath);
270         return (ft & FillType.INVERSE_WINDING.nativeInt) != 0;
271     }
272 
273     /**
274      * Toggles the INVERSE state of the filltype
275      */
toggleInverseFillType()276     public void toggleInverseFillType() {
277         int ft = nGetFillType(mNativePath);
278         ft ^= FillType.INVERSE_WINDING.nativeInt;
279         nSetFillType(mNativePath, ft);
280     }
281 
282     /**
283      * Returns true if the path is empty (contains no lines or curves)
284      *
285      * @return true if the path is empty (contains no lines or curves)
286      */
isEmpty()287     public boolean isEmpty() {
288         return nIsEmpty(mNativePath);
289     }
290 
291     /**
292      * Returns true if the path specifies a rectangle. If so, and if rect is
293      * not null, set rect to the bounds of the path. If the path does not
294      * specify a rectangle, return false and ignore rect.
295      *
296      * @param rect If not null, returns the bounds of the path if it specifies
297      *             a rectangle
298      * @return     true if the path specifies a rectangle
299      */
isRect(RectF rect)300     public boolean isRect(RectF rect) {
301         return nIsRect(mNativePath, rect);
302     }
303 
304     /**
305      * Compute the bounds of the control points of the path, and write the
306      * answer into bounds. If the path contains 0 or 1 points, the bounds is
307      * set to (0,0,0,0)
308      *
309      * @param bounds Returns the computed bounds of the path's control points.
310      * @param exact This parameter is no longer used.
311      */
312     @SuppressWarnings({"UnusedDeclaration"})
computeBounds(RectF bounds, boolean exact)313     public void computeBounds(RectF bounds, boolean exact) {
314         nComputeBounds(mNativePath, bounds);
315     }
316 
317     /**
318      * Hint to the path to prepare for adding more points. This can allow the
319      * path to more efficiently allocate its storage.
320      *
321      * @param extraPtCount The number of extra points that may be added to this
322      *                     path
323      */
incReserve(int extraPtCount)324     public void incReserve(int extraPtCount) {
325         nIncReserve(mNativePath, extraPtCount);
326     }
327 
328     /**
329      * Set the beginning of the next contour to the point (x,y).
330      *
331      * @param x The x-coordinate of the start of a new contour
332      * @param y The y-coordinate of the start of a new contour
333      */
moveTo(float x, float y)334     public void moveTo(float x, float y) {
335         nMoveTo(mNativePath, x, y);
336     }
337 
338     /**
339      * Set the beginning of the next contour relative to the last point on the
340      * previous contour. If there is no previous contour, this is treated the
341      * same as moveTo().
342      *
343      * @param dx The amount to add to the x-coordinate of the end of the
344      *           previous contour, to specify the start of a new contour
345      * @param dy The amount to add to the y-coordinate of the end of the
346      *           previous contour, to specify the start of a new contour
347      */
rMoveTo(float dx, float dy)348     public void rMoveTo(float dx, float dy) {
349         nRMoveTo(mNativePath, dx, dy);
350     }
351 
352     /**
353      * Add a line from the last point to the specified point (x,y).
354      * If no moveTo() call has been made for this contour, the first point is
355      * automatically set to (0,0).
356      *
357      * @param x The x-coordinate of the end of a line
358      * @param y The y-coordinate of the end of a line
359      */
lineTo(float x, float y)360     public void lineTo(float x, float y) {
361         isSimplePath = false;
362         nLineTo(mNativePath, x, y);
363     }
364 
365     /**
366      * Same as lineTo, but the coordinates are considered relative to the last
367      * point on this contour. If there is no previous point, then a moveTo(0,0)
368      * is inserted automatically.
369      *
370      * @param dx The amount to add to the x-coordinate of the previous point on
371      *           this contour, to specify a line
372      * @param dy The amount to add to the y-coordinate of the previous point on
373      *           this contour, to specify a line
374      */
rLineTo(float dx, float dy)375     public void rLineTo(float dx, float dy) {
376         isSimplePath = false;
377         nRLineTo(mNativePath, dx, dy);
378     }
379 
380     /**
381      * Add a quadratic bezier from the last point, approaching control point
382      * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
383      * this contour, the first point is automatically set to (0,0).
384      *
385      * @param x1 The x-coordinate of the control point on a quadratic curve
386      * @param y1 The y-coordinate of the control point on a quadratic curve
387      * @param x2 The x-coordinate of the end point on a quadratic curve
388      * @param y2 The y-coordinate of the end point on a quadratic curve
389      */
quadTo(float x1, float y1, float x2, float y2)390     public void quadTo(float x1, float y1, float x2, float y2) {
391         isSimplePath = false;
392         nQuadTo(mNativePath, x1, y1, x2, y2);
393     }
394 
395     /**
396      * Same as quadTo, but the coordinates are considered relative to the last
397      * point on this contour. If there is no previous point, then a moveTo(0,0)
398      * is inserted automatically.
399      *
400      * @param dx1 The amount to add to the x-coordinate of the last point on
401      *            this contour, for the control point of a quadratic curve
402      * @param dy1 The amount to add to the y-coordinate of the last point on
403      *            this contour, for the control point of a quadratic curve
404      * @param dx2 The amount to add to the x-coordinate of the last point on
405      *            this contour, for the end point of a quadratic curve
406      * @param dy2 The amount to add to the y-coordinate of the last point on
407      *            this contour, for the end point of a quadratic curve
408      */
rQuadTo(float dx1, float dy1, float dx2, float dy2)409     public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
410         isSimplePath = false;
411         nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
412     }
413 
414     /**
415      * Add a cubic bezier from the last point, approaching control points
416      * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
417      * made for this contour, the first point is automatically set to (0,0).
418      *
419      * @param x1 The x-coordinate of the 1st control point on a cubic curve
420      * @param y1 The y-coordinate of the 1st control point on a cubic curve
421      * @param x2 The x-coordinate of the 2nd control point on a cubic curve
422      * @param y2 The y-coordinate of the 2nd control point on a cubic curve
423      * @param x3 The x-coordinate of the end point on a cubic curve
424      * @param y3 The y-coordinate of the end point on a cubic curve
425      */
cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)426     public void cubicTo(float x1, float y1, float x2, float y2,
427                         float x3, float y3) {
428         isSimplePath = false;
429         nCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
430     }
431 
432     /**
433      * Same as cubicTo, but the coordinates are considered relative to the
434      * current point on this contour. If there is no previous point, then a
435      * moveTo(0,0) is inserted automatically.
436      */
rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)437     public void rCubicTo(float x1, float y1, float x2, float y2,
438                          float x3, float y3) {
439         isSimplePath = false;
440         nRCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
441     }
442 
443     /**
444      * Append the specified arc to the path as a new contour. If the start of
445      * the path is different from the path's current last point, then an
446      * automatic lineTo() is added to connect the current contour to the
447      * start of the arc. However, if the path is empty, then we call moveTo()
448      * with the first point of the arc.
449      *
450      * @param oval        The bounds of oval defining shape and size of the arc
451      * @param startAngle  Starting angle (in degrees) where the arc begins
452      * @param sweepAngle  Sweep angle (in degrees) measured clockwise, treated
453      *                    mod 360.
454      * @param forceMoveTo If true, always begin a new contour with the arc
455      */
arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)456     public void arcTo(RectF oval, float startAngle, float sweepAngle,
457                       boolean forceMoveTo) {
458         arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, forceMoveTo);
459     }
460 
461     /**
462      * Append the specified arc to the path as a new contour. If the start of
463      * the path is different from the path's current last point, then an
464      * automatic lineTo() is added to connect the current contour to the
465      * start of the arc. However, if the path is empty, then we call moveTo()
466      * with the first point of the arc.
467      *
468      * @param oval        The bounds of oval defining shape and size of the arc
469      * @param startAngle  Starting angle (in degrees) where the arc begins
470      * @param sweepAngle  Sweep angle (in degrees) measured clockwise
471      */
arcTo(RectF oval, float startAngle, float sweepAngle)472     public void arcTo(RectF oval, float startAngle, float sweepAngle) {
473         arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, false);
474     }
475 
476     /**
477      * Append the specified arc to the path as a new contour. If the start of
478      * the path is different from the path's current last point, then an
479      * automatic lineTo() is added to connect the current contour to the
480      * start of the arc. However, if the path is empty, then we call moveTo()
481      * with the first point of the arc.
482      *
483      * @param startAngle  Starting angle (in degrees) where the arc begins
484      * @param sweepAngle  Sweep angle (in degrees) measured clockwise, treated
485      *                    mod 360.
486      * @param forceMoveTo If true, always begin a new contour with the arc
487      */
arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo)488     public void arcTo(float left, float top, float right, float bottom, float startAngle,
489             float sweepAngle, boolean forceMoveTo) {
490         isSimplePath = false;
491         nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
492     }
493 
494     /**
495      * Close the current contour. If the current point is not equal to the
496      * first point of the contour, a line segment is automatically added.
497      */
close()498     public void close() {
499         isSimplePath = false;
500         nClose(mNativePath);
501     }
502 
503     /**
504      * Specifies how closed shapes (e.g. rects, ovals) are oriented when they
505      * are added to a path.
506      */
507     public enum Direction {
508         /** clockwise */
509         CW  (0),    // must match enum in SkPath.h
510         /** counter-clockwise */
511         CCW (1);    // must match enum in SkPath.h
512 
Direction(int ni)513         Direction(int ni) {
514             nativeInt = ni;
515         }
516         final int nativeInt;
517     }
518 
detectSimplePath(float left, float top, float right, float bottom, Direction dir)519     private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) {
520         if (mLastDirection == null) {
521             mLastDirection = dir;
522         }
523         if (mLastDirection != dir) {
524             isSimplePath = false;
525         } else {
526             if (rects == null) rects = new Region();
527             rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION);
528         }
529     }
530 
531     /**
532      * Add a closed rectangle contour to the path
533      *
534      * @param rect The rectangle to add as a closed contour to the path
535      * @param dir  The direction to wind the rectangle's contour
536      */
addRect(RectF rect, Direction dir)537     public void addRect(RectF rect, Direction dir) {
538         addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
539     }
540 
541     /**
542      * Add a closed rectangle contour to the path
543      *
544      * @param left   The left side of a rectangle to add to the path
545      * @param top    The top of a rectangle to add to the path
546      * @param right  The right side of a rectangle to add to the path
547      * @param bottom The bottom of a rectangle to add to the path
548      * @param dir    The direction to wind the rectangle's contour
549      */
addRect(float left, float top, float right, float bottom, Direction dir)550     public void addRect(float left, float top, float right, float bottom, Direction dir) {
551         detectSimplePath(left, top, right, bottom, dir);
552         nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt);
553     }
554 
555     /**
556      * Add a closed oval contour to the path
557      *
558      * @param oval The bounds of the oval to add as a closed contour to the path
559      * @param dir  The direction to wind the oval's contour
560      */
addOval(RectF oval, Direction dir)561     public void addOval(RectF oval, Direction dir) {
562         addOval(oval.left, oval.top, oval.right, oval.bottom, dir);
563     }
564 
565     /**
566      * Add a closed oval contour to the path
567      *
568      * @param dir The direction to wind the oval's contour
569      */
addOval(float left, float top, float right, float bottom, Direction dir)570     public void addOval(float left, float top, float right, float bottom, Direction dir) {
571         isSimplePath = false;
572         nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt);
573     }
574 
575     /**
576      * Add a closed circle contour to the path
577      *
578      * @param x   The x-coordinate of the center of a circle to add to the path
579      * @param y   The y-coordinate of the center of a circle to add to the path
580      * @param radius The radius of a circle to add to the path
581      * @param dir    The direction to wind the circle's contour
582      */
addCircle(float x, float y, float radius, Direction dir)583     public void addCircle(float x, float y, float radius, Direction dir) {
584         isSimplePath = false;
585         nAddCircle(mNativePath, x, y, radius, dir.nativeInt);
586     }
587 
588     /**
589      * Add the specified arc to the path as a new contour.
590      *
591      * @param oval The bounds of oval defining the shape and size of the arc
592      * @param startAngle Starting angle (in degrees) where the arc begins
593      * @param sweepAngle Sweep angle (in degrees) measured clockwise
594      */
addArc(RectF oval, float startAngle, float sweepAngle)595     public void addArc(RectF oval, float startAngle, float sweepAngle) {
596         addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle);
597     }
598 
599     /**
600      * Add the specified arc to the path as a new contour.
601      *
602      * @param startAngle Starting angle (in degrees) where the arc begins
603      * @param sweepAngle Sweep angle (in degrees) measured clockwise
604      */
addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle)605     public void addArc(float left, float top, float right, float bottom, float startAngle,
606             float sweepAngle) {
607         isSimplePath = false;
608         nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
609     }
610 
611     /**
612         * Add a closed round-rectangle contour to the path
613      *
614      * @param rect The bounds of a round-rectangle to add to the path
615      * @param rx   The x-radius of the rounded corners on the round-rectangle
616      * @param ry   The y-radius of the rounded corners on the round-rectangle
617      * @param dir  The direction to wind the round-rectangle's contour
618      */
addRoundRect(RectF rect, float rx, float ry, Direction dir)619     public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
620         addRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, dir);
621     }
622 
623     /**
624      * Add a closed round-rectangle contour to the path
625      *
626      * @param rx   The x-radius of the rounded corners on the round-rectangle
627      * @param ry   The y-radius of the rounded corners on the round-rectangle
628      * @param dir  The direction to wind the round-rectangle's contour
629      */
addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Direction dir)630     public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
631             Direction dir) {
632         isSimplePath = false;
633         nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
634     }
635 
636     /**
637      * Add a closed round-rectangle contour to the path. Each corner receives
638      * two radius values [X, Y]. The corners are ordered top-left, top-right,
639      * bottom-right, bottom-left
640      *
641      * @param rect The bounds of a round-rectangle to add to the path
642      * @param radii Array of 8 values, 4 pairs of [X,Y] radii
643      * @param dir  The direction to wind the round-rectangle's contour
644      */
addRoundRect(RectF rect, float[] radii, Direction dir)645     public void addRoundRect(RectF rect, float[] radii, Direction dir) {
646         if (rect == null) {
647             throw new NullPointerException("need rect parameter");
648         }
649         addRoundRect(rect.left, rect.top, rect.right, rect.bottom, radii, dir);
650     }
651 
652     /**
653      * Add a closed round-rectangle contour to the path. Each corner receives
654      * two radius values [X, Y]. The corners are ordered top-left, top-right,
655      * bottom-right, bottom-left
656      *
657      * @param radii Array of 8 values, 4 pairs of [X,Y] radii
658      * @param dir  The direction to wind the round-rectangle's contour
659      */
addRoundRect(float left, float top, float right, float bottom, float[] radii, Direction dir)660     public void addRoundRect(float left, float top, float right, float bottom, float[] radii,
661             Direction dir) {
662         if (radii.length < 8) {
663             throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
664         }
665         isSimplePath = false;
666         nAddRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
667     }
668 
669     /**
670      * Add a copy of src to the path, offset by (dx,dy)
671      *
672      * @param src The path to add as a new contour
673      * @param dx  The amount to translate the path in X as it is added
674      */
addPath(Path src, float dx, float dy)675     public void addPath(Path src, float dx, float dy) {
676         isSimplePath = false;
677         nAddPath(mNativePath, src.mNativePath, dx, dy);
678     }
679 
680     /**
681      * Add a copy of src to the path
682      *
683      * @param src The path that is appended to the current path
684      */
addPath(Path src)685     public void addPath(Path src) {
686         isSimplePath = false;
687         nAddPath(mNativePath, src.mNativePath);
688     }
689 
690     /**
691      * Add a copy of src to the path, transformed by matrix
692      *
693      * @param src The path to add as a new contour
694      */
addPath(Path src, Matrix matrix)695     public void addPath(Path src, Matrix matrix) {
696         if (!src.isSimplePath) isSimplePath = false;
697         nAddPath(mNativePath, src.mNativePath, matrix.native_instance);
698     }
699 
700     /**
701      * Offset the path by (dx,dy)
702      *
703      * @param dx  The amount in the X direction to offset the entire path
704      * @param dy  The amount in the Y direction to offset the entire path
705      * @param dst The translated path is written here. If this is null, then
706      *            the original path is modified.
707      */
offset(float dx, float dy, @Nullable Path dst)708     public void offset(float dx, float dy, @Nullable Path dst) {
709         if (dst != null) {
710             dst.set(this);
711         } else {
712             dst = this;
713         }
714         dst.offset(dx, dy);
715     }
716 
717     /**
718      * Offset the path by (dx,dy)
719      *
720      * @param dx The amount in the X direction to offset the entire path
721      * @param dy The amount in the Y direction to offset the entire path
722      */
offset(float dx, float dy)723     public void offset(float dx, float dy) {
724         if (isSimplePath && rects == null) {
725             // nothing to offset
726             return;
727         }
728         if (isSimplePath && dx == Math.rint(dx) && dy == Math.rint(dy)) {
729             rects.translate((int) dx, (int) dy);
730         } else {
731             isSimplePath = false;
732         }
733         nOffset(mNativePath, dx, dy);
734     }
735 
736     /**
737      * Sets the last point of the path.
738      *
739      * @param dx The new X coordinate for the last point
740      * @param dy The new Y coordinate for the last point
741      */
setLastPoint(float dx, float dy)742     public void setLastPoint(float dx, float dy) {
743         isSimplePath = false;
744         nSetLastPoint(mNativePath, dx, dy);
745     }
746 
747     /**
748      * Transform the points in this path by matrix, and write the answer
749      * into dst. If dst is null, then the the original path is modified.
750      *
751      * @param matrix The matrix to apply to the path
752      * @param dst    The transformed path is written here. If dst is null,
753      *               then the the original path is modified
754      */
transform(Matrix matrix, Path dst)755     public void transform(Matrix matrix, Path dst) {
756         long dstNative = 0;
757         if (dst != null) {
758             dst.isSimplePath = false;
759             dstNative = dst.mNativePath;
760         }
761         nTransform(mNativePath, matrix.native_instance, dstNative);
762     }
763 
764     /**
765      * Transform the points in this path by matrix.
766      *
767      * @param matrix The matrix to apply to the path
768      */
transform(Matrix matrix)769     public void transform(Matrix matrix) {
770         isSimplePath = false;
771         nTransform(mNativePath, matrix.native_instance);
772     }
773 
finalize()774     protected void finalize() throws Throwable {
775         try {
776             nFinalize(mNativePath);
777             mNativePath = 0;  //  Other finalizers can still call us.
778         } finally {
779             super.finalize();
780         }
781     }
782 
783     /** @hide */
readOnlyNI()784     public final long readOnlyNI() {
785         return mNativePath;
786     }
787 
mutateNI()788     final long mutateNI() {
789         isSimplePath = false;
790         return mNativePath;
791     }
792 
793     /**
794      * Approximate the <code>Path</code> with a series of line segments.
795      * This returns float[] with the array containing point components.
796      * There are three components for each point, in order:
797      * <ul>
798      *     <li>Fraction along the length of the path that the point resides</li>
799      *     <li>The x coordinate of the point</li>
800      *     <li>The y coordinate of the point</li>
801      * </ul>
802      * <p>Two points may share the same fraction along its length when there is
803      * a move action within the Path.</p>
804      *
805      * @param acceptableError The acceptable error for a line on the
806      *                        Path. Typically this would be 0.5 so that
807      *                        the error is less than half a pixel.
808      * @return An array of components for points approximating the Path.
809      */
810     @NonNull
811     @Size(min = 6, multiple = 3)
approximate(@loatRangefrom = 0) float acceptableError)812     public float[] approximate(@FloatRange(from = 0) float acceptableError) {
813         if (acceptableError < 0) {
814             throw new IllegalArgumentException("AcceptableError must be greater than or equal to 0");
815         }
816         return nApproximate(mNativePath, acceptableError);
817     }
818 
819     // ------------------ Regular JNI ------------------------
820 
nInit()821     private static native long nInit();
nInit(long nPath)822     private static native long nInit(long nPath);
nFinalize(long nPath)823     private static native void nFinalize(long nPath);
nSet(long native_dst, long nSrc)824     private static native void nSet(long native_dst, long nSrc);
nComputeBounds(long nPath, RectF bounds)825     private static native void nComputeBounds(long nPath, RectF bounds);
nIncReserve(long nPath, int extraPtCount)826     private static native void nIncReserve(long nPath, int extraPtCount);
nMoveTo(long nPath, float x, float y)827     private static native void nMoveTo(long nPath, float x, float y);
nRMoveTo(long nPath, float dx, float dy)828     private static native void nRMoveTo(long nPath, float dx, float dy);
nLineTo(long nPath, float x, float y)829     private static native void nLineTo(long nPath, float x, float y);
nRLineTo(long nPath, float dx, float dy)830     private static native void nRLineTo(long nPath, float dx, float dy);
nQuadTo(long nPath, float x1, float y1, float x2, float y2)831     private static native void nQuadTo(long nPath, float x1, float y1, float x2, float y2);
nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2)832     private static native void nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2);
nCubicTo(long nPath, float x1, float y1, float x2, float y2, float x3, float y3)833     private static native void nCubicTo(long nPath, float x1, float y1, float x2, float y2,
834             float x3, float y3);
nRCubicTo(long nPath, float x1, float y1, float x2, float y2, float x3, float y3)835     private static native void nRCubicTo(long nPath, float x1, float y1, float x2, float y2,
836             float x3, float y3);
nArcTo(long nPath, float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo)837     private static native void nArcTo(long nPath, float left, float top, float right, float bottom,
838             float startAngle, float sweepAngle, boolean forceMoveTo);
nClose(long nPath)839     private static native void nClose(long nPath);
nAddRect(long nPath, float left, float top, float right, float bottom, int dir)840     private static native void nAddRect(long nPath, float left, float top,
841             float right, float bottom, int dir);
nAddOval(long nPath, float left, float top, float right, float bottom, int dir)842     private static native void nAddOval(long nPath, float left, float top,
843             float right, float bottom, int dir);
nAddCircle(long nPath, float x, float y, float radius, int dir)844     private static native void nAddCircle(long nPath, float x, float y, float radius, int dir);
nAddArc(long nPath, float left, float top, float right, float bottom, float startAngle, float sweepAngle)845     private static native void nAddArc(long nPath, float left, float top, float right, float bottom,
846             float startAngle, float sweepAngle);
nAddRoundRect(long nPath, float left, float top, float right, float bottom, float rx, float ry, int dir)847     private static native void nAddRoundRect(long nPath, float left, float top,
848             float right, float bottom, float rx, float ry, int dir);
nAddRoundRect(long nPath, float left, float top, float right, float bottom, float[] radii, int dir)849     private static native void nAddRoundRect(long nPath, float left, float top,
850             float right, float bottom, float[] radii, int dir);
nAddPath(long nPath, long src, float dx, float dy)851     private static native void nAddPath(long nPath, long src, float dx, float dy);
nAddPath(long nPath, long src)852     private static native void nAddPath(long nPath, long src);
nAddPath(long nPath, long src, long matrix)853     private static native void nAddPath(long nPath, long src, long matrix);
nOffset(long nPath, float dx, float dy)854     private static native void nOffset(long nPath, float dx, float dy);
nSetLastPoint(long nPath, float dx, float dy)855     private static native void nSetLastPoint(long nPath, float dx, float dy);
nTransform(long nPath, long matrix, long dst_path)856     private static native void nTransform(long nPath, long matrix, long dst_path);
nTransform(long nPath, long matrix)857     private static native void nTransform(long nPath, long matrix);
nOp(long path1, long path2, int op, long result)858     private static native boolean nOp(long path1, long path2, int op, long result);
nApproximate(long nPath, float error)859     private static native float[] nApproximate(long nPath, float error);
860 
861     // ------------------ Fast JNI ------------------------
862 
863     @FastNative
nIsRect(long nPath, RectF rect)864     private static native boolean nIsRect(long nPath, RectF rect);
865 
866     // ------------------ Critical JNI ------------------------
867 
868     @CriticalNative
nReset(long nPath)869     private static native void nReset(long nPath);
870     @CriticalNative
nRewind(long nPath)871     private static native void nRewind(long nPath);
872     @CriticalNative
nIsEmpty(long nPath)873     private static native boolean nIsEmpty(long nPath);
874     @CriticalNative
nIsConvex(long nPath)875     private static native boolean nIsConvex(long nPath);
876     @CriticalNative
nGetFillType(long nPath)877     private static native int nGetFillType(long nPath);
878     @CriticalNative
nSetFillType(long nPath, int ft)879     private static native void nSetFillType(long nPath, int ft);
880 }
881