1 /* 2 * Copyright (C) 2008 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 import java.awt.geom.AffineTransform; 20 import java.awt.geom.NoninvertibleTransformException; 21 22 23 /** 24 * A matrix implementation overridden by the LayoutLib bridge. 25 */ 26 public class Matrix extends _Original_Matrix { 27 28 float mValues[] = new float[9]; 29 30 /** 31 * Create an identity matrix 32 */ 33 public Matrix() { 34 reset(); 35 } 36 37 /** 38 * Create a matrix that is a (deep) copy of src 39 * @param src The matrix to copy into this matrix 40 */ 41 public Matrix(Matrix src) { 42 set(src); 43 } 44 45 /** 46 * Creates a Matrix object from the float array. The array becomes the internal storage 47 * of the object. 48 * @param data 49 */ 50 private Matrix(float[] data) { 51 assert data.length != 9; 52 mValues = data; 53 } 54 55 //---------- Custom Methods 56 57 /** 58 * Adds the given transformation to the current Matrix 59 * <p/>This in effect does this = this*matrix 60 * @param matrix 61 */ 62 private void addTransform(float[] matrix) { 63 float[] tmp = new float[9]; 64 65 // first row 66 tmp[0] = matrix[0] * mValues[0] + matrix[1] * mValues[3] + matrix[2] * mValues[6]; 67 tmp[1] = matrix[0] * mValues[1] + matrix[1] * mValues[4] + matrix[2] * mValues[7]; 68 tmp[2] = matrix[0] * mValues[2] + matrix[1] * mValues[5] + matrix[2] * mValues[8]; 69 70 // 2nd row 71 tmp[3] = matrix[3] * mValues[0] + matrix[4] * mValues[3] + matrix[5] * mValues[6]; 72 tmp[4] = matrix[3] * mValues[1] + matrix[4] * mValues[4] + matrix[5] * mValues[7]; 73 tmp[5] = matrix[3] * mValues[2] + matrix[4] * mValues[5] + matrix[5] * mValues[8]; 74 75 // 3rd row 76 tmp[6] = matrix[6] * mValues[0] + matrix[7] * mValues[3] + matrix[8] * mValues[6]; 77 tmp[7] = matrix[6] * mValues[1] + matrix[7] * mValues[4] + matrix[8] * mValues[7]; 78 tmp[8] = matrix[6] * mValues[2] + matrix[7] * mValues[5] + matrix[8] * mValues[8]; 79 80 // copy the result over to mValues 81 mValues = tmp; 82 } 83 84 public AffineTransform getTransform() { 85 // the AffineTransform constructor takes the value in a different order 86 // for a matrix [ 0 1 2 ] 87 // [ 3 4 5 ] 88 // the order is 0, 3, 1, 4, 2, 5... 89 return new AffineTransform(mValues[0], mValues[3], mValues[1], 90 mValues[4], mValues[2], mValues[5]); 91 } 92 93 public boolean hasPerspective() { 94 return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1); 95 } 96 97 //---------- 98 99 /** 100 * Returns true if the matrix is identity. 101 * This maybe faster than testing if (getType() == 0) 102 */ 103 @Override 104 public boolean isIdentity() { 105 for (int i = 0, k = 0; i < 3; i++) { 106 for (int j = 0; j < 3; j++, k++) { 107 if (mValues[k] != ((i==j) ? 1 : 0)) { 108 return false; 109 } 110 } 111 } 112 113 return true; 114 } 115 116 /** 117 * Returns true if will map a rectangle to another rectangle. This can be 118 * true if the matrix is identity, scale-only, or rotates a multiple of 90 119 * degrees. 120 */ 121 @Override 122 public boolean rectStaysRect() { 123 return (computeTypeMask() & kRectStaysRect_Mask) != 0; 124 } 125 126 /** 127 * (deep) copy the src matrix into this matrix. If src is null, reset this 128 * matrix to the identity matrix. 129 */ 130 public void set(Matrix src) { 131 if (src == null) { 132 reset(); 133 } else { 134 System.arraycopy(src.mValues, 0, mValues, 0, mValues.length); 135 } 136 } 137 138 @Override 139 public void set(_Original_Matrix src) { 140 throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); 141 } 142 143 /** Returns true if obj is a Matrix and its values equal our values. 144 */ 145 @Override 146 public boolean equals(Object obj) { 147 if (obj != null && obj instanceof Matrix) { 148 Matrix matrix = (Matrix)obj; 149 for (int i = 0 ; i < 9 ; i++) { 150 if (mValues[i] != matrix.mValues[i]) { 151 return false; 152 } 153 } 154 155 return true; 156 } 157 158 return false; 159 } 160 161 /** Set the matrix to identity */ 162 @Override 163 public void reset() { 164 for (int i = 0, k = 0; i < 3; i++) { 165 for (int j = 0; j < 3; j++, k++) { 166 mValues[k] = ((i==j) ? 1 : 0); 167 } 168 } 169 } 170 171 /** Set the matrix to translate by (dx, dy). */ 172 @Override 173 public void setTranslate(float dx, float dy) { 174 mValues[0] = 1; 175 mValues[1] = 0; 176 mValues[2] = dx; 177 mValues[3] = 0; 178 mValues[4] = 1; 179 mValues[5] = dy; 180 mValues[6] = 0; 181 mValues[7] = 0; 182 mValues[8] = 1; 183 } 184 185 /** 186 * Set the matrix to scale by sx and sy, with a pivot point at (px, py). 187 * The pivot point is the coordinate that should remain unchanged by the 188 * specified transformation. 189 */ 190 @Override 191 public void setScale(float sx, float sy, float px, float py) { 192 // TODO: do it in one pass 193 194 // translate so that the pivot is in 0,0 195 mValues[0] = 1; 196 mValues[1] = 0; 197 mValues[2] = -px; 198 mValues[3] = 0; 199 mValues[4] = 1; 200 mValues[5] = -py; 201 mValues[6] = 0; 202 mValues[7] = 0; 203 mValues[8] = 1; 204 205 // scale 206 addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 }); 207 // translate back the pivot 208 addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); 209 } 210 211 /** Set the matrix to scale by sx and sy. */ 212 @Override 213 public void setScale(float sx, float sy) { 214 mValues[0] = sx; 215 mValues[1] = 0; 216 mValues[2] = 0; 217 mValues[3] = 0; 218 mValues[4] = sy; 219 mValues[5] = 0; 220 mValues[6] = 0; 221 mValues[7] = 0; 222 mValues[8] = 1; 223 } 224 225 /** 226 * Set the matrix to rotate by the specified number of degrees, with a pivot 227 * point at (px, py). The pivot point is the coordinate that should remain 228 * unchanged by the specified transformation. 229 */ 230 @Override 231 public void setRotate(float degrees, float px, float py) { 232 // TODO: do it in one pass 233 234 // translate so that the pivot is in 0,0 235 mValues[0] = 1; 236 mValues[1] = 0; 237 mValues[2] = -px; 238 mValues[3] = 0; 239 mValues[4] = 1; 240 mValues[5] = -py; 241 mValues[6] = 0; 242 mValues[7] = 0; 243 mValues[8] = 1; 244 245 // scale 246 double rad = Math.toRadians(degrees); 247 float cos = (float)Math.cos(rad); 248 float sin = (float)Math.sin(rad); 249 addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); 250 // translate back the pivot 251 addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); 252 } 253 254 /** 255 * Set the matrix to rotate about (0,0) by the specified number of degrees. 256 */ 257 @Override 258 public void setRotate(float degrees) { 259 double rad = Math.toRadians(degrees); 260 float cos = (float)Math.cos(rad); 261 float sin = (float)Math.sin(rad); 262 263 mValues[0] = cos; 264 mValues[1] = -sin; 265 mValues[2] = 0; 266 mValues[3] = sin; 267 mValues[4] = cos; 268 mValues[5] = 0; 269 mValues[6] = 0; 270 mValues[7] = 0; 271 mValues[8] = 1; 272 } 273 274 /** 275 * Set the matrix to rotate by the specified sine and cosine values, with a 276 * pivot point at (px, py). The pivot point is the coordinate that should 277 * remain unchanged by the specified transformation. 278 */ 279 @Override 280 public void setSinCos(float sinValue, float cosValue, float px, float py) { 281 // TODO: do it in one pass 282 283 // translate so that the pivot is in 0,0 284 mValues[0] = 1; 285 mValues[1] = 0; 286 mValues[2] = -px; 287 mValues[3] = 0; 288 mValues[4] = 1; 289 mValues[5] = -py; 290 mValues[6] = 0; 291 mValues[7] = 0; 292 mValues[8] = 1; 293 294 // scale 295 addTransform(new float[] { cosValue, -sinValue, 0, sinValue, cosValue, 0, 0, 0, 1 }); 296 // translate back the pivot 297 addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); 298 } 299 300 /** Set the matrix to rotate by the specified sine and cosine values. */ 301 @Override 302 public void setSinCos(float sinValue, float cosValue) { 303 mValues[0] = cosValue; 304 mValues[1] = -sinValue; 305 mValues[2] = 0; 306 mValues[3] = sinValue; 307 mValues[4] = cosValue; 308 mValues[5] = 0; 309 mValues[6] = 0; 310 mValues[7] = 0; 311 mValues[8] = 1; 312 } 313 314 /** 315 * Set the matrix to skew by sx and sy, with a pivot point at (px, py). 316 * The pivot point is the coordinate that should remain unchanged by the 317 * specified transformation. 318 */ 319 @Override 320 public void setSkew(float kx, float ky, float px, float py) { 321 // TODO: do it in one pass 322 323 // translate so that the pivot is in 0,0 324 mValues[0] = 1; 325 mValues[1] = 0; 326 mValues[2] = -px; 327 mValues[3] = 0; 328 mValues[4] = 1; 329 mValues[5] = -py; 330 mValues[6] = 0; 331 mValues[7] = 0; 332 mValues[8] = 1; 333 334 // scale 335 addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }); 336 // translate back the pivot 337 addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); 338 } 339 340 /** Set the matrix to skew by sx and sy. */ 341 @Override 342 public void setSkew(float kx, float ky) { 343 mValues[0] = 1; 344 mValues[1] = kx; 345 mValues[2] = -0; 346 mValues[3] = ky; 347 mValues[4] = 1; 348 mValues[5] = 0; 349 mValues[6] = 0; 350 mValues[7] = 0; 351 mValues[8] = 1; 352 } 353 354 /** 355 * Set the matrix to the concatenation of the two specified matrices, 356 * returning true if the the result can be represented. Either of the two 357 * matrices may also be the target matrix. this = a * b 358 */ 359 public boolean setConcat(Matrix a, Matrix b) { 360 if (a == this) { 361 preConcat(b); 362 } else if (b == this) { 363 postConcat(b); 364 } else { 365 Matrix tmp = new Matrix(b); 366 tmp.addTransform(a.mValues); 367 set(tmp); 368 } 369 370 return true; 371 } 372 373 @Override 374 public boolean setConcat(_Original_Matrix a, _Original_Matrix b) { 375 throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); 376 } 377 378 /** 379 * Preconcats the matrix with the specified translation. 380 * M' = M * T(dx, dy) 381 */ 382 @Override 383 public boolean preTranslate(float dx, float dy) { 384 // create a matrix that will be multiply by this 385 Matrix m = new Matrix(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 }); 386 m.addTransform(this.mValues); 387 388 System.arraycopy(m.mValues, 0, mValues, 0, 9); 389 return true; 390 } 391 392 /** 393 * Preconcats the matrix with the specified scale. 394 * M' = M * S(sx, sy, px, py) 395 */ 396 @Override 397 public boolean preScale(float sx, float sy, float px, float py) { 398 Matrix m = new Matrix(); 399 m.setScale(sx, sy, px, py); 400 m.addTransform(mValues); 401 set(m); 402 403 return true; 404 } 405 406 /** 407 * Preconcats the matrix with the specified scale. 408 * M' = M * S(sx, sy) 409 */ 410 @Override 411 public boolean preScale(float sx, float sy) { 412 Matrix m = new Matrix(); 413 m.setScale(sx, sy); 414 m.addTransform(mValues); 415 set(m); 416 417 return true; 418 } 419 420 /** 421 * Preconcats the matrix with the specified rotation. 422 * M' = M * R(degrees, px, py) 423 */ 424 @Override 425 public boolean preRotate(float degrees, float px, float py) { 426 Matrix m = new Matrix(); 427 m.setRotate(degrees, px, py); 428 m.addTransform(mValues); 429 set(m); 430 431 return true; 432 } 433 434 /** 435 * Preconcats the matrix with the specified rotation. 436 * M' = M * R(degrees) 437 */ 438 @Override 439 public boolean preRotate(float degrees) { 440 Matrix m = new Matrix(); 441 m.setRotate(degrees); 442 m.addTransform(mValues); 443 set(m); 444 445 return true; 446 } 447 448 /** 449 * Preconcats the matrix with the specified skew. 450 * M' = M * K(kx, ky, px, py) 451 */ 452 @Override 453 public boolean preSkew(float kx, float ky, float px, float py) { 454 Matrix m = new Matrix(); 455 m.setSkew(kx, ky, px, py); 456 m.addTransform(mValues); 457 set(m); 458 459 return true; 460 } 461 462 /** 463 * Preconcats the matrix with the specified skew. 464 * M' = M * K(kx, ky) 465 */ 466 @Override 467 public boolean preSkew(float kx, float ky) { 468 Matrix m = new Matrix(); 469 m.setSkew(kx, ky); 470 m.addTransform(mValues); 471 set(m); 472 473 return true; 474 } 475 476 /** 477 * Preconcats the matrix with the specified matrix. 478 * M' = M * other 479 */ 480 public boolean preConcat(Matrix other) { 481 Matrix m = new Matrix(other); 482 other.addTransform(mValues); 483 set(m); 484 485 return true; 486 } 487 488 @Override 489 public boolean preConcat(_Original_Matrix other) { 490 throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); 491 } 492 493 /** 494 * Postconcats the matrix with the specified translation. 495 * M' = T(dx, dy) * M 496 */ 497 @Override 498 public boolean postTranslate(float dx, float dy) { 499 addTransform(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 }); 500 return true; 501 } 502 503 /** 504 * Postconcats the matrix with the specified scale. 505 * M' = S(sx, sy, px, py) * M 506 */ 507 @Override 508 public boolean postScale(float sx, float sy, float px, float py) { 509 // TODO: do it in one pass 510 // translate so that the pivot is in 0,0 511 addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 }); 512 // scale 513 addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 }); 514 // translate back the pivot 515 addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); 516 517 return true; 518 } 519 520 /** 521 * Postconcats the matrix with the specified scale. 522 * M' = S(sx, sy) * M 523 */ 524 @Override 525 public boolean postScale(float sx, float sy) { 526 addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 }); 527 return true; 528 } 529 530 /** 531 * Postconcats the matrix with the specified rotation. 532 * M' = R(degrees, px, py) * M 533 */ 534 @Override 535 public boolean postRotate(float degrees, float px, float py) { 536 // TODO: do it in one pass 537 // translate so that the pivot is in 0,0 538 addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 }); 539 // scale 540 double rad = Math.toRadians(degrees); 541 float cos = (float)Math.cos(rad); 542 float sin = (float)Math.sin(rad); 543 addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); 544 // translate back the pivot 545 addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); 546 547 return true; 548 } 549 550 /** 551 * Postconcats the matrix with the specified rotation. 552 * M' = R(degrees) * M 553 */ 554 @Override 555 public boolean postRotate(float degrees) { 556 double rad = Math.toRadians(degrees); 557 float cos = (float)Math.cos(rad); 558 float sin = (float)Math.sin(rad); 559 addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 }); 560 561 return true; 562 } 563 564 /** 565 * Postconcats the matrix with the specified skew. 566 * M' = K(kx, ky, px, py) * M 567 */ 568 @Override 569 public boolean postSkew(float kx, float ky, float px, float py) { 570 // TODO: do it in one pass 571 // translate so that the pivot is in 0,0 572 addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 }); 573 // scale 574 addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }); 575 // translate back the pivot 576 addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 }); 577 578 return true; 579 } 580 581 /** 582 * Postconcats the matrix with the specified skew. 583 * M' = K(kx, ky) * M 584 */ 585 @Override 586 public boolean postSkew(float kx, float ky) { 587 addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 }); 588 589 return true; 590 } 591 592 /** 593 * Postconcats the matrix with the specified matrix. 594 * M' = other * M 595 */ 596 public boolean postConcat(Matrix other) { 597 addTransform(other.mValues); 598 599 return true; 600 } 601 602 @Override 603 public boolean postConcat(_Original_Matrix other) { 604 throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); 605 } 606 607 /** Controlls how the src rect should align into the dst rect for 608 setRectToRect(). 609 */ 610 public enum ScaleToFit { 611 /** 612 * Scale in X and Y independently, so that src matches dst exactly. 613 * This may change the aspect ratio of the src. 614 */ 615 FILL (0), 616 /** 617 * Compute a scale that will maintain the original src aspect ratio, 618 * but will also ensure that src fits entirely inside dst. At least one 619 * axis (X or Y) will fit exactly. START aligns the result to the 620 * left and top edges of dst. 621 */ 622 START (1), 623 /** 624 * Compute a scale that will maintain the original src aspect ratio, 625 * but will also ensure that src fits entirely inside dst. At least one 626 * axis (X or Y) will fit exactly. The result is centered inside dst. 627 */ 628 CENTER (2), 629 /** 630 * Compute a scale that will maintain the original src aspect ratio, 631 * but will also ensure that src fits entirely inside dst. At least one 632 * axis (X or Y) will fit exactly. END aligns the result to the 633 * right and bottom edges of dst. 634 */ 635 END (3); 636 637 // the native values must match those in SkMatrix.h 638 ScaleToFit(int nativeInt) { 639 this.nativeInt = nativeInt; 640 } 641 final int nativeInt; 642 } 643 644 /** 645 * Set the matrix to the scale and translate values that map the source 646 * rectangle to the destination rectangle, returning true if the result 647 * can be represented. 648 * 649 * @param src the source rectangle to map from. 650 * @param dst the destination rectangle to map to. 651 * @param stf the ScaleToFit option 652 * @return true if the matrix can be represented by the rectangle mapping. 653 */ 654 public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) { 655 if (dst == null || src == null) { 656 throw new NullPointerException(); 657 } 658 659 if (src.isEmpty()) { 660 reset(); 661 return false; 662 } 663 664 if (dst.isEmpty()) { 665 mValues[0] = mValues[1] = mValues[2] = mValues[3] = mValues[4] = mValues[5] 666 = mValues[6] = mValues[7] = 0; 667 mValues[8] = 1; 668 } else { 669 float tx, sx = dst.width() / src.width(); 670 float ty, sy = dst.height() / src.height(); 671 boolean xLarger = false; 672 673 if (stf != ScaleToFit.FILL) { 674 if (sx > sy) { 675 xLarger = true; 676 sx = sy; 677 } else { 678 sy = sx; 679 } 680 } 681 682 tx = dst.left - src.left * sx; 683 ty = dst.top - src.top * sy; 684 if (stf == ScaleToFit.CENTER || stf == ScaleToFit.END) { 685 float diff; 686 687 if (xLarger) { 688 diff = dst.width() - src.width() * sy; 689 } else { 690 diff = dst.height() - src.height() * sy; 691 } 692 693 if (stf == ScaleToFit.CENTER) { 694 diff = diff / 2; 695 } 696 697 if (xLarger) { 698 tx += diff; 699 } else { 700 ty += diff; 701 } 702 } 703 704 mValues[0] = sx; 705 mValues[4] = sy; 706 mValues[2] = tx; 707 mValues[5] = ty; 708 mValues[1] = mValues[3] = mValues[6] = mValues[7] = 0; 709 710 } 711 // shared cleanup 712 mValues[8] = 1; 713 return true; 714 } 715 716 @Override 717 public boolean setRectToRect(RectF src, RectF dst, _Original_Matrix.ScaleToFit stf) { 718 throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); 719 } 720 721 /** 722 * Set the matrix such that the specified src points would map to the 723 * specified dst points. The "points" are represented as an array of floats, 724 * order [x0, y0, x1, y1, ...], where each "point" is 2 float values. 725 * 726 * @param src The array of src [x,y] pairs (points) 727 * @param srcIndex Index of the first pair of src values 728 * @param dst The array of dst [x,y] pairs (points) 729 * @param dstIndex Index of the first pair of dst values 730 * @param pointCount The number of pairs/points to be used. Must be [0..4] 731 * @return true if the matrix was set to the specified transformation 732 */ 733 @Override 734 public boolean setPolyToPoly(float[] src, int srcIndex, 735 float[] dst, int dstIndex, 736 int pointCount) { 737 if (pointCount > 4) { 738 throw new IllegalArgumentException(); 739 } 740 checkPointArrays(src, srcIndex, dst, dstIndex, pointCount); 741 throw new UnsupportedOperationException("STUB NEEDED"); 742 } 743 744 /** 745 * If this matrix can be inverted, return true and if inverse is not null, 746 * set inverse to be the inverse of this matrix. If this matrix cannot be 747 * inverted, ignore inverse and return false. 748 */ 749 public boolean invert(Matrix inverse) { 750 if (inverse == null) { 751 return false; 752 } 753 754 try { 755 AffineTransform affineTransform = getTransform(); 756 AffineTransform inverseTransform = affineTransform.createInverse(); 757 inverse.mValues[0] = (float)inverseTransform.getScaleX(); 758 inverse.mValues[1] = (float)inverseTransform.getShearX(); 759 inverse.mValues[2] = (float)inverseTransform.getTranslateX(); 760 inverse.mValues[3] = (float)inverseTransform.getScaleX(); 761 inverse.mValues[4] = (float)inverseTransform.getShearY(); 762 inverse.mValues[5] = (float)inverseTransform.getTranslateY(); 763 764 return true; 765 } catch (NoninvertibleTransformException e) { 766 return false; 767 } 768 } 769 770 @Override 771 public boolean invert(_Original_Matrix inverse) { 772 throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); 773 } 774 775 /** 776 * Apply this matrix to the array of 2D points specified by src, and write 777 * the transformed points into the array of points specified by dst. The 778 * two arrays represent their "points" as pairs of floats [x, y]. 779 * 780 * @param dst The array of dst points (x,y pairs) 781 * @param dstIndex The index of the first [x,y] pair of dst floats 782 * @param src The array of src points (x,y pairs) 783 * @param srcIndex The index of the first [x,y] pair of src floats 784 * @param pointCount The number of points (x,y pairs) to transform 785 */ 786 @Override 787 public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, 788 int pointCount) { 789 checkPointArrays(src, srcIndex, dst, dstIndex, pointCount); 790 791 for (int i = 0 ; i < pointCount ; i++) { 792 // just in case we are doing in place, we better put this in temp vars 793 float x = mValues[0] * src[i + srcIndex] + 794 mValues[1] * src[i + srcIndex + 1] + 795 mValues[2]; 796 float y = mValues[3] * src[i + srcIndex] + 797 mValues[4] * src[i + srcIndex + 1] + 798 mValues[5]; 799 800 dst[i + dstIndex] = x; 801 dst[i + dstIndex + 1] = y; 802 } 803 } 804 805 /** 806 * Apply this matrix to the array of 2D vectors specified by src, and write 807 * the transformed vectors into the array of vectors specified by dst. The 808 * two arrays represent their "vectors" as pairs of floats [x, y]. 809 * 810 * @param dst The array of dst vectors (x,y pairs) 811 * @param dstIndex The index of the first [x,y] pair of dst floats 812 * @param src The array of src vectors (x,y pairs) 813 * @param srcIndex The index of the first [x,y] pair of src floats 814 * @param vectorCount The number of vectors (x,y pairs) to transform 815 */ 816 @Override 817 public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, 818 int vectorCount) { 819 checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount); 820 throw new UnsupportedOperationException("STUB NEEDED"); 821 } 822 823 /** 824 * Apply this matrix to the array of 2D points specified by src, and write 825 * the transformed points into the array of points specified by dst. The 826 * two arrays represent their "points" as pairs of floats [x, y]. 827 * 828 * @param dst The array of dst points (x,y pairs) 829 * @param src The array of src points (x,y pairs) 830 */ 831 @Override 832 public void mapPoints(float[] dst, float[] src) { 833 if (dst.length != src.length) { 834 throw new ArrayIndexOutOfBoundsException(); 835 } 836 mapPoints(dst, 0, src, 0, dst.length >> 1); 837 } 838 839 /** 840 * Apply this matrix to the array of 2D vectors specified by src, and write 841 * the transformed vectors into the array of vectors specified by dst. The 842 * two arrays represent their "vectors" as pairs of floats [x, y]. 843 * 844 * @param dst The array of dst vectors (x,y pairs) 845 * @param src The array of src vectors (x,y pairs) 846 */ 847 @Override 848 public void mapVectors(float[] dst, float[] src) { 849 if (dst.length != src.length) { 850 throw new ArrayIndexOutOfBoundsException(); 851 } 852 mapVectors(dst, 0, src, 0, dst.length >> 1); 853 } 854 855 /** 856 * Apply this matrix to the array of 2D points, and write the transformed 857 * points back into the array 858 * 859 * @param pts The array [x0, y0, x1, y1, ...] of points to transform. 860 */ 861 @Override 862 public void mapPoints(float[] pts) { 863 mapPoints(pts, 0, pts, 0, pts.length >> 1); 864 } 865 866 /** 867 * Apply this matrix to the array of 2D vectors, and write the transformed 868 * vectors back into the array. 869 * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform. 870 */ 871 @Override 872 public void mapVectors(float[] vecs) { 873 mapVectors(vecs, 0, vecs, 0, vecs.length >> 1); 874 } 875 876 /** 877 * Apply this matrix to the src rectangle, and write the transformed 878 * rectangle into dst. This is accomplished by transforming the 4 corners of 879 * src, and then setting dst to the bounds of those points. 880 * 881 * @param dst Where the transformed rectangle is written. 882 * @param src The original rectangle to be transformed. 883 * @return the result of calling rectStaysRect() 884 */ 885 @Override 886 public boolean mapRect(RectF dst, RectF src) { 887 if (dst == null || src == null) { 888 throw new NullPointerException(); 889 } 890 891 // array with 4 corners 892 float[] corners = new float[] { 893 src.left, src.top, 894 src.right, src.top, 895 src.right, src.bottom, 896 src.left, src.bottom, 897 }; 898 899 // apply the transform to them. 900 mapPoints(corners); 901 902 // now put the result in the rect. We take the min/max of Xs and min/max of Ys 903 dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6])); 904 dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6])); 905 906 dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7])); 907 dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7])); 908 909 return rectStaysRect(); 910 } 911 912 /** 913 * Apply this matrix to the rectangle, and write the transformed rectangle 914 * back into it. This is accomplished by transforming the 4 corners of rect, 915 * and then setting it to the bounds of those points 916 * 917 * @param rect The rectangle to transform. 918 * @return the result of calling rectStaysRect() 919 */ 920 @Override 921 public boolean mapRect(RectF rect) { 922 return mapRect(rect, rect); 923 } 924 925 /** 926 * Return the mean radius of a circle after it has been mapped by 927 * this matrix. NOTE: in perspective this value assumes the circle 928 * has its center at the origin. 929 */ 930 @Override 931 public float mapRadius(float radius) { 932 throw new UnsupportedOperationException("STUB NEEDED"); 933 } 934 935 /** Copy 9 values from the matrix into the array. 936 */ 937 @Override 938 public void getValues(float[] values) { 939 if (values.length < 9) { 940 throw new ArrayIndexOutOfBoundsException(); 941 } 942 System.arraycopy(mValues, 0, values, 0, mValues.length); 943 } 944 945 /** Copy 9 values from the array into the matrix. 946 Depending on the implementation of Matrix, these may be 947 transformed into 16.16 integers in the Matrix, such that 948 a subsequent call to getValues() will not yield exactly 949 the same values. 950 */ 951 @Override 952 public void setValues(float[] values) { 953 if (values.length < 9) { 954 throw new ArrayIndexOutOfBoundsException(); 955 } 956 System.arraycopy(values, 0, mValues, 0, mValues.length); 957 } 958 959 @SuppressWarnings("unused") 960 private final static int kIdentity_Mask = 0; 961 private final static int kTranslate_Mask = 0x01; //!< set if the matrix has translation 962 private final static int kScale_Mask = 0x02; //!< set if the matrix has X or Y scale 963 private final static int kAffine_Mask = 0x04; //!< set if the matrix skews or rotates 964 private final static int kPerspective_Mask = 0x08; //!< set if the matrix is in perspective 965 private final static int kRectStaysRect_Mask = 0x10; 966 @SuppressWarnings("unused") 967 private final static int kUnknown_Mask = 0x80; 968 969 @SuppressWarnings("unused") 970 private final static int kAllMasks = kTranslate_Mask | 971 kScale_Mask | 972 kAffine_Mask | 973 kPerspective_Mask | 974 kRectStaysRect_Mask; 975 976 // these guys align with the masks, so we can compute a mask from a variable 0/1 977 @SuppressWarnings("unused") 978 private final static int kTranslate_Shift = 0; 979 @SuppressWarnings("unused") 980 private final static int kScale_Shift = 1; 981 @SuppressWarnings("unused") 982 private final static int kAffine_Shift = 2; 983 @SuppressWarnings("unused") 984 private final static int kPerspective_Shift = 3; 985 private final static int kRectStaysRect_Shift = 4; 986 987 private int computeTypeMask() { 988 int mask = 0; 989 990 if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) { 991 mask |= kPerspective_Mask; 992 } 993 994 if (mValues[2] != 0. || mValues[5] != 0.) { 995 mask |= kTranslate_Mask; 996 } 997 998 float m00 = mValues[0]; 999 float m01 = mValues[1]; 1000 float m10 = mValues[3]; 1001 float m11 = mValues[4]; 1002 1003 if (m01 != 0. || m10 != 0.) { 1004 mask |= kAffine_Mask; 1005 } 1006 1007 if (m00 != 1. || m11 != 1.) { 1008 mask |= kScale_Mask; 1009 } 1010 1011 if ((mask & kPerspective_Mask) == 0) { 1012 // map non-zero to 1 1013 int im00 = m00 != 0 ? 1 : 0; 1014 int im01 = m01 != 0 ? 1 : 0; 1015 int im10 = m10 != 0 ? 1 : 0; 1016 int im11 = m11 != 0 ? 1 : 0; 1017 1018 // record if the (p)rimary and (s)econdary diagonals are all 0 or 1019 // all non-zero (answer is 0 or 1) 1020 int dp0 = (im00 | im11) ^ 1; // true if both are 0 1021 int dp1 = im00 & im11; // true if both are 1 1022 int ds0 = (im01 | im10) ^ 1; // true if both are 0 1023 int ds1 = im01 & im10; // true if both are 1 1024 1025 // return 1 if primary is 1 and secondary is 0 or 1026 // primary is 0 and secondary is 1 1027 mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift; 1028 } 1029 1030 return mask; 1031 } 1032 } 1033