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