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 * @deprecated use computeBounds(RectF) instead 306 */ 307 @Deprecated 308 @SuppressWarnings({"UnusedDeclaration"}) computeBounds(@onNull RectF bounds, boolean exact)309 public void computeBounds(@NonNull RectF bounds, boolean exact) { 310 computeBounds(bounds); 311 } 312 313 /** 314 * Compute the bounds of the control points of the path, and write the 315 * answer into bounds. If the path contains 0 or 1 points, the bounds is 316 * set to (0,0,0,0) 317 * 318 * @param bounds Returns the computed bounds of the path's control points. 319 */ 320 @FlaggedApi(Flags.FLAG_EXACT_COMPUTE_BOUNDS) computeBounds(@onNull RectF bounds)321 public void computeBounds(@NonNull RectF bounds) { 322 nComputeBounds(mNativePath, bounds); 323 } 324 325 /** 326 * Hint to the path to prepare for adding more points. This can allow the 327 * path to more efficiently allocate its storage. 328 * 329 * @param extraPtCount The number of extra points that may be added to this 330 * path 331 */ incReserve(int extraPtCount)332 public void incReserve(int extraPtCount) { 333 nIncReserve(mNativePath, extraPtCount); 334 } 335 336 /** 337 * Set the beginning of the next contour to the point (x,y). 338 * 339 * @param x The x-coordinate of the start of a new contour 340 * @param y The y-coordinate of the start of a new contour 341 */ moveTo(float x, float y)342 public void moveTo(float x, float y) { 343 nMoveTo(mNativePath, x, y); 344 } 345 346 /** 347 * Set the beginning of the next contour relative to the last point on the 348 * previous contour. If there is no previous contour, this is treated the 349 * same as moveTo(). 350 * 351 * @param dx The amount to add to the x-coordinate of the end of the 352 * previous contour, to specify the start of a new contour 353 * @param dy The amount to add to the y-coordinate of the end of the 354 * previous contour, to specify the start of a new contour 355 */ rMoveTo(float dx, float dy)356 public void rMoveTo(float dx, float dy) { 357 nRMoveTo(mNativePath, dx, dy); 358 } 359 360 /** 361 * Add a line from the last point to the specified point (x,y). 362 * If no moveTo() call has been made for this contour, the first point is 363 * automatically set to (0,0). 364 * 365 * @param x The x-coordinate of the end of a line 366 * @param y The y-coordinate of the end of a line 367 */ lineTo(float x, float y)368 public void lineTo(float x, float y) { 369 nLineTo(mNativePath, x, y); 370 } 371 372 /** 373 * Same as lineTo, but the coordinates are considered relative to the last 374 * point on this contour. If there is no previous point, then a moveTo(0,0) 375 * is inserted automatically. 376 * 377 * @param dx The amount to add to the x-coordinate of the previous point on 378 * this contour, to specify a line 379 * @param dy The amount to add to the y-coordinate of the previous point on 380 * this contour, to specify a line 381 */ rLineTo(float dx, float dy)382 public void rLineTo(float dx, float dy) { 383 nRLineTo(mNativePath, dx, dy); 384 } 385 386 /** 387 * Add a quadratic bezier from the last point, approaching control point 388 * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for 389 * this contour, the first point is automatically set to (0,0). 390 * 391 * @param x1 The x-coordinate of the control point on a quadratic curve 392 * @param y1 The y-coordinate of the control point on a quadratic curve 393 * @param x2 The x-coordinate of the end point on a quadratic curve 394 * @param y2 The y-coordinate of the end point on a quadratic curve 395 */ quadTo(float x1, float y1, float x2, float y2)396 public void quadTo(float x1, float y1, float x2, float y2) { 397 nQuadTo(mNativePath, x1, y1, x2, y2); 398 } 399 400 /** 401 * Same as quadTo, but the coordinates are considered relative to the last 402 * point on this contour. If there is no previous point, then a moveTo(0,0) 403 * is inserted automatically. 404 * 405 * @param dx1 The amount to add to the x-coordinate of the last point on 406 * this contour, for the control point of a quadratic curve 407 * @param dy1 The amount to add to the y-coordinate of the last point on 408 * this contour, for the control point of a quadratic curve 409 * @param dx2 The amount to add to the x-coordinate of the last point on 410 * this contour, for the end point of a quadratic curve 411 * @param dy2 The amount to add to the y-coordinate of the last point on 412 * this contour, for the end point of a quadratic curve 413 */ rQuadTo(float dx1, float dy1, float dx2, float dy2)414 public void rQuadTo(float dx1, float dy1, float dx2, float dy2) { 415 nRQuadTo(mNativePath, dx1, dy1, dx2, dy2); 416 } 417 418 /** 419 * Add a quadratic bezier from the last point, approaching control point 420 * (x1,y1), and ending at (x2,y2), weighted by <code>weight</code>. If no 421 * moveTo() call has been made for this contour, the first point is 422 * automatically set to (0,0). 423 * 424 * A weight of 1 is equivalent to calling {@link #quadTo(float, float, float, float)}. 425 * A weight of 0 is equivalent to calling {@link #lineTo(float, float)} to 426 * <code>(x1, y1)</code> followed by {@link #lineTo(float, float)} to <code>(x2, y2)</code>. 427 * 428 * @param x1 The x-coordinate of the control point on a conic curve 429 * @param y1 The y-coordinate of the control point on a conic curve 430 * @param x2 The x-coordinate of the end point on a conic curve 431 * @param y2 The y-coordinate of the end point on a conic curve 432 * @param weight The weight of the conic applied to the curve. A value of 1 is equivalent 433 * to a quadratic with the given control and anchor points and a value of 0 is 434 * equivalent to a line to the first and another line to the second point. 435 */ conicTo(float x1, float y1, float x2, float y2, float weight)436 public void conicTo(float x1, float y1, float x2, float y2, float weight) { 437 nConicTo(mNativePath, x1, y1, x2, y2, weight); 438 } 439 440 /** 441 * Same as conicTo, but the coordinates are considered relative to the last 442 * point on this contour. If there is no previous point, then a moveTo(0,0) 443 * is inserted automatically. 444 * 445 * @param dx1 The amount to add to the x-coordinate of the last point on 446 * this contour, for the control point of a conic curve 447 * @param dy1 The amount to add to the y-coordinate of the last point on 448 * this contour, for the control point of a conic curve 449 * @param dx2 The amount to add to the x-coordinate of the last point on 450 * this contour, for the end point of a conic curve 451 * @param dy2 The amount to add to the y-coordinate of the last point on 452 * this contour, for the end point of a conic curve 453 * @param weight The weight of the conic applied to the curve. A value of 1 is equivalent 454 * to a quadratic with the given control and anchor points and a value of 0 is 455 * equivalent to a line to the first and another line to the second point. 456 */ rConicTo(float dx1, float dy1, float dx2, float dy2, float weight)457 public void rConicTo(float dx1, float dy1, float dx2, float dy2, float weight) { 458 nRConicTo(mNativePath, dx1, dy1, dx2, dy2, weight); 459 } 460 461 /** 462 * Add a cubic bezier from the last point, approaching control points 463 * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been 464 * made for this contour, the first point is automatically set to (0,0). 465 * 466 * @param x1 The x-coordinate of the 1st control point on a cubic curve 467 * @param y1 The y-coordinate of the 1st control point on a cubic curve 468 * @param x2 The x-coordinate of the 2nd control point on a cubic curve 469 * @param y2 The y-coordinate of the 2nd control point on a cubic curve 470 * @param x3 The x-coordinate of the end point on a cubic curve 471 * @param y3 The y-coordinate of the end point on a cubic curve 472 */ cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)473 public void cubicTo(float x1, float y1, float x2, float y2, 474 float x3, float y3) { 475 nCubicTo(mNativePath, x1, y1, x2, y2, x3, y3); 476 } 477 478 /** 479 * Same as cubicTo, but the coordinates are considered relative to the 480 * current point on this contour. If there is no previous point, then a 481 * moveTo(0,0) is inserted automatically. 482 */ rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)483 public void rCubicTo(float x1, float y1, float x2, float y2, 484 float x3, float y3) { 485 nRCubicTo(mNativePath, x1, y1, x2, y2, x3, y3); 486 } 487 488 /** 489 * Append the specified arc to the path as a new contour. If the start of 490 * the path is different from the path's current last point, then an 491 * automatic lineTo() is added to connect the current contour to the 492 * start of the arc. However, if the path is empty, then we call moveTo() 493 * with the first point of the arc. 494 * 495 * @param oval The bounds of oval defining shape and size of the arc 496 * @param startAngle Starting angle (in degrees) where the arc begins 497 * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated 498 * mod 360. 499 * @param forceMoveTo If true, always begin a new contour with the arc 500 */ arcTo(@onNull RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)501 public void arcTo(@NonNull RectF oval, float startAngle, float sweepAngle, 502 boolean forceMoveTo) { 503 arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, forceMoveTo); 504 } 505 506 /** 507 * Append the specified arc to the path as a new contour. If the start of 508 * the path is different from the path's current last point, then an 509 * automatic lineTo() is added to connect the current contour to the 510 * start of the arc. However, if the path is empty, then we call moveTo() 511 * with the first point of the arc. 512 * 513 * @param oval The bounds of oval defining shape and size of the arc 514 * @param startAngle Starting angle (in degrees) where the arc begins 515 * @param sweepAngle Sweep angle (in degrees) measured clockwise 516 */ arcTo(@onNull RectF oval, float startAngle, float sweepAngle)517 public void arcTo(@NonNull RectF oval, float startAngle, float sweepAngle) { 518 arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, false); 519 } 520 521 /** 522 * Append the specified arc to the path as a new contour. If the start of 523 * the path is different from the path's current last point, then an 524 * automatic lineTo() is added to connect the current contour to the 525 * start of the arc. However, if the path is empty, then we call moveTo() 526 * with the first point of the arc. 527 * 528 * @param startAngle Starting angle (in degrees) where the arc begins 529 * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated 530 * mod 360. 531 * @param forceMoveTo If true, always begin a new contour with the arc 532 */ arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo)533 public void arcTo(float left, float top, float right, float bottom, float startAngle, 534 float sweepAngle, boolean forceMoveTo) { 535 nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo); 536 } 537 538 /** 539 * Close the current contour. If the current point is not equal to the 540 * first point of the contour, a line segment is automatically added. 541 */ close()542 public void close() { 543 nClose(mNativePath); 544 } 545 546 /** 547 * Specifies how closed shapes (e.g. rects, ovals) are oriented when they 548 * are added to a path. 549 */ 550 public enum Direction { 551 /** clockwise */ 552 CW (0), // must match enum in SkPath.h 553 /** counter-clockwise */ 554 CCW (1); // must match enum in SkPath.h 555 Direction(int ni)556 Direction(int ni) { 557 nativeInt = ni; 558 } 559 final int nativeInt; 560 } 561 562 /** 563 * Add a closed rectangle contour to the path 564 * 565 * @param rect The rectangle to add as a closed contour to the path 566 * @param dir The direction to wind the rectangle's contour 567 */ addRect(@onNull RectF rect, @NonNull Direction dir)568 public void addRect(@NonNull RectF rect, @NonNull Direction dir) { 569 addRect(rect.left, rect.top, rect.right, rect.bottom, dir); 570 } 571 572 /** 573 * Add a closed rectangle contour to the path 574 * 575 * @param left The left side of a rectangle to add to the path 576 * @param top The top of a rectangle to add to the path 577 * @param right The right side of a rectangle to add to the path 578 * @param bottom The bottom of a rectangle to add to the path 579 * @param dir The direction to wind the rectangle's contour 580 */ addRect(float left, float top, float right, float bottom, @NonNull Direction dir)581 public void addRect(float left, float top, float right, float bottom, @NonNull Direction dir) { 582 nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt); 583 } 584 585 /** 586 * Add a closed oval contour to the path 587 * 588 * @param oval The bounds of the oval to add as a closed contour to the path 589 * @param dir The direction to wind the oval's contour 590 */ addOval(@onNull RectF oval, @NonNull Direction dir)591 public void addOval(@NonNull RectF oval, @NonNull Direction dir) { 592 addOval(oval.left, oval.top, oval.right, oval.bottom, dir); 593 } 594 595 /** 596 * Add a closed oval contour to the path 597 * 598 * @param dir The direction to wind the oval's contour 599 */ addOval(float left, float top, float right, float bottom, @NonNull Direction dir)600 public void addOval(float left, float top, float right, float bottom, @NonNull Direction dir) { 601 nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt); 602 } 603 604 /** 605 * Add a closed circle contour to the path 606 * 607 * @param x The x-coordinate of the center of a circle to add to the path 608 * @param y The y-coordinate of the center of a circle to add to the path 609 * @param radius The radius of a circle to add to the path 610 * @param dir The direction to wind the circle's contour 611 */ addCircle(float x, float y, float radius, @NonNull Direction dir)612 public void addCircle(float x, float y, float radius, @NonNull Direction dir) { 613 nAddCircle(mNativePath, x, y, radius, dir.nativeInt); 614 } 615 616 /** 617 * Add the specified arc to the path as a new contour. 618 * 619 * @param oval The bounds of oval defining the shape and size of the arc 620 * @param startAngle Starting angle (in degrees) where the arc begins 621 * @param sweepAngle Sweep angle (in degrees) measured clockwise 622 */ addArc(@onNull RectF oval, float startAngle, float sweepAngle)623 public void addArc(@NonNull RectF oval, float startAngle, float sweepAngle) { 624 addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle); 625 } 626 627 /** 628 * Add the specified arc to the path as a new contour. 629 * 630 * @param startAngle Starting angle (in degrees) where the arc begins 631 * @param sweepAngle Sweep angle (in degrees) measured clockwise 632 */ addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle)633 public void addArc(float left, float top, float right, float bottom, float startAngle, 634 float sweepAngle) { 635 nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle); 636 } 637 638 /** 639 * Add a closed round-rectangle contour to the path 640 * 641 * @param rect The bounds of a round-rectangle to add to the path 642 * @param rx The x-radius of the rounded corners on the round-rectangle 643 * @param ry The y-radius of the rounded corners on the round-rectangle 644 * @param dir The direction to wind the round-rectangle's contour 645 */ addRoundRect(@onNull RectF rect, float rx, float ry, @NonNull Direction dir)646 public void addRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Direction dir) { 647 addRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, dir); 648 } 649 650 /** 651 * Add a closed round-rectangle contour to the path 652 * 653 * @param rx The x-radius of the rounded corners on the round-rectangle 654 * @param ry The y-radius of the rounded corners on the round-rectangle 655 * @param dir The direction to wind the round-rectangle's contour 656 */ addRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Direction dir)657 public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry, 658 @NonNull Direction dir) { 659 nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt); 660 } 661 662 /** 663 * Add a closed round-rectangle contour to the path. Each corner receives 664 * two radius values [X, Y]. The corners are ordered top-left, top-right, 665 * bottom-right, bottom-left 666 * 667 * @param rect The bounds of a round-rectangle to add to the path 668 * @param radii Array of 8 values, 4 pairs of [X,Y] radii 669 * @param dir The direction to wind the round-rectangle's contour 670 */ addRoundRect(@onNull RectF rect, @NonNull float[] radii, @NonNull Direction dir)671 public void addRoundRect(@NonNull RectF rect, @NonNull float[] radii, @NonNull Direction dir) { 672 if (rect == null) { 673 throw new NullPointerException("need rect parameter"); 674 } 675 addRoundRect(rect.left, rect.top, rect.right, rect.bottom, radii, dir); 676 } 677 678 /** 679 * Add a closed round-rectangle contour to the path. Each corner receives 680 * two radius values [X, Y]. The corners are ordered top-left, top-right, 681 * bottom-right, bottom-left 682 * 683 * @param radii Array of 8 values, 4 pairs of [X,Y] radii 684 * @param dir The direction to wind the round-rectangle's contour 685 */ addRoundRect(float left, float top, float right, float bottom, @NonNull float[] radii, @NonNull Direction dir)686 public void addRoundRect(float left, float top, float right, float bottom, 687 @NonNull float[] radii, @NonNull Direction dir) { 688 if (radii.length < 8) { 689 throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values"); 690 } 691 nAddRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt); 692 } 693 694 /** 695 * Add a copy of src to the path, offset by (dx,dy) 696 * 697 * @param src The path to add as a new contour 698 * @param dx The amount to translate the path in X as it is added 699 */ addPath(@onNull Path src, float dx, float dy)700 public void addPath(@NonNull Path src, float dx, float dy) { 701 nAddPath(mNativePath, src.mNativePath, dx, dy); 702 } 703 704 /** 705 * Add a copy of src to the path 706 * 707 * @param src The path that is appended to the current path 708 */ addPath(@onNull Path src)709 public void addPath(@NonNull Path src) { 710 nAddPath(mNativePath, src.mNativePath); 711 } 712 713 /** 714 * Add a copy of src to the path, transformed by matrix 715 * 716 * @param src The path to add as a new contour 717 */ addPath(@onNull Path src, @NonNull Matrix matrix)718 public void addPath(@NonNull Path src, @NonNull Matrix matrix) { 719 nAddPath(mNativePath, src.mNativePath, matrix.ni()); 720 } 721 722 /** 723 * Offset the path by (dx,dy) 724 * 725 * @param dx The amount in the X direction to offset the entire path 726 * @param dy The amount in the Y direction to offset the entire path 727 * @param dst The translated path is written here. If this is null, then 728 * the original path is modified. 729 */ offset(float dx, float dy, @Nullable Path dst)730 public void offset(float dx, float dy, @Nullable Path dst) { 731 if (dst != null) { 732 dst.set(this); 733 } else { 734 dst = this; 735 } 736 dst.offset(dx, dy); 737 } 738 739 /** 740 * Offset the path by (dx,dy) 741 * 742 * @param dx The amount in the X direction to offset the entire path 743 * @param dy The amount in the Y direction to offset the entire path 744 */ offset(float dx, float dy)745 public void offset(float dx, float dy) { 746 nOffset(mNativePath, dx, dy); 747 } 748 749 /** 750 * Sets the last point of the path. 751 * 752 * @param dx The new X coordinate for the last point 753 * @param dy The new Y coordinate for the last point 754 */ setLastPoint(float dx, float dy)755 public void setLastPoint(float dx, float dy) { 756 nSetLastPoint(mNativePath, dx, dy); 757 } 758 759 /** 760 * Transform the points in this path by matrix, and write the answer 761 * into dst. If dst is null, then the the original path is modified. 762 * 763 * @param matrix The matrix to apply to the path 764 * @param dst The transformed path is written here. If dst is null, 765 * then the the original path is modified 766 */ transform(@onNull Matrix matrix, @Nullable Path dst)767 public void transform(@NonNull Matrix matrix, @Nullable Path dst) { 768 nTransform(mNativePath, matrix.ni(), dst != null ? dst.mNativePath : 0); 769 } 770 771 /** 772 * Transform the points in this path by matrix. 773 * 774 * @param matrix The matrix to apply to the path 775 */ transform(@onNull Matrix matrix)776 public void transform(@NonNull Matrix matrix) { 777 nTransform(mNativePath, matrix.ni()); 778 } 779 780 /** @hide */ readOnlyNI()781 public final long readOnlyNI() { 782 return mNativePath; 783 } 784 mutateNI()785 final long mutateNI() { 786 return mNativePath; 787 } 788 789 /** 790 * Approximate the <code>Path</code> with a series of line segments. 791 * This returns float[] with the array containing point components. 792 * There are three components for each point, in order: 793 * <ul> 794 * <li>Fraction along the length of the path that the point resides</li> 795 * <li>The x coordinate of the point</li> 796 * <li>The y coordinate of the point</li> 797 * </ul> 798 * <p>Two points may share the same fraction along its length when there is 799 * a move action within the Path.</p> 800 * 801 * @param acceptableError The acceptable error for a line on the 802 * Path. Typically this would be 0.5 so that 803 * the error is less than half a pixel. 804 * @return An array of components for points approximating the Path. 805 */ 806 @NonNull 807 @Size(min = 6, multiple = 3) approximate(@loatRangefrom = 0) float acceptableError)808 public float[] approximate(@FloatRange(from = 0) float acceptableError) { 809 if (acceptableError < 0) { 810 throw new IllegalArgumentException("AcceptableError must be greater than or equal to 0"); 811 } 812 return nApproximate(mNativePath, acceptableError); 813 } 814 815 /** 816 * Returns the generation ID of this path. The generation ID changes 817 * whenever the path is modified. This can be used as an efficient way to 818 * check if a path has changed. 819 * 820 * @return The current generation ID for this path 821 */ getGenerationId()822 public int getGenerationId() { 823 return nGetGenerationID(mNativePath); 824 } 825 826 /** 827 * Two paths can be interpolated, by calling {@link #interpolate(Path, float, Path)}, if they 828 * have exactly the same structure. That is, both paths must have the same 829 * operations, in the same order. If any of the operations are 830 * of type {@link PathIterator#VERB_CONIC}, then the weights of those conics must also match. 831 * 832 * @param otherPath The other <code>Path</code> being interpolated to from this one. 833 * @return true if interpolation is possible, false otherwise 834 */ isInterpolatable(@onNull Path otherPath)835 public boolean isInterpolatable(@NonNull Path otherPath) { 836 return nIsInterpolatable(mNativePath, otherPath.mNativePath); 837 } 838 839 /** 840 * This method will linearly interpolate from this path to <code>otherPath</code> given 841 * the interpolation parameter <code>t</code>, returning the result in 842 * <code>interpolatedPath</code>. Interpolation will only succeed if the structures of the 843 * two paths match exactly, as discussed in {@link #isInterpolatable(Path)}. 844 * 845 * @param otherPath The other <code>Path</code> being interpolated to. 846 * @param t The interpolation parameter. A value of 0 results in a <code>Path</code> 847 * equivalent to this path, a value of 1 results in one equivalent to 848 * <code>otherPath</code>. 849 * @param interpolatedPath The interpolated results. 850 */ interpolate(@onNull Path otherPath, float t, @NonNull Path interpolatedPath)851 public boolean interpolate(@NonNull Path otherPath, float t, @NonNull Path interpolatedPath) { 852 return nInterpolate(mNativePath, otherPath.mNativePath, t, interpolatedPath.mNativePath); 853 } 854 855 // ------------------ Regular JNI ------------------------ 856 nInit()857 private static native long nInit(); nInit(long nPath)858 private static native long nInit(long nPath); nGetFinalizer()859 private static native long nGetFinalizer(); nSet(long native_dst, long nSrc)860 private static native void nSet(long native_dst, long nSrc); nComputeBounds(long nPath, RectF bounds)861 private static native void nComputeBounds(long nPath, RectF bounds); nIncReserve(long nPath, int extraPtCount)862 private static native void nIncReserve(long nPath, int extraPtCount); nMoveTo(long nPath, float x, float y)863 private static native void nMoveTo(long nPath, float x, float y); nRMoveTo(long nPath, float dx, float dy)864 private static native void nRMoveTo(long nPath, float dx, float dy); nLineTo(long nPath, float x, float y)865 private static native void nLineTo(long nPath, float x, float y); nRLineTo(long nPath, float dx, float dy)866 private static native void nRLineTo(long nPath, float dx, float dy); nQuadTo(long nPath, float x1, float y1, float x2, float y2)867 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)868 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)869 private static native void nConicTo(long nPath, float x1, float y1, float x2, float y2, 870 float weight); nRConicTo(long nPath, float dx1, float dy1, float dx2, float dy2, float weight)871 private static native void nRConicTo(long nPath, float dx1, float dy1, float dx2, float dy2, 872 float weight); nCubicTo(long nPath, float x1, float y1, float x2, float y2, float x3, float y3)873 private static native void nCubicTo(long nPath, float x1, float y1, float x2, float y2, 874 float x3, float y3); nRCubicTo(long nPath, float x1, float y1, float x2, float y2, float x3, float y3)875 private static native void nRCubicTo(long nPath, float x1, float y1, float x2, float y2, 876 float x3, float y3); nArcTo(long nPath, float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo)877 private static native void nArcTo(long nPath, float left, float top, float right, float bottom, 878 float startAngle, float sweepAngle, boolean forceMoveTo); nClose(long nPath)879 private static native void nClose(long nPath); nAddRect(long nPath, float left, float top, float right, float bottom, int dir)880 private static native void nAddRect(long nPath, float left, float top, 881 float right, float bottom, int dir); nAddOval(long nPath, float left, float top, float right, float bottom, int dir)882 private static native void nAddOval(long nPath, float left, float top, 883 float right, float bottom, int dir); nAddCircle(long nPath, float x, float y, float radius, int dir)884 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)885 private static native void nAddArc(long nPath, float left, float top, float right, float bottom, 886 float startAngle, float sweepAngle); nAddRoundRect(long nPath, float left, float top, float right, float bottom, float rx, float ry, int dir)887 private static native void nAddRoundRect(long nPath, float left, float top, 888 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)889 private static native void nAddRoundRect(long nPath, float left, float top, 890 float right, float bottom, float[] radii, int dir); nAddPath(long nPath, long src, float dx, float dy)891 private static native void nAddPath(long nPath, long src, float dx, float dy); nAddPath(long nPath, long src)892 private static native void nAddPath(long nPath, long src); nAddPath(long nPath, long src, long matrix)893 private static native void nAddPath(long nPath, long src, long matrix); nOffset(long nPath, float dx, float dy)894 private static native void nOffset(long nPath, float dx, float dy); nSetLastPoint(long nPath, float dx, float dy)895 private static native void nSetLastPoint(long nPath, float dx, float dy); nTransform(long nPath, long matrix, long dst_path)896 private static native void nTransform(long nPath, long matrix, long dst_path); nTransform(long nPath, long matrix)897 private static native void nTransform(long nPath, long matrix); nOp(long path1, long path2, int op, long result)898 private static native boolean nOp(long path1, long path2, int op, long result); nApproximate(long nPath, float error)899 private static native float[] nApproximate(long nPath, float error); nInterpolate(long startPath, long endPath, float t, long interpolatedPath)900 private static native boolean nInterpolate(long startPath, long endPath, float t, 901 long interpolatedPath); 902 903 // ------------------ Fast JNI ------------------------ 904 905 @FastNative nIsRect(long nPath, RectF rect)906 private static native boolean nIsRect(long nPath, RectF rect); 907 908 // ------------------ Critical JNI ------------------------ 909 910 @CriticalNative nGetGenerationID(long nativePath)911 private static native int nGetGenerationID(long nativePath); 912 @CriticalNative nIsInterpolatable(long startPath, long endPath)913 private static native boolean nIsInterpolatable(long startPath, long endPath); 914 @CriticalNative nReset(long nPath)915 private static native void nReset(long nPath); 916 @CriticalNative nRewind(long nPath)917 private static native void nRewind(long nPath); 918 @CriticalNative nIsEmpty(long nPath)919 private static native boolean nIsEmpty(long nPath); 920 @CriticalNative nIsConvex(long nPath)921 private static native boolean nIsConvex(long nPath); 922 @CriticalNative nGetFillType(long nPath)923 private static native int nGetFillType(long nPath); 924 @CriticalNative nSetFillType(long nPath, int ft)925 private static native void nSetFillType(long nPath, int ft); 926 } 927