• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "Test.h"
9 #include "TestClassDef.h"
10 #include "SkMath.h"
11 #include "SkMatrix.h"
12 #include "SkMatrixUtils.h"
13 #include "SkRandom.h"
14 
nearly_equal_scalar(SkScalar a,SkScalar b)15 static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
16     // Note that we get more compounded error for multiple operations when
17     // SK_SCALAR_IS_FIXED.
18 #ifdef SK_SCALAR_IS_FLOAT
19     const SkScalar tolerance = SK_Scalar1 / 200000;
20 #else
21     const SkScalar tolerance = SK_Scalar1 / 1024;
22 #endif
23 
24     return SkScalarAbs(a - b) <= tolerance;
25 }
26 
nearly_equal(const SkMatrix & a,const SkMatrix & b)27 static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
28     for (int i = 0; i < 9; i++) {
29         if (!nearly_equal_scalar(a[i], b[i])) {
30             SkDebugf("not equal %g %g\n", (float)a[i], (float)b[i]);
31             return false;
32         }
33     }
34     return true;
35 }
36 
are_equal(skiatest::Reporter * reporter,const SkMatrix & a,const SkMatrix & b)37 static bool are_equal(skiatest::Reporter* reporter,
38                       const SkMatrix& a,
39                       const SkMatrix& b) {
40     bool equal = a == b;
41     bool cheapEqual = a.cheapEqualTo(b);
42     if (equal != cheapEqual) {
43 #ifdef SK_SCALAR_IS_FLOAT
44         if (equal) {
45             bool foundZeroSignDiff = false;
46             for (int i = 0; i < 9; ++i) {
47                 float aVal = a.get(i);
48                 float bVal = b.get(i);
49                 int aValI = *SkTCast<int*>(&aVal);
50                 int bValI = *SkTCast<int*>(&bVal);
51                 if (0 == aVal && 0 == bVal && aValI != bValI) {
52                     foundZeroSignDiff = true;
53                 } else {
54                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
55                 }
56             }
57             REPORTER_ASSERT(reporter, foundZeroSignDiff);
58         } else {
59             bool foundNaN = false;
60             for (int i = 0; i < 9; ++i) {
61                 float aVal = a.get(i);
62                 float bVal = b.get(i);
63                 int aValI = *SkTCast<int*>(&aVal);
64                 int bValI = *SkTCast<int*>(&bVal);
65                 if (sk_float_isnan(aVal) && aValI == bValI) {
66                     foundNaN = true;
67                 } else {
68                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
69                 }
70             }
71             REPORTER_ASSERT(reporter, foundNaN);
72         }
73 #else
74         REPORTER_ASSERT(reporter, false);
75 #endif
76     }
77     return equal;
78 }
79 
is_identity(const SkMatrix & m)80 static bool is_identity(const SkMatrix& m) {
81     SkMatrix identity;
82     identity.reset();
83     return nearly_equal(m, identity);
84 }
85 
test_matrix_recttorect(skiatest::Reporter * reporter)86 static void test_matrix_recttorect(skiatest::Reporter* reporter) {
87     SkRect src, dst;
88     SkMatrix matrix;
89 
90     src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10);
91     dst = src;
92     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
93     REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
94     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
95 
96     dst.offset(SK_Scalar1, SK_Scalar1);
97     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
98     REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
99     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
100 
101     dst.fRight += SK_Scalar1;
102     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
103     REPORTER_ASSERT(reporter,
104                     (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
105     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
106 
107     dst = src;
108     dst.fRight = src.fRight * 2;
109     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
110     REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
111     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
112 }
113 
test_flatten(skiatest::Reporter * reporter,const SkMatrix & m)114 static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
115     // add 100 in case we have a bug, I don't want to kill my stack in the test
116     static const size_t kBufferSize = SkMatrix::kMaxFlattenSize + 100;
117     char buffer[kBufferSize];
118     size_t size1 = m.writeToMemory(NULL);
119     size_t size2 = m.writeToMemory(buffer);
120     REPORTER_ASSERT(reporter, size1 == size2);
121     REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
122 
123     SkMatrix m2;
124     size_t size3 = m2.readFromMemory(buffer, kBufferSize);
125     REPORTER_ASSERT(reporter, size1 == size3);
126     REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
127 
128     char buffer2[kBufferSize];
129     size3 = m2.writeToMemory(buffer2);
130     REPORTER_ASSERT(reporter, size1 == size3);
131     REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
132 }
133 
test_matrix_min_max_stretch(skiatest::Reporter * reporter)134 static void test_matrix_min_max_stretch(skiatest::Reporter* reporter) {
135     SkMatrix identity;
136     identity.reset();
137     REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinStretch());
138     REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
139 
140     SkMatrix scale;
141     scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
142     REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinStretch());
143     REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch());
144 
145     SkMatrix rot90Scale;
146     rot90Scale.setRotate(90 * SK_Scalar1);
147     rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
148     REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinStretch());
149     REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch());
150 
151     SkMatrix rotate;
152     rotate.setRotate(128 * SK_Scalar1);
153     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinStretch() ,SK_ScalarNearlyZero));
154     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxStretch(), SK_ScalarNearlyZero));
155 
156     SkMatrix translate;
157     translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
158     REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinStretch());
159     REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());
160 
161     SkMatrix perspX;
162     perspX.reset();
163     perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
164     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinStretch());
165     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());
166 
167     SkMatrix perspY;
168     perspY.reset();
169     perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
170     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinStretch());
171     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());
172 
173     SkMatrix baseMats[] = {scale, rot90Scale, rotate,
174                            translate, perspX, perspY};
175     SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
176     for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
177         mats[i] = baseMats[i];
178         bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
179         REPORTER_ASSERT(reporter, invertable);
180     }
181     SkRandom rand;
182     for (int m = 0; m < 1000; ++m) {
183         SkMatrix mat;
184         mat.reset();
185         for (int i = 0; i < 4; ++i) {
186             int x = rand.nextU() % SK_ARRAY_COUNT(mats);
187             mat.postConcat(mats[x]);
188         }
189 
190         SkScalar minStretch = mat.getMinStretch();
191         SkScalar maxStretch = mat.getMaxStretch();
192         REPORTER_ASSERT(reporter, (minStretch < 0) == (maxStretch < 0));
193         REPORTER_ASSERT(reporter, (maxStretch < 0) == mat.hasPerspective());
194 
195         if (mat.hasPerspective()) {
196             m -= 1; // try another non-persp matrix
197             continue;
198         }
199 
200         // test a bunch of vectors. All should be scaled by between minStretch and maxStretch
201         // (modulo some error) and we should find a vector that is scaled by almost each.
202         static const SkScalar gVectorStretchTol = (105 * SK_Scalar1) / 100;
203         static const SkScalar gClosestStretchTol = (97 * SK_Scalar1) / 100;
204         SkScalar max = 0, min = SK_ScalarMax;
205         SkVector vectors[1000];
206         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
207             vectors[i].fX = rand.nextSScalar1();
208             vectors[i].fY = rand.nextSScalar1();
209             if (!vectors[i].normalize()) {
210                 i -= 1;
211                 continue;
212             }
213         }
214         mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
215         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
216             SkScalar d = vectors[i].length();
217             REPORTER_ASSERT(reporter, SkScalarDiv(d, maxStretch) < gVectorStretchTol);
218             REPORTER_ASSERT(reporter, SkScalarDiv(minStretch, d) < gVectorStretchTol);
219             if (max < d) {
220                 max = d;
221             }
222             if (min > d) {
223                 min = d;
224             }
225         }
226         REPORTER_ASSERT(reporter, SkScalarDiv(max, maxStretch) >= gClosestStretchTol);
227         REPORTER_ASSERT(reporter, SkScalarDiv(minStretch, min) >= gClosestStretchTol);
228     }
229 }
230 
test_matrix_is_similarity(skiatest::Reporter * reporter)231 static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
232     SkMatrix mat;
233 
234     // identity
235     mat.setIdentity();
236     REPORTER_ASSERT(reporter, mat.isSimilarity());
237 
238     // translation only
239     mat.reset();
240     mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
241     REPORTER_ASSERT(reporter, mat.isSimilarity());
242 
243     // scale with same size
244     mat.reset();
245     mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
246     REPORTER_ASSERT(reporter, mat.isSimilarity());
247 
248     // scale with one negative
249     mat.reset();
250     mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
251     REPORTER_ASSERT(reporter, mat.isSimilarity());
252 
253     // scale with different size
254     mat.reset();
255     mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
256     REPORTER_ASSERT(reporter, !mat.isSimilarity());
257 
258     // scale with same size at a pivot point
259     mat.reset();
260     mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
261                  SkIntToScalar(2), SkIntToScalar(2));
262     REPORTER_ASSERT(reporter, mat.isSimilarity());
263 
264     // scale with different size at a pivot point
265     mat.reset();
266     mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
267                  SkIntToScalar(2), SkIntToScalar(2));
268     REPORTER_ASSERT(reporter, !mat.isSimilarity());
269 
270     // skew with same size
271     mat.reset();
272     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
273     REPORTER_ASSERT(reporter, !mat.isSimilarity());
274 
275     // skew with different size
276     mat.reset();
277     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
278     REPORTER_ASSERT(reporter, !mat.isSimilarity());
279 
280     // skew with same size at a pivot point
281     mat.reset();
282     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
283                 SkIntToScalar(2), SkIntToScalar(2));
284     REPORTER_ASSERT(reporter, !mat.isSimilarity());
285 
286     // skew with different size at a pivot point
287     mat.reset();
288     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
289                 SkIntToScalar(2), SkIntToScalar(2));
290     REPORTER_ASSERT(reporter, !mat.isSimilarity());
291 
292     // perspective x
293     mat.reset();
294     mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
295     REPORTER_ASSERT(reporter, !mat.isSimilarity());
296 
297     // perspective y
298     mat.reset();
299     mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
300     REPORTER_ASSERT(reporter, !mat.isSimilarity());
301 
302 #ifdef SK_SCALAR_IS_FLOAT
303     /* We bypass the following tests for SK_SCALAR_IS_FIXED build.
304      * The long discussion can be found in this issue:
305      *     http://codereview.appspot.com/5999050/
306      * In short, we haven't found a perfect way to fix the precision
307      * issue, i.e. the way we use tolerance in isSimilarityTransformation
308      * is incorrect. The situation becomes worse in fixed build, so
309      * we disabled rotation related tests for fixed build.
310      */
311 
312     // rotate
313     for (int angle = 0; angle < 360; ++angle) {
314         mat.reset();
315         mat.setRotate(SkIntToScalar(angle));
316         REPORTER_ASSERT(reporter, mat.isSimilarity());
317     }
318 
319     // see if there are any accumulated precision issues
320     mat.reset();
321     for (int i = 1; i < 360; i++) {
322         mat.postRotate(SkIntToScalar(1));
323     }
324     REPORTER_ASSERT(reporter, mat.isSimilarity());
325 
326     // rotate + translate
327     mat.reset();
328     mat.setRotate(SkIntToScalar(30));
329     mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
330     REPORTER_ASSERT(reporter, mat.isSimilarity());
331 
332     // rotate + uniform scale
333     mat.reset();
334     mat.setRotate(SkIntToScalar(30));
335     mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
336     REPORTER_ASSERT(reporter, mat.isSimilarity());
337 
338     // rotate + non-uniform scale
339     mat.reset();
340     mat.setRotate(SkIntToScalar(30));
341     mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
342     REPORTER_ASSERT(reporter, !mat.isSimilarity());
343 #endif
344 
345     // all zero
346     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
347     REPORTER_ASSERT(reporter, !mat.isSimilarity());
348 
349     // all zero except perspective
350     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
351     REPORTER_ASSERT(reporter, !mat.isSimilarity());
352 
353     // scales zero, only skews
354     mat.setAll(0, SK_Scalar1, 0,
355                SK_Scalar1, 0, 0,
356                0, 0, SkMatrix::I()[8]);
357     REPORTER_ASSERT(reporter, mat.isSimilarity());
358 }
359 
360 // For test_matrix_decomposition, below.
scalar_nearly_equal_relative(SkScalar a,SkScalar b,SkScalar tolerance=SK_ScalarNearlyZero)361 static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
362                                          SkScalar tolerance = SK_ScalarNearlyZero) {
363     // from Bruce Dawson
364     // absolute check
365     SkScalar diff = SkScalarAbs(a - b);
366     if (diff < tolerance) {
367         return true;
368     }
369 
370     // relative check
371     a = SkScalarAbs(a);
372     b = SkScalarAbs(b);
373     SkScalar largest = (b > a) ? b : a;
374 
375     if (diff <= largest*tolerance) {
376         return true;
377     }
378 
379     return false;
380 }
381 
check_matrix_recomposition(const SkMatrix & mat,const SkPoint & rotation1,const SkPoint & scale,const SkPoint & rotation2)382 static bool check_matrix_recomposition(const SkMatrix& mat,
383                                        const SkPoint& rotation1,
384                                        const SkPoint& scale,
385                                        const SkPoint& rotation2) {
386     SkScalar c1 = rotation1.fX;
387     SkScalar s1 = rotation1.fY;
388     SkScalar scaleX = scale.fX;
389     SkScalar scaleY = scale.fY;
390     SkScalar c2 = rotation2.fX;
391     SkScalar s2 = rotation2.fY;
392 
393     // We do a relative check here because large scale factors cause problems with an absolute check
394     bool result = scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
395                                                scaleX*c1*c2 - scaleY*s1*s2) &&
396                   scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
397                                                -scaleX*s1*c2 - scaleY*c1*s2) &&
398                   scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
399                                                scaleX*c1*s2 + scaleY*s1*c2) &&
400                   scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
401                                                -scaleX*s1*s2 + scaleY*c1*c2);
402     return result;
403 }
404 
test_matrix_decomposition(skiatest::Reporter * reporter)405 static void test_matrix_decomposition(skiatest::Reporter* reporter) {
406     SkMatrix mat;
407     SkPoint rotation1, scale, rotation2;
408 
409     const float kRotation0 = 15.5f;
410     const float kRotation1 = -50.f;
411     const float kScale0 = 5000.f;
412     const float kScale1 = 0.001f;
413 
414     // identity
415     mat.reset();
416     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
417     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
418     // make sure it doesn't crash if we pass in NULLs
419     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL));
420 
421     // rotation only
422     mat.setRotate(kRotation0);
423     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
424     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
425 
426     // uniform scale only
427     mat.setScale(kScale0, kScale0);
428     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
429     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
430 
431     // anisotropic scale only
432     mat.setScale(kScale1, kScale0);
433     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
434     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
435 
436     // rotation then uniform scale
437     mat.setRotate(kRotation1);
438     mat.postScale(kScale0, kScale0);
439     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
440     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
441 
442     // uniform scale then rotation
443     mat.setScale(kScale0, kScale0);
444     mat.postRotate(kRotation1);
445     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
446     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
447 
448     // rotation then uniform scale+reflection
449     mat.setRotate(kRotation0);
450     mat.postScale(kScale1, -kScale1);
451     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
452     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
453 
454     // uniform scale+reflection, then rotate
455     mat.setScale(kScale0, -kScale0);
456     mat.postRotate(kRotation1);
457     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
458     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
459 
460     // rotation then anisotropic scale
461     mat.setRotate(kRotation1);
462     mat.postScale(kScale1, kScale0);
463     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
464     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
465 
466     // rotation then anisotropic scale
467     mat.setRotate(90);
468     mat.postScale(kScale1, kScale0);
469     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
470     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
471 
472     // anisotropic scale then rotation
473     mat.setScale(kScale1, kScale0);
474     mat.postRotate(kRotation0);
475     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
476     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
477 
478     // anisotropic scale then rotation
479     mat.setScale(kScale1, kScale0);
480     mat.postRotate(90);
481     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
482     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
483 
484     // rotation, uniform scale, then different rotation
485     mat.setRotate(kRotation1);
486     mat.postScale(kScale0, kScale0);
487     mat.postRotate(kRotation0);
488     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
489     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
490 
491     // rotation, anisotropic scale, then different rotation
492     mat.setRotate(kRotation0);
493     mat.postScale(kScale1, kScale0);
494     mat.postRotate(kRotation1);
495     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
496     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
497 
498     // rotation, anisotropic scale + reflection, then different rotation
499     mat.setRotate(kRotation0);
500     mat.postScale(-kScale1, kScale0);
501     mat.postRotate(kRotation1);
502     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
503     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
504 
505     // try some random matrices
506     SkRandom rand;
507     for (int m = 0; m < 1000; ++m) {
508         SkScalar rot0 = rand.nextRangeF(-180, 180);
509         SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
510         SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
511         SkScalar rot1 = rand.nextRangeF(-180, 180);
512         mat.setRotate(rot0);
513         mat.postScale(sx, sy);
514         mat.postRotate(rot1);
515 
516         if (SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)) {
517             REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
518         } else {
519             // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
520             SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
521                                mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
522             REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
523         }
524     }
525 
526     // translation shouldn't affect this
527     mat.postTranslate(-1000.f, 1000.f);
528     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
529     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
530 
531     // perspective shouldn't affect this
532     mat[SkMatrix::kMPersp0] = 12.f;
533     mat[SkMatrix::kMPersp1] = 4.f;
534     mat[SkMatrix::kMPersp2] = 1872.f;
535     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
536     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
537 
538     // degenerate matrices
539     // mostly zero entries
540     mat.reset();
541     mat[SkMatrix::kMScaleX] = 0.f;
542     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
543     mat.reset();
544     mat[SkMatrix::kMScaleY] = 0.f;
545     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
546     mat.reset();
547     // linearly dependent entries
548     mat[SkMatrix::kMScaleX] = 1.f;
549     mat[SkMatrix::kMSkewX] = 2.f;
550     mat[SkMatrix::kMSkewY] = 4.f;
551     mat[SkMatrix::kMScaleY] = 8.f;
552     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
553 }
554 
555 // For test_matrix_homogeneous, below.
scalar_array_nearly_equal_relative(const SkScalar a[],const SkScalar b[],int count)556 static bool scalar_array_nearly_equal_relative(const SkScalar a[], const SkScalar b[], int count) {
557     for (int i = 0; i < count; ++i) {
558         if (!scalar_nearly_equal_relative(a[i], b[i])) {
559             return false;
560         }
561     }
562     return true;
563 }
564 
565 // For test_matrix_homogeneous, below.
566 // Maps a single triple in src using m and compares results to those in dst
naive_homogeneous_mapping(const SkMatrix & m,const SkScalar src[3],const SkScalar dst[3])567 static bool naive_homogeneous_mapping(const SkMatrix& m, const SkScalar src[3],
568                                       const SkScalar dst[3]) {
569     SkScalar res[3];
570     SkScalar ms[9] = {m[0], m[1], m[2],
571                       m[3], m[4], m[5],
572                       m[6], m[7], m[8]};
573     res[0] = src[0] * ms[0] + src[1] * ms[1] + src[2] * ms[2];
574     res[1] = src[0] * ms[3] + src[1] * ms[4] + src[2] * ms[5];
575     res[2] = src[0] * ms[6] + src[1] * ms[7] + src[2] * ms[8];
576     return scalar_array_nearly_equal_relative(res, dst, 3);
577 }
578 
test_matrix_homogeneous(skiatest::Reporter * reporter)579 static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
580     SkMatrix mat;
581 
582     const float kRotation0 = 15.5f;
583     const float kRotation1 = -50.f;
584     const float kScale0 = 5000.f;
585 
586     const int kTripleCount = 1000;
587     const int kMatrixCount = 1000;
588     SkRandom rand;
589 
590     SkScalar randTriples[3*kTripleCount];
591     for (int i = 0; i < 3*kTripleCount; ++i) {
592         randTriples[i] = rand.nextRangeF(-3000.f, 3000.f);
593     }
594 
595     SkMatrix mats[kMatrixCount];
596     for (int i = 0; i < kMatrixCount; ++i) {
597         for (int j = 0; j < 9; ++j) {
598             mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
599         }
600     }
601 
602     // identity
603     {
604     mat.reset();
605     SkScalar dst[3*kTripleCount];
606     mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
607     REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3));
608     }
609 
610     // zero matrix
611     {
612     mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
613     SkScalar dst[3*kTripleCount];
614     mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
615     SkScalar zeros[3] = {0.f, 0.f, 0.f};
616     for (int i = 0; i < kTripleCount; ++i) {
617         REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3));
618     }
619     }
620 
621     // zero point
622     {
623     SkScalar zeros[3] = {0.f, 0.f, 0.f};
624     for (int i = 0; i < kMatrixCount; ++i) {
625         SkScalar dst[3];
626         mats[i].mapHomogeneousPoints(dst, zeros, 1);
627         REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3));
628     }
629     }
630 
631     // doesn't crash with null dst, src, count == 0
632     {
633     mats[0].mapHomogeneousPoints(NULL, NULL, 0);
634     }
635 
636     // uniform scale of point
637     {
638     mat.setScale(kScale0, kScale0);
639     SkScalar dst[3];
640     SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
641     SkPoint pnt;
642     pnt.set(src[0], src[1]);
643     mat.mapHomogeneousPoints(dst, src, 1);
644     mat.mapPoints(&pnt, &pnt, 1);
645     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
646     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
647     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
648     }
649 
650     // rotation of point
651     {
652     mat.setRotate(kRotation0);
653     SkScalar dst[3];
654     SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
655     SkPoint pnt;
656     pnt.set(src[0], src[1]);
657     mat.mapHomogeneousPoints(dst, src, 1);
658     mat.mapPoints(&pnt, &pnt, 1);
659     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
660     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
661     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
662     }
663 
664     // rotation, scale, rotation of point
665     {
666     mat.setRotate(kRotation1);
667     mat.postScale(kScale0, kScale0);
668     mat.postRotate(kRotation0);
669     SkScalar dst[3];
670     SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
671     SkPoint pnt;
672     pnt.set(src[0], src[1]);
673     mat.mapHomogeneousPoints(dst, src, 1);
674     mat.mapPoints(&pnt, &pnt, 1);
675     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
676     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
677     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
678     }
679 
680     // compare with naive approach
681     {
682     for (int i = 0; i < kMatrixCount; ++i) {
683         for (int j = 0; j < kTripleCount; ++j) {
684             SkScalar dst[3];
685             mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1);
686             REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst));
687         }
688     }
689     }
690 
691 }
692 
DEF_TEST(Matrix,reporter)693 DEF_TEST(Matrix, reporter) {
694     SkMatrix    mat, inverse, iden1, iden2;
695 
696     mat.reset();
697     mat.setTranslate(SK_Scalar1, SK_Scalar1);
698     REPORTER_ASSERT(reporter, mat.invert(&inverse));
699     iden1.setConcat(mat, inverse);
700     REPORTER_ASSERT(reporter, is_identity(iden1));
701 
702     mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
703     REPORTER_ASSERT(reporter, mat.invert(&inverse));
704     iden1.setConcat(mat, inverse);
705     REPORTER_ASSERT(reporter, is_identity(iden1));
706     test_flatten(reporter, mat);
707 
708     mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
709     REPORTER_ASSERT(reporter, mat.invert(&inverse));
710     iden1.setConcat(mat, inverse);
711     REPORTER_ASSERT(reporter, is_identity(iden1));
712     test_flatten(reporter, mat);
713 
714     mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
715     mat.postRotate(SkIntToScalar(25));
716     REPORTER_ASSERT(reporter, mat.invert(NULL));
717     REPORTER_ASSERT(reporter, mat.invert(&inverse));
718     iden1.setConcat(mat, inverse);
719     REPORTER_ASSERT(reporter, is_identity(iden1));
720     iden2.setConcat(inverse, mat);
721     REPORTER_ASSERT(reporter, is_identity(iden2));
722     test_flatten(reporter, mat);
723     test_flatten(reporter, iden2);
724 
725     mat.setScale(0, SK_Scalar1);
726     REPORTER_ASSERT(reporter, !mat.invert(NULL));
727     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
728     mat.setScale(SK_Scalar1, 0);
729     REPORTER_ASSERT(reporter, !mat.invert(NULL));
730     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
731 
732     // rectStaysRect test
733     {
734         static const struct {
735             SkScalar    m00, m01, m10, m11;
736             bool        mStaysRect;
737         }
738         gRectStaysRectSamples[] = {
739             {          0,          0,          0,           0, false },
740             {          0,          0,          0,  SK_Scalar1, false },
741             {          0,          0, SK_Scalar1,           0, false },
742             {          0,          0, SK_Scalar1,  SK_Scalar1, false },
743             {          0, SK_Scalar1,          0,           0, false },
744             {          0, SK_Scalar1,          0,  SK_Scalar1, false },
745             {          0, SK_Scalar1, SK_Scalar1,           0, true },
746             {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
747             { SK_Scalar1,          0,          0,           0, false },
748             { SK_Scalar1,          0,          0,  SK_Scalar1, true },
749             { SK_Scalar1,          0, SK_Scalar1,           0, false },
750             { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
751             { SK_Scalar1, SK_Scalar1,          0,           0, false },
752             { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
753             { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
754             { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
755         };
756 
757         for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
758             SkMatrix    m;
759 
760             m.reset();
761             m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
762             m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
763             m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
764             m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
765             REPORTER_ASSERT(reporter,
766                     m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
767         }
768     }
769 
770     mat.reset();
771     mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
772     mat.set(SkMatrix::kMSkewX,  SkIntToScalar(2));
773     mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
774     mat.set(SkMatrix::kMSkewY,  SkIntToScalar(4));
775     mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
776     mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
777     SkScalar affine[6];
778     REPORTER_ASSERT(reporter, mat.asAffine(affine));
779 
780     #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
781     REPORTER_ASSERT(reporter, affineEqual(ScaleX));
782     REPORTER_ASSERT(reporter, affineEqual(SkewY));
783     REPORTER_ASSERT(reporter, affineEqual(SkewX));
784     REPORTER_ASSERT(reporter, affineEqual(ScaleY));
785     REPORTER_ASSERT(reporter, affineEqual(TransX));
786     REPORTER_ASSERT(reporter, affineEqual(TransY));
787     #undef affineEqual
788 
789     mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
790     REPORTER_ASSERT(reporter, !mat.asAffine(affine));
791 
792     SkMatrix mat2;
793     mat2.reset();
794     mat.reset();
795     SkScalar zero = 0;
796     mat.set(SkMatrix::kMSkewX, -zero);
797     REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
798 
799     mat2.reset();
800     mat.reset();
801     mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
802     mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
803     // fixed pt doesn't have the property that NaN does not equal itself.
804 #ifdef SK_SCALAR_IS_FIXED
805     REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
806 #else
807     REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
808 #endif
809 
810     test_matrix_min_max_stretch(reporter);
811     test_matrix_is_similarity(reporter);
812     test_matrix_recttorect(reporter);
813     test_matrix_decomposition(reporter);
814     test_matrix_homogeneous(reporter);
815 }
816