Home | History | Annotate | Download | only in tests
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 #include "Test.h"
      9 #include "SkMath.h"
     10 #include "SkMatrix.h"
     11 #include "SkMatrixUtils.h"
     12 #include "SkRandom.h"
     13 
     14 static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
     15     // Note that we get more compounded error for multiple operations when
     16     // SK_SCALAR_IS_FIXED.
     17 #ifdef SK_SCALAR_IS_FLOAT
     18     const SkScalar tolerance = SK_Scalar1 / 200000;
     19 #else
     20     const SkScalar tolerance = SK_Scalar1 / 1024;
     21 #endif
     22 
     23     return SkScalarAbs(a - b) <= tolerance;
     24 }
     25 
     26 static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
     27     for (int i = 0; i < 9; i++) {
     28         if (!nearly_equal_scalar(a[i], b[i])) {
     29             printf("not equal %g %g\n", (float)a[i], (float)b[i]);
     30             return false;
     31         }
     32     }
     33     return true;
     34 }
     35 
     36 static bool are_equal(skiatest::Reporter* reporter,
     37                       const SkMatrix& a,
     38                       const SkMatrix& b) {
     39     bool equal = a == b;
     40     bool cheapEqual = a.cheapEqualTo(b);
     41     if (equal != cheapEqual) {
     42 #ifdef SK_SCALAR_IS_FLOAT
     43         if (equal) {
     44             bool foundZeroSignDiff = false;
     45             for (int i = 0; i < 9; ++i) {
     46                 float aVal = a.get(i);
     47                 float bVal = b.get(i);
     48                 int aValI = *SkTCast<int*>(&aVal);
     49                 int bValI = *SkTCast<int*>(&bVal);
     50                 if (0 == aVal && 0 == bVal && aValI != bValI) {
     51                     foundZeroSignDiff = true;
     52                 } else {
     53                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
     54                 }
     55             }
     56             REPORTER_ASSERT(reporter, foundZeroSignDiff);
     57         } else {
     58             bool foundNaN = false;
     59             for (int i = 0; i < 9; ++i) {
     60                 float aVal = a.get(i);
     61                 float bVal = b.get(i);
     62                 int aValI = *SkTCast<int*>(&aVal);
     63                 int bValI = *SkTCast<int*>(&bVal);
     64                 if (sk_float_isnan(aVal) && aValI == bValI) {
     65                     foundNaN = true;
     66                 } else {
     67                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
     68                 }
     69             }
     70             REPORTER_ASSERT(reporter, foundNaN);
     71         }
     72 #else
     73         REPORTER_ASSERT(reporter, false);
     74 #endif
     75     }
     76     return equal;
     77 }
     78 
     79 static bool is_identity(const SkMatrix& m) {
     80     SkMatrix identity;
     81     identity.reset();
     82     return nearly_equal(m, identity);
     83 }
     84 
     85 static void test_matrix_recttorect(skiatest::Reporter* reporter) {
     86     SkRect src, dst;
     87     SkMatrix matrix;
     88 
     89     src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10);
     90     dst = src;
     91     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
     92     REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
     93     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
     94 
     95     dst.offset(SK_Scalar1, SK_Scalar1);
     96     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
     97     REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
     98     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
     99 
    100     dst.fRight += SK_Scalar1;
    101     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
    102     REPORTER_ASSERT(reporter,
    103                     (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
    104     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
    105 
    106     dst = src;
    107     dst.fRight = src.fRight * 2;
    108     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
    109     REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
    110     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
    111 }
    112 
    113 static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
    114     // add 100 in case we have a bug, I don't want to kill my stack in the test
    115     char buffer[SkMatrix::kMaxFlattenSize + 100];
    116     uint32_t size1 = m.writeToMemory(NULL);
    117     uint32_t size2 = m.writeToMemory(buffer);
    118     REPORTER_ASSERT(reporter, size1 == size2);
    119     REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
    120 
    121     SkMatrix m2;
    122     uint32_t size3 = m2.readFromMemory(buffer);
    123     REPORTER_ASSERT(reporter, size1 == size3);
    124     REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
    125 
    126     char buffer2[SkMatrix::kMaxFlattenSize + 100];
    127     size3 = m2.writeToMemory(buffer2);
    128     REPORTER_ASSERT(reporter, size1 == size3);
    129     REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
    130 }
    131 
    132 static void test_matrix_max_stretch(skiatest::Reporter* reporter) {
    133     SkMatrix identity;
    134     identity.reset();
    135     REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
    136 
    137     SkMatrix scale;
    138     scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
    139     REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch());
    140 
    141     SkMatrix rot90Scale;
    142     rot90Scale.setRotate(90 * SK_Scalar1);
    143     rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
    144     REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch());
    145 
    146     SkMatrix rotate;
    147     rotate.setRotate(128 * SK_Scalar1);
    148     REPORTER_ASSERT(reporter, SkScalarAbs(SK_Scalar1 - rotate.getMaxStretch()) <= SK_ScalarNearlyZero);
    149 
    150     SkMatrix translate;
    151     translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
    152     REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());
    153 
    154     SkMatrix perspX;
    155     perspX.reset();
    156     perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
    157     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());
    158 
    159     SkMatrix perspY;
    160     perspY.reset();
    161     perspY.setPerspX(SkScalarToPersp(-SK_Scalar1 / 500));
    162     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());
    163 
    164     SkMatrix baseMats[] = {scale, rot90Scale, rotate,
    165                            translate, perspX, perspY};
    166     SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
    167     for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
    168         mats[i] = baseMats[i];
    169         bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
    170         REPORTER_ASSERT(reporter, invertable);
    171     }
    172     SkMWCRandom rand;
    173     for (int m = 0; m < 1000; ++m) {
    174         SkMatrix mat;
    175         mat.reset();
    176         for (int i = 0; i < 4; ++i) {
    177             int x = rand.nextU() % SK_ARRAY_COUNT(mats);
    178             mat.postConcat(mats[x]);
    179         }
    180         SkScalar stretch = mat.getMaxStretch();
    181 
    182         if ((stretch < 0) != mat.hasPerspective()) {
    183             stretch = mat.getMaxStretch();
    184         }
    185 
    186         REPORTER_ASSERT(reporter, (stretch < 0) == mat.hasPerspective());
    187 
    188         if (mat.hasPerspective()) {
    189             m -= 1; // try another non-persp matrix
    190             continue;
    191         }
    192 
    193         // test a bunch of vectors. None should be scaled by more than stretch
    194         // (modulo some error) and we should find a vector that is scaled by
    195         // almost stretch.
    196         static const SkScalar gStretchTol = (105 * SK_Scalar1) / 100;
    197         static const SkScalar gMaxStretchTol = (97 * SK_Scalar1) / 100;
    198         SkScalar max = 0;
    199         SkVector vectors[1000];
    200         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
    201             vectors[i].fX = rand.nextSScalar1();
    202             vectors[i].fY = rand.nextSScalar1();
    203             if (!vectors[i].normalize()) {
    204                 i -= 1;
    205                 continue;
    206             }
    207         }
    208         mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
    209         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
    210             SkScalar d = vectors[i].length();
    211             REPORTER_ASSERT(reporter, SkScalarDiv(d, stretch) < gStretchTol);
    212             if (max < d) {
    213                 max = d;
    214             }
    215         }
    216         REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol);
    217     }
    218 }
    219 
    220 static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
    221     SkMatrix mat;
    222 
    223     // identity
    224     mat.setIdentity();
    225     REPORTER_ASSERT(reporter, mat.isSimilarity());
    226 
    227     // translation only
    228     mat.reset();
    229     mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
    230     REPORTER_ASSERT(reporter, mat.isSimilarity());
    231 
    232     // scale with same size
    233     mat.reset();
    234     mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
    235     REPORTER_ASSERT(reporter, mat.isSimilarity());
    236 
    237     // scale with one negative
    238     mat.reset();
    239     mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
    240     REPORTER_ASSERT(reporter, mat.isSimilarity());
    241 
    242     // scale with different size
    243     mat.reset();
    244     mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
    245     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    246 
    247     // scale with same size at a pivot point
    248     mat.reset();
    249     mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
    250                  SkIntToScalar(2), SkIntToScalar(2));
    251     REPORTER_ASSERT(reporter, mat.isSimilarity());
    252 
    253     // scale with different size at a pivot point
    254     mat.reset();
    255     mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
    256                  SkIntToScalar(2), SkIntToScalar(2));
    257     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    258 
    259     // skew with same size
    260     mat.reset();
    261     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
    262     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    263 
    264     // skew with different size
    265     mat.reset();
    266     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
    267     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    268 
    269     // skew with same size at a pivot point
    270     mat.reset();
    271     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
    272                 SkIntToScalar(2), SkIntToScalar(2));
    273     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    274 
    275     // skew with different size at a pivot point
    276     mat.reset();
    277     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
    278                 SkIntToScalar(2), SkIntToScalar(2));
    279     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    280 
    281     // perspective x
    282     mat.reset();
    283     mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
    284     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    285 
    286     // perspective y
    287     mat.reset();
    288     mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
    289     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    290 
    291 #ifdef SK_SCALAR_IS_FLOAT
    292     /* We bypass the following tests for SK_SCALAR_IS_FIXED build.
    293      * The long discussion can be found in this issue:
    294      *     http://codereview.appspot.com/5999050/
    295      * In short, we haven't found a perfect way to fix the precision
    296      * issue, i.e. the way we use tolerance in isSimilarityTransformation
    297      * is incorrect. The situation becomes worse in fixed build, so
    298      * we disabled rotation related tests for fixed build.
    299      */
    300 
    301     // rotate
    302     for (int angle = 0; angle < 360; ++angle) {
    303         mat.reset();
    304         mat.setRotate(SkIntToScalar(angle));
    305         REPORTER_ASSERT(reporter, mat.isSimilarity());
    306     }
    307 
    308     // see if there are any accumulated precision issues
    309     mat.reset();
    310     for (int i = 1; i < 360; i++) {
    311         mat.postRotate(SkIntToScalar(1));
    312     }
    313     REPORTER_ASSERT(reporter, mat.isSimilarity());
    314 
    315     // rotate + translate
    316     mat.reset();
    317     mat.setRotate(SkIntToScalar(30));
    318     mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
    319     REPORTER_ASSERT(reporter, mat.isSimilarity());
    320 
    321     // rotate + uniform scale
    322     mat.reset();
    323     mat.setRotate(SkIntToScalar(30));
    324     mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
    325     REPORTER_ASSERT(reporter, mat.isSimilarity());
    326 
    327     // rotate + non-uniform scale
    328     mat.reset();
    329     mat.setRotate(SkIntToScalar(30));
    330     mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
    331     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    332 #endif
    333 
    334     // all zero
    335     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
    336     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    337 
    338     // all zero except perspective
    339     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
    340     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    341 
    342     // scales zero, only skews
    343     mat.setAll(0, SK_Scalar1, 0,
    344                SK_Scalar1, 0, 0,
    345                0, 0, SkMatrix::I()[8]);
    346     REPORTER_ASSERT(reporter, mat.isSimilarity());
    347 }
    348 
    349 // For test_matrix_decomposition, below.
    350 static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
    351                                          SkScalar tolerance = SK_ScalarNearlyZero) {
    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, &scaleY, &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, &scaleY, &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, &scaleY, &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, &scaleY, &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, &scaleY, &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, &scaleY, &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, &scaleY, &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, &scaleY, &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, &scaleY, &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, &scaleY, &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, &scaleY, &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, &scaleY, &rotation1));
    482     // Because of the shear/skew we won't get the same results, so we need to multiply it out.
    483     // Generating the matrices requires doing a radian-to-degree calculation, then 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 with an absolute check
    491     REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
    492                                                            scaleX*c0*c1 - scaleY*s0*s1));
    493     REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
    494                                                            -scaleX*s0*c1 - scaleY*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::kMScaleY],
    498                                                            -scaleX*s0*s1 + scaleY*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-parallel 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, &scaleY, &rotation1));
    535     s0 = SkScalarSinCos(rotation0, &c0);
    536     s1 = SkScalarSinCos(rotation1, &c1);
    537     REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
    538                                                            scaleX*c0*c1 - scaleY*s0*s1));
    539     REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
    540                                                            -scaleX*s0*c1 - scaleY*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::kMScaleY],
    544                                                            -scaleX*s0*s1 + scaleY*c0*c1));
    545 
    546     // perspective shouldn't affect this
    547     mat[SkMatrix::kMPersp0] = 12.f;
    548     mat[SkMatrix::kMPersp1] = 4.f;
    549     mat[SkMatrix::kMPersp2] = 1872.f;
    550     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    551     s0 = SkScalarSinCos(rotation0, &c0);
    552     s1 = SkScalarSinCos(rotation1, &c1);
    553     REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
    554                                                            scaleX*c0*c1 - scaleY*s0*s1));
    555     REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
    556                                                            -scaleX*s0*c1 - scaleY*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::kMScaleY],
    560                                                            -scaleX*s0*s1 + scaleY*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, &scaleY, &rotation1));
    567     s0 = SkScalarSinCos(rotation0, &c0);
    568     s1 = SkScalarSinCos(rotation1, &c1);
    569     REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
    570                                                            scaleX*c0*c1 - scaleY*s0*s1));
    571     REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
    572                                                            -scaleX*s0*c1 - scaleY*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::kMScaleY],
    576                                                            -scaleX*s0*s1 + scaleY*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, &scaleY, &rotation1));
    583     mat.reset();
    584     mat[SkMatrix::kMScaleY] = 0.f;
    585     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &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, &scaleY, &rotation1));
    593 }
    594 
    595 static void TestMatrix(skiatest::Reporter* reporter) {
    596     SkMatrix    mat, inverse, iden1, iden2;
    597 
    598     mat.reset();
    599     mat.setTranslate(SK_Scalar1, SK_Scalar1);
    600     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    601     iden1.setConcat(mat, inverse);
    602     REPORTER_ASSERT(reporter, is_identity(iden1));
    603 
    604     mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
    605     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    606     iden1.setConcat(mat, inverse);
    607     REPORTER_ASSERT(reporter, is_identity(iden1));
    608     test_flatten(reporter, mat);
    609 
    610     mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
    611     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    612     iden1.setConcat(mat, inverse);
    613     REPORTER_ASSERT(reporter, is_identity(iden1));
    614     test_flatten(reporter, mat);
    615 
    616     mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
    617     mat.postRotate(SkIntToScalar(25));
    618     REPORTER_ASSERT(reporter, mat.invert(NULL));
    619     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    620     iden1.setConcat(mat, inverse);
    621     REPORTER_ASSERT(reporter, is_identity(iden1));
    622     iden2.setConcat(inverse, mat);
    623     REPORTER_ASSERT(reporter, is_identity(iden2));
    624     test_flatten(reporter, mat);
    625     test_flatten(reporter, iden2);
    626 
    627     mat.setScale(0, SK_Scalar1);
    628     REPORTER_ASSERT(reporter, !mat.invert(NULL));
    629     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
    630     mat.setScale(SK_Scalar1, 0);
    631     REPORTER_ASSERT(reporter, !mat.invert(NULL));
    632     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
    633 
    634     // rectStaysRect test
    635     {
    636         static const struct {
    637             SkScalar    m00, m01, m10, m11;
    638             bool        mStaysRect;
    639         }
    640         gRectStaysRectSamples[] = {
    641             {          0,          0,          0,           0, false },
    642             {          0,          0,          0,  SK_Scalar1, false },
    643             {          0,          0, SK_Scalar1,           0, false },
    644             {          0,          0, SK_Scalar1,  SK_Scalar1, false },
    645             {          0, SK_Scalar1,          0,           0, false },
    646             {          0, SK_Scalar1,          0,  SK_Scalar1, false },
    647             {          0, SK_Scalar1, SK_Scalar1,           0, true },
    648             {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
    649             { SK_Scalar1,          0,          0,           0, false },
    650             { SK_Scalar1,          0,          0,  SK_Scalar1, true },
    651             { SK_Scalar1,          0, SK_Scalar1,           0, false },
    652             { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
    653             { SK_Scalar1, SK_Scalar1,          0,           0, false },
    654             { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
    655             { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
    656             { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
    657         };
    658 
    659         for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
    660             SkMatrix    m;
    661 
    662             m.reset();
    663             m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
    664             m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
    665             m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
    666             m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
    667             REPORTER_ASSERT(reporter,
    668                     m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
    669         }
    670     }
    671 
    672     mat.reset();
    673     mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
    674     mat.set(SkMatrix::kMSkewX,  SkIntToScalar(2));
    675     mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
    676     mat.set(SkMatrix::kMSkewY,  SkIntToScalar(4));
    677     mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
    678     mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
    679     SkScalar affine[6];
    680     REPORTER_ASSERT(reporter, mat.asAffine(affine));
    681 
    682     #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
    683     REPORTER_ASSERT(reporter, affineEqual(ScaleX));
    684     REPORTER_ASSERT(reporter, affineEqual(SkewY));
    685     REPORTER_ASSERT(reporter, affineEqual(SkewX));
    686     REPORTER_ASSERT(reporter, affineEqual(ScaleY));
    687     REPORTER_ASSERT(reporter, affineEqual(TransX));
    688     REPORTER_ASSERT(reporter, affineEqual(TransY));
    689     #undef affineEqual
    690 
    691     mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
    692     REPORTER_ASSERT(reporter, !mat.asAffine(affine));
    693 
    694     SkMatrix mat2;
    695     mat2.reset();
    696     mat.reset();
    697     SkScalar zero = 0;
    698     mat.set(SkMatrix::kMSkewX, -zero);
    699     REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
    700 
    701     mat2.reset();
    702     mat.reset();
    703     mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
    704     mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
    705     // fixed pt doesn't have the property that NaN does not equal itself.
    706 #ifdef SK_SCALAR_IS_FIXED
    707     REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
    708 #else
    709     REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
    710 #endif
    711 
    712     test_matrix_max_stretch(reporter);
    713     test_matrix_is_similarity(reporter);
    714     test_matrix_recttorect(reporter);
    715     test_matrix_decomposition(reporter);
    716 }
    717 
    718 #include "TestClassDef.h"
    719 DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix)
    720