1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkMatrix44.h" 9#include "Test.h" 10 11static bool nearly_equal_double(double a, double b) { 12 const double tolerance = 1e-7; 13 double diff = a - b; 14 if (diff < 0) 15 diff = -diff; 16 return diff <= tolerance; 17} 18 19static bool nearly_equal_mscalar(SkMScalar a, SkMScalar b) { 20 const SkMScalar tolerance = SK_MScalar1 / 200000; 21 22 return SkTAbs<SkMScalar>(a - b) <= tolerance; 23} 24 25static bool nearly_equal_scalar(SkScalar a, SkScalar b) { 26 const SkScalar tolerance = SK_Scalar1 / 200000; 27 return SkScalarAbs(a - b) <= tolerance; 28} 29 30template <typename T> void assert16(skiatest::Reporter* reporter, const T data[], 31 T m0, T m1, T m2, T m3, 32 T m4, T m5, T m6, T m7, 33 T m8, T m9, T m10, T m11, 34 T m12, T m13, T m14, T m15) { 35 REPORTER_ASSERT(reporter, data[0] == m0); 36 REPORTER_ASSERT(reporter, data[1] == m1); 37 REPORTER_ASSERT(reporter, data[2] == m2); 38 REPORTER_ASSERT(reporter, data[3] == m3); 39 40 REPORTER_ASSERT(reporter, data[4] == m4); 41 REPORTER_ASSERT(reporter, data[5] == m5); 42 REPORTER_ASSERT(reporter, data[6] == m6); 43 REPORTER_ASSERT(reporter, data[7] == m7); 44 45 REPORTER_ASSERT(reporter, data[8] == m8); 46 REPORTER_ASSERT(reporter, data[9] == m9); 47 REPORTER_ASSERT(reporter, data[10] == m10); 48 REPORTER_ASSERT(reporter, data[11] == m11); 49 50 REPORTER_ASSERT(reporter, data[12] == m12); 51 REPORTER_ASSERT(reporter, data[13] == m13); 52 REPORTER_ASSERT(reporter, data[14] == m14); 53 REPORTER_ASSERT(reporter, data[15] == m15); 54} 55 56static bool nearly_equal(const SkMatrix44& a, const SkMatrix44& b) { 57 for (int i = 0; i < 4; ++i) { 58 for (int j = 0; j < 4; ++j) { 59 if (!nearly_equal_mscalar(a.get(i, j), b.get(i, j))) { 60 SkDebugf("not equal %g %g\n", a.get(i, j), b.get(i, j)); 61 return false; 62 } 63 } 64 } 65 return true; 66} 67 68static bool is_identity(const SkMatrix44& m) { 69 SkMatrix44 identity(SkMatrix44::kIdentity_Constructor); 70 return nearly_equal(m, identity); 71} 72 73/////////////////////////////////////////////////////////////////////////////// 74static bool bits_isonly(int value, int mask) { 75 return 0 == (value & ~mask); 76} 77 78static void test_constructor(skiatest::Reporter* reporter) { 79 // Allocate a matrix on the heap 80 SkMatrix44* placeholderMatrix = new SkMatrix44(SkMatrix44::kUninitialized_Constructor); 81 SkAutoTDelete<SkMatrix44> deleteMe(placeholderMatrix); 82 83 for (int row = 0; row < 4; ++row) { 84 for (int col = 0; col < 4; ++col) { 85 placeholderMatrix->setDouble(row, col, row * col); 86 } 87 } 88 89 // Use placement-new syntax to trigger the constructor on top of the heap 90 // address we already initialized. This allows us to check that the 91 // constructor did avoid initializing the matrix contents. 92 SkMatrix44* testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kUninitialized_Constructor); 93 REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix); 94 REPORTER_ASSERT(reporter, !testMatrix->isIdentity()); 95 for (int row = 0; row < 4; ++row) { 96 for (int col = 0; col < 4; ++col) { 97 REPORTER_ASSERT(reporter, nearly_equal_double(row * col, testMatrix->getDouble(row, col))); 98 } 99 } 100 101 // Verify that kIdentity_Constructor really does initialize to an identity matrix. 102 testMatrix = 0; 103 testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kIdentity_Constructor); 104 REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix); 105 REPORTER_ASSERT(reporter, testMatrix->isIdentity()); 106 REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I()); 107 108 // Verify that that constructing from an SkMatrix initializes everything. 109 SkMatrix44 scaleMatrix(SkMatrix44::kUninitialized_Constructor); 110 scaleMatrix.setScale(3, 4, 5); 111 REPORTER_ASSERT(reporter, scaleMatrix.isScale()); 112 testMatrix = new(&scaleMatrix) SkMatrix44(SkMatrix::I()); 113 REPORTER_ASSERT(reporter, testMatrix->isIdentity()); 114 REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I()); 115} 116 117static void test_translate(skiatest::Reporter* reporter) { 118 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 119 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor); 120 121 mat.setTranslate(0, 0, 0); 122 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask)); 123 mat.setTranslate(1, 2, 3); 124 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kTranslate_Mask)); 125 REPORTER_ASSERT(reporter, mat.invert(&inverse)); 126 REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kTranslate_Mask)); 127 128 SkMatrix44 a(SkMatrix44::kUninitialized_Constructor); 129 SkMatrix44 b(SkMatrix44::kUninitialized_Constructor); 130 SkMatrix44 c(SkMatrix44::kUninitialized_Constructor); 131 a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9); 132 b.setTranslate(10, 11, 12); 133 134 c.setConcat(a, b); 135 mat = a; 136 mat.preTranslate(10, 11, 12); 137 REPORTER_ASSERT(reporter, mat == c); 138 139 c.setConcat(b, a); 140 mat = a; 141 mat.postTranslate(10, 11, 12); 142 REPORTER_ASSERT(reporter, mat == c); 143} 144 145static void test_scale(skiatest::Reporter* reporter) { 146 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 147 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor); 148 149 mat.setScale(1, 1, 1); 150 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask)); 151 mat.setScale(1, 2, 3); 152 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kScale_Mask)); 153 REPORTER_ASSERT(reporter, mat.invert(&inverse)); 154 REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kScale_Mask)); 155 156 SkMatrix44 a(SkMatrix44::kUninitialized_Constructor); 157 SkMatrix44 b(SkMatrix44::kUninitialized_Constructor); 158 SkMatrix44 c(SkMatrix44::kUninitialized_Constructor); 159 a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9); 160 b.setScale(10, 11, 12); 161 162 c.setConcat(a, b); 163 mat = a; 164 mat.preScale(10, 11, 12); 165 REPORTER_ASSERT(reporter, mat == c); 166 167 c.setConcat(b, a); 168 mat = a; 169 mat.postScale(10, 11, 12); 170 REPORTER_ASSERT(reporter, mat == c); 171} 172 173static void make_i(SkMatrix44* mat) { mat->setIdentity(); } 174static void make_t(SkMatrix44* mat) { mat->setTranslate(1, 2, 3); } 175static void make_s(SkMatrix44* mat) { mat->setScale(1, 2, 3); } 176static void make_st(SkMatrix44* mat) { 177 mat->setScale(1, 2, 3); 178 mat->postTranslate(1, 2, 3); 179} 180static void make_a(SkMatrix44* mat) { 181 mat->setRotateDegreesAbout(1, 2, 3, 45); 182} 183static void make_p(SkMatrix44* mat) { 184 SkMScalar data[] = { 185 1, 2, 3, 4, 5, 6, 7, 8, 186 1, 2, 3, 4, 5, 6, 7, 8, 187 }; 188 mat->setRowMajor(data); 189} 190 191typedef void (*Make44Proc)(SkMatrix44*); 192 193static const Make44Proc gMakeProcs[] = { 194 make_i, make_t, make_s, make_st, make_a, make_p 195}; 196 197static void test_map2(skiatest::Reporter* reporter, const SkMatrix44& mat) { 198 SkMScalar src2[] = { 1, 2 }; 199 SkMScalar src4[] = { src2[0], src2[1], 0, 1 }; 200 SkMScalar dstA[4], dstB[4]; 201 202 for (int i = 0; i < 4; ++i) { 203 dstA[i] = SkDoubleToMScalar(123456789); 204 dstB[i] = SkDoubleToMScalar(987654321); 205 } 206 207 mat.map2(src2, 1, dstA); 208 mat.mapMScalars(src4, dstB); 209 210 for (int i = 0; i < 4; ++i) { 211 REPORTER_ASSERT(reporter, dstA[i] == dstB[i]); 212 } 213} 214 215static void test_map2(skiatest::Reporter* reporter) { 216 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 217 218 for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProcs); ++i) { 219 gMakeProcs[i](&mat); 220 test_map2(reporter, mat); 221 } 222} 223 224static void test_gettype(skiatest::Reporter* reporter) { 225 SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); 226 227 REPORTER_ASSERT(reporter, matrix.isIdentity()); 228 REPORTER_ASSERT(reporter, SkMatrix44::kIdentity_Mask == matrix.getType()); 229 230 int expectedMask; 231 232 matrix.set(1, 1, 0); 233 expectedMask = SkMatrix44::kScale_Mask; 234 REPORTER_ASSERT(reporter, matrix.getType() == expectedMask); 235 236 matrix.set(0, 3, 1); // translate-x 237 expectedMask |= SkMatrix44::kTranslate_Mask; 238 REPORTER_ASSERT(reporter, matrix.getType() == expectedMask); 239 240 matrix.set(2, 0, 1); 241 expectedMask |= SkMatrix44::kAffine_Mask; 242 REPORTER_ASSERT(reporter, matrix.getType() == expectedMask); 243 244 matrix.set(3, 2, 1); 245 REPORTER_ASSERT(reporter, matrix.getType() & SkMatrix44::kPerspective_Mask); 246 247 // ensure that negative zero is treated as zero 248 SkMScalar dx = 0; 249 SkMScalar dy = 0; 250 SkMScalar dz = 0; 251 matrix.setTranslate(-dx, -dy, -dz); 252 REPORTER_ASSERT(reporter, matrix.isIdentity()); 253 matrix.preTranslate(-dx, -dy, -dz); 254 REPORTER_ASSERT(reporter, matrix.isIdentity()); 255 matrix.postTranslate(-dx, -dy, -dz); 256 REPORTER_ASSERT(reporter, matrix.isIdentity()); 257} 258 259static void test_common_angles(skiatest::Reporter* reporter) { 260 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor); 261 // Test precision of rotation in common cases 262 int common_angles[] = { 0, 90, -90, 180, -180, 270, -270, 360, -360 }; 263 for (int i = 0; i < 9; ++i) { 264 rot.setRotateDegreesAbout(0, 0, -1, SkIntToScalar(common_angles[i])); 265 266 SkMatrix rot3x3 = rot; 267 REPORTER_ASSERT(reporter, rot3x3.rectStaysRect()); 268 } 269} 270 271static void test_concat(skiatest::Reporter* reporter) { 272 int i; 273 SkMatrix44 a(SkMatrix44::kUninitialized_Constructor); 274 SkMatrix44 b(SkMatrix44::kUninitialized_Constructor); 275 SkMatrix44 c(SkMatrix44::kUninitialized_Constructor); 276 SkMatrix44 d(SkMatrix44::kUninitialized_Constructor); 277 278 a.setTranslate(10, 10, 10); 279 b.setScale(2, 2, 2); 280 281 SkScalar src[8] = { 282 0, 0, 0, 1, 283 1, 1, 1, 1 284 }; 285 SkScalar dst[8]; 286 287 c.setConcat(a, b); 288 289 d = a; 290 d.preConcat(b); 291 REPORTER_ASSERT(reporter, d == c); 292 293 c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4); 294 for (i = 0; i < 3; ++i) { 295 REPORTER_ASSERT(reporter, 10 == dst[i]); 296 REPORTER_ASSERT(reporter, 12 == dst[i + 4]); 297 } 298 299 c.setConcat(b, a); 300 301 d = a; 302 d.postConcat(b); 303 REPORTER_ASSERT(reporter, d == c); 304 305 c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4); 306 for (i = 0; i < 3; ++i) { 307 REPORTER_ASSERT(reporter, 20 == dst[i]); 308 REPORTER_ASSERT(reporter, 22 == dst[i + 4]); 309 } 310} 311 312static void test_determinant(skiatest::Reporter* reporter) { 313 SkMatrix44 a(SkMatrix44::kIdentity_Constructor); 314 REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant())); 315 a.set(1, 1, 2); 316 REPORTER_ASSERT(reporter, nearly_equal_double(2, a.determinant())); 317 SkMatrix44 b(SkMatrix44::kUninitialized_Constructor); 318 REPORTER_ASSERT(reporter, a.invert(&b)); 319 REPORTER_ASSERT(reporter, nearly_equal_double(0.5, b.determinant())); 320 SkMatrix44 c = b = a; 321 c.set(0, 1, 4); 322 b.set(1, 0, 4); 323 REPORTER_ASSERT(reporter, 324 nearly_equal_double(a.determinant(), 325 b.determinant())); 326 SkMatrix44 d = a; 327 d.set(0, 0, 8); 328 REPORTER_ASSERT(reporter, nearly_equal_double(16, d.determinant())); 329 330 SkMatrix44 e = a; 331 e.postConcat(d); 332 REPORTER_ASSERT(reporter, nearly_equal_double(32, e.determinant())); 333 e.set(0, 0, 0); 334 REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant())); 335} 336 337static void test_invert(skiatest::Reporter* reporter) { 338 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor); 339 double inverseData[16]; 340 341 SkMatrix44 identity(SkMatrix44::kIdentity_Constructor); 342 identity.invert(&inverse); 343 inverse.asRowMajord(inverseData); 344 assert16<double>(reporter, inverseData, 345 1, 0, 0, 0, 346 0, 1, 0, 0, 347 0, 0, 1, 0, 348 0, 0, 0, 1); 349 350 SkMatrix44 translation(SkMatrix44::kUninitialized_Constructor); 351 translation.setTranslate(2, 3, 4); 352 translation.invert(&inverse); 353 inverse.asRowMajord(inverseData); 354 assert16<double>(reporter, inverseData, 355 1, 0, 0, -2, 356 0, 1, 0, -3, 357 0, 0, 1, -4, 358 0, 0, 0, 1); 359 360 SkMatrix44 scale(SkMatrix44::kUninitialized_Constructor); 361 scale.setScale(2, 4, 8); 362 scale.invert(&inverse); 363 inverse.asRowMajord(inverseData); 364 assert16<double>(reporter, inverseData, 365 0.5, 0, 0, 0, 366 0, 0.25, 0, 0, 367 0, 0, 0.125, 0, 368 0, 0, 0, 1); 369 370 SkMatrix44 scaleTranslation(SkMatrix44::kUninitialized_Constructor); 371 scaleTranslation.setScale(32, 128, 1024); 372 scaleTranslation.preTranslate(2, 3, 4); 373 scaleTranslation.invert(&inverse); 374 inverse.asRowMajord(inverseData); 375 assert16<double>(reporter, inverseData, 376 0.03125, 0, 0, -2, 377 0, 0.0078125, 0, -3, 378 0, 0, 0.0009765625, -4, 379 0, 0, 0, 1); 380 381 SkMatrix44 rotation(SkMatrix44::kUninitialized_Constructor); 382 rotation.setRotateDegreesAbout(0, 0, 1, 90); 383 rotation.invert(&inverse); 384 SkMatrix44 expected(SkMatrix44::kUninitialized_Constructor); 385 double expectedInverseRotation[16] = 386 {0, 1, 0, 0, 387 -1, 0, 0, 0, 388 0, 0, 1, 0, 389 0, 0, 0, 1}; 390 expected.setRowMajord(expectedInverseRotation); 391 REPORTER_ASSERT(reporter, nearly_equal(expected, inverse)); 392 393 SkMatrix44 affine(SkMatrix44::kUninitialized_Constructor); 394 affine.setRotateDegreesAbout(0, 0, 1, 90); 395 affine.preScale(10, 20, 100); 396 affine.preTranslate(2, 3, 4); 397 affine.invert(&inverse); 398 double expectedInverseAffine[16] = 399 {0, 0.1, 0, -2, 400 -0.05, 0, 0, -3, 401 0, 0, 0.01, -4, 402 0, 0, 0, 1}; 403 expected.setRowMajord(expectedInverseAffine); 404 REPORTER_ASSERT(reporter, nearly_equal(expected, inverse)); 405 406 SkMatrix44 perspective(SkMatrix44::kIdentity_Constructor); 407 perspective.setDouble(3, 2, 1.0); 408 perspective.invert(&inverse); 409 double expectedInversePerspective[16] = 410 {1, 0, 0, 0, 411 0, 1, 0, 0, 412 0, 0, 1, 0, 413 0, 0, -1, 1}; 414 expected.setRowMajord(expectedInversePerspective); 415 REPORTER_ASSERT(reporter, nearly_equal(expected, inverse)); 416 417 SkMatrix44 affineAndPerspective(SkMatrix44::kIdentity_Constructor); 418 affineAndPerspective.setDouble(3, 2, 1.0); 419 affineAndPerspective.preScale(10, 20, 100); 420 affineAndPerspective.preTranslate(2, 3, 4); 421 affineAndPerspective.invert(&inverse); 422 double expectedInverseAffineAndPerspective[16] = 423 {0.1, 0, 2, -2, 424 0, 0.05, 3, -3, 425 0, 0, 4.01, -4, 426 0, 0, -1, 1}; 427 expected.setRowMajord(expectedInverseAffineAndPerspective); 428 REPORTER_ASSERT(reporter, nearly_equal(expected, inverse)); 429 430 SkMatrix44 tinyScale(SkMatrix44::kIdentity_Constructor); 431 tinyScale.setDouble(0, 0, 1e-39); 432 REPORTER_ASSERT(reporter, tinyScale.getType() == SkMatrix44::kScale_Mask); 433 REPORTER_ASSERT(reporter, !tinyScale.invert(nullptr)); 434 REPORTER_ASSERT(reporter, !tinyScale.invert(&inverse)); 435 436 SkMatrix44 tinyScaleTranslate(SkMatrix44::kIdentity_Constructor); 437 tinyScaleTranslate.setDouble(0, 0, 1e-38); 438 REPORTER_ASSERT(reporter, tinyScaleTranslate.invert(nullptr)); 439 tinyScaleTranslate.setDouble(0, 3, 10); 440 REPORTER_ASSERT( 441 reporter, tinyScaleTranslate.getType() == 442 (SkMatrix44::kScale_Mask | SkMatrix44::kTranslate_Mask)); 443 REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(nullptr)); 444 REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(&inverse)); 445 446 SkMatrix44 tinyScalePerspective(SkMatrix44::kIdentity_Constructor); 447 tinyScalePerspective.setDouble(0, 0, 1e-39); 448 tinyScalePerspective.setDouble(3, 2, -1); 449 REPORTER_ASSERT(reporter, (tinyScalePerspective.getType() & 450 SkMatrix44::kPerspective_Mask) == 451 SkMatrix44::kPerspective_Mask); 452 REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(nullptr)); 453 REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(&inverse)); 454} 455 456static void test_transpose(skiatest::Reporter* reporter) { 457 SkMatrix44 a(SkMatrix44::kUninitialized_Constructor); 458 SkMatrix44 b(SkMatrix44::kUninitialized_Constructor); 459 460 int i = 0; 461 for (int row = 0; row < 4; ++row) { 462 for (int col = 0; col < 4; ++col) { 463 a.setDouble(row, col, i); 464 b.setDouble(col, row, i++); 465 } 466 } 467 468 a.transpose(); 469 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 470} 471 472static void test_get_set_double(skiatest::Reporter* reporter) { 473 SkMatrix44 a(SkMatrix44::kUninitialized_Constructor); 474 for (int row = 0; row < 4; ++row) { 475 for (int col = 0; col < 4; ++col) { 476 a.setDouble(row, col, 3.141592653589793); 477 REPORTER_ASSERT(reporter, 478 nearly_equal_double(3.141592653589793, 479 a.getDouble(row, col))); 480 a.setDouble(row, col, 0); 481 REPORTER_ASSERT(reporter, 482 nearly_equal_double(0, a.getDouble(row, col))); 483 } 484 } 485} 486 487static void test_set_row_col_major(skiatest::Reporter* reporter) { 488 SkMatrix44 a(SkMatrix44::kUninitialized_Constructor); 489 SkMatrix44 b(SkMatrix44::kUninitialized_Constructor); 490 491 for (int row = 0; row < 4; ++row) { 492 for (int col = 0; col < 4; ++col) { 493 a.setDouble(row, col, row * 4 + col); 494 } 495 } 496 497 double bufferd[16]; 498 float bufferf[16]; 499 a.asColMajord(bufferd); 500 b.setColMajord(bufferd); 501 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 502 b.setRowMajord(bufferd); 503 b.transpose(); 504 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 505 a.asColMajorf(bufferf); 506 b.setColMajorf(bufferf); 507 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 508 b.setRowMajorf(bufferf); 509 b.transpose(); 510 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 511} 512 513static void test_3x3_conversion(skiatest::Reporter* reporter) { 514 SkMScalar values4x4[16] = { 1, 2, 3, 4, 515 5, 6, 7, 8, 516 9, 10, 11, 12, 517 13, 14, 15, 16 }; 518 SkScalar values3x3[9] = { 1, 2, 4, 519 5, 6, 8, 520 13, 14, 16 }; 521 SkMScalar values4x4flattened[16] = { 1, 2, 0, 4, 522 5, 6, 0, 8, 523 0, 0, 1, 0, 524 13, 14, 0, 16 }; 525 SkMatrix44 a44(SkMatrix44::kUninitialized_Constructor); 526 a44.setRowMajor(values4x4); 527 528 SkMatrix a33 = a44; 529 SkMatrix expected33; 530 for (int i = 0; i < 9; i++) expected33[i] = values3x3[i]; 531 REPORTER_ASSERT(reporter, expected33 == a33); 532 533 SkMatrix44 a44flattened = a33; 534 SkMatrix44 expected44flattened(SkMatrix44::kUninitialized_Constructor); 535 expected44flattened.setRowMajor(values4x4flattened); 536 REPORTER_ASSERT(reporter, nearly_equal(a44flattened, expected44flattened)); 537 538 // Test that a point with a Z value of 0 is transformed the same way. 539 SkScalar vec4[4] = { 2, 4, 0, 8 }; 540 SkScalar vec3[3] = { 2, 4, 8 }; 541 542 SkScalar vec4transformed[4]; 543 SkScalar vec3transformed[3]; 544 SkScalar vec4transformed2[4]; 545 a44.mapScalars(vec4, vec4transformed); 546 a33.mapHomogeneousPoints(vec3transformed, vec3, 1); 547 a44flattened.mapScalars(vec4, vec4transformed2); 548 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec3transformed[0])); 549 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec3transformed[1])); 550 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec3transformed[2])); 551 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec4transformed2[0])); 552 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec4transformed2[1])); 553 REPORTER_ASSERT(reporter, !nearly_equal_scalar(vec4transformed[2], vec4transformed2[2])); 554 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec4transformed2[3])); 555} 556 557static void test_has_perspective(skiatest::Reporter* reporter) { 558 SkMatrix44 transform(SkMatrix44::kIdentity_Constructor); 559 560 transform.setDouble(3, 2, -0.1); 561 REPORTER_ASSERT(reporter, transform.hasPerspective()); 562 563 transform.reset(); 564 REPORTER_ASSERT(reporter, !transform.hasPerspective()); 565 566 transform.setDouble(3, 0, -1.0); 567 REPORTER_ASSERT(reporter, transform.hasPerspective()); 568 569 transform.reset(); 570 transform.setDouble(3, 1, -1.0); 571 REPORTER_ASSERT(reporter, transform.hasPerspective()); 572 573 transform.reset(); 574 transform.setDouble(3, 2, -0.3); 575 REPORTER_ASSERT(reporter, transform.hasPerspective()); 576 577 transform.reset(); 578 transform.setDouble(3, 3, 0.5); 579 REPORTER_ASSERT(reporter, transform.hasPerspective()); 580 581 transform.reset(); 582 transform.setDouble(3, 3, 0.0); 583 REPORTER_ASSERT(reporter, transform.hasPerspective()); 584} 585 586static bool is_rectilinear (SkVector4& p1, SkVector4& p2, SkVector4& p3, SkVector4& p4) { 587 return (SkScalarNearlyEqual(p1.fData[0], p2.fData[0]) && 588 SkScalarNearlyEqual(p2.fData[1], p3.fData[1]) && 589 SkScalarNearlyEqual(p3.fData[0], p4.fData[0]) && 590 SkScalarNearlyEqual(p4.fData[1], p1.fData[1])) || 591 (SkScalarNearlyEqual(p1.fData[1], p2.fData[1]) && 592 SkScalarNearlyEqual(p2.fData[0], p3.fData[0]) && 593 SkScalarNearlyEqual(p3.fData[1], p4.fData[1]) && 594 SkScalarNearlyEqual(p4.fData[0], p1.fData[0])); 595} 596 597static SkVector4 mul_with_persp_divide(const SkMatrix44& transform, const SkVector4& target) { 598 SkVector4 result = transform * target; 599 if (result.fData[3] != 0.0f && result.fData[3] != SK_Scalar1) { 600 float wInverse = SK_Scalar1 / result.fData[3]; 601 result.set(result.fData[0] * wInverse, 602 result.fData[1] * wInverse, 603 result.fData[2] * wInverse, 604 SK_Scalar1); 605 } 606 return result; 607} 608 609static bool empirically_preserves_2d_axis_alignment(skiatest::Reporter* reporter, 610 const SkMatrix44& transform) { 611 SkVector4 p1(5.0f, 5.0f, 0.0f); 612 SkVector4 p2(10.0f, 5.0f, 0.0f); 613 SkVector4 p3(10.0f, 20.0f, 0.0f); 614 SkVector4 p4(5.0f, 20.0f, 0.0f); 615 616 REPORTER_ASSERT(reporter, is_rectilinear(p1, p2, p3, p4)); 617 618 p1 = mul_with_persp_divide(transform, p1); 619 p2 = mul_with_persp_divide(transform, p2); 620 p3 = mul_with_persp_divide(transform, p3); 621 p4 = mul_with_persp_divide(transform, p4); 622 623 return is_rectilinear(p1, p2, p3, p4); 624} 625 626static void test(bool expected, skiatest::Reporter* reporter, const SkMatrix44& transform) { 627 if (expected) { 628 REPORTER_ASSERT(reporter, empirically_preserves_2d_axis_alignment(reporter, transform)); 629 REPORTER_ASSERT(reporter, transform.preserves2dAxisAlignment()); 630 } else { 631 REPORTER_ASSERT(reporter, !empirically_preserves_2d_axis_alignment(reporter, transform)); 632 REPORTER_ASSERT(reporter, !transform.preserves2dAxisAlignment()); 633 } 634} 635 636static void test_preserves_2d_axis_alignment(skiatest::Reporter* reporter) { 637 SkMatrix44 transform(SkMatrix44::kUninitialized_Constructor); 638 SkMatrix44 transform2(SkMatrix44::kUninitialized_Constructor); 639 640 static const struct TestCase { 641 SkMScalar a; // row 1, column 1 642 SkMScalar b; // row 1, column 2 643 SkMScalar c; // row 2, column 1 644 SkMScalar d; // row 2, column 2 645 bool expected; 646 } test_cases[] = { 647 { 3.f, 0.f, 648 0.f, 4.f, true }, // basic case 649 { 0.f, 4.f, 650 3.f, 0.f, true }, // rotate by 90 651 { 0.f, 0.f, 652 0.f, 4.f, true }, // degenerate x 653 { 3.f, 0.f, 654 0.f, 0.f, true }, // degenerate y 655 { 0.f, 0.f, 656 3.f, 0.f, true }, // degenerate x + rotate by 90 657 { 0.f, 4.f, 658 0.f, 0.f, true }, // degenerate y + rotate by 90 659 { 3.f, 4.f, 660 0.f, 0.f, false }, 661 { 0.f, 0.f, 662 3.f, 4.f, false }, 663 { 0.f, 3.f, 664 0.f, 4.f, false }, 665 { 3.f, 0.f, 666 4.f, 0.f, false }, 667 { 3.f, 4.f, 668 5.f, 0.f, false }, 669 { 3.f, 4.f, 670 0.f, 5.f, false }, 671 { 3.f, 0.f, 672 4.f, 5.f, false }, 673 { 0.f, 3.f, 674 4.f, 5.f, false }, 675 { 2.f, 3.f, 676 4.f, 5.f, false }, 677 }; 678 679 for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { 680 const TestCase& value = test_cases[i]; 681 transform.setIdentity(); 682 transform.set(0, 0, value.a); 683 transform.set(0, 1, value.b); 684 transform.set(1, 0, value.c); 685 transform.set(1, 1, value.d); 686 687 test(value.expected, reporter, transform); 688 } 689 690 // Try the same test cases again, but this time make sure that other matrix 691 // elements (except perspective) have entries, to test that they are ignored. 692 for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { 693 const TestCase& value = test_cases[i]; 694 transform.setIdentity(); 695 transform.set(0, 0, value.a); 696 transform.set(0, 1, value.b); 697 transform.set(1, 0, value.c); 698 transform.set(1, 1, value.d); 699 700 transform.set(0, 2, 1.f); 701 transform.set(0, 3, 2.f); 702 transform.set(1, 2, 3.f); 703 transform.set(1, 3, 4.f); 704 transform.set(2, 0, 5.f); 705 transform.set(2, 1, 6.f); 706 transform.set(2, 2, 7.f); 707 transform.set(2, 3, 8.f); 708 709 test(value.expected, reporter, transform); 710 } 711 712 // Try the same test cases again, but this time add perspective which is 713 // always assumed to not-preserve axis alignment. 714 for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { 715 const TestCase& value = test_cases[i]; 716 transform.setIdentity(); 717 transform.set(0, 0, value.a); 718 transform.set(0, 1, value.b); 719 transform.set(1, 0, value.c); 720 transform.set(1, 1, value.d); 721 722 transform.set(0, 2, 1.f); 723 transform.set(0, 3, 2.f); 724 transform.set(1, 2, 3.f); 725 transform.set(1, 3, 4.f); 726 transform.set(2, 0, 5.f); 727 transform.set(2, 1, 6.f); 728 transform.set(2, 2, 7.f); 729 transform.set(2, 3, 8.f); 730 transform.set(3, 0, 9.f); 731 transform.set(3, 1, 10.f); 732 transform.set(3, 2, 11.f); 733 transform.set(3, 3, 12.f); 734 735 test(false, reporter, transform); 736 } 737 738 // Try a few more practical situations to check precision 739 // Reuse TestCase (a, b, c, d) as (x, y, z, degrees) axis to rotate about. 740 TestCase rotation_tests[] = { 741 { 0.0, 0.0, 1.0, 90.0, true }, 742 { 0.0, 0.0, 1.0, 180.0, true }, 743 { 0.0, 0.0, 1.0, 270.0, true }, 744 { 0.0, 1.0, 0.0, 90.0, true }, 745 { 1.0, 0.0, 0.0, 90.0, true }, 746 { 0.0, 0.0, 1.0, 45.0, false }, 747 // In 3d these next two are non-preserving, but we're testing in 2d after 748 // orthographic projection, where they are. 749 { 0.0, 1.0, 0.0, 45.0, true }, 750 { 1.0, 0.0, 0.0, 45.0, true }, 751 }; 752 753 for (size_t i = 0; i < sizeof(rotation_tests)/sizeof(TestCase); ++i) { 754 const TestCase& value = rotation_tests[i]; 755 transform.setRotateDegreesAbout(value.a, value.b, value.c, value.d); 756 test(value.expected, reporter, transform); 757 } 758 759 static const struct DoubleRotationCase { 760 SkMScalar x1; 761 SkMScalar y1; 762 SkMScalar z1; 763 SkMScalar degrees1; 764 SkMScalar x2; 765 SkMScalar y2; 766 SkMScalar z2; 767 SkMScalar degrees2; 768 bool expected; 769 } double_rotation_tests[] = { 770 { 0.0, 0.0, 1.0, 90.0, 0.0, 1.0, 0.0, 90.0, true }, 771 { 0.0, 0.0, 1.0, 90.0, 1.0, 0.0, 0.0, 90.0, true }, 772 { 0.0, 1.0, 0.0, 90.0, 0.0, 0.0, 1.0, 90.0, true }, 773 }; 774 775 for (size_t i = 0; i < sizeof(double_rotation_tests)/sizeof(DoubleRotationCase); ++i) { 776 const DoubleRotationCase& value = double_rotation_tests[i]; 777 transform.setRotateDegreesAbout(value.x1, value.y1, value.z1, value.degrees1); 778 transform2.setRotateDegreesAbout(value.x2, value.y2, value.z2, value.degrees2); 779 transform.postConcat(transform2); 780 test(value.expected, reporter, transform); 781 } 782 783 // Perspective cases. 784 transform.setIdentity(); 785 transform.setDouble(3, 2, -0.1); // Perspective depth 10 786 transform2.setRotateDegreesAbout(0.0, 1.0, 0.0, 45.0); 787 transform.preConcat(transform2); 788 test(false, reporter, transform); 789 790 transform.setIdentity(); 791 transform.setDouble(3, 2, -0.1); // Perspective depth 10 792 transform2.setRotateDegreesAbout(0.0, 0.0, 1.0, 90.0); 793 transform.preConcat(transform2); 794 test(true, reporter, transform); 795} 796 797// just want to exercise the various converters for MScalar 798static void test_toint(skiatest::Reporter* reporter) { 799 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 800 mat.setScale(3, 3, 3); 801 802 SkMScalar sum = SkMScalarFloor(mat.get(0, 0)) + 803 SkMScalarRound(mat.get(1, 0)) + 804 SkMScalarCeil(mat.get(2, 0)); 805 int isum = SkMScalarFloorToInt(mat.get(0, 1)) + 806 SkMScalarRoundToInt(mat.get(1, 2)) + 807 SkMScalarCeilToInt(mat.get(2, 3)); 808 REPORTER_ASSERT(reporter, sum >= 0); 809 REPORTER_ASSERT(reporter, isum >= 0); 810 REPORTER_ASSERT(reporter, static_cast<SkMScalar>(isum) == SkIntToMScalar(isum)); 811} 812 813DEF_TEST(Matrix44, reporter) { 814 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 815 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor); 816 SkMatrix44 iden1(SkMatrix44::kUninitialized_Constructor); 817 SkMatrix44 iden2(SkMatrix44::kUninitialized_Constructor); 818 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor); 819 820 mat.setTranslate(1, 1, 1); 821 mat.invert(&inverse); 822 iden1.setConcat(mat, inverse); 823 REPORTER_ASSERT(reporter, is_identity(iden1)); 824 825 mat.setScale(2, 2, 2); 826 mat.invert(&inverse); 827 iden1.setConcat(mat, inverse); 828 REPORTER_ASSERT(reporter, is_identity(iden1)); 829 830 mat.setScale(SK_MScalar1/2, SK_MScalar1/2, SK_MScalar1/2); 831 mat.invert(&inverse); 832 iden1.setConcat(mat, inverse); 833 REPORTER_ASSERT(reporter, is_identity(iden1)); 834 835 mat.setScale(3, 3, 3); 836 rot.setRotateDegreesAbout(0, 0, -1, 90); 837 mat.postConcat(rot); 838 REPORTER_ASSERT(reporter, mat.invert(nullptr)); 839 mat.invert(&inverse); 840 iden1.setConcat(mat, inverse); 841 REPORTER_ASSERT(reporter, is_identity(iden1)); 842 iden2.setConcat(inverse, mat); 843 REPORTER_ASSERT(reporter, is_identity(iden2)); 844 845 // test tiny-valued matrix inverse 846 mat.reset(); 847 auto v = SkDoubleToMScalar(1.0e-12); 848 mat.setScale(v,v,v); 849 rot.setRotateDegreesAbout(0, 0, -1, 90); 850 mat.postConcat(rot); 851 mat.postTranslate(v,v,v); 852 REPORTER_ASSERT(reporter, mat.invert(nullptr)); 853 mat.invert(&inverse); 854 iden1.setConcat(mat, inverse); 855 REPORTER_ASSERT(reporter, is_identity(iden1)); 856 857 // test mixed-valued matrix inverse 858 mat.reset(); 859 mat.setScale(SkDoubleToMScalar(1.0e-2), 860 SkDoubleToMScalar(3.0), 861 SkDoubleToMScalar(1.0e+2)); 862 rot.setRotateDegreesAbout(0, 0, -1, 90); 863 mat.postConcat(rot); 864 mat.postTranslate(SkDoubleToMScalar(1.0e+2), 865 SkDoubleToMScalar(3.0), 866 SkDoubleToMScalar(1.0e-2)); 867 REPORTER_ASSERT(reporter, mat.invert(nullptr)); 868 mat.invert(&inverse); 869 iden1.setConcat(mat, inverse); 870 REPORTER_ASSERT(reporter, is_identity(iden1)); 871 872 // test degenerate matrix 873 mat.reset(); 874 mat.set3x3(1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0); 875 REPORTER_ASSERT(reporter, !mat.invert(nullptr)); 876 877 // test rol/col Major getters 878 { 879 mat.setTranslate(2, 3, 4); 880 float dataf[16]; 881 double datad[16]; 882 883 mat.asColMajorf(dataf); 884 assert16<float>(reporter, dataf, 885 1, 0, 0, 0, 886 0, 1, 0, 0, 887 0, 0, 1, 0, 888 2, 3, 4, 1); 889 mat.asColMajord(datad); 890 assert16<double>(reporter, datad, 1, 0, 0, 0, 891 0, 1, 0, 0, 892 0, 0, 1, 0, 893 2, 3, 4, 1); 894 mat.asRowMajorf(dataf); 895 assert16<float>(reporter, dataf, 1, 0, 0, 2, 896 0, 1, 0, 3, 897 0, 0, 1, 4, 898 0, 0, 0, 1); 899 mat.asRowMajord(datad); 900 assert16<double>(reporter, datad, 1, 0, 0, 2, 901 0, 1, 0, 3, 902 0, 0, 1, 4, 903 0, 0, 0, 1); 904 } 905 906 test_concat(reporter); 907 908 if (false) { // avoid bit rot, suppress warning (working on making this pass) 909 test_common_angles(reporter); 910 } 911 912 test_constructor(reporter); 913 test_gettype(reporter); 914 test_determinant(reporter); 915 test_invert(reporter); 916 test_transpose(reporter); 917 test_get_set_double(reporter); 918 test_set_row_col_major(reporter); 919 test_translate(reporter); 920 test_scale(reporter); 921 test_map2(reporter); 922 test_3x3_conversion(reporter); 923 test_has_perspective(reporter); 924 test_preserves_2d_axis_alignment(reporter); 925 test_toint(reporter); 926} 927