Home | History | Annotate | Download | only in tests
      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 "TestClassDef.h"
     10 #include "SkMath.h"
     11 #include "SkMatrix.h"
     12 #include "SkMatrixUtils.h"
     13 #include "SkRandom.h"
     14 
     15 static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
     16     // Note that we get more compounded error for multiple operations when
     17     // SK_SCALAR_IS_FIXED.
     18 #ifdef SK_SCALAR_IS_FLOAT
     19     const SkScalar tolerance = SK_Scalar1 / 200000;
     20 #else
     21     const SkScalar tolerance = SK_Scalar1 / 1024;
     22 #endif
     23 
     24     return SkScalarAbs(a - b) <= tolerance;
     25 }
     26 
     27 static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
     28     for (int i = 0; i < 9; i++) {
     29         if (!nearly_equal_scalar(a[i], b[i])) {
     30             SkDebugf("not equal %g %g\n", (float)a[i], (float)b[i]);
     31             return false;
     32         }
     33     }
     34     return true;
     35 }
     36 
     37 static bool are_equal(skiatest::Reporter* reporter,
     38                       const SkMatrix& a,
     39                       const SkMatrix& b) {
     40     bool equal = a == b;
     41     bool cheapEqual = a.cheapEqualTo(b);
     42     if (equal != cheapEqual) {
     43 #ifdef SK_SCALAR_IS_FLOAT
     44         if (equal) {
     45             bool foundZeroSignDiff = false;
     46             for (int i = 0; i < 9; ++i) {
     47                 float aVal = a.get(i);
     48                 float bVal = b.get(i);
     49                 int aValI = *SkTCast<int*>(&aVal);
     50                 int bValI = *SkTCast<int*>(&bVal);
     51                 if (0 == aVal && 0 == bVal && aValI != bValI) {
     52                     foundZeroSignDiff = true;
     53                 } else {
     54                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
     55                 }
     56             }
     57             REPORTER_ASSERT(reporter, foundZeroSignDiff);
     58         } else {
     59             bool foundNaN = false;
     60             for (int i = 0; i < 9; ++i) {
     61                 float aVal = a.get(i);
     62                 float bVal = b.get(i);
     63                 int aValI = *SkTCast<int*>(&aVal);
     64                 int bValI = *SkTCast<int*>(&bVal);
     65                 if (sk_float_isnan(aVal) && aValI == bValI) {
     66                     foundNaN = true;
     67                 } else {
     68                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
     69                 }
     70             }
     71             REPORTER_ASSERT(reporter, foundNaN);
     72         }
     73 #else
     74         REPORTER_ASSERT(reporter, false);
     75 #endif
     76     }
     77     return equal;
     78 }
     79 
     80 static bool is_identity(const SkMatrix& m) {
     81     SkMatrix identity;
     82     identity.reset();
     83     return nearly_equal(m, identity);
     84 }
     85 
     86 static void test_matrix_recttorect(skiatest::Reporter* reporter) {
     87     SkRect src, dst;
     88     SkMatrix matrix;
     89 
     90     src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10);
     91     dst = src;
     92     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
     93     REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
     94     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
     95 
     96     dst.offset(SK_Scalar1, SK_Scalar1);
     97     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
     98     REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
     99     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
    100 
    101     dst.fRight += SK_Scalar1;
    102     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
    103     REPORTER_ASSERT(reporter,
    104                     (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
    105     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
    106 
    107     dst = src;
    108     dst.fRight = src.fRight * 2;
    109     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
    110     REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
    111     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
    112 }
    113 
    114 static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
    115     // add 100 in case we have a bug, I don't want to kill my stack in the test
    116     static const size_t kBufferSize = SkMatrix::kMaxFlattenSize + 100;
    117     char buffer[kBufferSize];
    118     size_t size1 = m.writeToMemory(NULL);
    119     size_t size2 = m.writeToMemory(buffer);
    120     REPORTER_ASSERT(reporter, size1 == size2);
    121     REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
    122 
    123     SkMatrix m2;
    124     size_t size3 = m2.readFromMemory(buffer, kBufferSize);
    125     REPORTER_ASSERT(reporter, size1 == size3);
    126     REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
    127 
    128     char buffer2[kBufferSize];
    129     size3 = m2.writeToMemory(buffer2);
    130     REPORTER_ASSERT(reporter, size1 == size3);
    131     REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
    132 }
    133 
    134 static void test_matrix_min_max_stretch(skiatest::Reporter* reporter) {
    135     SkMatrix identity;
    136     identity.reset();
    137     REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinStretch());
    138     REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
    139 
    140     SkMatrix scale;
    141     scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
    142     REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinStretch());
    143     REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch());
    144 
    145     SkMatrix rot90Scale;
    146     rot90Scale.setRotate(90 * SK_Scalar1);
    147     rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
    148     REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinStretch());
    149     REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch());
    150 
    151     SkMatrix rotate;
    152     rotate.setRotate(128 * SK_Scalar1);
    153     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinStretch() ,SK_ScalarNearlyZero));
    154     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxStretch(), SK_ScalarNearlyZero));
    155 
    156     SkMatrix translate;
    157     translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
    158     REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinStretch());
    159     REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());
    160 
    161     SkMatrix perspX;
    162     perspX.reset();
    163     perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
    164     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinStretch());
    165     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());
    166 
    167     SkMatrix perspY;
    168     perspY.reset();
    169     perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
    170     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinStretch());
    171     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());
    172 
    173     SkMatrix baseMats[] = {scale, rot90Scale, rotate,
    174                            translate, perspX, perspY};
    175     SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
    176     for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
    177         mats[i] = baseMats[i];
    178         bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
    179         REPORTER_ASSERT(reporter, invertable);
    180     }
    181     SkRandom rand;
    182     for (int m = 0; m < 1000; ++m) {
    183         SkMatrix mat;
    184         mat.reset();
    185         for (int i = 0; i < 4; ++i) {
    186             int x = rand.nextU() % SK_ARRAY_COUNT(mats);
    187             mat.postConcat(mats[x]);
    188         }
    189 
    190         SkScalar minStretch = mat.getMinStretch();
    191         SkScalar maxStretch = mat.getMaxStretch();
    192         REPORTER_ASSERT(reporter, (minStretch < 0) == (maxStretch < 0));
    193         REPORTER_ASSERT(reporter, (maxStretch < 0) == mat.hasPerspective());
    194 
    195         if (mat.hasPerspective()) {
    196             m -= 1; // try another non-persp matrix
    197             continue;
    198         }
    199 
    200         // test a bunch of vectors. All should be scaled by between minStretch and maxStretch
    201         // (modulo some error) and we should find a vector that is scaled by almost each.
    202         static const SkScalar gVectorStretchTol = (105 * SK_Scalar1) / 100;
    203         static const SkScalar gClosestStretchTol = (97 * SK_Scalar1) / 100;
    204         SkScalar max = 0, min = SK_ScalarMax;
    205         SkVector vectors[1000];
    206         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
    207             vectors[i].fX = rand.nextSScalar1();
    208             vectors[i].fY = rand.nextSScalar1();
    209             if (!vectors[i].normalize()) {
    210                 i -= 1;
    211                 continue;
    212             }
    213         }
    214         mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
    215         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
    216             SkScalar d = vectors[i].length();
    217             REPORTER_ASSERT(reporter, SkScalarDiv(d, maxStretch) < gVectorStretchTol);
    218             REPORTER_ASSERT(reporter, SkScalarDiv(minStretch, d) < gVectorStretchTol);
    219             if (max < d) {
    220                 max = d;
    221             }
    222             if (min > d) {
    223                 min = d;
    224             }
    225         }
    226         REPORTER_ASSERT(reporter, SkScalarDiv(max, maxStretch) >= gClosestStretchTol);
    227         REPORTER_ASSERT(reporter, SkScalarDiv(minStretch, min) >= gClosestStretchTol);
    228     }
    229 }
    230 
    231 static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
    232     SkMatrix mat;
    233 
    234     // identity
    235     mat.setIdentity();
    236     REPORTER_ASSERT(reporter, mat.isSimilarity());
    237 
    238     // translation only
    239     mat.reset();
    240     mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
    241     REPORTER_ASSERT(reporter, mat.isSimilarity());
    242 
    243     // scale with same size
    244     mat.reset();
    245     mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
    246     REPORTER_ASSERT(reporter, mat.isSimilarity());
    247 
    248     // scale with one negative
    249     mat.reset();
    250     mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
    251     REPORTER_ASSERT(reporter, mat.isSimilarity());
    252 
    253     // scale with different size
    254     mat.reset();
    255     mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
    256     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    257 
    258     // scale with same size at a pivot point
    259     mat.reset();
    260     mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
    261                  SkIntToScalar(2), SkIntToScalar(2));
    262     REPORTER_ASSERT(reporter, mat.isSimilarity());
    263 
    264     // scale with different size at a pivot point
    265     mat.reset();
    266     mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
    267                  SkIntToScalar(2), SkIntToScalar(2));
    268     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    269 
    270     // skew with same size
    271     mat.reset();
    272     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
    273     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    274 
    275     // skew with different size
    276     mat.reset();
    277     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
    278     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    279 
    280     // skew with same size at a pivot point
    281     mat.reset();
    282     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
    283                 SkIntToScalar(2), SkIntToScalar(2));
    284     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    285 
    286     // skew with different size at a pivot point
    287     mat.reset();
    288     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
    289                 SkIntToScalar(2), SkIntToScalar(2));
    290     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    291 
    292     // perspective x
    293     mat.reset();
    294     mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
    295     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    296 
    297     // perspective y
    298     mat.reset();
    299     mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
    300     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    301 
    302 #ifdef SK_SCALAR_IS_FLOAT
    303     /* We bypass the following tests for SK_SCALAR_IS_FIXED build.
    304      * The long discussion can be found in this issue:
    305      *     http://codereview.appspot.com/5999050/
    306      * In short, we haven't found a perfect way to fix the precision
    307      * issue, i.e. the way we use tolerance in isSimilarityTransformation
    308      * is incorrect. The situation becomes worse in fixed build, so
    309      * we disabled rotation related tests for fixed build.
    310      */
    311 
    312     // rotate
    313     for (int angle = 0; angle < 360; ++angle) {
    314         mat.reset();
    315         mat.setRotate(SkIntToScalar(angle));
    316         REPORTER_ASSERT(reporter, mat.isSimilarity());
    317     }
    318 
    319     // see if there are any accumulated precision issues
    320     mat.reset();
    321     for (int i = 1; i < 360; i++) {
    322         mat.postRotate(SkIntToScalar(1));
    323     }
    324     REPORTER_ASSERT(reporter, mat.isSimilarity());
    325 
    326     // rotate + translate
    327     mat.reset();
    328     mat.setRotate(SkIntToScalar(30));
    329     mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
    330     REPORTER_ASSERT(reporter, mat.isSimilarity());
    331 
    332     // rotate + uniform scale
    333     mat.reset();
    334     mat.setRotate(SkIntToScalar(30));
    335     mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
    336     REPORTER_ASSERT(reporter, mat.isSimilarity());
    337 
    338     // rotate + non-uniform scale
    339     mat.reset();
    340     mat.setRotate(SkIntToScalar(30));
    341     mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
    342     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    343 #endif
    344 
    345     // all zero
    346     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
    347     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    348 
    349     // all zero except perspective
    350     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
    351     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    352 
    353     // scales zero, only skews
    354     mat.setAll(0, SK_Scalar1, 0,
    355                SK_Scalar1, 0, 0,
    356                0, 0, SkMatrix::I()[8]);
    357     REPORTER_ASSERT(reporter, mat.isSimilarity());
    358 }
    359 
    360 // For test_matrix_decomposition, below.
    361 static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
    362                                          SkScalar tolerance = SK_ScalarNearlyZero) {
    363     // from Bruce Dawson
    364     // absolute check
    365     SkScalar diff = SkScalarAbs(a - b);
    366     if (diff < tolerance) {
    367         return true;
    368     }
    369 
    370     // relative check
    371     a = SkScalarAbs(a);
    372     b = SkScalarAbs(b);
    373     SkScalar largest = (b > a) ? b : a;
    374 
    375     if (diff <= largest*tolerance) {
    376         return true;
    377     }
    378 
    379     return false;
    380 }
    381 
    382 static bool check_matrix_recomposition(const SkMatrix& mat,
    383                                        const SkPoint& rotation1,
    384                                        const SkPoint& scale,
    385                                        const SkPoint& rotation2) {
    386     SkScalar c1 = rotation1.fX;
    387     SkScalar s1 = rotation1.fY;
    388     SkScalar scaleX = scale.fX;
    389     SkScalar scaleY = scale.fY;
    390     SkScalar c2 = rotation2.fX;
    391     SkScalar s2 = rotation2.fY;
    392 
    393     // We do a relative check here because large scale factors cause problems with an absolute check
    394     bool result = scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
    395                                                scaleX*c1*c2 - scaleY*s1*s2) &&
    396                   scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
    397                                                -scaleX*s1*c2 - scaleY*c1*s2) &&
    398                   scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
    399                                                scaleX*c1*s2 + scaleY*s1*c2) &&
    400                   scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
    401                                                -scaleX*s1*s2 + scaleY*c1*c2);
    402     return result;
    403 }
    404 
    405 static void test_matrix_decomposition(skiatest::Reporter* reporter) {
    406     SkMatrix mat;
    407     SkPoint rotation1, scale, rotation2;
    408 
    409     const float kRotation0 = 15.5f;
    410     const float kRotation1 = -50.f;
    411     const float kScale0 = 5000.f;
    412     const float kScale1 = 0.001f;
    413 
    414     // identity
    415     mat.reset();
    416     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    417     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    418     // make sure it doesn't crash if we pass in NULLs
    419     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL));
    420 
    421     // rotation only
    422     mat.setRotate(kRotation0);
    423     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    424     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    425 
    426     // uniform scale only
    427     mat.setScale(kScale0, kScale0);
    428     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    429     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    430 
    431     // anisotropic scale only
    432     mat.setScale(kScale1, kScale0);
    433     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    434     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    435 
    436     // rotation then uniform scale
    437     mat.setRotate(kRotation1);
    438     mat.postScale(kScale0, kScale0);
    439     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    440     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    441 
    442     // uniform scale then rotation
    443     mat.setScale(kScale0, kScale0);
    444     mat.postRotate(kRotation1);
    445     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    446     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    447 
    448     // rotation then uniform scale+reflection
    449     mat.setRotate(kRotation0);
    450     mat.postScale(kScale1, -kScale1);
    451     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    452     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    453 
    454     // uniform scale+reflection, then rotate
    455     mat.setScale(kScale0, -kScale0);
    456     mat.postRotate(kRotation1);
    457     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    458     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    459 
    460     // rotation then anisotropic scale
    461     mat.setRotate(kRotation1);
    462     mat.postScale(kScale1, kScale0);
    463     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    464     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    465 
    466     // rotation then anisotropic scale
    467     mat.setRotate(90);
    468     mat.postScale(kScale1, kScale0);
    469     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    470     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    471 
    472     // anisotropic scale then rotation
    473     mat.setScale(kScale1, kScale0);
    474     mat.postRotate(kRotation0);
    475     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    476     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    477 
    478     // anisotropic scale then rotation
    479     mat.setScale(kScale1, kScale0);
    480     mat.postRotate(90);
    481     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    482     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    483 
    484     // rotation, uniform scale, then different rotation
    485     mat.setRotate(kRotation1);
    486     mat.postScale(kScale0, kScale0);
    487     mat.postRotate(kRotation0);
    488     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    489     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    490 
    491     // rotation, anisotropic scale, then different rotation
    492     mat.setRotate(kRotation0);
    493     mat.postScale(kScale1, kScale0);
    494     mat.postRotate(kRotation1);
    495     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    496     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    497 
    498     // rotation, anisotropic scale + reflection, then different rotation
    499     mat.setRotate(kRotation0);
    500     mat.postScale(-kScale1, kScale0);
    501     mat.postRotate(kRotation1);
    502     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    503     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    504 
    505     // try some random matrices
    506     SkRandom rand;
    507     for (int m = 0; m < 1000; ++m) {
    508         SkScalar rot0 = rand.nextRangeF(-180, 180);
    509         SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
    510         SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
    511         SkScalar rot1 = rand.nextRangeF(-180, 180);
    512         mat.setRotate(rot0);
    513         mat.postScale(sx, sy);
    514         mat.postRotate(rot1);
    515 
    516         if (SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)) {
    517             REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    518         } else {
    519             // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
    520             SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
    521                                mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
    522             REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
    523         }
    524     }
    525 
    526     // translation shouldn't affect this
    527     mat.postTranslate(-1000.f, 1000.f);
    528     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    529     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    530 
    531     // perspective shouldn't affect this
    532     mat[SkMatrix::kMPersp0] = 12.f;
    533     mat[SkMatrix::kMPersp1] = 4.f;
    534     mat[SkMatrix::kMPersp2] = 1872.f;
    535     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    536     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    537 
    538     // degenerate matrices
    539     // mostly zero entries
    540     mat.reset();
    541     mat[SkMatrix::kMScaleX] = 0.f;
    542     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    543     mat.reset();
    544     mat[SkMatrix::kMScaleY] = 0.f;
    545     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    546     mat.reset();
    547     // linearly dependent entries
    548     mat[SkMatrix::kMScaleX] = 1.f;
    549     mat[SkMatrix::kMSkewX] = 2.f;
    550     mat[SkMatrix::kMSkewY] = 4.f;
    551     mat[SkMatrix::kMScaleY] = 8.f;
    552     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    553 }
    554 
    555 // For test_matrix_homogeneous, below.
    556 static bool scalar_array_nearly_equal_relative(const SkScalar a[], const SkScalar b[], int count) {
    557     for (int i = 0; i < count; ++i) {
    558         if (!scalar_nearly_equal_relative(a[i], b[i])) {
    559             return false;
    560         }
    561     }
    562     return true;
    563 }
    564 
    565 // For test_matrix_homogeneous, below.
    566 // Maps a single triple in src using m and compares results to those in dst
    567 static bool naive_homogeneous_mapping(const SkMatrix& m, const SkScalar src[3],
    568                                       const SkScalar dst[3]) {
    569     SkScalar res[3];
    570     SkScalar ms[9] = {m[0], m[1], m[2],
    571                       m[3], m[4], m[5],
    572                       m[6], m[7], m[8]};
    573     res[0] = src[0] * ms[0] + src[1] * ms[1] + src[2] * ms[2];
    574     res[1] = src[0] * ms[3] + src[1] * ms[4] + src[2] * ms[5];
    575     res[2] = src[0] * ms[6] + src[1] * ms[7] + src[2] * ms[8];
    576     return scalar_array_nearly_equal_relative(res, dst, 3);
    577 }
    578 
    579 static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
    580     SkMatrix mat;
    581 
    582     const float kRotation0 = 15.5f;
    583     const float kRotation1 = -50.f;
    584     const float kScale0 = 5000.f;
    585 
    586     const int kTripleCount = 1000;
    587     const int kMatrixCount = 1000;
    588     SkRandom rand;
    589 
    590     SkScalar randTriples[3*kTripleCount];
    591     for (int i = 0; i < 3*kTripleCount; ++i) {
    592         randTriples[i] = rand.nextRangeF(-3000.f, 3000.f);
    593     }
    594 
    595     SkMatrix mats[kMatrixCount];
    596     for (int i = 0; i < kMatrixCount; ++i) {
    597         for (int j = 0; j < 9; ++j) {
    598             mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
    599         }
    600     }
    601 
    602     // identity
    603     {
    604     mat.reset();
    605     SkScalar dst[3*kTripleCount];
    606     mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
    607     REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3));
    608     }
    609 
    610     // zero matrix
    611     {
    612     mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
    613     SkScalar dst[3*kTripleCount];
    614     mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
    615     SkScalar zeros[3] = {0.f, 0.f, 0.f};
    616     for (int i = 0; i < kTripleCount; ++i) {
    617         REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3));
    618     }
    619     }
    620 
    621     // zero point
    622     {
    623     SkScalar zeros[3] = {0.f, 0.f, 0.f};
    624     for (int i = 0; i < kMatrixCount; ++i) {
    625         SkScalar dst[3];
    626         mats[i].mapHomogeneousPoints(dst, zeros, 1);
    627         REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3));
    628     }
    629     }
    630 
    631     // doesn't crash with null dst, src, count == 0
    632     {
    633     mats[0].mapHomogeneousPoints(NULL, NULL, 0);
    634     }
    635 
    636     // uniform scale of point
    637     {
    638     mat.setScale(kScale0, kScale0);
    639     SkScalar dst[3];
    640     SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
    641     SkPoint pnt;
    642     pnt.set(src[0], src[1]);
    643     mat.mapHomogeneousPoints(dst, src, 1);
    644     mat.mapPoints(&pnt, &pnt, 1);
    645     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
    646     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
    647     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
    648     }
    649 
    650     // rotation of point
    651     {
    652     mat.setRotate(kRotation0);
    653     SkScalar dst[3];
    654     SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
    655     SkPoint pnt;
    656     pnt.set(src[0], src[1]);
    657     mat.mapHomogeneousPoints(dst, src, 1);
    658     mat.mapPoints(&pnt, &pnt, 1);
    659     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
    660     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
    661     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
    662     }
    663 
    664     // rotation, scale, rotation of point
    665     {
    666     mat.setRotate(kRotation1);
    667     mat.postScale(kScale0, kScale0);
    668     mat.postRotate(kRotation0);
    669     SkScalar dst[3];
    670     SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
    671     SkPoint pnt;
    672     pnt.set(src[0], src[1]);
    673     mat.mapHomogeneousPoints(dst, src, 1);
    674     mat.mapPoints(&pnt, &pnt, 1);
    675     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
    676     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
    677     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
    678     }
    679 
    680     // compare with naive approach
    681     {
    682     for (int i = 0; i < kMatrixCount; ++i) {
    683         for (int j = 0; j < kTripleCount; ++j) {
    684             SkScalar dst[3];
    685             mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1);
    686             REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst));
    687         }
    688     }
    689     }
    690 
    691 }
    692 
    693 DEF_TEST(Matrix, reporter) {
    694     SkMatrix    mat, inverse, iden1, iden2;
    695 
    696     mat.reset();
    697     mat.setTranslate(SK_Scalar1, SK_Scalar1);
    698     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    699     iden1.setConcat(mat, inverse);
    700     REPORTER_ASSERT(reporter, is_identity(iden1));
    701 
    702     mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
    703     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    704     iden1.setConcat(mat, inverse);
    705     REPORTER_ASSERT(reporter, is_identity(iden1));
    706     test_flatten(reporter, mat);
    707 
    708     mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
    709     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    710     iden1.setConcat(mat, inverse);
    711     REPORTER_ASSERT(reporter, is_identity(iden1));
    712     test_flatten(reporter, mat);
    713 
    714     mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
    715     mat.postRotate(SkIntToScalar(25));
    716     REPORTER_ASSERT(reporter, mat.invert(NULL));
    717     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    718     iden1.setConcat(mat, inverse);
    719     REPORTER_ASSERT(reporter, is_identity(iden1));
    720     iden2.setConcat(inverse, mat);
    721     REPORTER_ASSERT(reporter, is_identity(iden2));
    722     test_flatten(reporter, mat);
    723     test_flatten(reporter, iden2);
    724 
    725     mat.setScale(0, SK_Scalar1);
    726     REPORTER_ASSERT(reporter, !mat.invert(NULL));
    727     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
    728     mat.setScale(SK_Scalar1, 0);
    729     REPORTER_ASSERT(reporter, !mat.invert(NULL));
    730     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
    731 
    732     // rectStaysRect test
    733     {
    734         static const struct {
    735             SkScalar    m00, m01, m10, m11;
    736             bool        mStaysRect;
    737         }
    738         gRectStaysRectSamples[] = {
    739             {          0,          0,          0,           0, false },
    740             {          0,          0,          0,  SK_Scalar1, false },
    741             {          0,          0, SK_Scalar1,           0, false },
    742             {          0,          0, SK_Scalar1,  SK_Scalar1, false },
    743             {          0, SK_Scalar1,          0,           0, false },
    744             {          0, SK_Scalar1,          0,  SK_Scalar1, false },
    745             {          0, SK_Scalar1, SK_Scalar1,           0, true },
    746             {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
    747             { SK_Scalar1,          0,          0,           0, false },
    748             { SK_Scalar1,          0,          0,  SK_Scalar1, true },
    749             { SK_Scalar1,          0, SK_Scalar1,           0, false },
    750             { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
    751             { SK_Scalar1, SK_Scalar1,          0,           0, false },
    752             { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
    753             { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
    754             { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
    755         };
    756 
    757         for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
    758             SkMatrix    m;
    759 
    760             m.reset();
    761             m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
    762             m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
    763             m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
    764             m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
    765             REPORTER_ASSERT(reporter,
    766                     m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
    767         }
    768     }
    769 
    770     mat.reset();
    771     mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
    772     mat.set(SkMatrix::kMSkewX,  SkIntToScalar(2));
    773     mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
    774     mat.set(SkMatrix::kMSkewY,  SkIntToScalar(4));
    775     mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
    776     mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
    777     SkScalar affine[6];
    778     REPORTER_ASSERT(reporter, mat.asAffine(affine));
    779 
    780     #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
    781     REPORTER_ASSERT(reporter, affineEqual(ScaleX));
    782     REPORTER_ASSERT(reporter, affineEqual(SkewY));
    783     REPORTER_ASSERT(reporter, affineEqual(SkewX));
    784     REPORTER_ASSERT(reporter, affineEqual(ScaleY));
    785     REPORTER_ASSERT(reporter, affineEqual(TransX));
    786     REPORTER_ASSERT(reporter, affineEqual(TransY));
    787     #undef affineEqual
    788 
    789     mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
    790     REPORTER_ASSERT(reporter, !mat.asAffine(affine));
    791 
    792     SkMatrix mat2;
    793     mat2.reset();
    794     mat.reset();
    795     SkScalar zero = 0;
    796     mat.set(SkMatrix::kMSkewX, -zero);
    797     REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
    798 
    799     mat2.reset();
    800     mat.reset();
    801     mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
    802     mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
    803     // fixed pt doesn't have the property that NaN does not equal itself.
    804 #ifdef SK_SCALAR_IS_FIXED
    805     REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
    806 #else
    807     REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
    808 #endif
    809 
    810     test_matrix_min_max_stretch(reporter);
    811     test_matrix_is_similarity(reporter);
    812     test_matrix_recttorect(reporter);
    813     test_matrix_decomposition(reporter);
    814     test_matrix_homogeneous(reporter);
    815 }
    816