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