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