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