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