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