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