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