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