• 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 
are_equal(skiatest::Reporter * reporter,const SkMatrix & a,const SkMatrix & b)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 
is_identity(const SkMatrix & m)78 static bool is_identity(const SkMatrix& m) {
79     SkMatrix identity;
80     identity.reset();
81     return nearly_equal(m, identity);
82 }
83 
test_matrix_recttorect(skiatest::Reporter * reporter)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 
test_flatten(skiatest::Reporter * reporter,const SkMatrix & m)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 
test_matrix_max_stretch(skiatest::Reporter * reporter)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 
test_matrix_is_similarity(skiatest::Reporter * reporter)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 
TestMatrix(skiatest::Reporter * reporter)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