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