OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2011 Google Inc. | 3 * Copyright 2011 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 #include "Test.h" | 8 #include "Test.h" |
9 #include "SkMath.h" | 9 #include "SkMath.h" |
10 #include "SkMatrix.h" | 10 #include "SkMatrix.h" |
| 11 #include "SkMatrixUtils.h" |
11 #include "SkRandom.h" | 12 #include "SkRandom.h" |
12 | 13 |
13 static bool nearly_equal_scalar(SkScalar a, SkScalar b) { | 14 static bool nearly_equal_scalar(SkScalar a, SkScalar b) { |
14 // Note that we get more compounded error for multiple operations when | 15 // Note that we get more compounded error for multiple operations when |
15 // SK_SCALAR_IS_FIXED. | 16 // SK_SCALAR_IS_FIXED. |
16 #ifdef SK_SCALAR_IS_FLOAT | 17 #ifdef SK_SCALAR_IS_FLOAT |
17 const SkScalar tolerance = SK_Scalar1 / 200000; | 18 const SkScalar tolerance = SK_Scalar1 / 200000; |
18 #else | 19 #else |
19 const SkScalar tolerance = SK_Scalar1 / 1024; | 20 const SkScalar tolerance = SK_Scalar1 / 1024; |
20 #endif | 21 #endif |
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
338 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1); | 339 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1); |
339 REPORTER_ASSERT(reporter, !mat.isSimilarity()); | 340 REPORTER_ASSERT(reporter, !mat.isSimilarity()); |
340 | 341 |
341 // scales zero, only skews | 342 // scales zero, only skews |
342 mat.setAll(0, SK_Scalar1, 0, | 343 mat.setAll(0, SK_Scalar1, 0, |
343 SK_Scalar1, 0, 0, | 344 SK_Scalar1, 0, 0, |
344 0, 0, SkMatrix::I()[8]); | 345 0, 0, SkMatrix::I()[8]); |
345 REPORTER_ASSERT(reporter, mat.isSimilarity()); | 346 REPORTER_ASSERT(reporter, mat.isSimilarity()); |
346 } | 347 } |
347 | 348 |
| 349 // For test_matrix_decomposition, below. |
| 350 static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b, |
| 351 SkScalar tolerance = SK_ScalarNearlyZer
o) { |
| 352 // from Bruce Dawson |
| 353 SkScalar diff = SkScalarAbs(a - b); |
| 354 if (diff < tolerance) { |
| 355 return true; |
| 356 } |
| 357 |
| 358 a = SkScalarAbs(a); |
| 359 b = SkScalarAbs(b); |
| 360 SkScalar largest = (b > a) ? b : a; |
| 361 |
| 362 if (diff <= largest*tolerance) { |
| 363 return true; |
| 364 } |
| 365 |
| 366 return false; |
| 367 } |
| 368 |
| 369 static void test_matrix_decomposition(skiatest::Reporter* reporter) { |
| 370 SkMatrix mat; |
| 371 SkScalar rotation0, scaleX, scaleY, rotation1; |
| 372 |
| 373 const float kRotation0 = 15.5f; |
| 374 const float kRotation1 = -50.f; |
| 375 const float kScale0 = 5000.f; |
| 376 const float kScale1 = 0.001f; |
| 377 |
| 378 // identity |
| 379 mat.reset(); |
| 380 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 381 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0)); |
| 382 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, SK_Scalar1)); |
| 383 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, SK_Scalar1)); |
| 384 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 385 // make sure it doesn't crash if we pass in NULLs |
| 386 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL, NULL)); |
| 387 |
| 388 // rotation only |
| 389 mat.setRotate(kRotation0); |
| 390 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 391 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(
kRotation0))); |
| 392 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, SK_Scalar1)); |
| 393 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, SK_Scalar1)); |
| 394 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 395 |
| 396 // uniform scale only |
| 397 mat.setScale(kScale0, kScale0); |
| 398 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 399 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0)); |
| 400 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); |
| 401 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); |
| 402 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 403 |
| 404 // anisotropic scale only |
| 405 mat.setScale(kScale1, kScale0); |
| 406 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 407 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0)); |
| 408 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1)); |
| 409 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); |
| 410 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 411 |
| 412 // rotation then uniform scale |
| 413 mat.setRotate(kRotation1); |
| 414 mat.postScale(kScale0, kScale0); |
| 415 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 416 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(
kRotation1))); |
| 417 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); |
| 418 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); |
| 419 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 420 |
| 421 // uniform scale then rotation |
| 422 mat.setScale(kScale0, kScale0); |
| 423 mat.postRotate(kRotation1); |
| 424 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 425 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(
kRotation1))); |
| 426 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); |
| 427 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); |
| 428 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 429 |
| 430 // rotation then uniform scale+reflection |
| 431 mat.setRotate(kRotation0); |
| 432 mat.postScale(kScale1, -kScale1); |
| 433 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 434 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(
kRotation0))); |
| 435 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1)); |
| 436 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, -kScale1)); |
| 437 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 438 |
| 439 // uniform scale+reflection, then rotate |
| 440 mat.setScale(kScale0, -kScale0); |
| 441 mat.postRotate(kRotation1); |
| 442 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 443 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(
-kRotation1))); |
| 444 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); |
| 445 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, -kScale0)); |
| 446 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 447 |
| 448 // rotation then anisotropic scale |
| 449 mat.setRotate(kRotation1); |
| 450 mat.postScale(kScale1, kScale0); |
| 451 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 452 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(
kRotation1))); |
| 453 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1)); |
| 454 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); |
| 455 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 456 |
| 457 // anisotropic scale then rotation |
| 458 mat.setScale(kScale1, kScale0); |
| 459 mat.postRotate(kRotation0); |
| 460 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 461 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0)); |
| 462 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1)); |
| 463 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); |
| 464 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation1, SkDegreesToRadians(
kRotation0))); |
| 465 |
| 466 // rotation, uniform scale, then different rotation |
| 467 mat.setRotate(kRotation1); |
| 468 mat.postScale(kScale0, kScale0); |
| 469 mat.postRotate(kRotation0); |
| 470 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 471 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, |
| 472 SkDegreesToRadians(kRotation0
+ kRotation1))); |
| 473 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); |
| 474 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); |
| 475 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); |
| 476 |
| 477 // rotation, anisotropic scale, then different rotation |
| 478 mat.setRotate(kRotation0); |
| 479 mat.postScale(kScale1, kScale0); |
| 480 mat.postRotate(kRotation1); |
| 481 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 482 // Because of the shear/skew we won't get the same results, so we need to mu
ltiply it out. |
| 483 // Generating the matrices requires doing a radian-to-degree calculation, th
en degree-to-radian |
| 484 // calculation (in setRotate()), which adds error, so this just computes the
matrix elements |
| 485 // directly. |
| 486 SkScalar c0; |
| 487 SkScalar s0 = SkScalarSinCos(rotation0, &c0); |
| 488 SkScalar c1; |
| 489 SkScalar s1 = SkScalarSinCos(rotation1, &c1); |
| 490 // We do a relative check here because large scale factors cause problems wi
th an absolute check |
| 491 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale
X], |
| 492 scaleX*c0*c1 - scaleY
*s0*s1)); |
| 493 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX
], |
| 494 -scaleX*s0*c1 - scale
Y*c0*s1)); |
| 495 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY
], |
| 496 scaleX*c0*s1 + scaleY
*s0*c1)); |
| 497 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale
Y], |
| 498 -scaleX*s0*s1 + scale
Y*c0*c1)); |
| 499 |
| 500 // try some random matrices |
| 501 SkMWCRandom rand; |
| 502 for (int m = 0; m < 1000; ++m) { |
| 503 SkScalar rot0 = rand.nextRangeF(-SK_ScalarPI, SK_ScalarPI); |
| 504 SkScalar sx = rand.nextRangeF(-3000.f, 3000.f); |
| 505 SkScalar sy = rand.nextRangeF(-3000.f, 3000.f); |
| 506 SkScalar rot1 = rand.nextRangeF(-SK_ScalarPI, SK_ScalarPI); |
| 507 mat.setRotate(rot0); |
| 508 mat.postScale(sx, sy); |
| 509 mat.postRotate(rot1); |
| 510 |
| 511 if (SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1))
{ |
| 512 SkScalar c0; |
| 513 SkScalar s0 = SkScalarSinCos(rotation0, &c0); |
| 514 SkScalar c1; |
| 515 SkScalar s1 = SkScalarSinCos(rotation1, &c1); |
| 516 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix:
:kMScaleX], |
| 517 scaleX*c0*c1
- scaleY*s0*s1)); |
| 518 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix:
:kMSkewX], |
| 519 -scaleX*s0*c1
- scaleY*c0*s1)); |
| 520 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix:
:kMSkewY], |
| 521 scaleX*c0*s1
+ scaleY*s0*c1)); |
| 522 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix:
:kMScaleY], |
| 523 -scaleX*s0*s1
+ scaleY*c0*c1)); |
| 524 } else { |
| 525 // if the matrix is degenerate, the basis vectors should be near-par
allel or near-zero |
| 526 SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] - |
| 527 mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY]; |
| 528 REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot)); |
| 529 } |
| 530 } |
| 531 |
| 532 // translation shouldn't affect this |
| 533 mat.postTranslate(-1000.f, 1000.f); |
| 534 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 535 s0 = SkScalarSinCos(rotation0, &c0); |
| 536 s1 = SkScalarSinCos(rotation1, &c1); |
| 537 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale
X], |
| 538 scaleX*c0*c1 - scaleY
*s0*s1)); |
| 539 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX
], |
| 540 -scaleX*s0*c1 - scale
Y*c0*s1)); |
| 541 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY
], |
| 542 scaleX*c0*s1 + scaleY
*s0*c1)); |
| 543 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale
Y], |
| 544 -scaleX*s0*s1 + scale
Y*c0*c1)); |
| 545 |
| 546 // perspective shouldn't affect this |
| 547 mat[SkMatrix::kMPersp0] = 12.0; |
| 548 mat[SkMatrix::kMPersp1] = 4.0; |
| 549 mat[SkMatrix::kMPersp2] = 1872.0; |
| 550 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 551 s0 = SkScalarSinCos(rotation0, &c0); |
| 552 s1 = SkScalarSinCos(rotation1, &c1); |
| 553 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale
X], |
| 554 scaleX*c0*c1 - scaleY
*s0*s1)); |
| 555 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX
], |
| 556 -scaleX*s0*c1 - scale
Y*c0*s1)); |
| 557 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY
], |
| 558 scaleX*c0*s1 + scaleY
*s0*c1)); |
| 559 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale
Y], |
| 560 -scaleX*s0*s1 + scale
Y*c0*c1)); |
| 561 |
| 562 // rotation, anisotropic scale + reflection, then different rotation |
| 563 mat.setRotate(kRotation0); |
| 564 mat.postScale(-kScale1, kScale0); |
| 565 mat.postRotate(kRotation1); |
| 566 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca
leY, &rotation1)); |
| 567 s0 = SkScalarSinCos(rotation0, &c0); |
| 568 s1 = SkScalarSinCos(rotation1, &c1); |
| 569 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale
X], |
| 570 scaleX*c0*c1 - scaleY
*s0*s1)); |
| 571 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX
], |
| 572 -scaleX*s0*c1 - scale
Y*c0*s1)); |
| 573 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY
], |
| 574 scaleX*c0*s1 + scaleY
*s0*c1)); |
| 575 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale
Y], |
| 576 -scaleX*s0*s1 + scale
Y*c0*c1)); |
| 577 |
| 578 // degenerate matrices |
| 579 // mostly zero entries |
| 580 mat.reset(); |
| 581 mat[SkMatrix::kMScaleX] = 0.f; |
| 582 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sc
aleY, &rotation1)); |
| 583 mat.reset(); |
| 584 mat[SkMatrix::kMScaleY] = 0.f; |
| 585 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sc
aleY, &rotation1)); |
| 586 mat.reset(); |
| 587 // linearly dependent entries |
| 588 mat[SkMatrix::kMScaleX] = 1.f; |
| 589 mat[SkMatrix::kMSkewX] = 2.f; |
| 590 mat[SkMatrix::kMSkewY] = 4.f; |
| 591 mat[SkMatrix::kMScaleY] = 8.f; |
| 592 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sc
aleY, &rotation1)); |
| 593 } |
| 594 |
348 static void TestMatrix(skiatest::Reporter* reporter) { | 595 static void TestMatrix(skiatest::Reporter* reporter) { |
349 SkMatrix mat, inverse, iden1, iden2; | 596 SkMatrix mat, inverse, iden1, iden2; |
350 | 597 |
351 mat.reset(); | 598 mat.reset(); |
352 mat.setTranslate(SK_Scalar1, SK_Scalar1); | 599 mat.setTranslate(SK_Scalar1, SK_Scalar1); |
353 REPORTER_ASSERT(reporter, mat.invert(&inverse)); | 600 REPORTER_ASSERT(reporter, mat.invert(&inverse)); |
354 iden1.setConcat(mat, inverse); | 601 iden1.setConcat(mat, inverse); |
355 REPORTER_ASSERT(reporter, is_identity(iden1)); | 602 REPORTER_ASSERT(reporter, is_identity(iden1)); |
356 | 603 |
357 mat.setScale(SkIntToScalar(2), SkIntToScalar(4)); | 604 mat.setScale(SkIntToScalar(2), SkIntToScalar(4)); |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
458 // fixed pt doesn't have the property that NaN does not equal itself. | 705 // fixed pt doesn't have the property that NaN does not equal itself. |
459 #ifdef SK_SCALAR_IS_FIXED | 706 #ifdef SK_SCALAR_IS_FIXED |
460 REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2)); | 707 REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2)); |
461 #else | 708 #else |
462 REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2)); | 709 REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2)); |
463 #endif | 710 #endif |
464 | 711 |
465 test_matrix_max_stretch(reporter); | 712 test_matrix_max_stretch(reporter); |
466 test_matrix_is_similarity(reporter); | 713 test_matrix_is_similarity(reporter); |
467 test_matrix_recttorect(reporter); | 714 test_matrix_recttorect(reporter); |
| 715 test_matrix_decomposition(reporter); |
468 } | 716 } |
469 | 717 |
470 #include "TestClassDef.h" | 718 #include "TestClassDef.h" |
471 DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix) | 719 DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix) |
OLD | NEW |