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