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