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 "SkRandom.h"
     12 
     13 static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
     14     // Note that we get more compounded error for multiple operations when
     15     // SK_SCALAR_IS_FIXED.
     16 #ifdef SK_SCALAR_IS_FLOAT
     17     const SkScalar tolerance = SK_Scalar1 / 200000;
     18 #else
     19     const SkScalar tolerance = SK_Scalar1 / 1024;
     20 #endif
     21 
     22     return SkScalarAbs(a - b) <= tolerance;
     23 }
     24 
     25 static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
     26     for (int i = 0; i < 9; i++) {
     27         if (!nearly_equal_scalar(a[i], b[i])) {
     28             printf("not equal %g %g\n", (float)a[i], (float)b[i]);
     29             return false;
     30         }
     31     }
     32     return true;
     33 }
     34 
     35 static bool are_equal(skiatest::Reporter* reporter,
     36                       const SkMatrix& a,
     37                       const SkMatrix& b) {
     38     bool equal = a == b;
     39     bool cheapEqual = a.cheapEqualTo(b);
     40     if (equal != cheapEqual) {
     41 #ifdef SK_SCALAR_IS_FLOAT
     42         if (equal) {
     43             bool foundZeroSignDiff = false;
     44             for (int i = 0; i < 9; ++i) {
     45                 float aVal = a.get(i);
     46                 float bVal = b.get(i);
     47                 int aValI = *SkTCast<int*>(&aVal);
     48                 int bValI = *SkTCast<int*>(&bVal);
     49                 if (0 == aVal && 0 == bVal && aValI != bValI) {
     50                     foundZeroSignDiff = true;
     51                 } else {
     52                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
     53                 }
     54             }
     55             REPORTER_ASSERT(reporter, foundZeroSignDiff);
     56         } else {
     57             bool foundNaN = false;
     58             for (int i = 0; i < 9; ++i) {
     59                 float aVal = a.get(i);
     60                 float bVal = b.get(i);
     61                 int aValI = *SkTCast<int*>(&aVal);
     62                 int bValI = *SkTCast<int*>(&bVal);
     63                 if (sk_float_isnan(aVal) && aValI == bValI) {
     64                     foundNaN = true;
     65                 } else {
     66                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
     67                 }
     68             }
     69             REPORTER_ASSERT(reporter, foundNaN);
     70         }
     71 #else
     72         REPORTER_ASSERT(reporter, false);
     73 #endif
     74     }
     75     return equal;
     76 }
     77 
     78 static bool is_identity(const SkMatrix& m) {
     79     SkMatrix identity;
     80     identity.reset();
     81     return nearly_equal(m, identity);
     82 }
     83 
     84 static void test_matrix_recttorect(skiatest::Reporter* reporter) {
     85     SkRect src, dst;
     86     SkMatrix matrix;
     87 
     88     src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10);
     89     dst = src;
     90     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
     91     REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
     92     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
     93 
     94     dst.offset(SK_Scalar1, SK_Scalar1);
     95     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
     96     REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
     97     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
     98 
     99     dst.fRight += SK_Scalar1;
    100     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
    101     REPORTER_ASSERT(reporter,
    102                     (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
    103     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
    104 
    105     dst = src;
    106     dst.fRight = src.fRight * 2;
    107     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
    108     REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
    109     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
    110 }
    111 
    112 static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
    113     // add 100 in case we have a bug, I don't want to kill my stack in the test
    114     char buffer[SkMatrix::kMaxFlattenSize + 100];
    115     uint32_t size1 = m.writeToMemory(NULL);
    116     uint32_t size2 = m.writeToMemory(buffer);
    117     REPORTER_ASSERT(reporter, size1 == size2);
    118     REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
    119 
    120     SkMatrix m2;
    121     uint32_t size3 = m2.readFromMemory(buffer);
    122     REPORTER_ASSERT(reporter, size1 == size3);
    123     REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
    124 
    125     char buffer2[SkMatrix::kMaxFlattenSize + 100];
    126     size3 = m2.writeToMemory(buffer2);
    127     REPORTER_ASSERT(reporter, size1 == size3);
    128     REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
    129 }
    130 
    131 static void test_matrix_max_stretch(skiatest::Reporter* reporter) {
    132     SkMatrix identity;
    133     identity.reset();
    134     REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
    135 
    136     SkMatrix scale;
    137     scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
    138     REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch());
    139 
    140     SkMatrix rot90Scale;
    141     rot90Scale.setRotate(90 * SK_Scalar1);
    142     rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
    143     REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch());
    144 
    145     SkMatrix rotate;
    146     rotate.setRotate(128 * SK_Scalar1);
    147     REPORTER_ASSERT(reporter, SkScalarAbs(SK_Scalar1 - rotate.getMaxStretch()) <= SK_ScalarNearlyZero);
    148 
    149     SkMatrix translate;
    150     translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
    151     REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());
    152 
    153     SkMatrix perspX;
    154     perspX.reset();
    155     perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
    156     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());
    157 
    158     SkMatrix perspY;
    159     perspY.reset();
    160     perspY.setPerspX(SkScalarToPersp(-SK_Scalar1 / 500));
    161     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());
    162 
    163     SkMatrix baseMats[] = {scale, rot90Scale, rotate,
    164                            translate, perspX, perspY};
    165     SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
    166     for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
    167         mats[i] = baseMats[i];
    168         bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
    169         REPORTER_ASSERT(reporter, invertable);
    170     }
    171     SkRandom rand;
    172     for (int m = 0; m < 1000; ++m) {
    173         SkMatrix mat;
    174         mat.reset();
    175         for (int i = 0; i < 4; ++i) {
    176             int x = rand.nextU() % SK_ARRAY_COUNT(mats);
    177             mat.postConcat(mats[x]);
    178         }
    179         SkScalar stretch = mat.getMaxStretch();
    180 
    181         if ((stretch < 0) != mat.hasPerspective()) {
    182             stretch = mat.getMaxStretch();
    183         }
    184 
    185         REPORTER_ASSERT(reporter, (stretch < 0) == mat.hasPerspective());
    186 
    187         if (mat.hasPerspective()) {
    188             m -= 1; // try another non-persp matrix
    189             continue;
    190         }
    191 
    192         // test a bunch of vectors. None should be scaled by more than stretch
    193         // (modulo some error) and we should find a vector that is scaled by
    194         // almost stretch.
    195         static const SkScalar gStretchTol = (105 * SK_Scalar1) / 100;
    196         static const SkScalar gMaxStretchTol = (97 * SK_Scalar1) / 100;
    197         SkScalar max = 0;
    198         SkVector vectors[1000];
    199         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
    200             vectors[i].fX = rand.nextSScalar1();
    201             vectors[i].fY = rand.nextSScalar1();
    202             if (!vectors[i].normalize()) {
    203                 i -= 1;
    204                 continue;
    205             }
    206         }
    207         mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
    208         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
    209             SkScalar d = vectors[i].length();
    210             REPORTER_ASSERT(reporter, SkScalarDiv(d, stretch) < gStretchTol);
    211             if (max < d) {
    212                 max = d;
    213             }
    214         }
    215         REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol);
    216     }
    217 }
    218 
    219 static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
    220     SkMatrix mat;
    221 
    222     // identity
    223     mat.setIdentity();
    224     REPORTER_ASSERT(reporter, mat.isSimilarity());
    225 
    226     // translation only
    227     mat.reset();
    228     mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
    229     REPORTER_ASSERT(reporter, mat.isSimilarity());
    230 
    231     // scale with same size
    232     mat.reset();
    233     mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
    234     REPORTER_ASSERT(reporter, mat.isSimilarity());
    235 
    236     // scale with one negative
    237     mat.reset();
    238     mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
    239     REPORTER_ASSERT(reporter, mat.isSimilarity());
    240 
    241     // scale with different size
    242     mat.reset();
    243     mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
    244     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    245 
    246     // scale with same size at a pivot point
    247     mat.reset();
    248     mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
    249                  SkIntToScalar(2), SkIntToScalar(2));
    250     REPORTER_ASSERT(reporter, mat.isSimilarity());
    251 
    252     // scale with different size at a pivot point
    253     mat.reset();
    254     mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
    255                  SkIntToScalar(2), SkIntToScalar(2));
    256     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    257 
    258     // skew with same size
    259     mat.reset();
    260     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
    261     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    262 
    263     // skew with different size
    264     mat.reset();
    265     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
    266     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    267 
    268     // skew with same size at a pivot point
    269     mat.reset();
    270     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
    271                 SkIntToScalar(2), SkIntToScalar(2));
    272     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    273 
    274     // skew with different size at a pivot point
    275     mat.reset();
    276     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
    277                 SkIntToScalar(2), SkIntToScalar(2));
    278     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    279 
    280     // perspective x
    281     mat.reset();
    282     mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
    283     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    284 
    285     // perspective y
    286     mat.reset();
    287     mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
    288     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    289 
    290 #ifdef SK_SCALAR_IS_FLOAT
    291     /* We bypass the following tests for SK_SCALAR_IS_FIXED build.
    292      * The long discussion can be found in this issue:
    293      *     http://codereview.appspot.com/5999050/
    294      * In short, we haven't found a perfect way to fix the precision
    295      * issue, i.e. the way we use tolerance in isSimilarityTransformation
    296      * is incorrect. The situation becomes worse in fixed build, so
    297      * we disabled rotation related tests for fixed build.
    298      */
    299 
    300     // rotate
    301     for (int angle = 0; angle < 360; ++angle) {
    302         mat.reset();
    303         mat.setRotate(SkIntToScalar(angle));
    304         REPORTER_ASSERT(reporter, mat.isSimilarity());
    305     }
    306 
    307     // see if there are any accumulated precision issues
    308     mat.reset();
    309     for (int i = 1; i < 360; i++) {
    310         mat.postRotate(SkIntToScalar(1));
    311     }
    312     REPORTER_ASSERT(reporter, mat.isSimilarity());
    313 
    314     // rotate + translate
    315     mat.reset();
    316     mat.setRotate(SkIntToScalar(30));
    317     mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
    318     REPORTER_ASSERT(reporter, mat.isSimilarity());
    319 
    320     // rotate + uniform scale
    321     mat.reset();
    322     mat.setRotate(SkIntToScalar(30));
    323     mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
    324     REPORTER_ASSERT(reporter, mat.isSimilarity());
    325 
    326     // rotate + non-uniform scale
    327     mat.reset();
    328     mat.setRotate(SkIntToScalar(30));
    329     mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
    330     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    331 #endif
    332 
    333     // all zero
    334     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
    335     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    336 
    337     // all zero except perspective
    338     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
    339     REPORTER_ASSERT(reporter, !mat.isSimilarity());
    340 
    341     // scales zero, only skews
    342     mat.setAll(0, SK_Scalar1, 0,
    343                SK_Scalar1, 0, 0,
    344                0, 0, SkMatrix::I()[8]);
    345     REPORTER_ASSERT(reporter, mat.isSimilarity());
    346 }
    347 
    348 static void TestMatrix(skiatest::Reporter* reporter) {
    349     SkMatrix    mat, inverse, iden1, iden2;
    350 
    351     mat.reset();
    352     mat.setTranslate(SK_Scalar1, SK_Scalar1);
    353     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    354     iden1.setConcat(mat, inverse);
    355     REPORTER_ASSERT(reporter, is_identity(iden1));
    356 
    357     mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
    358     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    359     iden1.setConcat(mat, inverse);
    360     REPORTER_ASSERT(reporter, is_identity(iden1));
    361     test_flatten(reporter, mat);
    362 
    363     mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
    364     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    365     iden1.setConcat(mat, inverse);
    366     REPORTER_ASSERT(reporter, is_identity(iden1));
    367     test_flatten(reporter, mat);
    368 
    369     mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
    370     mat.postRotate(SkIntToScalar(25));
    371     REPORTER_ASSERT(reporter, mat.invert(NULL));
    372     REPORTER_ASSERT(reporter, mat.invert(&inverse));
    373     iden1.setConcat(mat, inverse);
    374     REPORTER_ASSERT(reporter, is_identity(iden1));
    375     iden2.setConcat(inverse, mat);
    376     REPORTER_ASSERT(reporter, is_identity(iden2));
    377     test_flatten(reporter, mat);
    378     test_flatten(reporter, iden2);
    379 
    380     mat.setScale(0, SK_Scalar1);
    381     REPORTER_ASSERT(reporter, !mat.invert(NULL));
    382     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
    383     mat.setScale(SK_Scalar1, 0);
    384     REPORTER_ASSERT(reporter, !mat.invert(NULL));
    385     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
    386 
    387     // rectStaysRect test
    388     {
    389         static const struct {
    390             SkScalar    m00, m01, m10, m11;
    391             bool        mStaysRect;
    392         }
    393         gRectStaysRectSamples[] = {
    394             {          0,          0,          0,           0, false },
    395             {          0,          0,          0,  SK_Scalar1, false },
    396             {          0,          0, SK_Scalar1,           0, false },
    397             {          0,          0, SK_Scalar1,  SK_Scalar1, false },
    398             {          0, SK_Scalar1,          0,           0, false },
    399             {          0, SK_Scalar1,          0,  SK_Scalar1, false },
    400             {          0, SK_Scalar1, SK_Scalar1,           0, true },
    401             {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
    402             { SK_Scalar1,          0,          0,           0, false },
    403             { SK_Scalar1,          0,          0,  SK_Scalar1, true },
    404             { SK_Scalar1,          0, SK_Scalar1,           0, false },
    405             { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
    406             { SK_Scalar1, SK_Scalar1,          0,           0, false },
    407             { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
    408             { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
    409             { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
    410         };
    411 
    412         for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
    413             SkMatrix    m;
    414 
    415             m.reset();
    416             m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
    417             m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
    418             m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
    419             m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
    420             REPORTER_ASSERT(reporter,
    421                     m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
    422         }
    423     }
    424 
    425     mat.reset();
    426     mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
    427     mat.set(SkMatrix::kMSkewX,  SkIntToScalar(2));
    428     mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
    429     mat.set(SkMatrix::kMSkewY,  SkIntToScalar(4));
    430     mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
    431     mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
    432     SkScalar affine[6];
    433     REPORTER_ASSERT(reporter, mat.asAffine(affine));
    434 
    435     #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
    436     REPORTER_ASSERT(reporter, affineEqual(ScaleX));
    437     REPORTER_ASSERT(reporter, affineEqual(SkewY));
    438     REPORTER_ASSERT(reporter, affineEqual(SkewX));
    439     REPORTER_ASSERT(reporter, affineEqual(ScaleY));
    440     REPORTER_ASSERT(reporter, affineEqual(TransX));
    441     REPORTER_ASSERT(reporter, affineEqual(TransY));
    442     #undef affineEqual
    443 
    444     mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
    445     REPORTER_ASSERT(reporter, !mat.asAffine(affine));
    446 
    447     SkMatrix mat2;
    448     mat2.reset();
    449     mat.reset();
    450     SkScalar zero = 0;
    451     mat.set(SkMatrix::kMSkewX, -zero);
    452     REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
    453 
    454     mat2.reset();
    455     mat.reset();
    456     mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
    457     mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
    458     // fixed pt doesn't have the property that NaN does not equal itself.
    459 #ifdef SK_SCALAR_IS_FIXED
    460     REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
    461 #else
    462     REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
    463 #endif
    464 
    465     test_matrix_max_stretch(reporter);
    466     test_matrix_is_similarity(reporter);
    467     test_matrix_recttorect(reporter);
    468 }
    469 
    470 #include "TestClassDef.h"
    471 DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix)
    472