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