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