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 std::unique_ptr<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_3x3(skiatest::Reporter* r) { 488 static float vals[9] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, }; 489 490 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 491 mat.set3x3RowMajorf(vals); 492 493 REPORTER_ASSERT(r, 1.0f == mat.getFloat(0, 0)); 494 REPORTER_ASSERT(r, 2.0f == mat.getFloat(0, 1)); 495 REPORTER_ASSERT(r, 3.0f == mat.getFloat(0, 2)); 496 REPORTER_ASSERT(r, 4.0f == mat.getFloat(1, 0)); 497 REPORTER_ASSERT(r, 5.0f == mat.getFloat(1, 1)); 498 REPORTER_ASSERT(r, 6.0f == mat.getFloat(1, 2)); 499 REPORTER_ASSERT(r, 7.0f == mat.getFloat(2, 0)); 500 REPORTER_ASSERT(r, 8.0f == mat.getFloat(2, 1)); 501 REPORTER_ASSERT(r, 9.0f == mat.getFloat(2, 2)); 502} 503 504static void test_set_row_col_major(skiatest::Reporter* reporter) { 505 SkMatrix44 a(SkMatrix44::kUninitialized_Constructor); 506 SkMatrix44 b(SkMatrix44::kUninitialized_Constructor); 507 508 for (int row = 0; row < 4; ++row) { 509 for (int col = 0; col < 4; ++col) { 510 a.setDouble(row, col, row * 4 + col); 511 } 512 } 513 514 double bufferd[16]; 515 float bufferf[16]; 516 a.asColMajord(bufferd); 517 b.setColMajord(bufferd); 518 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 519 b.setRowMajord(bufferd); 520 b.transpose(); 521 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 522 a.asColMajorf(bufferf); 523 b.setColMajorf(bufferf); 524 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 525 b.setRowMajorf(bufferf); 526 b.transpose(); 527 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 528} 529 530static void test_3x3_conversion(skiatest::Reporter* reporter) { 531 SkMScalar values4x4[16] = { 1, 2, 3, 4, 532 5, 6, 7, 8, 533 9, 10, 11, 12, 534 13, 14, 15, 16 }; 535 SkScalar values3x3[9] = { 1, 2, 4, 536 5, 6, 8, 537 13, 14, 16 }; 538 SkMScalar values4x4flattened[16] = { 1, 2, 0, 4, 539 5, 6, 0, 8, 540 0, 0, 1, 0, 541 13, 14, 0, 16 }; 542 SkMatrix44 a44(SkMatrix44::kUninitialized_Constructor); 543 a44.setRowMajor(values4x4); 544 545 SkMatrix a33 = a44; 546 SkMatrix expected33; 547 for (int i = 0; i < 9; i++) expected33[i] = values3x3[i]; 548 REPORTER_ASSERT(reporter, expected33 == a33); 549 550 SkMatrix44 a44flattened = a33; 551 SkMatrix44 expected44flattened(SkMatrix44::kUninitialized_Constructor); 552 expected44flattened.setRowMajor(values4x4flattened); 553 REPORTER_ASSERT(reporter, nearly_equal(a44flattened, expected44flattened)); 554 555 // Test that a point with a Z value of 0 is transformed the same way. 556 SkScalar vec4[4] = { 2, 4, 0, 8 }; 557 SkScalar vec3[3] = { 2, 4, 8 }; 558 559 SkScalar vec4transformed[4]; 560 SkScalar vec3transformed[3]; 561 SkScalar vec4transformed2[4]; 562 a44.mapScalars(vec4, vec4transformed); 563 a33.mapHomogeneousPoints(vec3transformed, vec3, 1); 564 a44flattened.mapScalars(vec4, vec4transformed2); 565 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec3transformed[0])); 566 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec3transformed[1])); 567 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec3transformed[2])); 568 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec4transformed2[0])); 569 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec4transformed2[1])); 570 REPORTER_ASSERT(reporter, !nearly_equal_scalar(vec4transformed[2], vec4transformed2[2])); 571 REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec4transformed2[3])); 572} 573 574static void test_has_perspective(skiatest::Reporter* reporter) { 575 SkMatrix44 transform(SkMatrix44::kIdentity_Constructor); 576 577 transform.setDouble(3, 2, -0.1); 578 REPORTER_ASSERT(reporter, transform.hasPerspective()); 579 580 transform.reset(); 581 REPORTER_ASSERT(reporter, !transform.hasPerspective()); 582 583 transform.setDouble(3, 0, -1.0); 584 REPORTER_ASSERT(reporter, transform.hasPerspective()); 585 586 transform.reset(); 587 transform.setDouble(3, 1, -1.0); 588 REPORTER_ASSERT(reporter, transform.hasPerspective()); 589 590 transform.reset(); 591 transform.setDouble(3, 2, -0.3); 592 REPORTER_ASSERT(reporter, transform.hasPerspective()); 593 594 transform.reset(); 595 transform.setDouble(3, 3, 0.5); 596 REPORTER_ASSERT(reporter, transform.hasPerspective()); 597 598 transform.reset(); 599 transform.setDouble(3, 3, 0.0); 600 REPORTER_ASSERT(reporter, transform.hasPerspective()); 601} 602 603static bool is_rectilinear (SkVector4& p1, SkVector4& p2, SkVector4& p3, SkVector4& p4) { 604 return (SkScalarNearlyEqual(p1.fData[0], p2.fData[0]) && 605 SkScalarNearlyEqual(p2.fData[1], p3.fData[1]) && 606 SkScalarNearlyEqual(p3.fData[0], p4.fData[0]) && 607 SkScalarNearlyEqual(p4.fData[1], p1.fData[1])) || 608 (SkScalarNearlyEqual(p1.fData[1], p2.fData[1]) && 609 SkScalarNearlyEqual(p2.fData[0], p3.fData[0]) && 610 SkScalarNearlyEqual(p3.fData[1], p4.fData[1]) && 611 SkScalarNearlyEqual(p4.fData[0], p1.fData[0])); 612} 613 614static SkVector4 mul_with_persp_divide(const SkMatrix44& transform, const SkVector4& target) { 615 SkVector4 result = transform * target; 616 if (result.fData[3] != 0.0f && result.fData[3] != SK_Scalar1) { 617 float wInverse = SK_Scalar1 / result.fData[3]; 618 result.set(result.fData[0] * wInverse, 619 result.fData[1] * wInverse, 620 result.fData[2] * wInverse, 621 SK_Scalar1); 622 } 623 return result; 624} 625 626static bool empirically_preserves_2d_axis_alignment(skiatest::Reporter* reporter, 627 const SkMatrix44& transform) { 628 SkVector4 p1(5.0f, 5.0f, 0.0f); 629 SkVector4 p2(10.0f, 5.0f, 0.0f); 630 SkVector4 p3(10.0f, 20.0f, 0.0f); 631 SkVector4 p4(5.0f, 20.0f, 0.0f); 632 633 REPORTER_ASSERT(reporter, is_rectilinear(p1, p2, p3, p4)); 634 635 p1 = mul_with_persp_divide(transform, p1); 636 p2 = mul_with_persp_divide(transform, p2); 637 p3 = mul_with_persp_divide(transform, p3); 638 p4 = mul_with_persp_divide(transform, p4); 639 640 return is_rectilinear(p1, p2, p3, p4); 641} 642 643static void test(bool expected, skiatest::Reporter* reporter, const SkMatrix44& transform) { 644 if (expected) { 645 REPORTER_ASSERT(reporter, empirically_preserves_2d_axis_alignment(reporter, transform)); 646 REPORTER_ASSERT(reporter, transform.preserves2dAxisAlignment()); 647 } else { 648 REPORTER_ASSERT(reporter, !empirically_preserves_2d_axis_alignment(reporter, transform)); 649 REPORTER_ASSERT(reporter, !transform.preserves2dAxisAlignment()); 650 } 651} 652 653static void test_preserves_2d_axis_alignment(skiatest::Reporter* reporter) { 654 SkMatrix44 transform(SkMatrix44::kUninitialized_Constructor); 655 SkMatrix44 transform2(SkMatrix44::kUninitialized_Constructor); 656 657 static const struct TestCase { 658 SkMScalar a; // row 1, column 1 659 SkMScalar b; // row 1, column 2 660 SkMScalar c; // row 2, column 1 661 SkMScalar d; // row 2, column 2 662 bool expected; 663 } test_cases[] = { 664 { 3.f, 0.f, 665 0.f, 4.f, true }, // basic case 666 { 0.f, 4.f, 667 3.f, 0.f, true }, // rotate by 90 668 { 0.f, 0.f, 669 0.f, 4.f, true }, // degenerate x 670 { 3.f, 0.f, 671 0.f, 0.f, true }, // degenerate y 672 { 0.f, 0.f, 673 3.f, 0.f, true }, // degenerate x + rotate by 90 674 { 0.f, 4.f, 675 0.f, 0.f, true }, // degenerate y + rotate by 90 676 { 3.f, 4.f, 677 0.f, 0.f, false }, 678 { 0.f, 0.f, 679 3.f, 4.f, false }, 680 { 0.f, 3.f, 681 0.f, 4.f, false }, 682 { 3.f, 0.f, 683 4.f, 0.f, false }, 684 { 3.f, 4.f, 685 5.f, 0.f, false }, 686 { 3.f, 4.f, 687 0.f, 5.f, false }, 688 { 3.f, 0.f, 689 4.f, 5.f, false }, 690 { 0.f, 3.f, 691 4.f, 5.f, false }, 692 { 2.f, 3.f, 693 4.f, 5.f, false }, 694 }; 695 696 for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { 697 const TestCase& value = test_cases[i]; 698 transform.setIdentity(); 699 transform.set(0, 0, value.a); 700 transform.set(0, 1, value.b); 701 transform.set(1, 0, value.c); 702 transform.set(1, 1, value.d); 703 704 test(value.expected, reporter, transform); 705 } 706 707 // Try the same test cases again, but this time make sure that other matrix 708 // elements (except perspective) have entries, to test that they are ignored. 709 for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { 710 const TestCase& value = test_cases[i]; 711 transform.setIdentity(); 712 transform.set(0, 0, value.a); 713 transform.set(0, 1, value.b); 714 transform.set(1, 0, value.c); 715 transform.set(1, 1, value.d); 716 717 transform.set(0, 2, 1.f); 718 transform.set(0, 3, 2.f); 719 transform.set(1, 2, 3.f); 720 transform.set(1, 3, 4.f); 721 transform.set(2, 0, 5.f); 722 transform.set(2, 1, 6.f); 723 transform.set(2, 2, 7.f); 724 transform.set(2, 3, 8.f); 725 726 test(value.expected, reporter, transform); 727 } 728 729 // Try the same test cases again, but this time add perspective which is 730 // always assumed to not-preserve axis alignment. 731 for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { 732 const TestCase& value = test_cases[i]; 733 transform.setIdentity(); 734 transform.set(0, 0, value.a); 735 transform.set(0, 1, value.b); 736 transform.set(1, 0, value.c); 737 transform.set(1, 1, value.d); 738 739 transform.set(0, 2, 1.f); 740 transform.set(0, 3, 2.f); 741 transform.set(1, 2, 3.f); 742 transform.set(1, 3, 4.f); 743 transform.set(2, 0, 5.f); 744 transform.set(2, 1, 6.f); 745 transform.set(2, 2, 7.f); 746 transform.set(2, 3, 8.f); 747 transform.set(3, 0, 9.f); 748 transform.set(3, 1, 10.f); 749 transform.set(3, 2, 11.f); 750 transform.set(3, 3, 12.f); 751 752 test(false, reporter, transform); 753 } 754 755 // Try a few more practical situations to check precision 756 // Reuse TestCase (a, b, c, d) as (x, y, z, degrees) axis to rotate about. 757 TestCase rotation_tests[] = { 758 { 0.0, 0.0, 1.0, 90.0, true }, 759 { 0.0, 0.0, 1.0, 180.0, true }, 760 { 0.0, 0.0, 1.0, 270.0, true }, 761 { 0.0, 1.0, 0.0, 90.0, true }, 762 { 1.0, 0.0, 0.0, 90.0, true }, 763 { 0.0, 0.0, 1.0, 45.0, false }, 764 // In 3d these next two are non-preserving, but we're testing in 2d after 765 // orthographic projection, where they are. 766 { 0.0, 1.0, 0.0, 45.0, true }, 767 { 1.0, 0.0, 0.0, 45.0, true }, 768 }; 769 770 for (size_t i = 0; i < sizeof(rotation_tests)/sizeof(TestCase); ++i) { 771 const TestCase& value = rotation_tests[i]; 772 transform.setRotateDegreesAbout(value.a, value.b, value.c, value.d); 773 test(value.expected, reporter, transform); 774 } 775 776 static const struct DoubleRotationCase { 777 SkMScalar x1; 778 SkMScalar y1; 779 SkMScalar z1; 780 SkMScalar degrees1; 781 SkMScalar x2; 782 SkMScalar y2; 783 SkMScalar z2; 784 SkMScalar degrees2; 785 bool expected; 786 } double_rotation_tests[] = { 787 { 0.0, 0.0, 1.0, 90.0, 0.0, 1.0, 0.0, 90.0, true }, 788 { 0.0, 0.0, 1.0, 90.0, 1.0, 0.0, 0.0, 90.0, true }, 789 { 0.0, 1.0, 0.0, 90.0, 0.0, 0.0, 1.0, 90.0, true }, 790 }; 791 792 for (size_t i = 0; i < sizeof(double_rotation_tests)/sizeof(DoubleRotationCase); ++i) { 793 const DoubleRotationCase& value = double_rotation_tests[i]; 794 transform.setRotateDegreesAbout(value.x1, value.y1, value.z1, value.degrees1); 795 transform2.setRotateDegreesAbout(value.x2, value.y2, value.z2, value.degrees2); 796 transform.postConcat(transform2); 797 test(value.expected, reporter, transform); 798 } 799 800 // Perspective cases. 801 transform.setIdentity(); 802 transform.setDouble(3, 2, -0.1); // Perspective depth 10 803 transform2.setRotateDegreesAbout(0.0, 1.0, 0.0, 45.0); 804 transform.preConcat(transform2); 805 test(false, reporter, transform); 806 807 transform.setIdentity(); 808 transform.setDouble(3, 2, -0.1); // Perspective depth 10 809 transform2.setRotateDegreesAbout(0.0, 0.0, 1.0, 90.0); 810 transform.preConcat(transform2); 811 test(true, reporter, transform); 812} 813 814// just want to exercise the various converters for MScalar 815static void test_toint(skiatest::Reporter* reporter) { 816 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 817 mat.setScale(3, 3, 3); 818 819 SkMScalar sum = SkMScalarFloor(mat.get(0, 0)) + 820 SkMScalarRound(mat.get(1, 0)) + 821 SkMScalarCeil(mat.get(2, 0)); 822 int isum = SkMScalarFloorToInt(mat.get(0, 1)) + 823 SkMScalarRoundToInt(mat.get(1, 2)) + 824 SkMScalarCeilToInt(mat.get(2, 3)); 825 REPORTER_ASSERT(reporter, sum >= 0); 826 REPORTER_ASSERT(reporter, isum >= 0); 827 REPORTER_ASSERT(reporter, static_cast<SkMScalar>(isum) == SkIntToMScalar(isum)); 828} 829 830DEF_TEST(Matrix44, reporter) { 831 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 832 SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor); 833 SkMatrix44 iden1(SkMatrix44::kUninitialized_Constructor); 834 SkMatrix44 iden2(SkMatrix44::kUninitialized_Constructor); 835 SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor); 836 837 mat.setTranslate(1, 1, 1); 838 mat.invert(&inverse); 839 iden1.setConcat(mat, inverse); 840 REPORTER_ASSERT(reporter, is_identity(iden1)); 841 842 mat.setScale(2, 2, 2); 843 mat.invert(&inverse); 844 iden1.setConcat(mat, inverse); 845 REPORTER_ASSERT(reporter, is_identity(iden1)); 846 847 mat.setScale(SK_MScalar1/2, SK_MScalar1/2, SK_MScalar1/2); 848 mat.invert(&inverse); 849 iden1.setConcat(mat, inverse); 850 REPORTER_ASSERT(reporter, is_identity(iden1)); 851 852 mat.setScale(3, 3, 3); 853 rot.setRotateDegreesAbout(0, 0, -1, 90); 854 mat.postConcat(rot); 855 REPORTER_ASSERT(reporter, mat.invert(nullptr)); 856 mat.invert(&inverse); 857 iden1.setConcat(mat, inverse); 858 REPORTER_ASSERT(reporter, is_identity(iden1)); 859 iden2.setConcat(inverse, mat); 860 REPORTER_ASSERT(reporter, is_identity(iden2)); 861 862 // test tiny-valued matrix inverse 863 mat.reset(); 864 auto v = SkDoubleToMScalar(1.0e-12); 865 mat.setScale(v,v,v); 866 rot.setRotateDegreesAbout(0, 0, -1, 90); 867 mat.postConcat(rot); 868 mat.postTranslate(v,v,v); 869 REPORTER_ASSERT(reporter, mat.invert(nullptr)); 870 mat.invert(&inverse); 871 iden1.setConcat(mat, inverse); 872 REPORTER_ASSERT(reporter, is_identity(iden1)); 873 874 // test mixed-valued matrix inverse 875 mat.reset(); 876 mat.setScale(SkDoubleToMScalar(1.0e-2), 877 SkDoubleToMScalar(3.0), 878 SkDoubleToMScalar(1.0e+2)); 879 rot.setRotateDegreesAbout(0, 0, -1, 90); 880 mat.postConcat(rot); 881 mat.postTranslate(SkDoubleToMScalar(1.0e+2), 882 SkDoubleToMScalar(3.0), 883 SkDoubleToMScalar(1.0e-2)); 884 REPORTER_ASSERT(reporter, mat.invert(nullptr)); 885 mat.invert(&inverse); 886 iden1.setConcat(mat, inverse); 887 REPORTER_ASSERT(reporter, is_identity(iden1)); 888 889 // test degenerate matrix 890 mat.reset(); 891 mat.set3x3(1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0); 892 REPORTER_ASSERT(reporter, !mat.invert(nullptr)); 893 894 // test rol/col Major getters 895 { 896 mat.setTranslate(2, 3, 4); 897 float dataf[16]; 898 double datad[16]; 899 900 mat.asColMajorf(dataf); 901 assert16<float>(reporter, dataf, 902 1, 0, 0, 0, 903 0, 1, 0, 0, 904 0, 0, 1, 0, 905 2, 3, 4, 1); 906 mat.asColMajord(datad); 907 assert16<double>(reporter, datad, 1, 0, 0, 0, 908 0, 1, 0, 0, 909 0, 0, 1, 0, 910 2, 3, 4, 1); 911 mat.asRowMajorf(dataf); 912 assert16<float>(reporter, dataf, 1, 0, 0, 2, 913 0, 1, 0, 3, 914 0, 0, 1, 4, 915 0, 0, 0, 1); 916 mat.asRowMajord(datad); 917 assert16<double>(reporter, datad, 1, 0, 0, 2, 918 0, 1, 0, 3, 919 0, 0, 1, 4, 920 0, 0, 0, 1); 921 } 922 923 test_concat(reporter); 924 925 if (false) { // avoid bit rot, suppress warning (working on making this pass) 926 test_common_angles(reporter); 927 } 928 929 test_constructor(reporter); 930 test_gettype(reporter); 931 test_determinant(reporter); 932 test_invert(reporter); 933 test_transpose(reporter); 934 test_get_set_double(reporter); 935 test_set_row_col_major(reporter); 936 test_set_3x3(reporter); 937 test_translate(reporter); 938 test_scale(reporter); 939 test_map2(reporter); 940 test_3x3_conversion(reporter); 941 test_has_perspective(reporter); 942 test_preserves_2d_axis_alignment(reporter); 943 test_toint(reporter); 944} 945