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 "Test.h" 9#include "SkMatrix44.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_scalar(SkMScalar a, SkMScalar b) { 20 // Note that we get more compounded error for multiple operations when 21 // SK_SCALAR_IS_FIXED. 22#ifdef SK_SCALAR_IS_FLOAT 23 const SkScalar tolerance = SK_Scalar1 / 200000; 24#else 25 const SkScalar tolerance = SK_Scalar1 / 1024; 26#endif 27 28 return SkTAbs<SkMScalar>(a - b) <= tolerance; 29} 30 31template <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 57static 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_scalar(a.get(i, j), b.get(i, j))) { 61 printf("not equal %g %g\n", a.get(i, j), b.get(i, j)); 62 return false; 63 } 64 } 65 } 66 return true; 67} 68 69static bool is_identity(const SkMatrix44& m) { 70 SkMatrix44 identity; 71 identity.reset(); 72 return nearly_equal(m, identity); 73} 74 75/////////////////////////////////////////////////////////////////////////////// 76static bool bits_isonly(int value, int mask) { 77 return 0 == (value & ~mask); 78} 79 80static void test_constructor(skiatest::Reporter* reporter) { 81 // Allocate a matrix on the heap 82 SkMatrix44* placeholderMatrix = new SkMatrix44(); 83 SkAutoTDelete<SkMatrix44> deleteMe(placeholderMatrix); 84 85 for (int row = 0; row < 4; ++row) { 86 for (int col = 0; col < 4; ++col) { 87 placeholderMatrix->setDouble(row, col, row * col); 88 } 89 } 90 91 // Use placement-new syntax to trigger the constructor on top of the heap 92 // address we already initialized. This allows us to check that the 93 // constructor did avoid initializing the matrix contents. 94 SkMatrix44* testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kUninitialized_Constructor); 95 REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix); 96 REPORTER_ASSERT(reporter, !testMatrix->isIdentity()); 97 for (int row = 0; row < 4; ++row) { 98 for (int col = 0; col < 4; ++col) { 99 REPORTER_ASSERT(reporter, nearly_equal_double(row * col, testMatrix->getDouble(row, col))); 100 } 101 } 102 103 // Verify that kIdentity_Constructor really does initialize to an identity matrix. 104 testMatrix = 0; 105 testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kIdentity_Constructor); 106 REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix); 107 REPORTER_ASSERT(reporter, testMatrix->isIdentity()); 108 REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I()); 109} 110 111static void test_translate(skiatest::Reporter* reporter) { 112 SkMatrix44 mat, inverse; 113 114 mat.setTranslate(0, 0, 0); 115 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask)); 116 mat.setTranslate(1, 2, 3); 117 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kTranslate_Mask)); 118 REPORTER_ASSERT(reporter, mat.invert(&inverse)); 119 REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kTranslate_Mask)); 120 121 SkMatrix44 a, b, c; 122 a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9); 123 b.setTranslate(10, 11, 12); 124 125 c.setConcat(a, b); 126 mat = a; 127 mat.preTranslate(10, 11, 12); 128 REPORTER_ASSERT(reporter, mat == c); 129 130 c.setConcat(b, a); 131 mat = a; 132 mat.postTranslate(10, 11, 12); 133 REPORTER_ASSERT(reporter, mat == c); 134} 135 136static void test_scale(skiatest::Reporter* reporter) { 137 SkMatrix44 mat, inverse; 138 139 mat.setScale(1, 1, 1); 140 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask)); 141 mat.setScale(1, 2, 3); 142 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kScale_Mask)); 143 REPORTER_ASSERT(reporter, mat.invert(&inverse)); 144 REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kScale_Mask)); 145 146 SkMatrix44 a, b, c; 147 a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9); 148 b.setScale(10, 11, 12); 149 150 c.setConcat(a, b); 151 mat = a; 152 mat.preScale(10, 11, 12); 153 REPORTER_ASSERT(reporter, mat == c); 154 155 c.setConcat(b, a); 156 mat = a; 157 mat.postScale(10, 11, 12); 158 REPORTER_ASSERT(reporter, mat == c); 159} 160 161static void make_i(SkMatrix44* mat) { mat->setIdentity(); } 162static void make_t(SkMatrix44* mat) { mat->setTranslate(1, 2, 3); } 163static void make_s(SkMatrix44* mat) { mat->setScale(1, 2, 3); } 164static void make_st(SkMatrix44* mat) { 165 mat->setScale(1, 2, 3); 166 mat->postTranslate(1, 2, 3); 167} 168static void make_a(SkMatrix44* mat) { 169 mat->setRotateDegreesAbout(1, 2, 3, 45); 170} 171static void make_p(SkMatrix44* mat) { 172 SkMScalar data[] = { 173 1, 2, 3, 4, 5, 6, 7, 8, 174 1, 2, 3, 4, 5, 6, 7, 8, 175 }; 176 mat->setRowMajor(data); 177} 178 179typedef void (*Make44Proc)(SkMatrix44*); 180 181static const Make44Proc gMakeProcs[] = { 182 make_i, make_t, make_s, make_st, make_a, make_p 183}; 184 185static void test_map2(skiatest::Reporter* reporter, const SkMatrix44& mat) { 186 SkMScalar src2[] = { 1, 2 }; 187 SkMScalar src4[] = { src2[0], src2[1], 0, 1 }; 188 SkMScalar dstA[4], dstB[4]; 189 190 for (int i = 0; i < 4; ++i) { 191 dstA[i] = 123456789; 192 dstB[i] = 987654321; 193 } 194 195 mat.map2(src2, 1, dstA); 196 mat.mapMScalars(src4, dstB); 197 198 for (int i = 0; i < 4; ++i) { 199 REPORTER_ASSERT(reporter, dstA[i] == dstB[i]); 200 } 201} 202 203static void test_map2(skiatest::Reporter* reporter) { 204 SkMatrix44 mat; 205 206 for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProcs); ++i) { 207 gMakeProcs[i](&mat); 208 test_map2(reporter, mat); 209 } 210} 211 212static void test_gettype(skiatest::Reporter* reporter) { 213 SkMatrix44 matrix; 214 215 REPORTER_ASSERT(reporter, matrix.isIdentity()); 216 REPORTER_ASSERT(reporter, SkMatrix44::kIdentity_Mask == matrix.getType()); 217 218 int expectedMask; 219 220 matrix.set(1, 1, 0); 221 expectedMask = SkMatrix44::kScale_Mask; 222 REPORTER_ASSERT(reporter, matrix.getType() == expectedMask); 223 224 matrix.set(0, 3, 1); // translate-x 225 expectedMask |= SkMatrix44::kTranslate_Mask; 226 REPORTER_ASSERT(reporter, matrix.getType() == expectedMask); 227 228 matrix.set(2, 0, 1); 229 expectedMask |= SkMatrix44::kAffine_Mask; 230 REPORTER_ASSERT(reporter, matrix.getType() == expectedMask); 231 232 matrix.set(3, 2, 1); 233 REPORTER_ASSERT(reporter, matrix.getType() & SkMatrix44::kPerspective_Mask); 234 235 // ensure that negative zero is treated as zero 236 SkMScalar dx = 0; 237 SkMScalar dy = 0; 238 SkMScalar dz = 0; 239 matrix.setTranslate(-dx, -dy, -dz); 240 REPORTER_ASSERT(reporter, matrix.isIdentity()); 241 matrix.preTranslate(-dx, -dy, -dz); 242 REPORTER_ASSERT(reporter, matrix.isIdentity()); 243 matrix.postTranslate(-dx, -dy, -dz); 244 REPORTER_ASSERT(reporter, matrix.isIdentity()); 245} 246 247static void test_common_angles(skiatest::Reporter* reporter) { 248 SkMatrix44 rot; 249 // Test precision of rotation in common cases 250 int common_angles[] = { 0, 90, -90, 180, -180, 270, -270, 360, -360 }; 251 for (int i = 0; i < 9; ++i) { 252 rot.setRotateDegreesAbout(0, 0, -1, SkIntToScalar(common_angles[i])); 253 254 SkMatrix rot3x3 = rot; 255 REPORTER_ASSERT(reporter, rot3x3.rectStaysRect()); 256 } 257} 258 259static void test_concat(skiatest::Reporter* reporter) { 260 int i; 261 SkMatrix44 a, b, c, d; 262 263 a.setTranslate(10, 10, 10); 264 b.setScale(2, 2, 2); 265 266 SkScalar src[8] = { 267 0, 0, 0, 1, 268 1, 1, 1, 1 269 }; 270 SkScalar dst[8]; 271 272 c.setConcat(a, b); 273 274 d = a; 275 d.preConcat(b); 276 REPORTER_ASSERT(reporter, d == c); 277 278 c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4); 279 for (i = 0; i < 3; ++i) { 280 REPORTER_ASSERT(reporter, 10 == dst[i]); 281 REPORTER_ASSERT(reporter, 12 == dst[i + 4]); 282 } 283 284 c.setConcat(b, a); 285 286 d = a; 287 d.postConcat(b); 288 REPORTER_ASSERT(reporter, d == c); 289 290 c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4); 291 for (i = 0; i < 3; ++i) { 292 REPORTER_ASSERT(reporter, 20 == dst[i]); 293 REPORTER_ASSERT(reporter, 22 == dst[i + 4]); 294 } 295} 296 297static void test_determinant(skiatest::Reporter* reporter) { 298 SkMatrix44 a; 299 REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant())); 300 a.set(1, 1, 2); 301 REPORTER_ASSERT(reporter, nearly_equal_double(2, a.determinant())); 302 SkMatrix44 b; 303 REPORTER_ASSERT(reporter, a.invert(&b)); 304 REPORTER_ASSERT(reporter, nearly_equal_double(0.5, b.determinant())); 305 SkMatrix44 c = b = a; 306 c.set(0, 1, 4); 307 b.set(1, 0, 4); 308 REPORTER_ASSERT(reporter, 309 nearly_equal_double(a.determinant(), 310 b.determinant())); 311 SkMatrix44 d = a; 312 d.set(0, 0, 8); 313 REPORTER_ASSERT(reporter, nearly_equal_double(16, d.determinant())); 314 315 SkMatrix44 e = a; 316 e.postConcat(d); 317 REPORTER_ASSERT(reporter, nearly_equal_double(32, e.determinant())); 318 e.set(0, 0, 0); 319 REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant())); 320} 321 322static void test_transpose(skiatest::Reporter* reporter) { 323 SkMatrix44 a; 324 SkMatrix44 b; 325 326 int i = 0; 327 for (int row = 0; row < 4; ++row) { 328 for (int col = 0; col < 4; ++col) { 329 a.setDouble(row, col, i); 330 b.setDouble(col, row, i++); 331 } 332 } 333 334 a.transpose(); 335 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 336} 337 338static void test_get_set_double(skiatest::Reporter* reporter) { 339 SkMatrix44 a; 340 for (int row = 0; row < 4; ++row) { 341 for (int col = 0; col < 4; ++col) { 342 a.setDouble(row, col, 3.141592653589793); 343 REPORTER_ASSERT(reporter, 344 nearly_equal_double(3.141592653589793, 345 a.getDouble(row, col))); 346 a.setDouble(row, col, 0); 347 REPORTER_ASSERT(reporter, 348 nearly_equal_double(0, a.getDouble(row, col))); 349 } 350 } 351} 352 353static void test_set_row_col_major(skiatest::Reporter* reporter) { 354 SkMatrix44 a, b, c, d; 355 for (int row = 0; row < 4; ++row) { 356 for (int col = 0; col < 4; ++col) { 357 a.setDouble(row, col, row * 4 + col); 358 } 359 } 360 361 double bufferd[16]; 362 float bufferf[16]; 363 a.asColMajord(bufferd); 364 b.setColMajord(bufferd); 365 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 366 b.setRowMajord(bufferd); 367 b.transpose(); 368 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 369 a.asColMajorf(bufferf); 370 b.setColMajorf(bufferf); 371 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 372 b.setRowMajorf(bufferf); 373 b.transpose(); 374 REPORTER_ASSERT(reporter, nearly_equal(a, b)); 375} 376 377static void TestMatrix44(skiatest::Reporter* reporter) { 378 SkMatrix44 mat, inverse, iden1, iden2, rot; 379 380 mat.reset(); 381 mat.setTranslate(1, 1, 1); 382 mat.invert(&inverse); 383 iden1.setConcat(mat, inverse); 384 REPORTER_ASSERT(reporter, is_identity(iden1)); 385 386 mat.setScale(2, 2, 2); 387 mat.invert(&inverse); 388 iden1.setConcat(mat, inverse); 389 REPORTER_ASSERT(reporter, is_identity(iden1)); 390 391 mat.setScale(SK_MScalar1/2, SK_MScalar1/2, SK_MScalar1/2); 392 mat.invert(&inverse); 393 iden1.setConcat(mat, inverse); 394 REPORTER_ASSERT(reporter, is_identity(iden1)); 395 396 mat.setScale(3, 3, 3); 397 rot.setRotateDegreesAbout(0, 0, -1, 90); 398 mat.postConcat(rot); 399 REPORTER_ASSERT(reporter, mat.invert(NULL)); 400 mat.invert(&inverse); 401 iden1.setConcat(mat, inverse); 402 REPORTER_ASSERT(reporter, is_identity(iden1)); 403 iden2.setConcat(inverse, mat); 404 REPORTER_ASSERT(reporter, is_identity(iden2)); 405 406 // test rol/col Major getters 407 { 408 mat.setTranslate(2, 3, 4); 409 float dataf[16]; 410 double datad[16]; 411 412 mat.asColMajorf(dataf); 413 assert16<float>(reporter, dataf, 414 1, 0, 0, 0, 415 0, 1, 0, 0, 416 0, 0, 1, 0, 417 2, 3, 4, 1); 418 mat.asColMajord(datad); 419 assert16<double>(reporter, datad, 1, 0, 0, 0, 420 0, 1, 0, 0, 421 0, 0, 1, 0, 422 2, 3, 4, 1); 423 mat.asRowMajorf(dataf); 424 assert16<float>(reporter, dataf, 1, 0, 0, 2, 425 0, 1, 0, 3, 426 0, 0, 1, 4, 427 0, 0, 0, 1); 428 mat.asRowMajord(datad); 429 assert16<double>(reporter, datad, 1, 0, 0, 2, 430 0, 1, 0, 3, 431 0, 0, 1, 4, 432 0, 0, 0, 1); 433 } 434 435 test_concat(reporter); 436 437 if (false) { // avoid bit rot, suppress warning (working on making this pass) 438 test_common_angles(reporter); 439 } 440 441 test_constructor(reporter); 442 test_gettype(reporter); 443 test_determinant(reporter); 444 test_transpose(reporter); 445 test_get_set_double(reporter); 446 test_set_row_col_major(reporter); 447 test_translate(reporter); 448 test_scale(reporter); 449 test_map2(reporter); 450} 451 452#include "TestClassDef.h" 453DEFINE_TESTCLASS("Matrix44", Matrix44TestClass, TestMatrix44) 454