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