• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
nearly_equal_scalar(SkScalar a,SkScalar b)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 
nearly_equal(const SkMatrix & a,const SkMatrix & b)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 
is_identity(const SkMatrix & m)35 static bool is_identity(const SkMatrix& m) {
36     SkMatrix identity;
37     identity.reset();
38     return nearly_equal(m, identity);
39 }
40 
test_flatten(skiatest::Reporter * reporter,const SkMatrix & m)41 static 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 
test_matrix_max_stretch(skiatest::Reporter * reporter)60 void 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 (size_t 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 (size_t 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 (size_t 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 
TestMatrix(skiatest::Reporter * reporter)148 void 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"
244 DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix)
245