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