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 * 213 * @deprecated This method is not reliable. The way convexity is computed may change from 214 * release to release, and convexity could change based on a matrix as well. This method was 215 * useful when non-convex Paths were unable to be used in certain contexts, but that is no 216 * longer the case. 217 */ 218 @Deprecated isConvex()219 public boolean isConvex() { 220 return nIsConvex(mNativePath); 221 } 222 223 /** 224 * Enum for the ways a path may be filled. 225 */ 226 public enum FillType { 227 // these must match the values in SkPath.h 228 /** 229 * Specifies that "inside" is computed by a non-zero sum of signed 230 * edge crossings. 231 */ 232 WINDING (0), 233 /** 234 * Specifies that "inside" is computed by an odd number of edge 235 * crossings. 236 */ 237 EVEN_ODD (1), 238 /** 239 * Same as {@link #WINDING}, but draws outside of the path, rather than inside. 240 */ 241 INVERSE_WINDING (2), 242 /** 243 * Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside. 244 */ 245 INVERSE_EVEN_ODD(3); 246 FillType(int ni)247 FillType(int ni) { 248 nativeInt = ni; 249 } 250 251 final int nativeInt; 252 } 253 254 // these must be in the same order as their native values 255 static final FillType[] sFillTypeArray = { 256 FillType.WINDING, 257 FillType.EVEN_ODD, 258 FillType.INVERSE_WINDING, 259 FillType.INVERSE_EVEN_ODD 260 }; 261 262 /** 263 * Return the path's fill type. This defines how "inside" is 264 * computed. The default value is WINDING. 265 * 266 * @return the path's fill type 267 */ 268 @NonNull getFillType()269 public FillType getFillType() { 270 return sFillTypeArray[nGetFillType(mNativePath)]; 271 } 272 273 /** 274 * Set the path's fill type. This defines how "inside" is computed. 275 * 276 * @param ft The new fill type for this path 277 */ setFillType(@onNull FillType ft)278 public void setFillType(@NonNull FillType ft) { 279 nSetFillType(mNativePath, ft.nativeInt); 280 } 281 282 /** 283 * Returns true if the filltype is one of the INVERSE variants 284 * 285 * @return true if the filltype is one of the INVERSE variants 286 */ isInverseFillType()287 public boolean isInverseFillType() { 288 final int ft = nGetFillType(mNativePath); 289 return (ft & FillType.INVERSE_WINDING.nativeInt) != 0; 290 } 291 292 /** 293 * Toggles the INVERSE state of the filltype 294 */ toggleInverseFillType()295 public void toggleInverseFillType() { 296 int ft = nGetFillType(mNativePath); 297 ft ^= FillType.INVERSE_WINDING.nativeInt; 298 nSetFillType(mNativePath, ft); 299 } 300 301 /** 302 * Returns true if the path is empty (contains no lines or curves) 303 * 304 * @return true if the path is empty (contains no lines or curves) 305 */ isEmpty()306 public boolean isEmpty() { 307 return nIsEmpty(mNativePath); 308 } 309 310 /** 311 * Returns true if the path specifies a rectangle. If so, and if rect is 312 * not null, set rect to the bounds of the path. If the path does not 313 * specify a rectangle, return false and ignore rect. 314 * 315 * @param rect If not null, returns the bounds of the path if it specifies 316 * a rectangle 317 * @return true if the path specifies a rectangle 318 */ isRect(@ullable RectF rect)319 public boolean isRect(@Nullable RectF rect) { 320 return nIsRect(mNativePath, rect); 321 } 322 323 /** 324 * Compute the bounds of the control points of the path, and write the 325 * answer into bounds. If the path contains 0 or 1 points, the bounds is 326 * set to (0,0,0,0) 327 * 328 * @param bounds Returns the computed bounds of the path's control points. 329 * @param exact This parameter is no longer used. 330 */ 331 @SuppressWarnings({"UnusedDeclaration"}) computeBounds(@onNull RectF bounds, boolean exact)332 public void computeBounds(@NonNull RectF bounds, boolean exact) { 333 nComputeBounds(mNativePath, bounds); 334 } 335 336 /** 337 * Hint to the path to prepare for adding more points. This can allow the 338 * path to more efficiently allocate its storage. 339 * 340 * @param extraPtCount The number of extra points that may be added to this 341 * path 342 */ incReserve(int extraPtCount)343 public void incReserve(int extraPtCount) { 344 nIncReserve(mNativePath, extraPtCount); 345 } 346 347 /** 348 * Set the beginning of the next contour to the point (x,y). 349 * 350 * @param x The x-coordinate of the start of a new contour 351 * @param y The y-coordinate of the start of a new contour 352 */ moveTo(float x, float y)353 public void moveTo(float x, float y) { 354 nMoveTo(mNativePath, x, y); 355 } 356 357 /** 358 * Set the beginning of the next contour relative to the last point on the 359 * previous contour. If there is no previous contour, this is treated the 360 * same as moveTo(). 361 * 362 * @param dx The amount to add to the x-coordinate of the end of the 363 * previous contour, to specify the start of a new contour 364 * @param dy The amount to add to the y-coordinate of the end of the 365 * previous contour, to specify the start of a new contour 366 */ rMoveTo(float dx, float dy)367 public void rMoveTo(float dx, float dy) { 368 nRMoveTo(mNativePath, dx, dy); 369 } 370 371 /** 372 * Add a line from the last point to the specified point (x,y). 373 * If no moveTo() call has been made for this contour, the first point is 374 * automatically set to (0,0). 375 * 376 * @param x The x-coordinate of the end of a line 377 * @param y The y-coordinate of the end of a line 378 */ lineTo(float x, float y)379 public void lineTo(float x, float y) { 380 isSimplePath = false; 381 nLineTo(mNativePath, x, y); 382 } 383 384 /** 385 * Same as lineTo, but the coordinates are considered relative to the last 386 * point on this contour. If there is no previous point, then a moveTo(0,0) 387 * is inserted automatically. 388 * 389 * @param dx The amount to add to the x-coordinate of the previous point on 390 * this contour, to specify a line 391 * @param dy The amount to add to the y-coordinate of the previous point on 392 * this contour, to specify a line 393 */ rLineTo(float dx, float dy)394 public void rLineTo(float dx, float dy) { 395 isSimplePath = false; 396 nRLineTo(mNativePath, dx, dy); 397 } 398 399 /** 400 * Add a quadratic bezier from the last point, approaching control point 401 * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for 402 * this contour, the first point is automatically set to (0,0). 403 * 404 * @param x1 The x-coordinate of the control point on a quadratic curve 405 * @param y1 The y-coordinate of the control point on a quadratic curve 406 * @param x2 The x-coordinate of the end point on a quadratic curve 407 * @param y2 The y-coordinate of the end point on a quadratic curve 408 */ quadTo(float x1, float y1, float x2, float y2)409 public void quadTo(float x1, float y1, float x2, float y2) { 410 isSimplePath = false; 411 nQuadTo(mNativePath, x1, y1, x2, y2); 412 } 413 414 /** 415 * Same as quadTo, but the coordinates are considered relative to the last 416 * point on this contour. If there is no previous point, then a moveTo(0,0) 417 * is inserted automatically. 418 * 419 * @param dx1 The amount to add to the x-coordinate of the last point on 420 * this contour, for the control point of a quadratic curve 421 * @param dy1 The amount to add to the y-coordinate of the last point on 422 * this contour, for the control point of a quadratic curve 423 * @param dx2 The amount to add to the x-coordinate of the last point on 424 * this contour, for the end point of a quadratic curve 425 * @param dy2 The amount to add to the y-coordinate of the last point on 426 * this contour, for the end point of a quadratic curve 427 */ rQuadTo(float dx1, float dy1, float dx2, float dy2)428 public void rQuadTo(float dx1, float dy1, float dx2, float dy2) { 429 isSimplePath = false; 430 nRQuadTo(mNativePath, dx1, dy1, dx2, dy2); 431 } 432 433 /** 434 * Add a cubic bezier from the last point, approaching control points 435 * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been 436 * made for this contour, the first point is automatically set to (0,0). 437 * 438 * @param x1 The x-coordinate of the 1st control point on a cubic curve 439 * @param y1 The y-coordinate of the 1st control point on a cubic curve 440 * @param x2 The x-coordinate of the 2nd control point on a cubic curve 441 * @param y2 The y-coordinate of the 2nd control point on a cubic curve 442 * @param x3 The x-coordinate of the end point on a cubic curve 443 * @param y3 The y-coordinate of the end point on a cubic curve 444 */ cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)445 public void cubicTo(float x1, float y1, float x2, float y2, 446 float x3, float y3) { 447 isSimplePath = false; 448 nCubicTo(mNativePath, x1, y1, x2, y2, x3, y3); 449 } 450 451 /** 452 * Same as cubicTo, but the coordinates are considered relative to the 453 * current point on this contour. If there is no previous point, then a 454 * moveTo(0,0) is inserted automatically. 455 */ rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)456 public void rCubicTo(float x1, float y1, float x2, float y2, 457 float x3, float y3) { 458 isSimplePath = false; 459 nRCubicTo(mNativePath, x1, y1, x2, y2, x3, y3); 460 } 461 462 /** 463 * Append the specified arc to the path as a new contour. If the start of 464 * the path is different from the path's current last point, then an 465 * automatic lineTo() is added to connect the current contour to the 466 * start of the arc. However, if the path is empty, then we call moveTo() 467 * with the first point of the arc. 468 * 469 * @param oval The bounds of oval defining shape and size of the arc 470 * @param startAngle Starting angle (in degrees) where the arc begins 471 * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated 472 * mod 360. 473 * @param forceMoveTo If true, always begin a new contour with the arc 474 */ arcTo(@onNull RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)475 public void arcTo(@NonNull RectF oval, float startAngle, float sweepAngle, 476 boolean forceMoveTo) { 477 arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, forceMoveTo); 478 } 479 480 /** 481 * Append the specified arc to the path as a new contour. If the start of 482 * the path is different from the path's current last point, then an 483 * automatic lineTo() is added to connect the current contour to the 484 * start of the arc. However, if the path is empty, then we call moveTo() 485 * with the first point of the arc. 486 * 487 * @param oval The bounds of oval defining shape and size of the arc 488 * @param startAngle Starting angle (in degrees) where the arc begins 489 * @param sweepAngle Sweep angle (in degrees) measured clockwise 490 */ arcTo(@onNull RectF oval, float startAngle, float sweepAngle)491 public void arcTo(@NonNull RectF oval, float startAngle, float sweepAngle) { 492 arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, false); 493 } 494 495 /** 496 * Append the specified arc to the path as a new contour. If the start of 497 * the path is different from the path's current last point, then an 498 * automatic lineTo() is added to connect the current contour to the 499 * start of the arc. However, if the path is empty, then we call moveTo() 500 * with the first point of the arc. 501 * 502 * @param startAngle Starting angle (in degrees) where the arc begins 503 * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated 504 * mod 360. 505 * @param forceMoveTo If true, always begin a new contour with the arc 506 */ arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo)507 public void arcTo(float left, float top, float right, float bottom, float startAngle, 508 float sweepAngle, boolean forceMoveTo) { 509 isSimplePath = false; 510 nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo); 511 } 512 513 /** 514 * Close the current contour. If the current point is not equal to the 515 * first point of the contour, a line segment is automatically added. 516 */ close()517 public void close() { 518 isSimplePath = false; 519 nClose(mNativePath); 520 } 521 522 /** 523 * Specifies how closed shapes (e.g. rects, ovals) are oriented when they 524 * are added to a path. 525 */ 526 public enum Direction { 527 /** clockwise */ 528 CW (0), // must match enum in SkPath.h 529 /** counter-clockwise */ 530 CCW (1); // must match enum in SkPath.h 531 Direction(int ni)532 Direction(int ni) { 533 nativeInt = ni; 534 } 535 final int nativeInt; 536 } 537 detectSimplePath(float left, float top, float right, float bottom, Direction dir)538 private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) { 539 if (mLastDirection == null) { 540 mLastDirection = dir; 541 } 542 if (mLastDirection != dir) { 543 isSimplePath = false; 544 } else { 545 if (rects == null) rects = new Region(); 546 rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION); 547 } 548 } 549 550 /** 551 * Add a closed rectangle contour to the path 552 * 553 * @param rect The rectangle to add as a closed contour to the path 554 * @param dir The direction to wind the rectangle's contour 555 */ addRect(@onNull RectF rect, @NonNull Direction dir)556 public void addRect(@NonNull RectF rect, @NonNull Direction dir) { 557 addRect(rect.left, rect.top, rect.right, rect.bottom, dir); 558 } 559 560 /** 561 * Add a closed rectangle contour to the path 562 * 563 * @param left The left side of a rectangle to add to the path 564 * @param top The top of a rectangle to add to the path 565 * @param right The right side of a rectangle to add to the path 566 * @param bottom The bottom of a rectangle to add to the path 567 * @param dir The direction to wind the rectangle's contour 568 */ addRect(float left, float top, float right, float bottom, @NonNull Direction dir)569 public void addRect(float left, float top, float right, float bottom, @NonNull Direction dir) { 570 detectSimplePath(left, top, right, bottom, dir); 571 nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt); 572 } 573 574 /** 575 * Add a closed oval contour to the path 576 * 577 * @param oval The bounds of the oval to add as a closed contour to the path 578 * @param dir The direction to wind the oval's contour 579 */ addOval(@onNull RectF oval, @NonNull Direction dir)580 public void addOval(@NonNull RectF oval, @NonNull Direction dir) { 581 addOval(oval.left, oval.top, oval.right, oval.bottom, dir); 582 } 583 584 /** 585 * Add a closed oval contour to the path 586 * 587 * @param dir The direction to wind the oval's contour 588 */ addOval(float left, float top, float right, float bottom, @NonNull Direction dir)589 public void addOval(float left, float top, float right, float bottom, @NonNull Direction dir) { 590 isSimplePath = false; 591 nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt); 592 } 593 594 /** 595 * Add a closed circle contour to the path 596 * 597 * @param x The x-coordinate of the center of a circle to add to the path 598 * @param y The y-coordinate of the center of a circle to add to the path 599 * @param radius The radius of a circle to add to the path 600 * @param dir The direction to wind the circle's contour 601 */ addCircle(float x, float y, float radius, @NonNull Direction dir)602 public void addCircle(float x, float y, float radius, @NonNull Direction dir) { 603 isSimplePath = false; 604 nAddCircle(mNativePath, x, y, radius, dir.nativeInt); 605 } 606 607 /** 608 * Add the specified arc to the path as a new contour. 609 * 610 * @param oval The bounds of oval defining the shape and size of the arc 611 * @param startAngle Starting angle (in degrees) where the arc begins 612 * @param sweepAngle Sweep angle (in degrees) measured clockwise 613 */ addArc(@onNull RectF oval, float startAngle, float sweepAngle)614 public void addArc(@NonNull RectF oval, float startAngle, float sweepAngle) { 615 addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle); 616 } 617 618 /** 619 * Add the specified arc to the path as a new contour. 620 * 621 * @param startAngle Starting angle (in degrees) where the arc begins 622 * @param sweepAngle Sweep angle (in degrees) measured clockwise 623 */ addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle)624 public void addArc(float left, float top, float right, float bottom, float startAngle, 625 float sweepAngle) { 626 isSimplePath = false; 627 nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle); 628 } 629 630 /** 631 * Add a closed round-rectangle contour to the path 632 * 633 * @param rect The bounds of a round-rectangle to add to the path 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(@onNull RectF rect, float rx, float ry, @NonNull Direction dir)638 public void addRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Direction dir) { 639 addRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, dir); 640 } 641 642 /** 643 * Add a closed round-rectangle contour to the path 644 * 645 * @param rx The x-radius of the rounded corners on the round-rectangle 646 * @param ry The y-radius of the rounded corners on the round-rectangle 647 * @param dir The direction to wind the round-rectangle's contour 648 */ addRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Direction dir)649 public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry, 650 @NonNull Direction dir) { 651 isSimplePath = false; 652 nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt); 653 } 654 655 /** 656 * Add a closed round-rectangle contour to the path. Each corner receives 657 * two radius values [X, Y]. The corners are ordered top-left, top-right, 658 * bottom-right, bottom-left 659 * 660 * @param rect The bounds of a round-rectangle to add to the path 661 * @param radii Array of 8 values, 4 pairs of [X,Y] radii 662 * @param dir The direction to wind the round-rectangle's contour 663 */ addRoundRect(@onNull RectF rect, @NonNull float[] radii, @NonNull Direction dir)664 public void addRoundRect(@NonNull RectF rect, @NonNull float[] radii, @NonNull Direction dir) { 665 if (rect == null) { 666 throw new NullPointerException("need rect parameter"); 667 } 668 addRoundRect(rect.left, rect.top, rect.right, rect.bottom, radii, dir); 669 } 670 671 /** 672 * Add a closed round-rectangle contour to the path. Each corner receives 673 * two radius values [X, Y]. The corners are ordered top-left, top-right, 674 * bottom-right, bottom-left 675 * 676 * @param radii Array of 8 values, 4 pairs of [X,Y] radii 677 * @param dir The direction to wind the round-rectangle's contour 678 */ addRoundRect(float left, float top, float right, float bottom, @NonNull float[] radii, @NonNull Direction dir)679 public void addRoundRect(float left, float top, float right, float bottom, 680 @NonNull float[] radii, @NonNull Direction dir) { 681 if (radii.length < 8) { 682 throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values"); 683 } 684 isSimplePath = false; 685 nAddRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt); 686 } 687 688 /** 689 * Add a copy of src to the path, offset by (dx,dy) 690 * 691 * @param src The path to add as a new contour 692 * @param dx The amount to translate the path in X as it is added 693 */ addPath(@onNull Path src, float dx, float dy)694 public void addPath(@NonNull Path src, float dx, float dy) { 695 isSimplePath = false; 696 nAddPath(mNativePath, src.mNativePath, dx, dy); 697 } 698 699 /** 700 * Add a copy of src to the path 701 * 702 * @param src The path that is appended to the current path 703 */ addPath(@onNull Path src)704 public void addPath(@NonNull Path src) { 705 isSimplePath = false; 706 nAddPath(mNativePath, src.mNativePath); 707 } 708 709 /** 710 * Add a copy of src to the path, transformed by matrix 711 * 712 * @param src The path to add as a new contour 713 */ addPath(@onNull Path src, @NonNull Matrix matrix)714 public void addPath(@NonNull Path src, @NonNull Matrix matrix) { 715 if (!src.isSimplePath) isSimplePath = false; 716 nAddPath(mNativePath, src.mNativePath, matrix.native_instance); 717 } 718 719 /** 720 * Offset the path by (dx,dy) 721 * 722 * @param dx The amount in the X direction to offset the entire path 723 * @param dy The amount in the Y direction to offset the entire path 724 * @param dst The translated path is written here. If this is null, then 725 * the original path is modified. 726 */ offset(float dx, float dy, @Nullable Path dst)727 public void offset(float dx, float dy, @Nullable Path dst) { 728 if (dst != null) { 729 dst.set(this); 730 } else { 731 dst = this; 732 } 733 dst.offset(dx, dy); 734 } 735 736 /** 737 * Offset the path by (dx,dy) 738 * 739 * @param dx The amount in the X direction to offset the entire path 740 * @param dy The amount in the Y direction to offset the entire path 741 */ offset(float dx, float dy)742 public void offset(float dx, float dy) { 743 if (isSimplePath && rects == null) { 744 // nothing to offset 745 return; 746 } 747 if (isSimplePath && dx == Math.rint(dx) && dy == Math.rint(dy)) { 748 rects.translate((int) dx, (int) dy); 749 } else { 750 isSimplePath = false; 751 } 752 nOffset(mNativePath, dx, dy); 753 } 754 755 /** 756 * Sets the last point of the path. 757 * 758 * @param dx The new X coordinate for the last point 759 * @param dy The new Y coordinate for the last point 760 */ setLastPoint(float dx, float dy)761 public void setLastPoint(float dx, float dy) { 762 isSimplePath = false; 763 nSetLastPoint(mNativePath, dx, dy); 764 } 765 766 /** 767 * Transform the points in this path by matrix, and write the answer 768 * into dst. If dst is null, then the the original path is modified. 769 * 770 * @param matrix The matrix to apply to the path 771 * @param dst The transformed path is written here. If dst is null, 772 * then the the original path is modified 773 */ transform(@onNull Matrix matrix, @Nullable Path dst)774 public void transform(@NonNull Matrix matrix, @Nullable Path dst) { 775 long dstNative = 0; 776 if (dst != null) { 777 dst.isSimplePath = false; 778 dstNative = dst.mNativePath; 779 } 780 nTransform(mNativePath, matrix.native_instance, dstNative); 781 } 782 783 /** 784 * Transform the points in this path by matrix. 785 * 786 * @param matrix The matrix to apply to the path 787 */ transform(@onNull Matrix matrix)788 public void transform(@NonNull Matrix matrix) { 789 isSimplePath = false; 790 nTransform(mNativePath, matrix.native_instance); 791 } 792 793 /** @hide */ readOnlyNI()794 public final long readOnlyNI() { 795 return mNativePath; 796 } 797 mutateNI()798 final long mutateNI() { 799 isSimplePath = false; 800 return mNativePath; 801 } 802 803 /** 804 * Approximate the <code>Path</code> with a series of line segments. 805 * This returns float[] with the array containing point components. 806 * There are three components for each point, in order: 807 * <ul> 808 * <li>Fraction along the length of the path that the point resides</li> 809 * <li>The x coordinate of the point</li> 810 * <li>The y coordinate of the point</li> 811 * </ul> 812 * <p>Two points may share the same fraction along its length when there is 813 * a move action within the Path.</p> 814 * 815 * @param acceptableError The acceptable error for a line on the 816 * Path. Typically this would be 0.5 so that 817 * the error is less than half a pixel. 818 * @return An array of components for points approximating the Path. 819 */ 820 @NonNull 821 @Size(min = 6, multiple = 3) approximate(@loatRangefrom = 0) float acceptableError)822 public float[] approximate(@FloatRange(from = 0) float acceptableError) { 823 if (acceptableError < 0) { 824 throw new IllegalArgumentException("AcceptableError must be greater than or equal to 0"); 825 } 826 return nApproximate(mNativePath, acceptableError); 827 } 828 829 // ------------------ Regular JNI ------------------------ 830 nInit()831 private static native long nInit(); nInit(long nPath)832 private static native long nInit(long nPath); nGetFinalizer()833 private static native long nGetFinalizer(); nSet(long native_dst, long nSrc)834 private static native void nSet(long native_dst, long nSrc); nComputeBounds(long nPath, RectF bounds)835 private static native void nComputeBounds(long nPath, RectF bounds); nIncReserve(long nPath, int extraPtCount)836 private static native void nIncReserve(long nPath, int extraPtCount); nMoveTo(long nPath, float x, float y)837 private static native void nMoveTo(long nPath, float x, float y); nRMoveTo(long nPath, float dx, float dy)838 private static native void nRMoveTo(long nPath, float dx, float dy); nLineTo(long nPath, float x, float y)839 private static native void nLineTo(long nPath, float x, float y); nRLineTo(long nPath, float dx, float dy)840 private static native void nRLineTo(long nPath, float dx, float dy); nQuadTo(long nPath, float x1, float y1, float x2, float y2)841 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)842 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)843 private static native void nCubicTo(long nPath, float x1, float y1, float x2, float y2, 844 float x3, float y3); nRCubicTo(long nPath, float x1, float y1, float x2, float y2, float x3, float y3)845 private static native void nRCubicTo(long nPath, float x1, float y1, float x2, float y2, 846 float x3, float y3); nArcTo(long nPath, float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo)847 private static native void nArcTo(long nPath, float left, float top, float right, float bottom, 848 float startAngle, float sweepAngle, boolean forceMoveTo); nClose(long nPath)849 private static native void nClose(long nPath); nAddRect(long nPath, float left, float top, float right, float bottom, int dir)850 private static native void nAddRect(long nPath, float left, float top, 851 float right, float bottom, int dir); nAddOval(long nPath, float left, float top, float right, float bottom, int dir)852 private static native void nAddOval(long nPath, float left, float top, 853 float right, float bottom, int dir); nAddCircle(long nPath, float x, float y, float radius, int dir)854 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)855 private static native void nAddArc(long nPath, float left, float top, float right, float bottom, 856 float startAngle, float sweepAngle); nAddRoundRect(long nPath, float left, float top, float right, float bottom, float rx, float ry, int dir)857 private static native void nAddRoundRect(long nPath, float left, float top, 858 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)859 private static native void nAddRoundRect(long nPath, float left, float top, 860 float right, float bottom, float[] radii, int dir); nAddPath(long nPath, long src, float dx, float dy)861 private static native void nAddPath(long nPath, long src, float dx, float dy); nAddPath(long nPath, long src)862 private static native void nAddPath(long nPath, long src); nAddPath(long nPath, long src, long matrix)863 private static native void nAddPath(long nPath, long src, long matrix); nOffset(long nPath, float dx, float dy)864 private static native void nOffset(long nPath, float dx, float dy); nSetLastPoint(long nPath, float dx, float dy)865 private static native void nSetLastPoint(long nPath, float dx, float dy); nTransform(long nPath, long matrix, long dst_path)866 private static native void nTransform(long nPath, long matrix, long dst_path); nTransform(long nPath, long matrix)867 private static native void nTransform(long nPath, long matrix); nOp(long path1, long path2, int op, long result)868 private static native boolean nOp(long path1, long path2, int op, long result); nApproximate(long nPath, float error)869 private static native float[] nApproximate(long nPath, float error); 870 871 // ------------------ Fast JNI ------------------------ 872 873 @FastNative nIsRect(long nPath, RectF rect)874 private static native boolean nIsRect(long nPath, RectF rect); 875 876 // ------------------ Critical JNI ------------------------ 877 878 @CriticalNative nReset(long nPath)879 private static native void nReset(long nPath); 880 @CriticalNative nRewind(long nPath)881 private static native void nRewind(long nPath); 882 @CriticalNative nIsEmpty(long nPath)883 private static native boolean nIsEmpty(long nPath); 884 @CriticalNative nIsConvex(long nPath)885 private static native boolean nIsConvex(long nPath); 886 @CriticalNative nGetFillType(long nPath)887 private static native int nGetFillType(long nPath); 888 @CriticalNative nSetFillType(long nPath, int ft)889 private static native void nSetFillType(long nPath, int ft); 890 } 891