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