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 17package android.graphics; 18 19import android.view.HardwareRenderer; 20 21/** 22 * The Path class encapsulates compound (multiple contour) geometric paths 23 * consisting of straight line segments, quadratic curves, and cubic curves. 24 * It can be drawn with canvas.drawPath(path, paint), either filled or stroked 25 * (based on the paint's Style), or it can be used for clipping or to draw 26 * text on a path. 27 */ 28public class Path { 29 /** 30 * @hide 31 */ 32 public final int mNativePath; 33 34 /** 35 * @hide 36 */ 37 public boolean isSimplePath = true; 38 /** 39 * @hide 40 */ 41 public Region rects; 42 private boolean mDetectSimplePaths; 43 private Direction mLastDirection = null; 44 45 /** 46 * Create an empty path 47 */ 48 public Path() { 49 mNativePath = init1(); 50 mDetectSimplePaths = HardwareRenderer.isAvailable(); 51 } 52 53 /** 54 * Create a new path, copying the contents from the src path. 55 * 56 * @param src The path to copy from when initializing the new path 57 */ 58 public Path(Path src) { 59 int valNative = 0; 60 if (src != null) { 61 valNative = src.mNativePath; 62 isSimplePath = src.isSimplePath; 63 if (src.rects != null) { 64 rects = new Region(src.rects); 65 } 66 } 67 mNativePath = init2(valNative); 68 mDetectSimplePaths = HardwareRenderer.isAvailable(); 69 } 70 71 /** 72 * Clear any lines and curves from the path, making it empty. 73 * This does NOT change the fill-type setting. 74 */ 75 public void reset() { 76 isSimplePath = true; 77 if (mDetectSimplePaths) { 78 mLastDirection = null; 79 if (rects != null) rects.setEmpty(); 80 } 81 native_reset(mNativePath); 82 } 83 84 /** 85 * Rewinds the path: clears any lines and curves from the path but 86 * keeps the internal data structure for faster reuse. 87 */ 88 public void rewind() { 89 isSimplePath = true; 90 if (mDetectSimplePaths) { 91 mLastDirection = null; 92 if (rects != null) rects.setEmpty(); 93 } 94 native_rewind(mNativePath); 95 } 96 97 /** Replace the contents of this with the contents of src. 98 */ 99 public void set(Path src) { 100 if (this != src) { 101 isSimplePath = src.isSimplePath; 102 native_set(mNativePath, src.mNativePath); 103 } 104 } 105 106 /** Enum for the ways a path may be filled 107 */ 108 public enum FillType { 109 // these must match the values in SkPath.h 110 WINDING (0), 111 EVEN_ODD (1), 112 INVERSE_WINDING (2), 113 INVERSE_EVEN_ODD(3); 114 115 FillType(int ni) { 116 nativeInt = ni; 117 } 118 final int nativeInt; 119 } 120 121 // these must be in the same order as their native values 122 static final FillType[] sFillTypeArray = { 123 FillType.WINDING, 124 FillType.EVEN_ODD, 125 FillType.INVERSE_WINDING, 126 FillType.INVERSE_EVEN_ODD 127 }; 128 129 /** 130 * Return the path's fill type. This defines how "inside" is 131 * computed. The default value is WINDING. 132 * 133 * @return the path's fill type 134 */ 135 public FillType getFillType() { 136 return sFillTypeArray[native_getFillType(mNativePath)]; 137 } 138 139 /** 140 * Set the path's fill type. This defines how "inside" is computed. 141 * 142 * @param ft The new fill type for this path 143 */ 144 public void setFillType(FillType ft) { 145 native_setFillType(mNativePath, ft.nativeInt); 146 } 147 148 /** 149 * Returns true if the filltype is one of the INVERSE variants 150 * 151 * @return true if the filltype is one of the INVERSE variants 152 */ 153 public boolean isInverseFillType() { 154 final int ft = native_getFillType(mNativePath); 155 return (ft & 2) != 0; 156 } 157 158 /** 159 * Toggles the INVERSE state of the filltype 160 */ 161 public void toggleInverseFillType() { 162 int ft = native_getFillType(mNativePath); 163 ft ^= 2; 164 native_setFillType(mNativePath, ft); 165 } 166 167 /** 168 * Returns true if the path is empty (contains no lines or curves) 169 * 170 * @return true if the path is empty (contains no lines or curves) 171 */ 172 public boolean isEmpty() { 173 return native_isEmpty(mNativePath); 174 } 175 176 /** 177 * Returns true if the path specifies a rectangle. If so, and if rect is 178 * not null, set rect to the bounds of the path. If the path does not 179 * specify a rectangle, return false and ignore rect. 180 * 181 * @param rect If not null, returns the bounds of the path if it specifies 182 * a rectangle 183 * @return true if the path specifies a rectangle 184 */ 185 public boolean isRect(RectF rect) { 186 return native_isRect(mNativePath, rect); 187 } 188 189 /** 190 * Compute the bounds of the control points of the path, and write the 191 * answer into bounds. If the path contains 0 or 1 points, the bounds is 192 * set to (0,0,0,0) 193 * 194 * @param bounds Returns the computed bounds of the path's control points. 195 * @param exact This parameter is no longer used. 196 */ 197 @SuppressWarnings({"UnusedDeclaration"}) 198 public void computeBounds(RectF bounds, boolean exact) { 199 native_computeBounds(mNativePath, bounds); 200 } 201 202 /** 203 * Hint to the path to prepare for adding more points. This can allow the 204 * path to more efficiently allocate its storage. 205 * 206 * @param extraPtCount The number of extra points that may be added to this 207 * path 208 */ 209 public void incReserve(int extraPtCount) { 210 native_incReserve(mNativePath, extraPtCount); 211 } 212 213 /** 214 * Set the beginning of the next contour to the point (x,y). 215 * 216 * @param x The x-coordinate of the start of a new contour 217 * @param y The y-coordinate of the start of a new contour 218 */ 219 public void moveTo(float x, float y) { 220 native_moveTo(mNativePath, x, y); 221 } 222 223 /** 224 * Set the beginning of the next contour relative to the last point on the 225 * previous contour. If there is no previous contour, this is treated the 226 * same as moveTo(). 227 * 228 * @param dx The amount to add to the x-coordinate of the end of the 229 * previous contour, to specify the start of a new contour 230 * @param dy The amount to add to the y-coordinate of the end of the 231 * previous contour, to specify the start of a new contour 232 */ 233 public void rMoveTo(float dx, float dy) { 234 native_rMoveTo(mNativePath, dx, dy); 235 } 236 237 /** 238 * Add a line from the last point to the specified point (x,y). 239 * If no moveTo() call has been made for this contour, the first point is 240 * automatically set to (0,0). 241 * 242 * @param x The x-coordinate of the end of a line 243 * @param y The y-coordinate of the end of a line 244 */ 245 public void lineTo(float x, float y) { 246 isSimplePath = false; 247 native_lineTo(mNativePath, x, y); 248 } 249 250 /** 251 * Same as lineTo, but the coordinates are considered relative to the last 252 * point on this contour. If there is no previous point, then a moveTo(0,0) 253 * is inserted automatically. 254 * 255 * @param dx The amount to add to the x-coordinate of the previous point on 256 * this contour, to specify a line 257 * @param dy The amount to add to the y-coordinate of the previous point on 258 * this contour, to specify a line 259 */ 260 public void rLineTo(float dx, float dy) { 261 isSimplePath = false; 262 native_rLineTo(mNativePath, dx, dy); 263 } 264 265 /** 266 * Add a quadratic bezier from the last point, approaching control point 267 * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for 268 * this contour, the first point is automatically set to (0,0). 269 * 270 * @param x1 The x-coordinate of the control point on a quadratic curve 271 * @param y1 The y-coordinate of the control point on a quadratic curve 272 * @param x2 The x-coordinate of the end point on a quadratic curve 273 * @param y2 The y-coordinate of the end point on a quadratic curve 274 */ 275 public void quadTo(float x1, float y1, float x2, float y2) { 276 isSimplePath = false; 277 native_quadTo(mNativePath, x1, y1, x2, y2); 278 } 279 280 /** 281 * Same as quadTo, but the coordinates are considered relative to the last 282 * point on this contour. If there is no previous point, then a moveTo(0,0) 283 * is inserted automatically. 284 * 285 * @param dx1 The amount to add to the x-coordinate of the last point on 286 * this contour, for the control point of a quadratic curve 287 * @param dy1 The amount to add to the y-coordinate of the last point on 288 * this contour, for the control point of a quadratic curve 289 * @param dx2 The amount to add to the x-coordinate of the last point on 290 * this contour, for the end point of a quadratic curve 291 * @param dy2 The amount to add to the y-coordinate of the last point on 292 * this contour, for the end point of a quadratic curve 293 */ 294 public void rQuadTo(float dx1, float dy1, float dx2, float dy2) { 295 isSimplePath = false; 296 native_rQuadTo(mNativePath, dx1, dy1, dx2, dy2); 297 } 298 299 /** 300 * Add a cubic bezier from the last point, approaching control points 301 * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been 302 * made for this contour, the first point is automatically set to (0,0). 303 * 304 * @param x1 The x-coordinate of the 1st control point on a cubic curve 305 * @param y1 The y-coordinate of the 1st control point on a cubic curve 306 * @param x2 The x-coordinate of the 2nd control point on a cubic curve 307 * @param y2 The y-coordinate of the 2nd control point on a cubic curve 308 * @param x3 The x-coordinate of the end point on a cubic curve 309 * @param y3 The y-coordinate of the end point on a cubic curve 310 */ 311 public void cubicTo(float x1, float y1, float x2, float y2, 312 float x3, float y3) { 313 isSimplePath = false; 314 native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3); 315 } 316 317 /** 318 * Same as cubicTo, but the coordinates are considered relative to the 319 * current point on this contour. If there is no previous point, then a 320 * moveTo(0,0) is inserted automatically. 321 */ 322 public void rCubicTo(float x1, float y1, float x2, float y2, 323 float x3, float y3) { 324 isSimplePath = false; 325 native_rCubicTo(mNativePath, x1, y1, x2, y2, x3, y3); 326 } 327 328 /** 329 * Append the specified arc to the path as a new contour. If the start of 330 * the path is different from the path's current last point, then an 331 * automatic lineTo() is added to connect the current contour to the 332 * start of the arc. However, if the path is empty, then we call moveTo() 333 * with the first point of the arc. The sweep angle is tread mod 360. 334 * 335 * @param oval The bounds of oval defining shape and size of the arc 336 * @param startAngle Starting angle (in degrees) where the arc begins 337 * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated 338 * mod 360. 339 * @param forceMoveTo If true, always begin a new contour with the arc 340 */ 341 public void arcTo(RectF oval, float startAngle, float sweepAngle, 342 boolean forceMoveTo) { 343 isSimplePath = false; 344 native_arcTo(mNativePath, oval, startAngle, sweepAngle, forceMoveTo); 345 } 346 347 /** 348 * Append the specified arc to the path as a new contour. If the start of 349 * the path is different from the path's current last point, then an 350 * automatic lineTo() is added to connect the current contour to the 351 * start of the arc. However, if the path is empty, then we call moveTo() 352 * with the first point of the arc. 353 * 354 * @param oval The bounds of oval defining shape and size of the arc 355 * @param startAngle Starting angle (in degrees) where the arc begins 356 * @param sweepAngle Sweep angle (in degrees) measured clockwise 357 */ 358 public void arcTo(RectF oval, float startAngle, float sweepAngle) { 359 isSimplePath = false; 360 native_arcTo(mNativePath, oval, startAngle, sweepAngle, false); 361 } 362 363 /** 364 * Close the current contour. If the current point is not equal to the 365 * first point of the contour, a line segment is automatically added. 366 */ 367 public void close() { 368 isSimplePath = false; 369 native_close(mNativePath); 370 } 371 372 /** 373 * Specifies how closed shapes (e.g. rects, ovals) are oriented when they 374 * are added to a path. 375 */ 376 public enum Direction { 377 /** clockwise */ 378 CW (0), // must match enum in SkPath.h 379 /** counter-clockwise */ 380 CCW (1); // must match enum in SkPath.h 381 382 Direction(int ni) { 383 nativeInt = ni; 384 } 385 final int nativeInt; 386 } 387 388 private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) { 389 if (mDetectSimplePaths) { 390 if (mLastDirection == null) { 391 mLastDirection = dir; 392 } 393 if (mLastDirection != dir) { 394 isSimplePath = false; 395 } else { 396 if (rects == null) rects = new Region(); 397 rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION); 398 } 399 } 400 } 401 402 /** 403 * Add a closed rectangle contour to the path 404 * 405 * @param rect The rectangle to add as a closed contour to the path 406 * @param dir The direction to wind the rectangle's contour 407 */ 408 public void addRect(RectF rect, Direction dir) { 409 if (rect == null) { 410 throw new NullPointerException("need rect parameter"); 411 } 412 detectSimplePath(rect.left, rect.top, rect.right, rect.bottom, dir); 413 native_addRect(mNativePath, rect, dir.nativeInt); 414 } 415 416 /** 417 * Add a closed rectangle contour to the path 418 * 419 * @param left The left side of a rectangle to add to the path 420 * @param top The top of a rectangle to add to the path 421 * @param right The right side of a rectangle to add to the path 422 * @param bottom The bottom of a rectangle to add to the path 423 * @param dir The direction to wind the rectangle's contour 424 */ 425 public void addRect(float left, float top, float right, float bottom, Direction dir) { 426 detectSimplePath(left, top, right, bottom, dir); 427 native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt); 428 } 429 430 /** 431 * Add a closed oval contour to the path 432 * 433 * @param oval The bounds of the oval to add as a closed contour to the path 434 * @param dir The direction to wind the oval's contour 435 */ 436 public void addOval(RectF oval, Direction dir) { 437 if (oval == null) { 438 throw new NullPointerException("need oval parameter"); 439 } 440 isSimplePath = false; 441 native_addOval(mNativePath, oval, dir.nativeInt); 442 } 443 444 /** 445 * Add a closed circle contour to the path 446 * 447 * @param x The x-coordinate of the center of a circle to add to the path 448 * @param y The y-coordinate of the center of a circle to add to the path 449 * @param radius The radius of a circle to add to the path 450 * @param dir The direction to wind the circle's contour 451 */ 452 public void addCircle(float x, float y, float radius, Direction dir) { 453 isSimplePath = false; 454 native_addCircle(mNativePath, x, y, radius, dir.nativeInt); 455 } 456 457 /** 458 * Add the specified arc to the path as a new contour. 459 * 460 * @param oval The bounds of oval defining the shape and size of the arc 461 * @param startAngle Starting angle (in degrees) where the arc begins 462 * @param sweepAngle Sweep angle (in degrees) measured clockwise 463 */ 464 public void addArc(RectF oval, float startAngle, float sweepAngle) { 465 if (oval == null) { 466 throw new NullPointerException("need oval parameter"); 467 } 468 isSimplePath = false; 469 native_addArc(mNativePath, oval, startAngle, sweepAngle); 470 } 471 472 /** 473 * Add a closed round-rectangle contour to the path 474 * 475 * @param rect The bounds of a round-rectangle to add to the path 476 * @param rx The x-radius of the rounded corners on the round-rectangle 477 * @param ry The y-radius of the rounded corners on the round-rectangle 478 * @param dir The direction to wind the round-rectangle's contour 479 */ 480 public void addRoundRect(RectF rect, float rx, float ry, Direction dir) { 481 if (rect == null) { 482 throw new NullPointerException("need rect parameter"); 483 } 484 isSimplePath = false; 485 native_addRoundRect(mNativePath, rect, rx, ry, dir.nativeInt); 486 } 487 488 /** 489 * Add a closed round-rectangle contour to the path. Each corner receives 490 * two radius values [X, Y]. The corners are ordered top-left, top-right, 491 * bottom-right, bottom-left 492 * 493 * @param rect The bounds of a round-rectangle to add to the path 494 * @param radii Array of 8 values, 4 pairs of [X,Y] radii 495 * @param dir The direction to wind the round-rectangle's contour 496 */ 497 public void addRoundRect(RectF rect, float[] radii, Direction dir) { 498 if (rect == null) { 499 throw new NullPointerException("need rect parameter"); 500 } 501 if (radii.length < 8) { 502 throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values"); 503 } 504 isSimplePath = false; 505 native_addRoundRect(mNativePath, rect, radii, dir.nativeInt); 506 } 507 508 /** 509 * Add a copy of src to the path, offset by (dx,dy) 510 * 511 * @param src The path to add as a new contour 512 * @param dx The amount to translate the path in X as it is added 513 */ 514 public void addPath(Path src, float dx, float dy) { 515 isSimplePath = false; 516 native_addPath(mNativePath, src.mNativePath, dx, dy); 517 } 518 519 /** 520 * Add a copy of src to the path 521 * 522 * @param src The path that is appended to the current path 523 */ 524 public void addPath(Path src) { 525 isSimplePath = false; 526 native_addPath(mNativePath, src.mNativePath); 527 } 528 529 /** 530 * Add a copy of src to the path, transformed by matrix 531 * 532 * @param src The path to add as a new contour 533 */ 534 public void addPath(Path src, Matrix matrix) { 535 if (!src.isSimplePath) isSimplePath = false; 536 native_addPath(mNativePath, src.mNativePath, matrix.native_instance); 537 } 538 539 /** 540 * Offset the path by (dx,dy), returning true on success 541 * 542 * @param dx The amount in the X direction to offset the entire path 543 * @param dy The amount in the Y direction to offset the entire path 544 * @param dst The translated path is written here. If this is null, then 545 * the original path is modified. 546 */ 547 public void offset(float dx, float dy, Path dst) { 548 int dstNative = 0; 549 if (dst != null) { 550 dstNative = dst.mNativePath; 551 dst.isSimplePath = false; 552 } 553 native_offset(mNativePath, dx, dy, dstNative); 554 } 555 556 /** 557 * Offset the path by (dx,dy), returning true on success 558 * 559 * @param dx The amount in the X direction to offset the entire path 560 * @param dy The amount in the Y direction to offset the entire path 561 */ 562 public void offset(float dx, float dy) { 563 isSimplePath = false; 564 native_offset(mNativePath, dx, dy); 565 } 566 567 /** 568 * Sets the last point of the path. 569 * 570 * @param dx The new X coordinate for the last point 571 * @param dy The new Y coordinate for the last point 572 */ 573 public void setLastPoint(float dx, float dy) { 574 isSimplePath = false; 575 native_setLastPoint(mNativePath, dx, dy); 576 } 577 578 /** 579 * Transform the points in this path by matrix, and write the answer 580 * into dst. If dst is null, then the the original path is modified. 581 * 582 * @param matrix The matrix to apply to the path 583 * @param dst The transformed path is written here. If dst is null, 584 * then the the original path is modified 585 */ 586 public void transform(Matrix matrix, Path dst) { 587 int dstNative = 0; 588 if (dst != null) { 589 dst.isSimplePath = false; 590 dstNative = dst.mNativePath; 591 } 592 native_transform(mNativePath, matrix.native_instance, dstNative); 593 } 594 595 /** 596 * Transform the points in this path by matrix. 597 * 598 * @param matrix The matrix to apply to the path 599 */ 600 public void transform(Matrix matrix) { 601 isSimplePath = false; 602 native_transform(mNativePath, matrix.native_instance); 603 } 604 605 protected void finalize() throws Throwable { 606 try { 607 finalizer(mNativePath); 608 } finally { 609 super.finalize(); 610 } 611 } 612 613 final int ni() { 614 return mNativePath; 615 } 616 617 private static native int init1(); 618 private static native int init2(int nPath); 619 private static native void native_reset(int nPath); 620 private static native void native_rewind(int nPath); 621 private static native void native_set(int native_dst, int native_src); 622 private static native int native_getFillType(int nPath); 623 private static native void native_setFillType(int nPath, int ft); 624 private static native boolean native_isEmpty(int nPath); 625 private static native boolean native_isRect(int nPath, RectF rect); 626 private static native void native_computeBounds(int nPath, RectF bounds); 627 private static native void native_incReserve(int nPath, int extraPtCount); 628 private static native void native_moveTo(int nPath, float x, float y); 629 private static native void native_rMoveTo(int nPath, float dx, float dy); 630 private static native void native_lineTo(int nPath, float x, float y); 631 private static native void native_rLineTo(int nPath, float dx, float dy); 632 private static native void native_quadTo(int nPath, float x1, float y1, 633 float x2, float y2); 634 private static native void native_rQuadTo(int nPath, float dx1, float dy1, 635 float dx2, float dy2); 636 private static native void native_cubicTo(int nPath, float x1, float y1, 637 float x2, float y2, float x3, float y3); 638 private static native void native_rCubicTo(int nPath, float x1, float y1, 639 float x2, float y2, float x3, float y3); 640 private static native void native_arcTo(int nPath, RectF oval, 641 float startAngle, float sweepAngle, boolean forceMoveTo); 642 private static native void native_close(int nPath); 643 private static native void native_addRect(int nPath, RectF rect, int dir); 644 private static native void native_addRect(int nPath, float left, float top, 645 float right, float bottom, int dir); 646 private static native void native_addOval(int nPath, RectF oval, int dir); 647 private static native void native_addCircle(int nPath, float x, float y, 648 float radius, int dir); 649 private static native void native_addArc(int nPath, RectF oval, 650 float startAngle, float sweepAngle); 651 private static native void native_addRoundRect(int nPath, RectF rect, 652 float rx, float ry, int dir); 653 private static native void native_addRoundRect(int nPath, RectF r, 654 float[] radii, int dir); 655 private static native void native_addPath(int nPath, int src, float dx, 656 float dy); 657 private static native void native_addPath(int nPath, int src); 658 private static native void native_addPath(int nPath, int src, int matrix); 659 private static native void native_offset(int nPath, float dx, float dy, 660 int dst_path); 661 private static native void native_offset(int nPath, float dx, float dy); 662 private static native void native_setLastPoint(int nPath, float dx, float dy); 663 private static native void native_transform(int nPath, int matrix, 664 int dst_path); 665 private static native void native_transform(int nPath, int matrix); 666 private static native void finalizer(int nPath); 667} 668