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