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