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(long 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 long native_create(long 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(long 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_isAffine(long native_object) { 207 Matrix_Delegate d = sManager.getDelegate(native_object); 208 if (d == null) { 209 return true; 210 } 211 212 return (d.computeTypeMask() & kPerspective_Mask) == 0; 213 } 214 215 @LayoutlibDelegate 216 /*package*/ static boolean native_rectStaysRect(long native_object) { 217 Matrix_Delegate d = sManager.getDelegate(native_object); 218 if (d == null) { 219 return true; 220 } 221 222 return (d.computeTypeMask() & kRectStaysRect_Mask) != 0; 223 } 224 225 @LayoutlibDelegate 226 /*package*/ static void native_reset(long native_object) { 227 Matrix_Delegate d = sManager.getDelegate(native_object); 228 if (d == null) { 229 return; 230 } 231 232 reset(d.mValues); 233 } 234 235 @LayoutlibDelegate 236 /*package*/ static void native_set(long native_object, long other) { 237 Matrix_Delegate d = sManager.getDelegate(native_object); 238 if (d == null) { 239 return; 240 } 241 242 Matrix_Delegate src = sManager.getDelegate(other); 243 if (src == null) { 244 return; 245 } 246 247 System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE); 248 } 249 250 @LayoutlibDelegate 251 /*package*/ static void native_setTranslate(long native_object, float dx, float dy) { 252 Matrix_Delegate d = sManager.getDelegate(native_object); 253 if (d == null) { 254 return; 255 } 256 257 setTranslate(d.mValues, dx, dy); 258 } 259 260 @LayoutlibDelegate 261 /*package*/ static void native_setScale(long native_object, float sx, float sy, 262 float px, float py) { 263 Matrix_Delegate d = sManager.getDelegate(native_object); 264 if (d == null) { 265 return; 266 } 267 268 d.mValues = getScale(sx, sy, px, py); 269 } 270 271 @LayoutlibDelegate 272 /*package*/ static void native_setScale(long native_object, float sx, float sy) { 273 Matrix_Delegate d = sManager.getDelegate(native_object); 274 if (d == null) { 275 return; 276 } 277 278 d.mValues[0] = sx; 279 d.mValues[1] = 0; 280 d.mValues[2] = 0; 281 d.mValues[3] = 0; 282 d.mValues[4] = sy; 283 d.mValues[5] = 0; 284 d.mValues[6] = 0; 285 d.mValues[7] = 0; 286 d.mValues[8] = 1; 287 } 288 289 @LayoutlibDelegate 290 /*package*/ static void native_setRotate(long native_object, float degrees, float px, float py) { 291 Matrix_Delegate d = sManager.getDelegate(native_object); 292 if (d == null) { 293 return; 294 } 295 296 d.mValues = getRotate(degrees, px, py); 297 } 298 299 @LayoutlibDelegate 300 /*package*/ static void native_setRotate(long native_object, float degrees) { 301 Matrix_Delegate d = sManager.getDelegate(native_object); 302 if (d == null) { 303 return; 304 } 305 306 setRotate(d.mValues, degrees); 307 } 308 309 @LayoutlibDelegate 310 /*package*/ static void native_setSinCos(long native_object, float sinValue, float cosValue, 311 float px, float py) { 312 Matrix_Delegate d = sManager.getDelegate(native_object); 313 if (d == null) { 314 return; 315 } 316 317 // TODO: do it in one pass 318 319 // translate so that the pivot is in 0,0 320 setTranslate(d.mValues, -px, -py); 321 322 // scale 323 d.postTransform(getRotate(sinValue, cosValue)); 324 // translate back the pivot 325 d.postTransform(getTranslate(px, py)); 326 } 327 328 @LayoutlibDelegate 329 /*package*/ static void native_setSinCos(long native_object, float sinValue, float cosValue) { 330 Matrix_Delegate d = sManager.getDelegate(native_object); 331 if (d == null) { 332 return; 333 } 334 335 setRotate(d.mValues, sinValue, cosValue); 336 } 337 338 @LayoutlibDelegate 339 /*package*/ static void native_setSkew(long native_object, float kx, float ky, 340 float px, float py) { 341 Matrix_Delegate d = sManager.getDelegate(native_object); 342 if (d == null) { 343 return; 344 } 345 346 d.mValues = getSkew(kx, ky, px, py); 347 } 348 349 @LayoutlibDelegate 350 /*package*/ static void native_setSkew(long native_object, float kx, float ky) { 351 Matrix_Delegate d = sManager.getDelegate(native_object); 352 if (d == null) { 353 return; 354 } 355 356 d.mValues[0] = 1; 357 d.mValues[1] = kx; 358 d.mValues[2] = -0; 359 d.mValues[3] = ky; 360 d.mValues[4] = 1; 361 d.mValues[5] = 0; 362 d.mValues[6] = 0; 363 d.mValues[7] = 0; 364 d.mValues[8] = 1; 365 } 366 367 @LayoutlibDelegate 368 /*package*/ static void native_setConcat(long native_object, long a, long b) { 369 if (a == native_object) { 370 native_preConcat(native_object, b); 371 return; 372 } else if (b == native_object) { 373 native_postConcat(native_object, a); 374 return; 375 } 376 377 Matrix_Delegate d = sManager.getDelegate(native_object); 378 Matrix_Delegate a_mtx = sManager.getDelegate(a); 379 Matrix_Delegate b_mtx = sManager.getDelegate(b); 380 if (d != null && a_mtx != null && b_mtx != null) { 381 multiply(d.mValues, a_mtx.mValues, b_mtx.mValues); 382 } 383 } 384 385 @LayoutlibDelegate 386 /*package*/ static void native_preTranslate(long native_object, float dx, float dy) { 387 Matrix_Delegate d = sManager.getDelegate(native_object); 388 if (d != null) { 389 d.preTransform(getTranslate(dx, dy)); 390 } 391 } 392 393 @LayoutlibDelegate 394 /*package*/ static void native_preScale(long native_object, float sx, float sy, 395 float px, float py) { 396 Matrix_Delegate d = sManager.getDelegate(native_object); 397 if (d != null) { 398 d.preTransform(getScale(sx, sy, px, py)); 399 } 400 } 401 402 @LayoutlibDelegate 403 /*package*/ static void native_preScale(long native_object, float sx, float sy) { 404 Matrix_Delegate d = sManager.getDelegate(native_object); 405 if (d != null) { 406 d.preTransform(getScale(sx, sy)); 407 } 408 } 409 410 @LayoutlibDelegate 411 /*package*/ static void native_preRotate(long native_object, float degrees, 412 float px, float py) { 413 Matrix_Delegate d = sManager.getDelegate(native_object); 414 if (d != null) { 415 d.preTransform(getRotate(degrees, px, py)); 416 } 417 } 418 419 @LayoutlibDelegate 420 /*package*/ static void native_preRotate(long native_object, float degrees) { 421 Matrix_Delegate d = sManager.getDelegate(native_object); 422 if (d != null) { 423 424 double rad = Math.toRadians(degrees); 425 float sin = (float) Math.sin(rad); 426 float cos = (float) Math.cos(rad); 427 428 d.preTransform(getRotate(sin, cos)); 429 } 430 } 431 432 @LayoutlibDelegate 433 /*package*/ static void native_preSkew(long native_object, float kx, float ky, 434 float px, float py) { 435 Matrix_Delegate d = sManager.getDelegate(native_object); 436 if (d != null) { 437 d.preTransform(getSkew(kx, ky, px, py)); 438 } 439 } 440 441 @LayoutlibDelegate 442 /*package*/ static void native_preSkew(long native_object, float kx, float ky) { 443 Matrix_Delegate d = sManager.getDelegate(native_object); 444 if (d != null) { 445 d.preTransform(getSkew(kx, ky)); 446 } 447 } 448 449 @LayoutlibDelegate 450 /*package*/ static void native_preConcat(long native_object, long other_matrix) { 451 Matrix_Delegate d = sManager.getDelegate(native_object); 452 Matrix_Delegate other = sManager.getDelegate(other_matrix); 453 if (d != null && other != null) { 454 d.preTransform(other.mValues); 455 } 456 } 457 458 @LayoutlibDelegate 459 /*package*/ static void native_postTranslate(long native_object, float dx, float dy) { 460 Matrix_Delegate d = sManager.getDelegate(native_object); 461 if (d != null) { 462 d.postTransform(getTranslate(dx, dy)); 463 } 464 } 465 466 @LayoutlibDelegate 467 /*package*/ static void native_postScale(long native_object, float sx, float sy, 468 float px, float py) { 469 Matrix_Delegate d = sManager.getDelegate(native_object); 470 if (d != null) { 471 d.postTransform(getScale(sx, sy, px, py)); 472 } 473 } 474 475 @LayoutlibDelegate 476 /*package*/ static void native_postScale(long native_object, float sx, float sy) { 477 Matrix_Delegate d = sManager.getDelegate(native_object); 478 if (d != null) { 479 d.postTransform(getScale(sx, sy)); 480 } 481 } 482 483 @LayoutlibDelegate 484 /*package*/ static void native_postRotate(long native_object, float degrees, 485 float px, float py) { 486 Matrix_Delegate d = sManager.getDelegate(native_object); 487 if (d != null) { 488 d.postTransform(getRotate(degrees, px, py)); 489 } 490 } 491 492 @LayoutlibDelegate 493 /*package*/ static void native_postRotate(long native_object, float degrees) { 494 Matrix_Delegate d = sManager.getDelegate(native_object); 495 if (d != null) { 496 d.postTransform(getRotate(degrees)); 497 } 498 } 499 500 @LayoutlibDelegate 501 /*package*/ static void native_postSkew(long native_object, float kx, float ky, 502 float px, float py) { 503 Matrix_Delegate d = sManager.getDelegate(native_object); 504 if (d != null) { 505 d.postTransform(getSkew(kx, ky, px, py)); 506 } 507 } 508 509 @LayoutlibDelegate 510 /*package*/ static void native_postSkew(long native_object, float kx, float ky) { 511 Matrix_Delegate d = sManager.getDelegate(native_object); 512 if (d != null) { 513 d.postTransform(getSkew(kx, ky)); 514 } 515 } 516 517 @LayoutlibDelegate 518 /*package*/ static void native_postConcat(long native_object, long other_matrix) { 519 Matrix_Delegate d = sManager.getDelegate(native_object); 520 Matrix_Delegate other = sManager.getDelegate(other_matrix); 521 if (d != null && other != null) { 522 d.postTransform(other.mValues); 523 } 524 } 525 526 @LayoutlibDelegate 527 /*package*/ static boolean native_setRectToRect(long native_object, RectF src, 528 RectF dst, int stf) { 529 Matrix_Delegate d = sManager.getDelegate(native_object); 530 if (d == null) { 531 return false; 532 } 533 534 if (src.isEmpty()) { 535 reset(d.mValues); 536 return false; 537 } 538 539 if (dst.isEmpty()) { 540 d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5] 541 = d.mValues[6] = d.mValues[7] = 0; 542 d.mValues[8] = 1; 543 } else { 544 float tx, sx = dst.width() / src.width(); 545 float ty, sy = dst.height() / src.height(); 546 boolean xLarger = false; 547 548 if (stf != ScaleToFit.FILL.nativeInt) { 549 if (sx > sy) { 550 xLarger = true; 551 sx = sy; 552 } else { 553 sy = sx; 554 } 555 } 556 557 tx = dst.left - src.left * sx; 558 ty = dst.top - src.top * sy; 559 if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) { 560 float diff; 561 562 if (xLarger) { 563 diff = dst.width() - src.width() * sy; 564 } else { 565 diff = dst.height() - src.height() * sy; 566 } 567 568 if (stf == ScaleToFit.CENTER.nativeInt) { 569 diff = diff / 2; 570 } 571 572 if (xLarger) { 573 tx += diff; 574 } else { 575 ty += diff; 576 } 577 } 578 579 d.mValues[0] = sx; 580 d.mValues[4] = sy; 581 d.mValues[2] = tx; 582 d.mValues[5] = ty; 583 d.mValues[1] = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0; 584 585 } 586 // shared cleanup 587 d.mValues[8] = 1; 588 return true; 589 } 590 591 @LayoutlibDelegate 592 /*package*/ static boolean native_setPolyToPoly(long native_object, float[] src, int srcIndex, 593 float[] dst, int dstIndex, int pointCount) { 594 // FIXME 595 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 596 "Matrix.setPolyToPoly is not supported.", 597 null, null /*data*/); 598 return false; 599 } 600 601 @LayoutlibDelegate 602 /*package*/ static boolean native_invert(long native_object, long inverse) { 603 Matrix_Delegate d = sManager.getDelegate(native_object); 604 if (d == null) { 605 return false; 606 } 607 608 Matrix_Delegate inv_mtx = sManager.getDelegate(inverse); 609 if (inv_mtx == null) { 610 return false; 611 } 612 613 try { 614 AffineTransform affineTransform = d.getAffineTransform(); 615 AffineTransform inverseTransform = affineTransform.createInverse(); 616 inv_mtx.mValues[0] = (float)inverseTransform.getScaleX(); 617 inv_mtx.mValues[1] = (float)inverseTransform.getShearX(); 618 inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX(); 619 inv_mtx.mValues[3] = (float)inverseTransform.getScaleX(); 620 inv_mtx.mValues[4] = (float)inverseTransform.getShearY(); 621 inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY(); 622 623 return true; 624 } catch (NoninvertibleTransformException e) { 625 return false; 626 } 627 } 628 629 @LayoutlibDelegate 630 /*package*/ static void native_mapPoints(long native_object, float[] dst, int dstIndex, 631 float[] src, int srcIndex, int ptCount, boolean isPts) { 632 Matrix_Delegate d = sManager.getDelegate(native_object); 633 if (d == null) { 634 return; 635 } 636 637 if (isPts) { 638 d.mapPoints(dst, dstIndex, src, srcIndex, ptCount); 639 } else { 640 d.mapVectors(dst, dstIndex, src, srcIndex, ptCount); 641 } 642 } 643 644 @LayoutlibDelegate 645 /*package*/ static boolean native_mapRect(long native_object, RectF dst, RectF src) { 646 Matrix_Delegate d = sManager.getDelegate(native_object); 647 if (d == null) { 648 return false; 649 } 650 651 return d.mapRect(dst, src); 652 } 653 654 @LayoutlibDelegate 655 /*package*/ static float native_mapRadius(long native_object, float radius) { 656 Matrix_Delegate d = sManager.getDelegate(native_object); 657 if (d == null) { 658 return 0.f; 659 } 660 661 float[] src = new float[] { radius, 0.f, 0.f, radius }; 662 d.mapVectors(src, 0, src, 0, 2); 663 664 float l1 = getPointLength(src, 0); 665 float l2 = getPointLength(src, 2); 666 667 return (float) Math.sqrt(l1 * l2); 668 } 669 670 @LayoutlibDelegate 671 /*package*/ static void native_getValues(long native_object, float[] values) { 672 Matrix_Delegate d = sManager.getDelegate(native_object); 673 if (d == null) { 674 return; 675 } 676 677 System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE); 678 } 679 680 @LayoutlibDelegate 681 /*package*/ static void native_setValues(long native_object, float[] values) { 682 Matrix_Delegate d = sManager.getDelegate(native_object); 683 if (d == null) { 684 return; 685 } 686 687 System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE); 688 } 689 690 @LayoutlibDelegate 691 /*package*/ static boolean native_equals(long native_a, long native_b) { 692 Matrix_Delegate a = sManager.getDelegate(native_a); 693 if (a == null) { 694 return false; 695 } 696 697 Matrix_Delegate b = sManager.getDelegate(native_b); 698 if (b == null) { 699 return false; 700 } 701 702 for (int i = 0 ; i < MATRIX_SIZE ; i++) { 703 if (a.mValues[i] != b.mValues[i]) { 704 return false; 705 } 706 } 707 708 return true; 709 } 710 711 @LayoutlibDelegate 712 /*package*/ static void finalizer(long native_instance) { 713 sManager.removeJavaReferenceFor(native_instance); 714 } 715 716 // ---- Private helper methods ---- 717 718 /*package*/ static AffineTransform getAffineTransform(float[] matrix) { 719 // the AffineTransform constructor takes the value in a different order 720 // for a matrix [ 0 1 2 ] 721 // [ 3 4 5 ] 722 // the order is 0, 3, 1, 4, 2, 5... 723 return new AffineTransform( 724 matrix[0], matrix[3], matrix[1], 725 matrix[4], matrix[2], matrix[5]); 726 } 727 728 /** 729 * Reset a matrix to the identity 730 */ 731 private static void reset(float[] mtx) { 732 for (int i = 0, k = 0; i < 3; i++) { 733 for (int j = 0; j < 3; j++, k++) { 734 mtx[k] = ((i==j) ? 1 : 0); 735 } 736 } 737 } 738 739 @SuppressWarnings("unused") 740 private final static int kIdentity_Mask = 0; 741 private final static int kTranslate_Mask = 0x01; //!< set if the matrix has translation 742 private final static int kScale_Mask = 0x02; //!< set if the matrix has X or Y scale 743 private final static int kAffine_Mask = 0x04; //!< set if the matrix skews or rotates 744 private final static int kPerspective_Mask = 0x08; //!< set if the matrix is in perspective 745 private final static int kRectStaysRect_Mask = 0x10; 746 @SuppressWarnings("unused") 747 private final static int kUnknown_Mask = 0x80; 748 749 @SuppressWarnings("unused") 750 private final static int kAllMasks = kTranslate_Mask | 751 kScale_Mask | 752 kAffine_Mask | 753 kPerspective_Mask | 754 kRectStaysRect_Mask; 755 756 // these guys align with the masks, so we can compute a mask from a variable 0/1 757 @SuppressWarnings("unused") 758 private final static int kTranslate_Shift = 0; 759 @SuppressWarnings("unused") 760 private final static int kScale_Shift = 1; 761 @SuppressWarnings("unused") 762 private final static int kAffine_Shift = 2; 763 @SuppressWarnings("unused") 764 private final static int kPerspective_Shift = 3; 765 private final static int kRectStaysRect_Shift = 4; 766 767 private int computeTypeMask() { 768 int mask = 0; 769 770 if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) { 771 mask |= kPerspective_Mask; 772 } 773 774 if (mValues[2] != 0. || mValues[5] != 0.) { 775 mask |= kTranslate_Mask; 776 } 777 778 float m00 = mValues[0]; 779 float m01 = mValues[1]; 780 float m10 = mValues[3]; 781 float m11 = mValues[4]; 782 783 if (m01 != 0. || m10 != 0.) { 784 mask |= kAffine_Mask; 785 } 786 787 if (m00 != 1. || m11 != 1.) { 788 mask |= kScale_Mask; 789 } 790 791 if ((mask & kPerspective_Mask) == 0) { 792 // map non-zero to 1 793 int im00 = m00 != 0 ? 1 : 0; 794 int im01 = m01 != 0 ? 1 : 0; 795 int im10 = m10 != 0 ? 1 : 0; 796 int im11 = m11 != 0 ? 1 : 0; 797 798 // record if the (p)rimary and (s)econdary diagonals are all 0 or 799 // all non-zero (answer is 0 or 1) 800 int dp0 = (im00 | im11) ^ 1; // true if both are 0 801 int dp1 = im00 & im11; // true if both are 1 802 int ds0 = (im01 | im10) ^ 1; // true if both are 0 803 int ds1 = im01 & im10; // true if both are 1 804 805 // return 1 if primary is 1 and secondary is 0 or 806 // primary is 0 and secondary is 1 807 mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift; 808 } 809 810 return mask; 811 } 812 813 private Matrix_Delegate() { 814 reset(); 815 } 816 817 private Matrix_Delegate(float[] values) { 818 System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE); 819 } 820 821 /** 822 * Adds the given transformation to the current Matrix 823 * <p/>This in effect does this = this*matrix 824 * @param matrix 825 */ 826 private void postTransform(float[] matrix) { 827 float[] tmp = new float[9]; 828 multiply(tmp, mValues, matrix); 829 mValues = tmp; 830 } 831 832 /** 833 * Adds the given transformation to the current Matrix 834 * <p/>This in effect does this = matrix*this 835 * @param matrix 836 */ 837 private void preTransform(float[] matrix) { 838 float[] tmp = new float[9]; 839 multiply(tmp, matrix, mValues); 840 mValues = tmp; 841 } 842 843 /** 844 * Apply this matrix to the array of 2D points specified by src, and write 845 * the transformed points into the array of points specified by dst. The 846 * two arrays represent their "points" as pairs of floats [x, y]. 847 * 848 * @param dst The array of dst points (x,y pairs) 849 * @param dstIndex The index of the first [x,y] pair of dst floats 850 * @param src The array of src points (x,y pairs) 851 * @param srcIndex The index of the first [x,y] pair of src floats 852 * @param pointCount The number of points (x,y pairs) to transform 853 */ 854 855 private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, 856 int pointCount) { 857 final int count = pointCount * 2; 858 859 float[] tmpDest = dst; 860 boolean inPlace = dst == src; 861 if (inPlace) { 862 tmpDest = new float[dstIndex + count]; 863 } 864 865 for (int i = 0 ; i < count ; i += 2) { 866 // just in case we are doing in place, we better put this in temp vars 867 float x = mValues[0] * src[i + srcIndex] + 868 mValues[1] * src[i + srcIndex + 1] + 869 mValues[2]; 870 float y = mValues[3] * src[i + srcIndex] + 871 mValues[4] * src[i + srcIndex + 1] + 872 mValues[5]; 873 874 tmpDest[i + dstIndex] = x; 875 tmpDest[i + dstIndex + 1] = y; 876 } 877 878 if (inPlace) { 879 System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count); 880 } 881 } 882 883 /** 884 * Apply this matrix to the array of 2D points, and write the transformed 885 * points back into the array 886 * 887 * @param pts The array [x0, y0, x1, y1, ...] of points to transform. 888 */ 889 890 private void mapPoints(float[] pts) { 891 mapPoints(pts, 0, pts, 0, pts.length >> 1); 892 } 893 894 private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) { 895 if (hasPerspective()) { 896 // transform the (0,0) point 897 float[] origin = new float[] { 0.f, 0.f}; 898 mapPoints(origin); 899 900 // translate the vector data as points 901 mapPoints(dst, dstIndex, src, srcIndex, ptCount); 902 903 // then substract the transformed origin. 904 final int count = ptCount * 2; 905 for (int i = 0 ; i < count ; i += 2) { 906 dst[dstIndex + i] = dst[dstIndex + i] - origin[0]; 907 dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1]; 908 } 909 } else { 910 // make a copy of the matrix 911 Matrix_Delegate copy = new Matrix_Delegate(mValues); 912 913 // remove the translation 914 setTranslate(copy.mValues, 0, 0); 915 916 // map the content as points. 917 copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount); 918 } 919 } 920 921 private static float getPointLength(float[] src, int index) { 922 return (float) Math.sqrt(src[index] * src[index] + src[index + 1] * src[index + 1]); 923 } 924 925 /** 926 * multiply two matrices and store them in a 3rd. 927 * <p/>This in effect does dest = a*b 928 * dest cannot be the same as a or b. 929 */ 930 /*package*/ static void multiply(float dest[], float[] a, float[] b) { 931 // first row 932 dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6]; 933 dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7]; 934 dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8]; 935 936 // 2nd row 937 dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6]; 938 dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7]; 939 dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8]; 940 941 // 3rd row 942 dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6]; 943 dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7]; 944 dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8]; 945 } 946 947 /** 948 * Returns a matrix that represents a given translate 949 * @param dx 950 * @param dy 951 * @return 952 */ 953 /*package*/ static float[] getTranslate(float dx, float dy) { 954 return setTranslate(new float[9], dx, dy); 955 } 956 957 /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) { 958 dest[0] = 1; 959 dest[1] = 0; 960 dest[2] = dx; 961 dest[3] = 0; 962 dest[4] = 1; 963 dest[5] = dy; 964 dest[6] = 0; 965 dest[7] = 0; 966 dest[8] = 1; 967 return dest; 968 } 969 970 /*package*/ static float[] getScale(float sx, float sy) { 971 return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 }; 972 } 973 974 /** 975 * Returns a matrix that represents the given scale info. 976 * @param sx 977 * @param sy 978 * @param px 979 * @param py 980 */ 981 /*package*/ static float[] getScale(float sx, float sy, float px, float py) { 982 float[] tmp = new float[9]; 983 float[] tmp2 = new float[9]; 984 985 // TODO: do it in one pass 986 987 // translate tmp so that the pivot is in 0,0 988 setTranslate(tmp, -px, -py); 989 990 // scale into tmp2 991 multiply(tmp2, tmp, getScale(sx, sy)); 992 993 // translate back the pivot back into tmp 994 multiply(tmp, tmp2, getTranslate(px, py)); 995 996 return tmp; 997 } 998 999 1000 /*package*/ static float[] getRotate(float degrees) { 1001 double rad = Math.toRadians(degrees); 1002 float sin = (float)Math.sin(rad); 1003 float cos = (float)Math.cos(rad); 1004 1005 return getRotate(sin, cos); 1006 } 1007 1008 /*package*/ static float[] getRotate(float sin, float cos) { 1009 return setRotate(new float[9], sin, cos); 1010 } 1011 1012 /*package*/ static float[] setRotate(float[] dest, float degrees) { 1013 double rad = Math.toRadians(degrees); 1014 float sin = (float)Math.sin(rad); 1015 float cos = (float)Math.cos(rad); 1016 1017 return setRotate(dest, sin, cos); 1018 } 1019 1020 /*package*/ static float[] setRotate(float[] dest, float sin, float cos) { 1021 dest[0] = cos; 1022 dest[1] = -sin; 1023 dest[2] = 0; 1024 dest[3] = sin; 1025 dest[4] = cos; 1026 dest[5] = 0; 1027 dest[6] = 0; 1028 dest[7] = 0; 1029 dest[8] = 1; 1030 return dest; 1031 } 1032 1033 /*package*/ static float[] getRotate(float degrees, float px, float py) { 1034 float[] tmp = new float[9]; 1035 float[] tmp2 = new float[9]; 1036 1037 // TODO: do it in one pass 1038 1039 // translate so that the pivot is in 0,0 1040 setTranslate(tmp, -px, -py); 1041 1042 // rotate into tmp2 1043 double rad = Math.toRadians(degrees); 1044 float cos = (float)Math.cos(rad); 1045 float sin = (float)Math.sin(rad); 1046 multiply(tmp2, tmp, getRotate(sin, cos)); 1047 1048 // translate back the pivot back into tmp 1049 multiply(tmp, tmp2, getTranslate(px, py)); 1050 1051 return tmp; 1052 } 1053 1054 /*package*/ static float[] getSkew(float kx, float ky) { 1055 return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }; 1056 } 1057 1058 /*package*/ static float[] getSkew(float kx, float ky, float px, float py) { 1059 float[] tmp = new float[9]; 1060 float[] tmp2 = new float[9]; 1061 1062 // TODO: do it in one pass 1063 1064 // translate so that the pivot is in 0,0 1065 setTranslate(tmp, -px, -py); 1066 1067 // skew into tmp2 1068 multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }); 1069 // translate back the pivot back into tmp 1070 multiply(tmp, tmp2, getTranslate(px, py)); 1071 1072 return tmp; 1073 } 1074 } 1075