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 17 package android.graphics; 18 19 20 import com.android.ide.common.rendering.api.LayoutLog; 21 import com.android.layoutlib.bridge.Bridge; 22 import com.android.layoutlib.bridge.impl.DelegateManager; 23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 24 25 import android.graphics.Matrix.ScaleToFit; 26 27 import java.awt.geom.AffineTransform; 28 import java.awt.geom.NoninvertibleTransformException; 29 30 /** 31 * Delegate implementing the native methods of android.graphics.Matrix 32 * 33 * Through the layoutlib_create tool, the original native methods of Matrix have been replaced 34 * by calls to methods of the same name in this delegate class. 35 * 36 * This class behaves like the original native implementation, but in Java, keeping previously 37 * native data into its own objects and mapping them to int that are sent back and forth between 38 * it and the original Matrix class. 39 * 40 * @see DelegateManager 41 * 42 */ 43 public final class Matrix_Delegate { 44 45 private final static int MATRIX_SIZE = 9; 46 47 // ---- delegate manager ---- 48 private static final DelegateManager<Matrix_Delegate> sManager = 49 new DelegateManager<Matrix_Delegate>(Matrix_Delegate.class); 50 51 // ---- delegate data ---- 52 private float mValues[] = new float[MATRIX_SIZE]; 53 54 // ---- Public Helper methods ---- 55 56 public static Matrix_Delegate getDelegate(int native_instance) { 57 return sManager.getDelegate(native_instance); 58 } 59 60 /** 61 * Returns an {@link AffineTransform} matching the given Matrix. 62 */ 63 public static AffineTransform getAffineTransform(Matrix m) { 64 Matrix_Delegate delegate = sManager.getDelegate(m.native_instance); 65 if (delegate == null) { 66 return null; 67 } 68 69 return delegate.getAffineTransform(); 70 } 71 72 public static boolean hasPerspective(Matrix m) { 73 Matrix_Delegate delegate = sManager.getDelegate(m.native_instance); 74 if (delegate == null) { 75 return false; 76 } 77 78 return delegate.hasPerspective(); 79 } 80 81 /** 82 * Sets the content of the matrix with the content of another matrix. 83 */ 84 public void set(Matrix_Delegate matrix) { 85 System.arraycopy(matrix.mValues, 0, mValues, 0, MATRIX_SIZE); 86 } 87 88 /** 89 * Sets the content of the matrix with the content of another matrix represented as an array 90 * of values. 91 */ 92 public void set(float[] values) { 93 System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE); 94 } 95 96 /** 97 * Resets the matrix to be the identity matrix. 98 */ 99 public void reset() { 100 reset(mValues); 101 } 102 103 /** 104 * Returns whether or not the matrix is identity. 105 */ 106 public boolean isIdentity() { 107 for (int i = 0, k = 0; i < 3; i++) { 108 for (int j = 0; j < 3; j++, k++) { 109 if (mValues[k] != ((i==j) ? 1 : 0)) { 110 return false; 111 } 112 } 113 } 114 115 return true; 116 } 117 118 public static float[] makeValues(AffineTransform matrix) { 119 float[] values = new float[MATRIX_SIZE]; 120 values[0] = (float) matrix.getScaleX(); 121 values[1] = (float) matrix.getShearX(); 122 values[2] = (float) matrix.getTranslateX(); 123 values[3] = (float) matrix.getShearY(); 124 values[4] = (float) matrix.getScaleY(); 125 values[5] = (float) matrix.getTranslateY(); 126 values[6] = 0.f; 127 values[7] = 0.f; 128 values[8] = 1.f; 129 130 return values; 131 } 132 133 public static Matrix_Delegate make(AffineTransform matrix) { 134 return new Matrix_Delegate(makeValues(matrix)); 135 } 136 137 public boolean mapRect(RectF dst, RectF src) { 138 // array with 4 corners 139 float[] corners = new float[] { 140 src.left, src.top, 141 src.right, src.top, 142 src.right, src.bottom, 143 src.left, src.bottom, 144 }; 145 146 // apply the transform to them. 147 mapPoints(corners); 148 149 // now put the result in the rect. We take the min/max of Xs and min/max of Ys 150 dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6])); 151 dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6])); 152 153 dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7])); 154 dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7])); 155 156 157 return (computeTypeMask() & kRectStaysRect_Mask) != 0; 158 } 159 160 161 /** 162 * Returns an {@link AffineTransform} matching the matrix. 163 */ 164 public AffineTransform getAffineTransform() { 165 return getAffineTransform(mValues); 166 } 167 168 public boolean hasPerspective() { 169 return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1); 170 } 171 172 173 174 // ---- native methods ---- 175 176 @LayoutlibDelegate 177 /*package*/ static int native_create(int native_src_or_zero) { 178 // create the delegate 179 Matrix_Delegate newDelegate = new Matrix_Delegate(); 180 181 // copy from values if needed. 182 if (native_src_or_zero > 0) { 183 Matrix_Delegate oldDelegate = sManager.getDelegate(native_src_or_zero); 184 if (oldDelegate != null) { 185 System.arraycopy( 186 oldDelegate.mValues, 0, 187 newDelegate.mValues, 0, 188 MATRIX_SIZE); 189 } 190 } 191 192 return sManager.addNewDelegate(newDelegate); 193 } 194 195 @LayoutlibDelegate 196 /*package*/ static boolean native_isIdentity(int native_object) { 197 Matrix_Delegate d = sManager.getDelegate(native_object); 198 if (d == null) { 199 return false; 200 } 201 202 return d.isIdentity(); 203 } 204 205 @LayoutlibDelegate 206 /*package*/ static boolean native_rectStaysRect(int native_object) { 207 Matrix_Delegate d = sManager.getDelegate(native_object); 208 if (d == null) { 209 return true; 210 } 211 212 return (d.computeTypeMask() & kRectStaysRect_Mask) != 0; 213 } 214 215 @LayoutlibDelegate 216 /*package*/ static void native_reset(int native_object) { 217 Matrix_Delegate d = sManager.getDelegate(native_object); 218 if (d == null) { 219 return; 220 } 221 222 reset(d.mValues); 223 } 224 225 @LayoutlibDelegate 226 /*package*/ static void native_set(int native_object, int other) { 227 Matrix_Delegate d = sManager.getDelegate(native_object); 228 if (d == null) { 229 return; 230 } 231 232 Matrix_Delegate src = sManager.getDelegate(other); 233 if (src == null) { 234 return; 235 } 236 237 System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE); 238 } 239 240 @LayoutlibDelegate 241 /*package*/ static void native_setTranslate(int native_object, float dx, float dy) { 242 Matrix_Delegate d = sManager.getDelegate(native_object); 243 if (d == null) { 244 return; 245 } 246 247 setTranslate(d.mValues, dx, dy); 248 } 249 250 @LayoutlibDelegate 251 /*package*/ static void native_setScale(int native_object, float sx, float sy, 252 float px, float py) { 253 Matrix_Delegate d = sManager.getDelegate(native_object); 254 if (d == null) { 255 return; 256 } 257 258 d.mValues = getScale(sx, sy, px, py); 259 } 260 261 @LayoutlibDelegate 262 /*package*/ static void native_setScale(int native_object, float sx, float sy) { 263 Matrix_Delegate d = sManager.getDelegate(native_object); 264 if (d == null) { 265 return; 266 } 267 268 d.mValues[0] = sx; 269 d.mValues[1] = 0; 270 d.mValues[2] = 0; 271 d.mValues[3] = 0; 272 d.mValues[4] = sy; 273 d.mValues[5] = 0; 274 d.mValues[6] = 0; 275 d.mValues[7] = 0; 276 d.mValues[8] = 1; 277 } 278 279 @LayoutlibDelegate 280 /*package*/ static void native_setRotate(int native_object, float degrees, float px, float py) { 281 Matrix_Delegate d = sManager.getDelegate(native_object); 282 if (d == null) { 283 return; 284 } 285 286 d.mValues = getRotate(degrees, px, py); 287 } 288 289 @LayoutlibDelegate 290 /*package*/ static void native_setRotate(int native_object, float degrees) { 291 Matrix_Delegate d = sManager.getDelegate(native_object); 292 if (d == null) { 293 return; 294 } 295 296 setRotate(d.mValues, degrees); 297 } 298 299 @LayoutlibDelegate 300 /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue, 301 float px, float py) { 302 Matrix_Delegate d = sManager.getDelegate(native_object); 303 if (d == null) { 304 return; 305 } 306 307 // TODO: do it in one pass 308 309 // translate so that the pivot is in 0,0 310 setTranslate(d.mValues, -px, -py); 311 312 // scale 313 d.postTransform(getRotate(sinValue, cosValue)); 314 // translate back the pivot 315 d.postTransform(getTranslate(px, py)); 316 } 317 318 @LayoutlibDelegate 319 /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue) { 320 Matrix_Delegate d = sManager.getDelegate(native_object); 321 if (d == null) { 322 return; 323 } 324 325 setRotate(d.mValues, sinValue, cosValue); 326 } 327 328 @LayoutlibDelegate 329 /*package*/ static void native_setSkew(int native_object, float kx, float ky, 330 float px, float py) { 331 Matrix_Delegate d = sManager.getDelegate(native_object); 332 if (d == null) { 333 return; 334 } 335 336 d.mValues = getSkew(kx, ky, px, py); 337 } 338 339 @LayoutlibDelegate 340 /*package*/ static void native_setSkew(int native_object, float kx, float ky) { 341 Matrix_Delegate d = sManager.getDelegate(native_object); 342 if (d == null) { 343 return; 344 } 345 346 d.mValues[0] = 1; 347 d.mValues[1] = kx; 348 d.mValues[2] = -0; 349 d.mValues[3] = ky; 350 d.mValues[4] = 1; 351 d.mValues[5] = 0; 352 d.mValues[6] = 0; 353 d.mValues[7] = 0; 354 d.mValues[8] = 1; 355 } 356 357 @LayoutlibDelegate 358 /*package*/ static boolean native_setConcat(int native_object, int a, int b) { 359 if (a == native_object) { 360 return native_preConcat(native_object, b); 361 } else if (b == native_object) { 362 return native_postConcat(native_object, a); 363 } 364 365 Matrix_Delegate d = sManager.getDelegate(native_object); 366 if (d == null) { 367 return false; 368 } 369 370 Matrix_Delegate a_mtx = sManager.getDelegate(a); 371 if (a_mtx == null) { 372 return false; 373 } 374 375 Matrix_Delegate b_mtx = sManager.getDelegate(b); 376 if (b_mtx == null) { 377 return false; 378 } 379 380 multiply(d.mValues, a_mtx.mValues, b_mtx.mValues); 381 382 return true; 383 } 384 385 @LayoutlibDelegate 386 /*package*/ static boolean native_preTranslate(int native_object, float dx, float dy) { 387 Matrix_Delegate d = sManager.getDelegate(native_object); 388 if (d == null) { 389 return false; 390 } 391 392 d.preTransform(getTranslate(dx, dy)); 393 return true; 394 } 395 396 @LayoutlibDelegate 397 /*package*/ static boolean native_preScale(int native_object, float sx, float sy, 398 float px, float py) { 399 Matrix_Delegate d = sManager.getDelegate(native_object); 400 if (d == null) { 401 return false; 402 } 403 404 d.preTransform(getScale(sx, sy, px, py)); 405 return true; 406 } 407 408 @LayoutlibDelegate 409 /*package*/ static boolean native_preScale(int native_object, float sx, float sy) { 410 Matrix_Delegate d = sManager.getDelegate(native_object); 411 if (d == null) { 412 return false; 413 } 414 415 d.preTransform(getScale(sx, sy)); 416 return true; 417 } 418 419 @LayoutlibDelegate 420 /*package*/ static boolean native_preRotate(int native_object, float degrees, 421 float px, float py) { 422 Matrix_Delegate d = sManager.getDelegate(native_object); 423 if (d == null) { 424 return false; 425 } 426 427 d.preTransform(getRotate(degrees, px, py)); 428 return true; 429 } 430 431 @LayoutlibDelegate 432 /*package*/ static boolean native_preRotate(int native_object, float degrees) { 433 Matrix_Delegate d = sManager.getDelegate(native_object); 434 if (d == null) { 435 return false; 436 } 437 438 double rad = Math.toRadians(degrees); 439 float sin = (float)Math.sin(rad); 440 float cos = (float)Math.cos(rad); 441 442 d.preTransform(getRotate(sin, cos)); 443 return true; 444 } 445 446 @LayoutlibDelegate 447 /*package*/ static boolean native_preSkew(int native_object, float kx, float ky, 448 float px, float py) { 449 Matrix_Delegate d = sManager.getDelegate(native_object); 450 if (d == null) { 451 return false; 452 } 453 454 d.preTransform(getSkew(kx, ky, px, py)); 455 return true; 456 } 457 458 @LayoutlibDelegate 459 /*package*/ static boolean native_preSkew(int native_object, float kx, float ky) { 460 Matrix_Delegate d = sManager.getDelegate(native_object); 461 if (d == null) { 462 return false; 463 } 464 465 d.preTransform(getSkew(kx, ky)); 466 return true; 467 } 468 469 @LayoutlibDelegate 470 /*package*/ static boolean native_preConcat(int native_object, int other_matrix) { 471 Matrix_Delegate d = sManager.getDelegate(native_object); 472 if (d == null) { 473 return false; 474 } 475 476 Matrix_Delegate other = sManager.getDelegate(other_matrix); 477 if (other == null) { 478 return false; 479 } 480 481 d.preTransform(other.mValues); 482 return true; 483 } 484 485 @LayoutlibDelegate 486 /*package*/ static boolean native_postTranslate(int native_object, float dx, float dy) { 487 Matrix_Delegate d = sManager.getDelegate(native_object); 488 if (d == null) { 489 return false; 490 } 491 492 d.postTransform(getTranslate(dx, dy)); 493 return true; 494 } 495 496 @LayoutlibDelegate 497 /*package*/ static boolean native_postScale(int native_object, float sx, float sy, 498 float px, float py) { 499 Matrix_Delegate d = sManager.getDelegate(native_object); 500 if (d == null) { 501 return false; 502 } 503 504 d.postTransform(getScale(sx, sy, px, py)); 505 return true; 506 } 507 508 @LayoutlibDelegate 509 /*package*/ static boolean native_postScale(int native_object, float sx, float sy) { 510 Matrix_Delegate d = sManager.getDelegate(native_object); 511 if (d == null) { 512 return false; 513 } 514 515 d.postTransform(getScale(sx, sy)); 516 return true; 517 } 518 519 @LayoutlibDelegate 520 /*package*/ static boolean native_postRotate(int native_object, float degrees, 521 float px, float py) { 522 Matrix_Delegate d = sManager.getDelegate(native_object); 523 if (d == null) { 524 return false; 525 } 526 527 d.postTransform(getRotate(degrees, px, py)); 528 return true; 529 } 530 531 @LayoutlibDelegate 532 /*package*/ static boolean native_postRotate(int native_object, float degrees) { 533 Matrix_Delegate d = sManager.getDelegate(native_object); 534 if (d == null) { 535 return false; 536 } 537 538 d.postTransform(getRotate(degrees)); 539 return true; 540 } 541 542 @LayoutlibDelegate 543 /*package*/ static boolean native_postSkew(int native_object, float kx, float ky, 544 float px, float py) { 545 Matrix_Delegate d = sManager.getDelegate(native_object); 546 if (d == null) { 547 return false; 548 } 549 550 d.postTransform(getSkew(kx, ky, px, py)); 551 return true; 552 } 553 554 @LayoutlibDelegate 555 /*package*/ static boolean native_postSkew(int native_object, float kx, float ky) { 556 Matrix_Delegate d = sManager.getDelegate(native_object); 557 if (d == null) { 558 return false; 559 } 560 561 d.postTransform(getSkew(kx, ky)); 562 return true; 563 } 564 565 @LayoutlibDelegate 566 /*package*/ static boolean native_postConcat(int native_object, int other_matrix) { 567 Matrix_Delegate d = sManager.getDelegate(native_object); 568 if (d == null) { 569 return false; 570 } 571 572 Matrix_Delegate other = sManager.getDelegate(other_matrix); 573 if (other == null) { 574 return false; 575 } 576 577 d.postTransform(other.mValues); 578 return true; 579 } 580 581 @LayoutlibDelegate 582 /*package*/ static boolean native_setRectToRect(int native_object, RectF src, 583 RectF dst, int stf) { 584 Matrix_Delegate d = sManager.getDelegate(native_object); 585 if (d == null) { 586 return false; 587 } 588 589 if (src.isEmpty()) { 590 reset(d.mValues); 591 return false; 592 } 593 594 if (dst.isEmpty()) { 595 d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5] 596 = d.mValues[6] = d.mValues[7] = 0; 597 d.mValues[8] = 1; 598 } else { 599 float tx, sx = dst.width() / src.width(); 600 float ty, sy = dst.height() / src.height(); 601 boolean xLarger = false; 602 603 if (stf != ScaleToFit.FILL.nativeInt) { 604 if (sx > sy) { 605 xLarger = true; 606 sx = sy; 607 } else { 608 sy = sx; 609 } 610 } 611 612 tx = dst.left - src.left * sx; 613 ty = dst.top - src.top * sy; 614 if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) { 615 float diff; 616 617 if (xLarger) { 618 diff = dst.width() - src.width() * sy; 619 } else { 620 diff = dst.height() - src.height() * sy; 621 } 622 623 if (stf == ScaleToFit.CENTER.nativeInt) { 624 diff = diff / 2; 625 } 626 627 if (xLarger) { 628 tx += diff; 629 } else { 630 ty += diff; 631 } 632 } 633 634 d.mValues[0] = sx; 635 d.mValues[4] = sy; 636 d.mValues[2] = tx; 637 d.mValues[5] = ty; 638 d.mValues[1] = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0; 639 640 } 641 // shared cleanup 642 d.mValues[8] = 1; 643 return true; 644 } 645 646 @LayoutlibDelegate 647 /*package*/ static boolean native_setPolyToPoly(int native_object, float[] src, int srcIndex, 648 float[] dst, int dstIndex, int pointCount) { 649 // FIXME 650 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 651 "Matrix.setPolyToPoly is not supported.", 652 null, null /*data*/); 653 return false; 654 } 655 656 @LayoutlibDelegate 657 /*package*/ static boolean native_invert(int native_object, int inverse) { 658 Matrix_Delegate d = sManager.getDelegate(native_object); 659 if (d == null) { 660 return false; 661 } 662 663 Matrix_Delegate inv_mtx = sManager.getDelegate(inverse); 664 if (inv_mtx == null) { 665 return false; 666 } 667 668 try { 669 AffineTransform affineTransform = d.getAffineTransform(); 670 AffineTransform inverseTransform = affineTransform.createInverse(); 671 inv_mtx.mValues[0] = (float)inverseTransform.getScaleX(); 672 inv_mtx.mValues[1] = (float)inverseTransform.getShearX(); 673 inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX(); 674 inv_mtx.mValues[3] = (float)inverseTransform.getScaleX(); 675 inv_mtx.mValues[4] = (float)inverseTransform.getShearY(); 676 inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY(); 677 678 return true; 679 } catch (NoninvertibleTransformException e) { 680 return false; 681 } 682 } 683 684 @LayoutlibDelegate 685 /*package*/ static void native_mapPoints(int native_object, float[] dst, int dstIndex, 686 float[] src, int srcIndex, int ptCount, boolean isPts) { 687 Matrix_Delegate d = sManager.getDelegate(native_object); 688 if (d == null) { 689 return; 690 } 691 692 if (isPts) { 693 d.mapPoints(dst, dstIndex, src, srcIndex, ptCount); 694 } else { 695 d.mapVectors(dst, dstIndex, src, srcIndex, ptCount); 696 } 697 } 698 699 @LayoutlibDelegate 700 /*package*/ static boolean native_mapRect(int native_object, RectF dst, RectF src) { 701 Matrix_Delegate d = sManager.getDelegate(native_object); 702 if (d == null) { 703 return false; 704 } 705 706 return d.mapRect(dst, src); 707 } 708 709 @LayoutlibDelegate 710 /*package*/ static float native_mapRadius(int native_object, float radius) { 711 Matrix_Delegate d = sManager.getDelegate(native_object); 712 if (d == null) { 713 return 0.f; 714 } 715 716 float[] src = new float[] { radius, 0.f, 0.f, radius }; 717 d.mapVectors(src, 0, src, 0, 2); 718 719 float l1 = getPointLength(src, 0); 720 float l2 = getPointLength(src, 2); 721 722 return (float) Math.sqrt(l1 * l2); 723 } 724 725 @LayoutlibDelegate 726 /*package*/ static void native_getValues(int native_object, float[] values) { 727 Matrix_Delegate d = sManager.getDelegate(native_object); 728 if (d == null) { 729 return; 730 } 731 732 System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE); 733 } 734 735 @LayoutlibDelegate 736 /*package*/ static void native_setValues(int native_object, float[] values) { 737 Matrix_Delegate d = sManager.getDelegate(native_object); 738 if (d == null) { 739 return; 740 } 741 742 System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE); 743 } 744 745 @LayoutlibDelegate 746 /*package*/ static boolean native_equals(int native_a, int native_b) { 747 Matrix_Delegate a = sManager.getDelegate(native_a); 748 if (a == null) { 749 return false; 750 } 751 752 Matrix_Delegate b = sManager.getDelegate(native_b); 753 if (b == null) { 754 return false; 755 } 756 757 for (int i = 0 ; i < MATRIX_SIZE ; i++) { 758 if (a.mValues[i] != b.mValues[i]) { 759 return false; 760 } 761 } 762 763 return true; 764 } 765 766 @LayoutlibDelegate 767 /*package*/ static void finalizer(int native_instance) { 768 sManager.removeJavaReferenceFor(native_instance); 769 } 770 771 // ---- Private helper methods ---- 772 773 /*package*/ static AffineTransform getAffineTransform(float[] matrix) { 774 // the AffineTransform constructor takes the value in a different order 775 // for a matrix [ 0 1 2 ] 776 // [ 3 4 5 ] 777 // the order is 0, 3, 1, 4, 2, 5... 778 return new AffineTransform( 779 matrix[0], matrix[3], matrix[1], 780 matrix[4], matrix[2], matrix[5]); 781 } 782 783 /** 784 * Reset a matrix to the identity 785 */ 786 private static void reset(float[] mtx) { 787 for (int i = 0, k = 0; i < 3; i++) { 788 for (int j = 0; j < 3; j++, k++) { 789 mtx[k] = ((i==j) ? 1 : 0); 790 } 791 } 792 } 793 794 @SuppressWarnings("unused") 795 private final static int kIdentity_Mask = 0; 796 private final static int kTranslate_Mask = 0x01; //!< set if the matrix has translation 797 private final static int kScale_Mask = 0x02; //!< set if the matrix has X or Y scale 798 private final static int kAffine_Mask = 0x04; //!< set if the matrix skews or rotates 799 private final static int kPerspective_Mask = 0x08; //!< set if the matrix is in perspective 800 private final static int kRectStaysRect_Mask = 0x10; 801 @SuppressWarnings("unused") 802 private final static int kUnknown_Mask = 0x80; 803 804 @SuppressWarnings("unused") 805 private final static int kAllMasks = kTranslate_Mask | 806 kScale_Mask | 807 kAffine_Mask | 808 kPerspective_Mask | 809 kRectStaysRect_Mask; 810 811 // these guys align with the masks, so we can compute a mask from a variable 0/1 812 @SuppressWarnings("unused") 813 private final static int kTranslate_Shift = 0; 814 @SuppressWarnings("unused") 815 private final static int kScale_Shift = 1; 816 @SuppressWarnings("unused") 817 private final static int kAffine_Shift = 2; 818 @SuppressWarnings("unused") 819 private final static int kPerspective_Shift = 3; 820 private final static int kRectStaysRect_Shift = 4; 821 822 private int computeTypeMask() { 823 int mask = 0; 824 825 if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) { 826 mask |= kPerspective_Mask; 827 } 828 829 if (mValues[2] != 0. || mValues[5] != 0.) { 830 mask |= kTranslate_Mask; 831 } 832 833 float m00 = mValues[0]; 834 float m01 = mValues[1]; 835 float m10 = mValues[3]; 836 float m11 = mValues[4]; 837 838 if (m01 != 0. || m10 != 0.) { 839 mask |= kAffine_Mask; 840 } 841 842 if (m00 != 1. || m11 != 1.) { 843 mask |= kScale_Mask; 844 } 845 846 if ((mask & kPerspective_Mask) == 0) { 847 // map non-zero to 1 848 int im00 = m00 != 0 ? 1 : 0; 849 int im01 = m01 != 0 ? 1 : 0; 850 int im10 = m10 != 0 ? 1 : 0; 851 int im11 = m11 != 0 ? 1 : 0; 852 853 // record if the (p)rimary and (s)econdary diagonals are all 0 or 854 // all non-zero (answer is 0 or 1) 855 int dp0 = (im00 | im11) ^ 1; // true if both are 0 856 int dp1 = im00 & im11; // true if both are 1 857 int ds0 = (im01 | im10) ^ 1; // true if both are 0 858 int ds1 = im01 & im10; // true if both are 1 859 860 // return 1 if primary is 1 and secondary is 0 or 861 // primary is 0 and secondary is 1 862 mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift; 863 } 864 865 return mask; 866 } 867 868 private Matrix_Delegate() { 869 reset(); 870 } 871 872 private Matrix_Delegate(float[] values) { 873 System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE); 874 } 875 876 /** 877 * Adds the given transformation to the current Matrix 878 * <p/>This in effect does this = this*matrix 879 * @param matrix 880 */ 881 private void postTransform(float[] matrix) { 882 float[] tmp = new float[9]; 883 multiply(tmp, mValues, matrix); 884 mValues = tmp; 885 } 886 887 /** 888 * Adds the given transformation to the current Matrix 889 * <p/>This in effect does this = matrix*this 890 * @param matrix 891 */ 892 private void preTransform(float[] matrix) { 893 float[] tmp = new float[9]; 894 multiply(tmp, matrix, mValues); 895 mValues = tmp; 896 } 897 898 /** 899 * Apply this matrix to the array of 2D points specified by src, and write 900 * the transformed points into the array of points specified by dst. The 901 * two arrays represent their "points" as pairs of floats [x, y]. 902 * 903 * @param dst The array of dst points (x,y pairs) 904 * @param dstIndex The index of the first [x,y] pair of dst floats 905 * @param src The array of src points (x,y pairs) 906 * @param srcIndex The index of the first [x,y] pair of src floats 907 * @param pointCount The number of points (x,y pairs) to transform 908 */ 909 910 private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, 911 int pointCount) { 912 final int count = pointCount * 2; 913 914 float[] tmpDest = dst; 915 boolean inPlace = dst == src; 916 if (inPlace) { 917 tmpDest = new float[dstIndex + count]; 918 } 919 920 for (int i = 0 ; i < count ; i += 2) { 921 // just in case we are doing in place, we better put this in temp vars 922 float x = mValues[0] * src[i + srcIndex] + 923 mValues[1] * src[i + srcIndex + 1] + 924 mValues[2]; 925 float y = mValues[3] * src[i + srcIndex] + 926 mValues[4] * src[i + srcIndex + 1] + 927 mValues[5]; 928 929 tmpDest[i + dstIndex] = x; 930 tmpDest[i + dstIndex + 1] = y; 931 } 932 933 if (inPlace) { 934 System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count); 935 } 936 } 937 938 /** 939 * Apply this matrix to the array of 2D points, and write the transformed 940 * points back into the array 941 * 942 * @param pts The array [x0, y0, x1, y1, ...] of points to transform. 943 */ 944 945 private void mapPoints(float[] pts) { 946 mapPoints(pts, 0, pts, 0, pts.length >> 1); 947 } 948 949 private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) { 950 if (hasPerspective()) { 951 // transform the (0,0) point 952 float[] origin = new float[] { 0.f, 0.f}; 953 mapPoints(origin); 954 955 // translate the vector data as points 956 mapPoints(dst, dstIndex, src, srcIndex, ptCount); 957 958 // then substract the transformed origin. 959 final int count = ptCount * 2; 960 for (int i = 0 ; i < count ; i += 2) { 961 dst[dstIndex + i] = dst[dstIndex + i] - origin[0]; 962 dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1]; 963 } 964 } else { 965 // make a copy of the matrix 966 Matrix_Delegate copy = new Matrix_Delegate(mValues); 967 968 // remove the translation 969 setTranslate(copy.mValues, 0, 0); 970 971 // map the content as points. 972 copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount); 973 } 974 } 975 976 private static float getPointLength(float[] src, int index) { 977 return (float) Math.sqrt(src[index] * src[index] + src[index + 1] * src[index + 1]); 978 } 979 980 /** 981 * multiply two matrices and store them in a 3rd. 982 * <p/>This in effect does dest = a*b 983 * dest cannot be the same as a or b. 984 */ 985 /*package*/ static void multiply(float dest[], float[] a, float[] b) { 986 // first row 987 dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6]; 988 dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7]; 989 dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8]; 990 991 // 2nd row 992 dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6]; 993 dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7]; 994 dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8]; 995 996 // 3rd row 997 dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6]; 998 dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7]; 999 dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8]; 1000 } 1001 1002 /** 1003 * Returns a matrix that represents a given translate 1004 * @param dx 1005 * @param dy 1006 * @return 1007 */ 1008 /*package*/ static float[] getTranslate(float dx, float dy) { 1009 return setTranslate(new float[9], dx, dy); 1010 } 1011 1012 /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) { 1013 dest[0] = 1; 1014 dest[1] = 0; 1015 dest[2] = dx; 1016 dest[3] = 0; 1017 dest[4] = 1; 1018 dest[5] = dy; 1019 dest[6] = 0; 1020 dest[7] = 0; 1021 dest[8] = 1; 1022 return dest; 1023 } 1024 1025 /*package*/ static float[] getScale(float sx, float sy) { 1026 return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 }; 1027 } 1028 1029 /** 1030 * Returns a matrix that represents the given scale info. 1031 * @param sx 1032 * @param sy 1033 * @param px 1034 * @param py 1035 */ 1036 /*package*/ static float[] getScale(float sx, float sy, float px, float py) { 1037 float[] tmp = new float[9]; 1038 float[] tmp2 = new float[9]; 1039 1040 // TODO: do it in one pass 1041 1042 // translate tmp so that the pivot is in 0,0 1043 setTranslate(tmp, -px, -py); 1044 1045 // scale into tmp2 1046 multiply(tmp2, tmp, getScale(sx, sy)); 1047 1048 // translate back the pivot back into tmp 1049 multiply(tmp, tmp2, getTranslate(px, py)); 1050 1051 return tmp; 1052 } 1053 1054 1055 /*package*/ static float[] getRotate(float degrees) { 1056 double rad = Math.toRadians(degrees); 1057 float sin = (float)Math.sin(rad); 1058 float cos = (float)Math.cos(rad); 1059 1060 return getRotate(sin, cos); 1061 } 1062 1063 /*package*/ static float[] getRotate(float sin, float cos) { 1064 return setRotate(new float[9], sin, cos); 1065 } 1066 1067 /*package*/ static float[] setRotate(float[] dest, float degrees) { 1068 double rad = Math.toRadians(degrees); 1069 float sin = (float)Math.sin(rad); 1070 float cos = (float)Math.cos(rad); 1071 1072 return setRotate(dest, sin, cos); 1073 } 1074 1075 /*package*/ static float[] setRotate(float[] dest, float sin, float cos) { 1076 dest[0] = cos; 1077 dest[1] = -sin; 1078 dest[2] = 0; 1079 dest[3] = sin; 1080 dest[4] = cos; 1081 dest[5] = 0; 1082 dest[6] = 0; 1083 dest[7] = 0; 1084 dest[8] = 1; 1085 return dest; 1086 } 1087 1088 /*package*/ static float[] getRotate(float degrees, float px, float py) { 1089 float[] tmp = new float[9]; 1090 float[] tmp2 = new float[9]; 1091 1092 // TODO: do it in one pass 1093 1094 // translate so that the pivot is in 0,0 1095 setTranslate(tmp, -px, -py); 1096 1097 // rotate into tmp2 1098 double rad = Math.toRadians(degrees); 1099 float cos = (float)Math.cos(rad); 1100 float sin = (float)Math.sin(rad); 1101 multiply(tmp2, tmp, getRotate(sin, cos)); 1102 1103 // translate back the pivot back into tmp 1104 multiply(tmp, tmp2, getTranslate(px, py)); 1105 1106 return tmp; 1107 } 1108 1109 /*package*/ static float[] getSkew(float kx, float ky) { 1110 return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }; 1111 } 1112 1113 /*package*/ static float[] getSkew(float kx, float ky, float px, float py) { 1114 float[] tmp = new float[9]; 1115 float[] tmp2 = new float[9]; 1116 1117 // TODO: do it in one pass 1118 1119 // translate so that the pivot is in 0,0 1120 setTranslate(tmp, -px, -py); 1121 1122 // skew into tmp2 1123 multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }); 1124 // translate back the pivot back into tmp 1125 multiply(tmp, tmp2, getTranslate(px, py)); 1126 1127 return tmp; 1128 } 1129 } 1130