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