MatrixTest.cpp revision 07faed110275048c83a55ae39042da2c9d916108
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
13static 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
25static 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
35static bool is_identity(const SkMatrix& m) {
36    SkMatrix identity;
37    identity.reset();
38    return nearly_equal(m, identity);
39}
40
41static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
42    // add 100 in case we have a bug, I don't want to kill my stack in the test
43    char buffer[SkMatrix::kMaxFlattenSize + 100];
44    uint32_t size1 = m.flatten(NULL);
45    uint32_t size2 = m.flatten(buffer);
46    REPORTER_ASSERT(reporter, size1 == size2);
47    REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
48
49    SkMatrix m2;
50    uint32_t size3 = m2.unflatten(buffer);
51    REPORTER_ASSERT(reporter, size1 == size2);
52    REPORTER_ASSERT(reporter, m == m2);
53
54    char buffer2[SkMatrix::kMaxFlattenSize + 100];
55    size3 = m2.flatten(buffer2);
56    REPORTER_ASSERT(reporter, size1 == size2);
57    REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
58}
59
60void test_matrix_max_stretch(skiatest::Reporter* reporter) {
61    SkMatrix identity;
62    identity.reset();
63    REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
64
65    SkMatrix scale;
66    scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
67    REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch());
68
69    SkMatrix rot90Scale;
70    rot90Scale.setRotate(90 * SK_Scalar1);
71    rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
72    REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch());
73
74    SkMatrix rotate;
75    rotate.setRotate(128 * SK_Scalar1);
76    REPORTER_ASSERT(reporter, SkScalarAbs(SK_Scalar1 - rotate.getMaxStretch()) <= SK_ScalarNearlyZero);
77
78    SkMatrix translate;
79    translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
80    REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());
81
82    SkMatrix perspX;
83    perspX.reset();
84    perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
85    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());
86
87    SkMatrix perspY;
88    perspY.reset();
89    perspY.setPerspX(SkScalarToPersp(-SK_Scalar1 / 500));
90    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());
91
92    SkMatrix baseMats[] = {scale, rot90Scale, rotate,
93                           translate, perspX, perspY};
94    SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
95    for (int i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
96        mats[i] = baseMats[i];
97        bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
98        REPORTER_ASSERT(reporter, invertable);
99    }
100    SkRandom rand;
101    for (int m = 0; m < 1000; ++m) {
102        SkMatrix mat;
103        mat.reset();
104        for (int i = 0; i < 4; ++i) {
105            int x = rand.nextU() % SK_ARRAY_COUNT(mats);
106            mat.postConcat(mats[x]);
107        }
108        SkScalar stretch = mat.getMaxStretch();
109
110        if ((stretch < 0) != mat.hasPerspective()) {
111            stretch = mat.getMaxStretch();
112        }
113
114        REPORTER_ASSERT(reporter, (stretch < 0) == mat.hasPerspective());
115
116        if (mat.hasPerspective()) {
117            m -= 1; // try another non-persp matrix
118            continue;
119        }
120
121        // test a bunch of vectors. None should be scaled by more than stretch
122        // (modulo some error) and we should find a vector that is scaled by
123        // almost stretch.
124        static const SkScalar gStretchTol = (105 * SK_Scalar1) / 100;
125        static const SkScalar gMaxStretchTol = (97 * SK_Scalar1) / 100;
126        SkScalar max = 0;
127        SkVector vectors[1000];
128        for (int i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
129            vectors[i].fX = rand.nextSScalar1();
130            vectors[i].fY = rand.nextSScalar1();
131            if (!vectors[i].normalize()) {
132                i -= 1;
133                continue;
134            }
135        }
136        mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
137        for (int i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
138            SkScalar d = vectors[i].length();
139            REPORTER_ASSERT(reporter, SkScalarDiv(d, stretch) < gStretchTol);
140            if (max < d) {
141                max = d;
142            }
143        }
144        REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol);
145    }
146}
147
148void TestMatrix(skiatest::Reporter* reporter) {
149    SkMatrix    mat, inverse, iden1, iden2;
150
151    mat.reset();
152    mat.setTranslate(SK_Scalar1, SK_Scalar1);
153    mat.invert(&inverse);
154    iden1.setConcat(mat, inverse);
155    REPORTER_ASSERT(reporter, is_identity(iden1));
156
157    mat.setScale(SkIntToScalar(2), SkIntToScalar(2));
158    mat.invert(&inverse);
159    iden1.setConcat(mat, inverse);
160    REPORTER_ASSERT(reporter, is_identity(iden1));
161    test_flatten(reporter, mat);
162
163    mat.setScale(SK_Scalar1/2, SK_Scalar1/2);
164    mat.invert(&inverse);
165    iden1.setConcat(mat, inverse);
166    REPORTER_ASSERT(reporter, is_identity(iden1));
167    test_flatten(reporter, mat);
168
169    mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
170    mat.postRotate(SkIntToScalar(25));
171    REPORTER_ASSERT(reporter, mat.invert(NULL));
172    mat.invert(&inverse);
173    iden1.setConcat(mat, inverse);
174    REPORTER_ASSERT(reporter, is_identity(iden1));
175    iden2.setConcat(inverse, mat);
176    REPORTER_ASSERT(reporter, is_identity(iden2));
177    test_flatten(reporter, mat);
178    test_flatten(reporter, iden2);
179
180    // rectStaysRect test
181    {
182        static const struct {
183            SkScalar    m00, m01, m10, m11;
184            bool        mStaysRect;
185        }
186        gRectStaysRectSamples[] = {
187            {          0,          0,          0,           0, false },
188            {          0,          0,          0,  SK_Scalar1, false },
189            {          0,          0, SK_Scalar1,           0, false },
190            {          0,          0, SK_Scalar1,  SK_Scalar1, false },
191            {          0, SK_Scalar1,          0,           0, false },
192            {          0, SK_Scalar1,          0,  SK_Scalar1, false },
193            {          0, SK_Scalar1, SK_Scalar1,           0, true },
194            {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
195            { SK_Scalar1,          0,          0,           0, false },
196            { SK_Scalar1,          0,          0,  SK_Scalar1, true },
197            { SK_Scalar1,          0, SK_Scalar1,           0, false },
198            { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
199            { SK_Scalar1, SK_Scalar1,          0,           0, false },
200            { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
201            { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
202            { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
203        };
204
205        for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
206            SkMatrix    m;
207
208            m.reset();
209            m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
210            m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
211            m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
212            m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
213            REPORTER_ASSERT(reporter,
214                    m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
215        }
216    }
217
218    mat.reset();
219    mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
220    mat.set(SkMatrix::kMSkewX,  SkIntToScalar(2));
221    mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
222    mat.set(SkMatrix::kMSkewY,  SkIntToScalar(4));
223    mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
224    mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
225    SkScalar affine[6];
226    REPORTER_ASSERT(reporter, mat.asAffine(affine));
227
228    #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
229    REPORTER_ASSERT(reporter, affineEqual(ScaleX));
230    REPORTER_ASSERT(reporter, affineEqual(SkewY));
231    REPORTER_ASSERT(reporter, affineEqual(SkewX));
232    REPORTER_ASSERT(reporter, affineEqual(ScaleY));
233    REPORTER_ASSERT(reporter, affineEqual(TransX));
234    REPORTER_ASSERT(reporter, affineEqual(TransY));
235    #undef affineEqual
236
237    mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
238    REPORTER_ASSERT(reporter, !mat.asAffine(affine));
239
240    test_matrix_max_stretch(reporter);
241}
242
243#include "TestClassDef.h"
244DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix)
245