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.view.HardwareRenderer;
20
21/**
22 * The Path class encapsulates compound (multiple contour) geometric paths
23 * consisting of straight line segments, quadratic curves, and cubic curves.
24 * It can be drawn with canvas.drawPath(path, paint), either filled or stroked
25 * (based on the paint's Style), or it can be used for clipping or to draw
26 * text on a path.
27 */
28public class Path {
29    /**
30     * @hide
31     */
32    public final int mNativePath;
33
34    /**
35     * @hide
36     */
37    public boolean isSimplePath = true;
38    /**
39     * @hide
40     */
41    public Region rects;
42    private boolean mDetectSimplePaths;
43    private Direction mLastDirection = null;
44
45    /**
46     * Create an empty path
47     */
48    public Path() {
49        mNativePath = init1();
50        mDetectSimplePaths = HardwareRenderer.isAvailable();
51    }
52
53    /**
54     * Create a new path, copying the contents from the src path.
55     *
56     * @param src The path to copy from when initializing the new path
57     */
58    public Path(Path src) {
59        int valNative = 0;
60        if (src != null) {
61            valNative = src.mNativePath;
62            isSimplePath = src.isSimplePath;
63            if (src.rects != null) {
64                rects = new Region(src.rects);
65            }
66        }
67        mNativePath = init2(valNative);
68        mDetectSimplePaths = HardwareRenderer.isAvailable();
69    }
70
71    /**
72     * Clear any lines and curves from the path, making it empty.
73     * This does NOT change the fill-type setting.
74     */
75    public void reset() {
76        isSimplePath = true;
77        if (mDetectSimplePaths) {
78            mLastDirection = null;
79            if (rects != null) rects.setEmpty();
80        }
81        native_reset(mNativePath);
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        if (mDetectSimplePaths) {
91            mLastDirection = null;
92            if (rects != null) rects.setEmpty();
93        }
94        native_rewind(mNativePath);
95    }
96
97    /** Replace the contents of this with the contents of src.
98    */
99    public void set(Path src) {
100        if (this != src) {
101            isSimplePath = src.isSimplePath;
102            native_set(mNativePath, src.mNativePath);
103        }
104    }
105
106    /** Enum for the ways a path may be filled
107    */
108    public enum FillType {
109        // these must match the values in SkPath.h
110        WINDING         (0),
111        EVEN_ODD        (1),
112        INVERSE_WINDING (2),
113        INVERSE_EVEN_ODD(3);
114
115        FillType(int ni) {
116            nativeInt = ni;
117        }
118        final int nativeInt;
119    }
120
121    // these must be in the same order as their native values
122    static final FillType[] sFillTypeArray = {
123        FillType.WINDING,
124        FillType.EVEN_ODD,
125        FillType.INVERSE_WINDING,
126        FillType.INVERSE_EVEN_ODD
127    };
128
129    /**
130     * Return the path's fill type. This defines how "inside" is
131     * computed. The default value is WINDING.
132     *
133     * @return the path's fill type
134     */
135    public FillType getFillType() {
136        return sFillTypeArray[native_getFillType(mNativePath)];
137    }
138
139    /**
140     * Set the path's fill type. This defines how "inside" is computed.
141     *
142     * @param ft The new fill type for this path
143     */
144    public void setFillType(FillType ft) {
145        native_setFillType(mNativePath, ft.nativeInt);
146    }
147
148    /**
149     * Returns true if the filltype is one of the INVERSE variants
150     *
151     * @return true if the filltype is one of the INVERSE variants
152     */
153    public boolean isInverseFillType() {
154        final int ft = native_getFillType(mNativePath);
155        return (ft & 2) != 0;
156    }
157
158    /**
159     * Toggles the INVERSE state of the filltype
160     */
161    public void toggleInverseFillType() {
162        int ft = native_getFillType(mNativePath);
163        ft ^= 2;
164        native_setFillType(mNativePath, ft);
165    }
166
167    /**
168     * Returns true if the path is empty (contains no lines or curves)
169     *
170     * @return true if the path is empty (contains no lines or curves)
171     */
172    public boolean isEmpty() {
173        return native_isEmpty(mNativePath);
174    }
175
176    /**
177     * Returns true if the path specifies a rectangle. If so, and if rect is
178     * not null, set rect to the bounds of the path. If the path does not
179     * specify a rectangle, return false and ignore rect.
180     *
181     * @param rect If not null, returns the bounds of the path if it specifies
182     *             a rectangle
183     * @return     true if the path specifies a rectangle
184     */
185    public boolean isRect(RectF rect) {
186        return native_isRect(mNativePath, rect);
187    }
188
189    /**
190     * Compute the bounds of the control points of the path, and write the
191     * answer into bounds. If the path contains 0 or 1 points, the bounds is
192     * set to (0,0,0,0)
193     *
194     * @param bounds Returns the computed bounds of the path's control points.
195     * @param exact This parameter is no longer used.
196     */
197    @SuppressWarnings({"UnusedDeclaration"})
198    public void computeBounds(RectF bounds, boolean exact) {
199        native_computeBounds(mNativePath, bounds);
200    }
201
202    /**
203     * Hint to the path to prepare for adding more points. This can allow the
204     * path to more efficiently allocate its storage.
205     *
206     * @param extraPtCount The number of extra points that may be added to this
207     *                     path
208     */
209    public void incReserve(int extraPtCount) {
210        native_incReserve(mNativePath, extraPtCount);
211    }
212
213    /**
214     * Set the beginning of the next contour to the point (x,y).
215     *
216     * @param x The x-coordinate of the start of a new contour
217     * @param y The y-coordinate of the start of a new contour
218     */
219    public void moveTo(float x, float y) {
220        native_moveTo(mNativePath, x, y);
221    }
222
223    /**
224     * Set the beginning of the next contour relative to the last point on the
225     * previous contour. If there is no previous contour, this is treated the
226     * same as moveTo().
227     *
228     * @param dx The amount to add to the x-coordinate of the end of the
229     *           previous contour, to specify the start of a new contour
230     * @param dy The amount to add to the y-coordinate of the end of the
231     *           previous contour, to specify the start of a new contour
232     */
233    public void rMoveTo(float dx, float dy) {
234        native_rMoveTo(mNativePath, dx, dy);
235    }
236
237    /**
238     * Add a line from the last point to the specified point (x,y).
239     * If no moveTo() call has been made for this contour, the first point is
240     * automatically set to (0,0).
241     *
242     * @param x The x-coordinate of the end of a line
243     * @param y The y-coordinate of the end of a line
244     */
245    public void lineTo(float x, float y) {
246        isSimplePath = false;
247        native_lineTo(mNativePath, x, y);
248    }
249
250    /**
251     * Same as lineTo, but the coordinates are considered relative to the last
252     * point on this contour. If there is no previous point, then a moveTo(0,0)
253     * is inserted automatically.
254     *
255     * @param dx The amount to add to the x-coordinate of the previous point on
256     *           this contour, to specify a line
257     * @param dy The amount to add to the y-coordinate of the previous point on
258     *           this contour, to specify a line
259     */
260    public void rLineTo(float dx, float dy) {
261        isSimplePath = false;
262        native_rLineTo(mNativePath, dx, dy);
263    }
264
265    /**
266     * Add a quadratic bezier from the last point, approaching control point
267     * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
268     * this contour, the first point is automatically set to (0,0).
269     *
270     * @param x1 The x-coordinate of the control point on a quadratic curve
271     * @param y1 The y-coordinate of the control point on a quadratic curve
272     * @param x2 The x-coordinate of the end point on a quadratic curve
273     * @param y2 The y-coordinate of the end point on a quadratic curve
274     */
275    public void quadTo(float x1, float y1, float x2, float y2) {
276        isSimplePath = false;
277        native_quadTo(mNativePath, x1, y1, x2, y2);
278    }
279
280    /**
281     * Same as quadTo, but the coordinates are considered relative to the last
282     * point on this contour. If there is no previous point, then a moveTo(0,0)
283     * is inserted automatically.
284     *
285     * @param dx1 The amount to add to the x-coordinate of the last point on
286     *            this contour, for the control point of a quadratic curve
287     * @param dy1 The amount to add to the y-coordinate of the last point on
288     *            this contour, for the control point of a quadratic curve
289     * @param dx2 The amount to add to the x-coordinate of the last point on
290     *            this contour, for the end point of a quadratic curve
291     * @param dy2 The amount to add to the y-coordinate of the last point on
292     *            this contour, for the end point of a quadratic curve
293     */
294    public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
295        isSimplePath = false;
296        native_rQuadTo(mNativePath, dx1, dy1, dx2, dy2);
297    }
298
299    /**
300     * Add a cubic bezier from the last point, approaching control points
301     * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
302     * made for this contour, the first point is automatically set to (0,0).
303     *
304     * @param x1 The x-coordinate of the 1st control point on a cubic curve
305     * @param y1 The y-coordinate of the 1st control point on a cubic curve
306     * @param x2 The x-coordinate of the 2nd control point on a cubic curve
307     * @param y2 The y-coordinate of the 2nd control point on a cubic curve
308     * @param x3 The x-coordinate of the end point on a cubic curve
309     * @param y3 The y-coordinate of the end point on a cubic curve
310     */
311    public void cubicTo(float x1, float y1, float x2, float y2,
312                        float x3, float y3) {
313        isSimplePath = false;
314        native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
315    }
316
317    /**
318     * Same as cubicTo, but the coordinates are considered relative to the
319     * current point on this contour. If there is no previous point, then a
320     * moveTo(0,0) is inserted automatically.
321     */
322    public void rCubicTo(float x1, float y1, float x2, float y2,
323                         float x3, float y3) {
324        isSimplePath = false;
325        native_rCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
326    }
327
328    /**
329     * Append the specified arc to the path as a new contour. If the start of
330     * the path is different from the path's current last point, then an
331     * automatic lineTo() is added to connect the current contour to the
332     * start of the arc. However, if the path is empty, then we call moveTo()
333     * with the first point of the arc. The sweep angle is tread mod 360.
334     *
335     * @param oval        The bounds of oval defining shape and size of the arc
336     * @param startAngle  Starting angle (in degrees) where the arc begins
337     * @param sweepAngle  Sweep angle (in degrees) measured clockwise, treated
338     *                    mod 360.
339     * @param forceMoveTo If true, always begin a new contour with the arc
340     */
341    public void arcTo(RectF oval, float startAngle, float sweepAngle,
342                      boolean forceMoveTo) {
343        isSimplePath = false;
344        native_arcTo(mNativePath, oval, startAngle, sweepAngle, forceMoveTo);
345    }
346
347    /**
348     * Append the specified arc to the path as a new contour. If the start of
349     * the path is different from the path's current last point, then an
350     * automatic lineTo() is added to connect the current contour to the
351     * start of the arc. However, if the path is empty, then we call moveTo()
352     * with the first point of the arc.
353     *
354     * @param oval        The bounds of oval defining shape and size of the arc
355     * @param startAngle  Starting angle (in degrees) where the arc begins
356     * @param sweepAngle  Sweep angle (in degrees) measured clockwise
357     */
358    public void arcTo(RectF oval, float startAngle, float sweepAngle) {
359        isSimplePath = false;
360        native_arcTo(mNativePath, oval, startAngle, sweepAngle, false);
361    }
362
363    /**
364     * Close the current contour. If the current point is not equal to the
365     * first point of the contour, a line segment is automatically added.
366     */
367    public void close() {
368        isSimplePath = false;
369        native_close(mNativePath);
370    }
371
372    /**
373     * Specifies how closed shapes (e.g. rects, ovals) are oriented when they
374     * are added to a path.
375     */
376    public enum Direction {
377        /** clockwise */
378        CW  (0),    // must match enum in SkPath.h
379        /** counter-clockwise */
380        CCW (1);    // must match enum in SkPath.h
381
382        Direction(int ni) {
383            nativeInt = ni;
384        }
385        final int nativeInt;
386    }
387
388    private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) {
389        if (mDetectSimplePaths) {
390            if (mLastDirection == null) {
391                mLastDirection = dir;
392            }
393            if (mLastDirection != dir) {
394                isSimplePath = false;
395            } else {
396                if (rects == null) rects = new Region();
397                rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION);
398            }
399        }
400    }
401
402    /**
403     * Add a closed rectangle contour to the path
404     *
405     * @param rect The rectangle to add as a closed contour to the path
406     * @param dir  The direction to wind the rectangle's contour
407     */
408    public void addRect(RectF rect, Direction dir) {
409        if (rect == null) {
410            throw new NullPointerException("need rect parameter");
411        }
412        detectSimplePath(rect.left, rect.top, rect.right, rect.bottom, dir);
413        native_addRect(mNativePath, rect, dir.nativeInt);
414    }
415
416    /**
417     * Add a closed rectangle contour to the path
418     *
419     * @param left   The left side of a rectangle to add to the path
420     * @param top    The top of a rectangle to add to the path
421     * @param right  The right side of a rectangle to add to the path
422     * @param bottom The bottom of a rectangle to add to the path
423     * @param dir    The direction to wind the rectangle's contour
424     */
425    public void addRect(float left, float top, float right, float bottom, Direction dir) {
426        detectSimplePath(left, top, right, bottom, dir);
427        native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt);
428    }
429
430    /**
431     * Add a closed oval contour to the path
432     *
433     * @param oval The bounds of the oval to add as a closed contour to the path
434     * @param dir  The direction to wind the oval's contour
435     */
436    public void addOval(RectF oval, Direction dir) {
437        if (oval == null) {
438            throw new NullPointerException("need oval parameter");
439        }
440        isSimplePath = false;
441        native_addOval(mNativePath, oval, dir.nativeInt);
442    }
443
444    /**
445     * Add a closed circle contour to the path
446     *
447     * @param x   The x-coordinate of the center of a circle to add to the path
448     * @param y   The y-coordinate of the center of a circle to add to the path
449     * @param radius The radius of a circle to add to the path
450     * @param dir    The direction to wind the circle's contour
451     */
452    public void addCircle(float x, float y, float radius, Direction dir) {
453        isSimplePath = false;
454        native_addCircle(mNativePath, x, y, radius, dir.nativeInt);
455    }
456
457    /**
458     * Add the specified arc to the path as a new contour.
459     *
460     * @param oval The bounds of oval defining the shape and size of the arc
461     * @param startAngle Starting angle (in degrees) where the arc begins
462     * @param sweepAngle Sweep angle (in degrees) measured clockwise
463     */
464    public void addArc(RectF oval, float startAngle, float sweepAngle) {
465        if (oval == null) {
466            throw new NullPointerException("need oval parameter");
467        }
468        isSimplePath = false;
469        native_addArc(mNativePath, oval, startAngle, sweepAngle);
470    }
471
472    /**
473        * Add a closed round-rectangle contour to the path
474     *
475     * @param rect The bounds of a round-rectangle to add to the path
476     * @param rx   The x-radius of the rounded corners on the round-rectangle
477     * @param ry   The y-radius of the rounded corners on the round-rectangle
478     * @param dir  The direction to wind the round-rectangle's contour
479     */
480    public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
481        if (rect == null) {
482            throw new NullPointerException("need rect parameter");
483        }
484        isSimplePath = false;
485        native_addRoundRect(mNativePath, rect, rx, ry, dir.nativeInt);
486    }
487
488    /**
489     * Add a closed round-rectangle contour to the path. Each corner receives
490     * two radius values [X, Y]. The corners are ordered top-left, top-right,
491     * bottom-right, bottom-left
492     *
493     * @param rect The bounds of a round-rectangle to add to the path
494     * @param radii Array of 8 values, 4 pairs of [X,Y] radii
495     * @param dir  The direction to wind the round-rectangle's contour
496     */
497    public void addRoundRect(RectF rect, float[] radii, Direction dir) {
498        if (rect == null) {
499            throw new NullPointerException("need rect parameter");
500        }
501        if (radii.length < 8) {
502            throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
503        }
504        isSimplePath = false;
505        native_addRoundRect(mNativePath, rect, radii, dir.nativeInt);
506    }
507
508    /**
509     * Add a copy of src to the path, offset by (dx,dy)
510     *
511     * @param src The path to add as a new contour
512     * @param dx  The amount to translate the path in X as it is added
513     */
514    public void addPath(Path src, float dx, float dy) {
515        isSimplePath = false;
516        native_addPath(mNativePath, src.mNativePath, dx, dy);
517    }
518
519    /**
520     * Add a copy of src to the path
521     *
522     * @param src The path that is appended to the current path
523     */
524    public void addPath(Path src) {
525        isSimplePath = false;
526        native_addPath(mNativePath, src.mNativePath);
527    }
528
529    /**
530     * Add a copy of src to the path, transformed by matrix
531     *
532     * @param src The path to add as a new contour
533     */
534    public void addPath(Path src, Matrix matrix) {
535        if (!src.isSimplePath) isSimplePath = false;
536        native_addPath(mNativePath, src.mNativePath, matrix.native_instance);
537    }
538
539    /**
540     * Offset the path by (dx,dy), returning true on success
541     *
542     * @param dx  The amount in the X direction to offset the entire path
543     * @param dy  The amount in the Y direction to offset the entire path
544     * @param dst The translated path is written here. If this is null, then
545     *            the original path is modified.
546     */
547    public void offset(float dx, float dy, Path dst) {
548        int dstNative = 0;
549        if (dst != null) {
550            dstNative = dst.mNativePath;
551            dst.isSimplePath = false;
552        }
553        native_offset(mNativePath, dx, dy, dstNative);
554    }
555
556    /**
557     * Offset the path by (dx,dy), returning true on success
558     *
559     * @param dx The amount in the X direction to offset the entire path
560     * @param dy The amount in the Y direction to offset the entire path
561     */
562    public void offset(float dx, float dy) {
563        isSimplePath = false;
564        native_offset(mNativePath, dx, dy);
565    }
566
567    /**
568     * Sets the last point of the path.
569     *
570     * @param dx The new X coordinate for the last point
571     * @param dy The new Y coordinate for the last point
572     */
573    public void setLastPoint(float dx, float dy) {
574        isSimplePath = false;
575        native_setLastPoint(mNativePath, dx, dy);
576    }
577
578    /**
579     * Transform the points in this path by matrix, and write the answer
580     * into dst. If dst is null, then the the original path is modified.
581     *
582     * @param matrix The matrix to apply to the path
583     * @param dst    The transformed path is written here. If dst is null,
584     *               then the the original path is modified
585     */
586    public void transform(Matrix matrix, Path dst) {
587        int dstNative = 0;
588        if (dst != null) {
589            dst.isSimplePath = false;
590            dstNative = dst.mNativePath;
591        }
592        native_transform(mNativePath, matrix.native_instance, dstNative);
593    }
594
595    /**
596     * Transform the points in this path by matrix.
597     *
598     * @param matrix The matrix to apply to the path
599     */
600    public void transform(Matrix matrix) {
601        isSimplePath = false;
602        native_transform(mNativePath, matrix.native_instance);
603    }
604
605    protected void finalize() throws Throwable {
606        try {
607            finalizer(mNativePath);
608        } finally {
609            super.finalize();
610        }
611    }
612
613    final int ni() {
614        return mNativePath;
615    }
616
617    private static native int init1();
618    private static native int init2(int nPath);
619    private static native void native_reset(int nPath);
620    private static native void native_rewind(int nPath);
621    private static native void native_set(int native_dst, int native_src);
622    private static native int native_getFillType(int nPath);
623    private static native void native_setFillType(int nPath, int ft);
624    private static native boolean native_isEmpty(int nPath);
625    private static native boolean native_isRect(int nPath, RectF rect);
626    private static native void native_computeBounds(int nPath, RectF bounds);
627    private static native void native_incReserve(int nPath, int extraPtCount);
628    private static native void native_moveTo(int nPath, float x, float y);
629    private static native void native_rMoveTo(int nPath, float dx, float dy);
630    private static native void native_lineTo(int nPath, float x, float y);
631    private static native void native_rLineTo(int nPath, float dx, float dy);
632    private static native void native_quadTo(int nPath, float x1, float y1,
633                                             float x2, float y2);
634    private static native void native_rQuadTo(int nPath, float dx1, float dy1,
635                                              float dx2, float dy2);
636    private static native void native_cubicTo(int nPath, float x1, float y1,
637                                        float x2, float y2, float x3, float y3);
638    private static native void native_rCubicTo(int nPath, float x1, float y1,
639                                        float x2, float y2, float x3, float y3);
640    private static native void native_arcTo(int nPath, RectF oval,
641                    float startAngle, float sweepAngle, boolean forceMoveTo);
642    private static native void native_close(int nPath);
643    private static native void native_addRect(int nPath, RectF rect, int dir);
644    private static native void native_addRect(int nPath, float left, float top,
645                                            float right, float bottom, int dir);
646    private static native void native_addOval(int nPath, RectF oval, int dir);
647    private static native void native_addCircle(int nPath, float x, float y,
648                                                float radius, int dir);
649    private static native void native_addArc(int nPath, RectF oval,
650                                            float startAngle, float sweepAngle);
651    private static native void native_addRoundRect(int nPath, RectF rect,
652                                                   float rx, float ry, int dir);
653    private static native void native_addRoundRect(int nPath, RectF r,
654                                                   float[] radii, int dir);
655    private static native void native_addPath(int nPath, int src, float dx,
656                                              float dy);
657    private static native void native_addPath(int nPath, int src);
658    private static native void native_addPath(int nPath, int src, int matrix);
659    private static native void native_offset(int nPath, float dx, float dy,
660                                             int dst_path);
661    private static native void native_offset(int nPath, float dx, float dy);
662    private static native void native_setLastPoint(int nPath, float dx, float dy);
663    private static native void native_transform(int nPath, int matrix,
664                                                int dst_path);
665    private static native void native_transform(int nPath, int matrix);
666    private static native void finalizer(int nPath);
667}
668