Path_Delegate.java revision 8a80a8555238cc564f445f902aff5231993a8f96
1/* 2 * Copyright (C) 2010 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 com.android.ide.common.rendering.api.LayoutLog; 20import com.android.layoutlib.bridge.Bridge; 21import com.android.layoutlib.bridge.impl.DelegateManager; 22import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 23 24import android.graphics.Path.Direction; 25import android.graphics.Path.FillType; 26 27import java.awt.Shape; 28import java.awt.geom.AffineTransform; 29import java.awt.geom.Arc2D; 30import java.awt.geom.Area; 31import java.awt.geom.GeneralPath; 32import java.awt.geom.PathIterator; 33import java.awt.geom.Point2D; 34import java.awt.geom.Rectangle2D; 35 36/** 37 * Delegate implementing the native methods of android.graphics.Path 38 * 39 * Through the layoutlib_create tool, the original native methods of Path have been replaced 40 * by calls to methods of the same name in this delegate class. 41 * 42 * This class behaves like the original native implementation, but in Java, keeping previously 43 * native data into its own objects and mapping them to int that are sent back and forth between 44 * it and the original Path class. 45 * 46 * @see DelegateManager 47 * 48 */ 49public final class Path_Delegate { 50 51 // ---- delegate manager ---- 52 private static final DelegateManager<Path_Delegate> sManager = 53 new DelegateManager<Path_Delegate>(); 54 55 // ---- delegate data ---- 56 private FillType mFillType = FillType.WINDING; 57 private GeneralPath mPath = new GeneralPath(); 58 59 private float mLastX = 0; 60 private float mLastY = 0; 61 62 // ---- Public Helper methods ---- 63 64 public static Path_Delegate getDelegate(int nPath) { 65 return sManager.getDelegate(nPath); 66 } 67 68 public Shape getJavaShape() { 69 return mPath; 70 } 71 72 public void setJavaShape(Shape shape) { 73 mPath.reset(); 74 mPath.append(shape, false /*connect*/); 75 } 76 77 public void reset() { 78 mPath.reset(); 79 } 80 81 public void setPathIterator(PathIterator iterator) { 82 mPath.reset(); 83 mPath.append(iterator, false /*connect*/); 84 } 85 86 // ---- native methods ---- 87 88 @LayoutlibDelegate 89 /*package*/ static int init1() { 90 // create the delegate 91 Path_Delegate newDelegate = new Path_Delegate(); 92 93 return sManager.addDelegate(newDelegate); 94 } 95 96 @LayoutlibDelegate 97 /*package*/ static int init2(int nPath) { 98 // create the delegate 99 Path_Delegate newDelegate = new Path_Delegate(); 100 101 // get the delegate to copy, which could be null if nPath is 0 102 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 103 if (pathDelegate != null) { 104 newDelegate.set(pathDelegate); 105 } 106 107 return sManager.addDelegate(newDelegate); 108 } 109 110 @LayoutlibDelegate 111 /*package*/ static void native_reset(int nPath) { 112 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 113 if (pathDelegate == null) { 114 return; 115 } 116 117 pathDelegate.mPath.reset(); 118 } 119 120 @LayoutlibDelegate 121 /*package*/ static void native_rewind(int nPath) { 122 // call out to reset since there's nothing to optimize in 123 // terms of data structs. 124 native_reset(nPath); 125 } 126 127 @LayoutlibDelegate 128 /*package*/ static void native_set(int native_dst, int native_src) { 129 Path_Delegate pathDstDelegate = sManager.getDelegate(native_dst); 130 if (pathDstDelegate == null) { 131 return; 132 } 133 134 Path_Delegate pathSrcDelegate = sManager.getDelegate(native_src); 135 if (pathSrcDelegate == null) { 136 return; 137 } 138 139 pathDstDelegate.set(pathSrcDelegate); 140 } 141 142 @LayoutlibDelegate 143 /*package*/ static int native_getFillType(int nPath) { 144 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 145 if (pathDelegate == null) { 146 return 0; 147 } 148 149 return pathDelegate.mFillType.nativeInt; 150 } 151 152 @LayoutlibDelegate 153 /*package*/ static void native_setFillType(int nPath, int ft) { 154 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 155 if (pathDelegate == null) { 156 return; 157 } 158 159 pathDelegate.mFillType = Path.sFillTypeArray[ft]; 160 } 161 162 @LayoutlibDelegate 163 /*package*/ static boolean native_isEmpty(int nPath) { 164 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 165 if (pathDelegate == null) { 166 return true; 167 } 168 169 return pathDelegate.isEmpty(); 170 } 171 172 @LayoutlibDelegate 173 /*package*/ static boolean native_isRect(int nPath, RectF rect) { 174 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 175 if (pathDelegate == null) { 176 return false; 177 } 178 179 // create an Area that can test if the path is a rect 180 Area area = new Area(pathDelegate.mPath); 181 if (area.isRectangular()) { 182 if (rect != null) { 183 pathDelegate.fillBounds(rect); 184 } 185 186 return true; 187 } 188 189 return false; 190 } 191 192 @LayoutlibDelegate 193 /*package*/ static void native_computeBounds(int nPath, RectF bounds) { 194 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 195 if (pathDelegate == null) { 196 return; 197 } 198 199 pathDelegate.fillBounds(bounds); 200 } 201 202 @LayoutlibDelegate 203 /*package*/ static void native_incReserve(int nPath, int extraPtCount) { 204 // since we use a java2D path, there's no way to pre-allocate new points, 205 // so we do nothing. 206 } 207 208 @LayoutlibDelegate 209 /*package*/ static void native_moveTo(int nPath, float x, float y) { 210 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 211 if (pathDelegate == null) { 212 return; 213 } 214 215 pathDelegate.moveTo(x, y); 216 } 217 218 @LayoutlibDelegate 219 /*package*/ static void native_rMoveTo(int nPath, float dx, float dy) { 220 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 221 if (pathDelegate == null) { 222 return; 223 } 224 225 pathDelegate.rMoveTo(dx, dy); 226 } 227 228 @LayoutlibDelegate 229 /*package*/ static void native_lineTo(int nPath, float x, float y) { 230 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 231 if (pathDelegate == null) { 232 return; 233 } 234 235 pathDelegate.lineTo(x, y); 236 } 237 238 @LayoutlibDelegate 239 /*package*/ static void native_rLineTo(int nPath, float dx, float dy) { 240 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 241 if (pathDelegate == null) { 242 return; 243 } 244 245 pathDelegate.rLineTo(dx, dy); 246 } 247 248 @LayoutlibDelegate 249 /*package*/ static void native_quadTo(int nPath, float x1, float y1, float x2, float y2) { 250 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 251 if (pathDelegate == null) { 252 return; 253 } 254 255 pathDelegate.quadTo(x1, y1, x2, y2); 256 } 257 258 @LayoutlibDelegate 259 /*package*/ static void native_rQuadTo(int nPath, float dx1, float dy1, float dx2, float dy2) { 260 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 261 if (pathDelegate == null) { 262 return; 263 } 264 265 pathDelegate.rQuadTo(dx1, dy1, dx2, dy2); 266 } 267 268 @LayoutlibDelegate 269 /*package*/ static void native_cubicTo(int nPath, float x1, float y1, 270 float x2, float y2, float x3, float y3) { 271 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 272 if (pathDelegate == null) { 273 return; 274 } 275 276 pathDelegate.cubicTo(x1, y1, x2, y2, x3, y3); 277 } 278 279 @LayoutlibDelegate 280 /*package*/ static void native_rCubicTo(int nPath, float x1, float y1, 281 float x2, float y2, float x3, float y3) { 282 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 283 if (pathDelegate == null) { 284 return; 285 } 286 287 pathDelegate.rCubicTo(x1, y1, x2, y2, x3, y3); 288 } 289 290 @LayoutlibDelegate 291 /*package*/ static void native_arcTo(int nPath, RectF oval, 292 float startAngle, float sweepAngle, boolean forceMoveTo) { 293 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 294 if (pathDelegate == null) { 295 return; 296 } 297 298 pathDelegate.arcTo(oval, startAngle, sweepAngle, forceMoveTo); 299 } 300 301 @LayoutlibDelegate 302 /*package*/ static void native_close(int nPath) { 303 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 304 if (pathDelegate == null) { 305 return; 306 } 307 308 pathDelegate.close(); 309 } 310 311 @LayoutlibDelegate 312 /*package*/ static void native_addRect(int nPath, RectF rect, int dir) { 313 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 314 if (pathDelegate == null) { 315 return; 316 } 317 318 pathDelegate.addRect(rect.left, rect.top, rect.right, rect.bottom, dir); 319 } 320 321 @LayoutlibDelegate 322 /*package*/ static void native_addRect(int nPath, 323 float left, float top, float right, float bottom, int dir) { 324 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 325 if (pathDelegate == null) { 326 return; 327 } 328 329 pathDelegate.addRect(left, top, right, bottom, dir); 330 } 331 332 @LayoutlibDelegate 333 /*package*/ static void native_addOval(int nPath, RectF oval, int dir) { 334 // FIXME 335 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 336 "Path.addOval is not supported.", null, null /*data*/); 337 } 338 339 @LayoutlibDelegate 340 /*package*/ static void native_addCircle(int nPath, float x, float y, float radius, int dir) { 341 // FIXME 342 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 343 "Path.addCircle is not supported.", null, null /*data*/); 344 } 345 346 @LayoutlibDelegate 347 /*package*/ static void native_addArc(int nPath, RectF oval, 348 float startAngle, float sweepAngle) { 349 // FIXME 350 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 351 "Path.addArc is not supported.", null, null /*data*/); 352 } 353 354 @LayoutlibDelegate 355 /*package*/ static void native_addRoundRect(int nPath, RectF rect, 356 float rx, float ry, int dir) { 357 // FIXME 358 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 359 "Path.addRoundRect is not supported.", null, null /*data*/); 360 } 361 362 @LayoutlibDelegate 363 /*package*/ static void native_addRoundRect(int nPath, RectF r, float[] radii, int dir) { 364 // FIXME 365 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 366 "Path.addRoundRect is not supported.", null, null /*data*/); 367 } 368 369 @LayoutlibDelegate 370 /*package*/ static void native_addPath(int nPath, int src, float dx, float dy) { 371 // FIXME 372 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 373 "Path.addPath is not supported.", null, null /*data*/); 374 } 375 376 @LayoutlibDelegate 377 /*package*/ static void native_addPath(int nPath, int src) { 378 native_addPath(nPath, src, 0, 0); 379 } 380 381 @LayoutlibDelegate 382 /*package*/ static void native_addPath(int nPath, int src, int matrix) { 383 // FIXME 384 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 385 "Path.addPath is not supported.", null, null /*data*/); 386 } 387 388 @LayoutlibDelegate 389 /*package*/ static void native_offset(int nPath, float dx, float dy, int dst_path) { 390 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 391 if (pathDelegate == null) { 392 return; 393 } 394 395 // could be null if the int is 0; 396 Path_Delegate dstDelegate = sManager.getDelegate(dst_path); 397 398 pathDelegate.offset(dx, dy, dstDelegate); 399 } 400 401 @LayoutlibDelegate 402 /*package*/ static void native_offset(int nPath, float dx, float dy) { 403 native_offset(nPath, dx, dy, 0); 404 } 405 406 @LayoutlibDelegate 407 /*package*/ static void native_setLastPoint(int nPath, float dx, float dy) { 408 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 409 if (pathDelegate == null) { 410 return; 411 } 412 413 pathDelegate.mLastX = dx; 414 pathDelegate.mLastY = dy; 415 } 416 417 @LayoutlibDelegate 418 /*package*/ static void native_transform(int nPath, int matrix, 419 int dst_path) { 420 Path_Delegate pathDelegate = sManager.getDelegate(nPath); 421 if (pathDelegate == null) { 422 return; 423 } 424 425 Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix); 426 if (matrixDelegate == null) { 427 return; 428 } 429 430 // this can be null if dst_path is 0 431 Path_Delegate dstDelegate = sManager.getDelegate(dst_path); 432 433 pathDelegate.transform(matrixDelegate, dstDelegate); 434 } 435 436 @LayoutlibDelegate 437 /*package*/ static void native_transform(int nPath, int matrix) { 438 native_transform(nPath, matrix, 0); 439 } 440 441 @LayoutlibDelegate 442 /*package*/ static void finalizer(int nPath) { 443 sManager.removeDelegate(nPath); 444 } 445 446 447 // ---- Private helper methods ---- 448 449 private void set(Path_Delegate delegate) { 450 mPath.reset(); 451 setFillType(delegate.mFillType); 452 mPath.append(delegate.mPath, false /*connect*/); 453 } 454 455 private void setFillType(FillType fillType) { 456 mFillType = fillType; 457 mPath.setWindingRule(getWindingRule(fillType)); 458 } 459 460 /** 461 * Returns the Java2D winding rules matching a given Android {@link FillType}. 462 * @param type the android fill type 463 * @return the matching java2d winding rule. 464 */ 465 private static int getWindingRule(FillType type) { 466 switch (type) { 467 case WINDING: 468 case INVERSE_WINDING: 469 return GeneralPath.WIND_NON_ZERO; 470 case EVEN_ODD: 471 case INVERSE_EVEN_ODD: 472 return GeneralPath.WIND_EVEN_ODD; 473 } 474 475 assert false; 476 throw new IllegalArgumentException(); 477 } 478 479 private static Direction getDirection(int direction) { 480 for (Direction d : Direction.values()) { 481 if (direction == d.nativeInt) { 482 return d; 483 } 484 } 485 486 assert false; 487 return null; 488 } 489 490 /** 491 * Returns whether the path is empty. 492 * @return true if the path is empty. 493 */ 494 private boolean isEmpty() { 495 return mPath.getCurrentPoint() == null; 496 } 497 498 /** 499 * Fills the given {@link RectF} with the path bounds. 500 * @param bounds the RectF to be filled. 501 */ 502 private void fillBounds(RectF bounds) { 503 Rectangle2D rect = mPath.getBounds2D(); 504 bounds.left = (float)rect.getMinX(); 505 bounds.right = (float)rect.getMaxX(); 506 bounds.top = (float)rect.getMinY(); 507 bounds.bottom = (float)rect.getMaxY(); 508 } 509 510 /** 511 * Set the beginning of the next contour to the point (x,y). 512 * 513 * @param x The x-coordinate of the start of a new contour 514 * @param y The y-coordinate of the start of a new contour 515 */ 516 private void moveTo(float x, float y) { 517 mPath.moveTo(mLastX = x, mLastY = y); 518 } 519 520 /** 521 * Set the beginning of the next contour relative to the last point on the 522 * previous contour. If there is no previous contour, this is treated the 523 * same as moveTo(). 524 * 525 * @param dx The amount to add to the x-coordinate of the end of the 526 * previous contour, to specify the start of a new contour 527 * @param dy The amount to add to the y-coordinate of the end of the 528 * previous contour, to specify the start of a new contour 529 */ 530 private void rMoveTo(float dx, float dy) { 531 dx += mLastX; 532 dy += mLastY; 533 mPath.moveTo(mLastX = dx, mLastY = dy); 534 } 535 536 /** 537 * Add a line from the last point to the specified point (x,y). 538 * If no moveTo() call has been made for this contour, the first point is 539 * automatically set to (0,0). 540 * 541 * @param x The x-coordinate of the end of a line 542 * @param y The y-coordinate of the end of a line 543 */ 544 private void lineTo(float x, float y) { 545 mPath.lineTo(mLastX = x, mLastY = y); 546 } 547 548 /** 549 * Same as lineTo, but the coordinates are considered relative to the last 550 * point on this contour. If there is no previous point, then a moveTo(0,0) 551 * is inserted automatically. 552 * 553 * @param dx The amount to add to the x-coordinate of the previous point on 554 * this contour, to specify a line 555 * @param dy The amount to add to the y-coordinate of the previous point on 556 * this contour, to specify a line 557 */ 558 private void rLineTo(float dx, float dy) { 559 if (isEmpty()) { 560 mPath.moveTo(mLastX = 0, mLastY = 0); 561 } 562 dx += mLastX; 563 dy += mLastY; 564 mPath.lineTo(mLastX = dx, mLastY = dy); 565 } 566 567 /** 568 * Add a quadratic bezier from the last point, approaching control point 569 * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for 570 * this contour, the first point is automatically set to (0,0). 571 * 572 * @param x1 The x-coordinate of the control point on a quadratic curve 573 * @param y1 The y-coordinate of the control point on a quadratic curve 574 * @param x2 The x-coordinate of the end point on a quadratic curve 575 * @param y2 The y-coordinate of the end point on a quadratic curve 576 */ 577 private void quadTo(float x1, float y1, float x2, float y2) { 578 mPath.quadTo(x1, y1, mLastX = x2, mLastY = y2); 579 } 580 581 /** 582 * Same as quadTo, but the coordinates are considered relative to the last 583 * point on this contour. If there is no previous point, then a moveTo(0,0) 584 * is inserted automatically. 585 * 586 * @param dx1 The amount to add to the x-coordinate of the last point on 587 * this contour, for the control point of a quadratic curve 588 * @param dy1 The amount to add to the y-coordinate of the last point on 589 * this contour, for the control point of a quadratic curve 590 * @param dx2 The amount to add to the x-coordinate of the last point on 591 * this contour, for the end point of a quadratic curve 592 * @param dy2 The amount to add to the y-coordinate of the last point on 593 * this contour, for the end point of a quadratic curve 594 */ 595 private void rQuadTo(float dx1, float dy1, float dx2, float dy2) { 596 if (isEmpty()) { 597 mPath.moveTo(mLastX = 0, mLastY = 0); 598 } 599 dx1 += mLastX; 600 dy1 += mLastY; 601 dx2 += mLastX; 602 dy2 += mLastY; 603 mPath.quadTo(dx1, dy1, mLastX = dx2, mLastY = dy2); 604 } 605 606 /** 607 * Add a cubic bezier from the last point, approaching control points 608 * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been 609 * made for this contour, the first point is automatically set to (0,0). 610 * 611 * @param x1 The x-coordinate of the 1st control point on a cubic curve 612 * @param y1 The y-coordinate of the 1st control point on a cubic curve 613 * @param x2 The x-coordinate of the 2nd control point on a cubic curve 614 * @param y2 The y-coordinate of the 2nd control point on a cubic curve 615 * @param x3 The x-coordinate of the end point on a cubic curve 616 * @param y3 The y-coordinate of the end point on a cubic curve 617 */ 618 private void cubicTo(float x1, float y1, float x2, float y2, 619 float x3, float y3) { 620 mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3); 621 } 622 623 /** 624 * Same as cubicTo, but the coordinates are considered relative to the 625 * current point on this contour. If there is no previous point, then a 626 * moveTo(0,0) is inserted automatically. 627 */ 628 private void rCubicTo(float dx1, float dy1, float dx2, float dy2, 629 float dx3, float dy3) { 630 if (isEmpty()) { 631 mPath.moveTo(mLastX = 0, mLastY = 0); 632 } 633 dx1 += mLastX; 634 dy1 += mLastY; 635 dx2 += mLastX; 636 dy2 += mLastY; 637 dx3 += mLastX; 638 dy3 += mLastY; 639 mPath.curveTo(dx1, dy1, dx2, dy2, mLastX = dx3, mLastY = dy3); 640 } 641 642 /** 643 * Append the specified arc to the path as a new contour. If the start of 644 * the path is different from the path's current last point, then an 645 * automatic lineTo() is added to connect the current contour to the 646 * start of the arc. However, if the path is empty, then we call moveTo() 647 * with the first point of the arc. The sweep angle is tread mod 360. 648 * 649 * @param oval The bounds of oval defining shape and size of the arc 650 * @param startAngle Starting angle (in degrees) where the arc begins 651 * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated 652 * mod 360. 653 * @param forceMoveTo If true, always begin a new contour with the arc 654 */ 655 private void arcTo(RectF oval, float startAngle, float sweepAngle, 656 boolean forceMoveTo) { 657 Arc2D arc = new Arc2D.Float(oval.left, oval.top, oval.width(), oval.height(), startAngle, 658 sweepAngle, Arc2D.OPEN); 659 mPath.append(arc, true /*connect*/); 660 661 resetLastPointFromPath(); 662 } 663 664 /** 665 * Close the current contour. If the current point is not equal to the 666 * first point of the contour, a line segment is automatically added. 667 */ 668 private void close() { 669 mPath.closePath(); 670 } 671 672 private void resetLastPointFromPath() { 673 Point2D last = mPath.getCurrentPoint(); 674 mLastX = (float) last.getX(); 675 mLastY = (float) last.getY(); 676 } 677 678 /** 679 * Add a closed rectangle contour to the path 680 * 681 * @param left The left side of a rectangle to add to the path 682 * @param top The top of a rectangle to add to the path 683 * @param right The right side of a rectangle to add to the path 684 * @param bottom The bottom of a rectangle to add to the path 685 * @param dir The direction to wind the rectangle's contour 686 */ 687 private void addRect(float left, float top, float right, float bottom, 688 int dir) { 689 moveTo(left, top); 690 691 Direction direction = getDirection(dir); 692 693 switch (direction) { 694 case CW: 695 lineTo(right, top); 696 lineTo(right, bottom); 697 lineTo(left, bottom); 698 break; 699 case CCW: 700 lineTo(left, bottom); 701 lineTo(right, bottom); 702 lineTo(right, top); 703 break; 704 } 705 706 close(); 707 708 resetLastPointFromPath(); 709 } 710 711 /** 712 * Offset the path by (dx,dy), returning true on success 713 * 714 * @param dx The amount in the X direction to offset the entire path 715 * @param dy The amount in the Y direction to offset the entire path 716 * @param dst The translated path is written here. If this is null, then 717 * the original path is modified. 718 */ 719 public void offset(float dx, float dy, Path_Delegate dst) { 720 GeneralPath newPath = new GeneralPath(); 721 722 PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy)); 723 724 newPath.append(iterator, false /*connect*/); 725 726 if (dst != null) { 727 dst.mPath = newPath; 728 } else { 729 mPath = newPath; 730 } 731 } 732 733 /** 734 * Transform the points in this path by matrix, and write the answer 735 * into dst. If dst is null, then the the original path is modified. 736 * 737 * @param matrix The matrix to apply to the path 738 * @param dst The transformed path is written here. If dst is null, 739 * then the the original path is modified 740 */ 741 public void transform(Matrix_Delegate matrix, Path_Delegate dst) { 742 if (matrix.hasPerspective()) { 743 assert false; 744 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE, 745 "android.graphics.Path#transform() only " + 746 "supports affine transformations.", null, null /*data*/); 747 } 748 749 GeneralPath newPath = new GeneralPath(); 750 751 PathIterator iterator = mPath.getPathIterator(matrix.getAffineTransform()); 752 753 newPath.append(iterator, false /*connect*/); 754 755 if (dst != null) { 756 dst.mPath = newPath; 757 } else { 758 mPath = newPath; 759 } 760 } 761} 762