1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.KITKAT; 4 import static android.os.Build.VERSION_CODES.LOLLIPOP; 5 6 import android.graphics.Matrix; 7 import android.graphics.Matrix.ScaleToFit; 8 import android.graphics.PointF; 9 import android.graphics.RectF; 10 import java.awt.geom.AffineTransform; 11 import java.util.ArrayDeque; 12 import java.util.ArrayList; 13 import java.util.Arrays; 14 import java.util.Collections; 15 import java.util.Deque; 16 import java.util.LinkedHashMap; 17 import java.util.List; 18 import java.util.Map; 19 import java.util.Objects; 20 import org.robolectric.annotation.Implementation; 21 import org.robolectric.annotation.Implements; 22 import org.robolectric.shadow.api.Shadow; 23 24 @SuppressWarnings({"UnusedDeclaration"}) 25 @Implements(Matrix.class) 26 public class ShadowMatrix { 27 public static final String TRANSLATE = "translate"; 28 public static final String SCALE = "scale"; 29 public static final String ROTATE = "rotate"; 30 public static final String SINCOS = "sincos"; 31 public static final String SKEW = "skew"; 32 public static final String MATRIX = "matrix"; 33 34 private static final float EPSILON = 1e-3f; 35 36 private final Deque<String> preOps = new ArrayDeque<>(); 37 private final Deque<String> postOps = new ArrayDeque<>(); 38 private final Map<String, String> setOps = new LinkedHashMap<>(); 39 40 private SimpleMatrix simpleMatrix = SimpleMatrix.newIdentityMatrix(); 41 42 @Implementation __constructor__(Matrix src)43 protected void __constructor__(Matrix src) { 44 set(src); 45 } 46 47 /** 48 * A list of all 'pre' operations performed on this Matrix. The last operation performed will 49 * be first in the list. 50 * @return A list of all 'pre' operations performed on this Matrix. 51 */ getPreOperations()52 public List<String> getPreOperations() { 53 return Collections.unmodifiableList(new ArrayList<>(preOps)); 54 } 55 56 /** 57 * A list of all 'post' operations performed on this Matrix. The last operation performed will 58 * be last in the list. 59 * @return A list of all 'post' operations performed on this Matrix. 60 */ getPostOperations()61 public List<String> getPostOperations() { 62 return Collections.unmodifiableList(new ArrayList<>(postOps)); 63 } 64 65 /** 66 * A map of all 'set' operations performed on this Matrix. 67 * @return A map of all 'set' operations performed on this Matrix. 68 */ getSetOperations()69 public Map<String, String> getSetOperations() { 70 return Collections.unmodifiableMap(new LinkedHashMap<>(setOps)); 71 } 72 73 @Implementation isIdentity()74 protected boolean isIdentity() { 75 return simpleMatrix.equals(SimpleMatrix.IDENTITY); 76 } 77 78 @Implementation(minSdk = LOLLIPOP) isAffine()79 protected boolean isAffine() { 80 return simpleMatrix.isAffine(); 81 } 82 83 @Implementation rectStaysRect()84 protected boolean rectStaysRect() { 85 return simpleMatrix.rectStaysRect(); 86 } 87 88 @Implementation getValues(float[] values)89 protected void getValues(float[] values) { 90 simpleMatrix.getValues(values); 91 } 92 93 @Implementation setValues(float[] values)94 protected void setValues(float[] values) { 95 simpleMatrix = new SimpleMatrix(values); 96 } 97 98 @Implementation set(Matrix src)99 protected void set(Matrix src) { 100 reset(); 101 if (src != null) { 102 ShadowMatrix shadowMatrix = Shadow.extract(src); 103 preOps.addAll(shadowMatrix.preOps); 104 postOps.addAll(shadowMatrix.postOps); 105 setOps.putAll(shadowMatrix.setOps); 106 simpleMatrix = new SimpleMatrix(getSimpleMatrix(src)); 107 } 108 } 109 110 @Implementation reset()111 protected void reset() { 112 preOps.clear(); 113 postOps.clear(); 114 setOps.clear(); 115 simpleMatrix = SimpleMatrix.newIdentityMatrix(); 116 } 117 118 @Implementation setTranslate(float dx, float dy)119 protected void setTranslate(float dx, float dy) { 120 setOps.put(TRANSLATE, dx + " " + dy); 121 simpleMatrix = SimpleMatrix.translate(dx, dy); 122 } 123 124 @Implementation setScale(float sx, float sy, float px, float py)125 protected void setScale(float sx, float sy, float px, float py) { 126 setOps.put(SCALE, sx + " " + sy + " " + px + " " + py); 127 simpleMatrix = SimpleMatrix.scale(sx, sy, px, py); 128 } 129 130 @Implementation setScale(float sx, float sy)131 protected void setScale(float sx, float sy) { 132 setOps.put(SCALE, sx + " " + sy); 133 simpleMatrix = SimpleMatrix.scale(sx, sy); 134 } 135 136 @Implementation setRotate(float degrees, float px, float py)137 protected void setRotate(float degrees, float px, float py) { 138 setOps.put(ROTATE, degrees + " " + px + " " + py); 139 simpleMatrix = SimpleMatrix.rotate(degrees, px, py); 140 } 141 142 @Implementation setRotate(float degrees)143 protected void setRotate(float degrees) { 144 setOps.put(ROTATE, Float.toString(degrees)); 145 simpleMatrix = SimpleMatrix.rotate(degrees); 146 } 147 148 @Implementation setSinCos(float sinValue, float cosValue, float px, float py)149 protected void setSinCos(float sinValue, float cosValue, float px, float py) { 150 setOps.put(SINCOS, sinValue + " " + cosValue + " " + px + " " + py); 151 simpleMatrix = SimpleMatrix.sinCos(sinValue, cosValue, px, py); 152 } 153 154 @Implementation setSinCos(float sinValue, float cosValue)155 protected void setSinCos(float sinValue, float cosValue) { 156 setOps.put(SINCOS, sinValue + " " + cosValue); 157 simpleMatrix = SimpleMatrix.sinCos(sinValue, cosValue); 158 } 159 160 @Implementation setSkew(float kx, float ky, float px, float py)161 protected void setSkew(float kx, float ky, float px, float py) { 162 setOps.put(SKEW, kx + " " + ky + " " + px + " " + py); 163 simpleMatrix = SimpleMatrix.skew(kx, ky, px, py); 164 } 165 166 @Implementation setSkew(float kx, float ky)167 protected void setSkew(float kx, float ky) { 168 setOps.put(SKEW, kx + " " + ky); 169 simpleMatrix = SimpleMatrix.skew(kx, ky); 170 } 171 172 @Implementation setConcat(Matrix a, Matrix b)173 protected boolean setConcat(Matrix a, Matrix b) { 174 simpleMatrix = getSimpleMatrix(a).multiply(getSimpleMatrix(b)); 175 return true; 176 } 177 178 @Implementation preTranslate(float dx, float dy)179 protected boolean preTranslate(float dx, float dy) { 180 preOps.addFirst(TRANSLATE + " " + dx + " " + dy); 181 return preConcat(SimpleMatrix.translate(dx, dy)); 182 } 183 184 @Implementation preScale(float sx, float sy, float px, float py)185 protected boolean preScale(float sx, float sy, float px, float py) { 186 preOps.addFirst(SCALE + " " + sx + " " + sy + " " + px + " " + py); 187 return preConcat(SimpleMatrix.scale(sx, sy, px, py)); 188 } 189 190 @Implementation preScale(float sx, float sy)191 protected boolean preScale(float sx, float sy) { 192 preOps.addFirst(SCALE + " " + sx + " " + sy); 193 return preConcat(SimpleMatrix.scale(sx, sy)); 194 } 195 196 @Implementation preRotate(float degrees, float px, float py)197 protected boolean preRotate(float degrees, float px, float py) { 198 preOps.addFirst(ROTATE + " " + degrees + " " + px + " " + py); 199 return preConcat(SimpleMatrix.rotate(degrees, px, py)); 200 } 201 202 @Implementation preRotate(float degrees)203 protected boolean preRotate(float degrees) { 204 preOps.addFirst(ROTATE + " " + Float.toString(degrees)); 205 return preConcat(SimpleMatrix.rotate(degrees)); 206 } 207 208 @Implementation preSkew(float kx, float ky, float px, float py)209 protected boolean preSkew(float kx, float ky, float px, float py) { 210 preOps.addFirst(SKEW + " " + kx + " " + ky + " " + px + " " + py); 211 return preConcat(SimpleMatrix.skew(kx, ky, px, py)); 212 } 213 214 @Implementation preSkew(float kx, float ky)215 protected boolean preSkew(float kx, float ky) { 216 preOps.addFirst(SKEW + " " + kx + " " + ky); 217 return preConcat(SimpleMatrix.skew(kx, ky)); 218 } 219 220 @Implementation preConcat(Matrix other)221 protected boolean preConcat(Matrix other) { 222 preOps.addFirst(MATRIX + " " + other); 223 return preConcat(getSimpleMatrix(other)); 224 } 225 226 @Implementation postTranslate(float dx, float dy)227 protected boolean postTranslate(float dx, float dy) { 228 postOps.addLast(TRANSLATE + " " + dx + " " + dy); 229 return postConcat(SimpleMatrix.translate(dx, dy)); 230 } 231 232 @Implementation postScale(float sx, float sy, float px, float py)233 protected boolean postScale(float sx, float sy, float px, float py) { 234 postOps.addLast(SCALE + " " + sx + " " + sy + " " + px + " " + py); 235 return postConcat(SimpleMatrix.scale(sx, sy, px, py)); 236 } 237 238 @Implementation postScale(float sx, float sy)239 protected boolean postScale(float sx, float sy) { 240 postOps.addLast(SCALE + " " + sx + " " + sy); 241 return postConcat(SimpleMatrix.scale(sx, sy)); 242 } 243 244 @Implementation postRotate(float degrees, float px, float py)245 protected boolean postRotate(float degrees, float px, float py) { 246 postOps.addLast(ROTATE + " " + degrees + " " + px + " " + py); 247 return postConcat(SimpleMatrix.rotate(degrees, px, py)); 248 } 249 250 @Implementation postRotate(float degrees)251 protected boolean postRotate(float degrees) { 252 postOps.addLast(ROTATE + " " + Float.toString(degrees)); 253 return postConcat(SimpleMatrix.rotate(degrees)); 254 } 255 256 @Implementation postSkew(float kx, float ky, float px, float py)257 protected boolean postSkew(float kx, float ky, float px, float py) { 258 postOps.addLast(SKEW + " " + kx + " " + ky + " " + px + " " + py); 259 return postConcat(SimpleMatrix.skew(kx, ky, px, py)); 260 } 261 262 @Implementation postSkew(float kx, float ky)263 protected boolean postSkew(float kx, float ky) { 264 postOps.addLast(SKEW + " " + kx + " " + ky); 265 return postConcat(SimpleMatrix.skew(kx, ky)); 266 } 267 268 @Implementation postConcat(Matrix other)269 protected boolean postConcat(Matrix other) { 270 postOps.addLast(MATRIX + " " + other); 271 return postConcat(getSimpleMatrix(other)); 272 } 273 274 @Implementation invert(Matrix inverse)275 protected boolean invert(Matrix inverse) { 276 final SimpleMatrix inverseMatrix = simpleMatrix.invert(); 277 if (inverseMatrix != null) { 278 if (inverse != null) { 279 final ShadowMatrix shadowInverse = Shadow.extract(inverse); 280 shadowInverse.simpleMatrix = inverseMatrix; 281 } 282 return true; 283 } 284 return false; 285 } 286 hasPerspective()287 boolean hasPerspective() { 288 return (simpleMatrix.mValues[6] != 0 || simpleMatrix.mValues[7] != 0 || simpleMatrix.mValues[8] != 1); 289 } 290 getAffineTransform()291 protected AffineTransform getAffineTransform() { 292 // the AffineTransform constructor takes the value in a different order 293 // for a matrix [ 0 1 2 ] 294 // [ 3 4 5 ] 295 // the order is 0, 3, 1, 4, 2, 5... 296 return new AffineTransform( 297 simpleMatrix.mValues[0], 298 simpleMatrix.mValues[3], 299 simpleMatrix.mValues[1], 300 simpleMatrix.mValues[4], 301 simpleMatrix.mValues[2], 302 simpleMatrix.mValues[5]); 303 } 304 mapPoint(float x, float y)305 public PointF mapPoint(float x, float y) { 306 return simpleMatrix.transform(new PointF(x, y)); 307 } 308 mapPoint(PointF point)309 public PointF mapPoint(PointF point) { 310 return simpleMatrix.transform(point); 311 } 312 313 @Implementation mapRect(RectF destination, RectF source)314 protected boolean mapRect(RectF destination, RectF source) { 315 final PointF leftTop = mapPoint(source.left, source.top); 316 final PointF rightBottom = mapPoint(source.right, source.bottom); 317 destination.set( 318 Math.min(leftTop.x, rightBottom.x), 319 Math.min(leftTop.y, rightBottom.y), 320 Math.max(leftTop.x, rightBottom.x), 321 Math.max(leftTop.y, rightBottom.y)); 322 return true; 323 } 324 325 @Implementation mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount)326 protected void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount) { 327 for (int i = 0; i < pointCount; i++) { 328 final PointF mapped = mapPoint(src[srcIndex + i * 2], src[srcIndex + i * 2 + 1]); 329 dst[dstIndex + i * 2] = mapped.x; 330 dst[dstIndex + i * 2 + 1] = mapped.y; 331 } 332 } 333 334 @Implementation mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount)335 protected void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount) { 336 final float transX = simpleMatrix.mValues[Matrix.MTRANS_X]; 337 final float transY = simpleMatrix.mValues[Matrix.MTRANS_Y]; 338 339 simpleMatrix.mValues[Matrix.MTRANS_X] = 0; 340 simpleMatrix.mValues[Matrix.MTRANS_Y] = 0; 341 342 for (int i = 0; i < vectorCount; i++) { 343 final PointF mapped = mapPoint(src[srcIndex + i * 2], src[srcIndex + i * 2 + 1]); 344 dst[dstIndex + i * 2] = mapped.x; 345 dst[dstIndex + i * 2 + 1] = mapped.y; 346 } 347 348 simpleMatrix.mValues[Matrix.MTRANS_X] = transX; 349 simpleMatrix.mValues[Matrix.MTRANS_Y] = transY; 350 } 351 352 @Implementation mapRadius(float radius)353 protected float mapRadius(float radius) { 354 float[] src = new float[] {radius, 0.f, 0.f, radius}; 355 mapVectors(src, 0, src, 0, 2); 356 357 float l1 = (float) Math.hypot(src[0], src[1]); 358 float l2 = (float) Math.hypot(src[2], src[3]); 359 return (float) Math.sqrt(l1 * l2); 360 } 361 362 @Implementation setRectToRect(RectF src, RectF dst, Matrix.ScaleToFit stf)363 protected boolean setRectToRect(RectF src, RectF dst, Matrix.ScaleToFit stf) { 364 if (src.isEmpty()) { 365 reset(); 366 return false; 367 } 368 return simpleMatrix.setRectToRect(src, dst, stf); 369 } 370 371 @Implementation 372 @Override equals(Object obj)373 public boolean equals(Object obj) { 374 if (obj instanceof Matrix) { 375 return getSimpleMatrix(((Matrix) obj)).equals(simpleMatrix); 376 } else { 377 return obj instanceof ShadowMatrix && obj.equals(simpleMatrix); 378 } 379 } 380 381 @Implementation(minSdk = KITKAT) 382 @Override hashCode()383 public int hashCode() { 384 return Objects.hashCode(simpleMatrix); 385 } 386 getDescription()387 public String getDescription() { 388 return "Matrix[pre=" + preOps + ", set=" + setOps + ", post=" + postOps + "]"; 389 } 390 getSimpleMatrix(Matrix matrix)391 private static SimpleMatrix getSimpleMatrix(Matrix matrix) { 392 final ShadowMatrix otherMatrix = Shadow.extract(matrix); 393 return otherMatrix.simpleMatrix; 394 } 395 postConcat(SimpleMatrix matrix)396 private boolean postConcat(SimpleMatrix matrix) { 397 simpleMatrix = matrix.multiply(simpleMatrix); 398 return true; 399 } 400 preConcat(SimpleMatrix matrix)401 private boolean preConcat(SimpleMatrix matrix) { 402 simpleMatrix = simpleMatrix.multiply(matrix); 403 return true; 404 } 405 406 /** 407 * A simple implementation of an immutable matrix. 408 */ 409 private static class SimpleMatrix { 410 private static final SimpleMatrix IDENTITY = newIdentityMatrix(); 411 newIdentityMatrix()412 private static SimpleMatrix newIdentityMatrix() { 413 return new SimpleMatrix( 414 new float[] { 415 1.0f, 0.0f, 0.0f, 416 0.0f, 1.0f, 0.0f, 417 0.0f, 0.0f, 1.0f, 418 }); 419 } 420 421 private final float[] mValues; 422 SimpleMatrix(SimpleMatrix matrix)423 SimpleMatrix(SimpleMatrix matrix) { 424 mValues = Arrays.copyOf(matrix.mValues, matrix.mValues.length); 425 } 426 SimpleMatrix(float[] values)427 private SimpleMatrix(float[] values) { 428 if (values.length != 9) { 429 throw new ArrayIndexOutOfBoundsException(); 430 } 431 mValues = Arrays.copyOf(values, 9); 432 } 433 isAffine()434 public boolean isAffine() { 435 return mValues[6] == 0.0f && mValues[7] == 0.0f && mValues[8] == 1.0f; 436 } 437 rectStaysRect()438 public boolean rectStaysRect() { 439 final float m00 = mValues[0]; 440 final float m01 = mValues[1]; 441 final float m10 = mValues[3]; 442 final float m11 = mValues[4]; 443 return (m00 == 0 && m11 == 0 && m01 != 0 && m10 != 0) || (m00 != 0 && m11 != 0 && m01 == 0 && m10 == 0); 444 } 445 getValues(float[] values)446 public void getValues(float[] values) { 447 if (values.length < 9) { 448 throw new ArrayIndexOutOfBoundsException(); 449 } 450 System.arraycopy(mValues, 0, values, 0, 9); 451 } 452 translate(float dx, float dy)453 public static SimpleMatrix translate(float dx, float dy) { 454 return new SimpleMatrix(new float[] { 455 1.0f, 0.0f, dx, 456 0.0f, 1.0f, dy, 457 0.0f, 0.0f, 1.0f, 458 }); 459 } 460 scale(float sx, float sy, float px, float py)461 public static SimpleMatrix scale(float sx, float sy, float px, float py) { 462 return new SimpleMatrix(new float[] { 463 sx, 0.0f, px * (1 - sx), 464 0.0f, sy, py * (1 - sy), 465 0.0f, 0.0f, 1.0f, 466 }); 467 } 468 scale(float sx, float sy)469 public static SimpleMatrix scale(float sx, float sy) { 470 return new SimpleMatrix(new float[] { 471 sx, 0.0f, 0.0f, 472 0.0f, sy, 0.0f, 473 0.0f, 0.0f, 1.0f, 474 }); 475 } 476 rotate(float degrees, float px, float py)477 public static SimpleMatrix rotate(float degrees, float px, float py) { 478 final double radians = Math.toRadians(degrees); 479 final float sin = (float) Math.sin(radians); 480 final float cos = (float) Math.cos(radians); 481 return sinCos(sin, cos, px, py); 482 } 483 rotate(float degrees)484 public static SimpleMatrix rotate(float degrees) { 485 final double radians = Math.toRadians(degrees); 486 final float sin = (float) Math.sin(radians); 487 final float cos = (float) Math.cos(radians); 488 return sinCos(sin, cos); 489 } 490 sinCos(float sin, float cos, float px, float py)491 public static SimpleMatrix sinCos(float sin, float cos, float px, float py) { 492 return new SimpleMatrix(new float[] { 493 cos, -sin, sin * py + (1 - cos) * px, 494 sin, cos, -sin * px + (1 - cos) * py, 495 0.0f, 0.0f, 1.0f, 496 }); 497 } 498 sinCos(float sin, float cos)499 public static SimpleMatrix sinCos(float sin, float cos) { 500 return new SimpleMatrix(new float[] { 501 cos, -sin, 0.0f, 502 sin, cos, 0.0f, 503 0.0f, 0.0f, 1.0f, 504 }); 505 } 506 skew(float kx, float ky, float px, float py)507 public static SimpleMatrix skew(float kx, float ky, float px, float py) { 508 return new SimpleMatrix(new float[] { 509 1.0f, kx, -kx * py, 510 ky, 1.0f, -ky * px, 511 0.0f, 0.0f, 1.0f, 512 }); 513 } 514 skew(float kx, float ky)515 public static SimpleMatrix skew(float kx, float ky) { 516 return new SimpleMatrix(new float[] { 517 1.0f, kx, 0.0f, 518 ky, 1.0f, 0.0f, 519 0.0f, 0.0f, 1.0f, 520 }); 521 } 522 multiply(SimpleMatrix matrix)523 public SimpleMatrix multiply(SimpleMatrix matrix) { 524 final float[] values = new float[9]; 525 for (int i = 0; i < values.length; ++i) { 526 final int row = i / 3; 527 final int col = i % 3; 528 for (int j = 0; j < 3; ++j) { 529 values[i] += mValues[row * 3 + j] * matrix.mValues[j * 3 + col]; 530 } 531 } 532 return new SimpleMatrix(values); 533 } 534 invert()535 public SimpleMatrix invert() { 536 final float invDet = inverseDeterminant(); 537 if (invDet == 0) { 538 return null; 539 } 540 541 final float[] src = mValues; 542 final float[] dst = new float[9]; 543 dst[0] = cross_scale(src[4], src[8], src[5], src[7], invDet); 544 dst[1] = cross_scale(src[2], src[7], src[1], src[8], invDet); 545 dst[2] = cross_scale(src[1], src[5], src[2], src[4], invDet); 546 547 dst[3] = cross_scale(src[5], src[6], src[3], src[8], invDet); 548 dst[4] = cross_scale(src[0], src[8], src[2], src[6], invDet); 549 dst[5] = cross_scale(src[2], src[3], src[0], src[5], invDet); 550 551 dst[6] = cross_scale(src[3], src[7], src[4], src[6], invDet); 552 dst[7] = cross_scale(src[1], src[6], src[0], src[7], invDet); 553 dst[8] = cross_scale(src[0], src[4], src[1], src[3], invDet); 554 return new SimpleMatrix(dst); 555 } 556 transform(PointF point)557 public PointF transform(PointF point) { 558 return new PointF( 559 point.x * mValues[0] + point.y * mValues[1] + mValues[2], 560 point.x * mValues[3] + point.y * mValues[4] + mValues[5]); 561 } 562 563 // See: https://android.googlesource.com/platform/frameworks/base/+/6fca81de9b2079ec88e785f58bf49bf1f0c105e2/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java setRectToRect(RectF src, RectF dst, ScaleToFit stf)564 protected boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) { 565 if (dst.isEmpty()) { 566 mValues[0] = 567 mValues[1] = 568 mValues[2] = mValues[3] = mValues[4] = mValues[5] = mValues[6] = mValues[7] = 0; 569 mValues[8] = 1; 570 } else { 571 float tx = dst.width() / src.width(); 572 float sx = dst.width() / src.width(); 573 float ty = dst.height() / src.height(); 574 float sy = dst.height() / src.height(); 575 boolean xLarger = false; 576 577 if (stf != ScaleToFit.FILL) { 578 if (sx > sy) { 579 xLarger = true; 580 sx = sy; 581 } else { 582 sy = sx; 583 } 584 } 585 586 tx = dst.left - src.left * sx; 587 ty = dst.top - src.top * sy; 588 if (stf == ScaleToFit.CENTER || stf == ScaleToFit.END) { 589 float diff; 590 591 if (xLarger) { 592 diff = dst.width() - src.width() * sy; 593 } else { 594 diff = dst.height() - src.height() * sy; 595 } 596 597 if (stf == ScaleToFit.CENTER) { 598 diff = diff / 2; 599 } 600 601 if (xLarger) { 602 tx += diff; 603 } else { 604 ty += diff; 605 } 606 } 607 608 mValues[0] = sx; 609 mValues[4] = sy; 610 mValues[2] = tx; 611 mValues[5] = ty; 612 mValues[1] = mValues[3] = mValues[6] = mValues[7] = 0; 613 } 614 // shared cleanup 615 mValues[8] = 1; 616 return true; 617 } 618 619 @Override equals(Object o)620 public boolean equals(Object o) { 621 return this == o || (o instanceof SimpleMatrix && equals((SimpleMatrix) o)); 622 } 623 624 @SuppressWarnings("NonOverridingEquals") equals(SimpleMatrix matrix)625 public boolean equals(SimpleMatrix matrix) { 626 if (matrix == null) { 627 return false; 628 } 629 for (int i = 0; i < mValues.length; i++) { 630 if (!isNearlyZero(matrix.mValues[i] - mValues[i])) { 631 return false; 632 } 633 } 634 return true; 635 } 636 637 @Override hashCode()638 public int hashCode() { 639 return Arrays.hashCode(mValues); 640 } 641 isNearlyZero(float value)642 private static boolean isNearlyZero(float value) { 643 return Math.abs(value) < EPSILON; 644 } 645 cross(float a, float b, float c, float d)646 private static float cross(float a, float b, float c, float d) { 647 return a * b - c * d; 648 } 649 cross_scale(float a, float b, float c, float d, float scale)650 private static float cross_scale(float a, float b, float c, float d, float scale) { 651 return cross(a, b, c, d) * scale; 652 } 653 inverseDeterminant()654 private float inverseDeterminant() { 655 final float determinant = mValues[0] * cross(mValues[4], mValues[8], mValues[5], mValues[7]) + 656 mValues[1] * cross(mValues[5], mValues[6], mValues[3], mValues[8]) + 657 mValues[2] * cross(mValues[3], mValues[7], mValues[4], mValues[6]); 658 return isNearlyZero(determinant) ? 0.0f : 1.0f / determinant; 659 } 660 } 661 } 662