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 "SkMath.h"
9 #include "SkMatrixPriv.h"
10 #include "SkMatrixUtils.h"
11 #include "SkPoint3.h"
12 #include "SkRandom.h"
13 #include "Test.h"
14 
nearly_equal_scalar(SkScalar a,SkScalar b)15 static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
16     const SkScalar tolerance = SK_Scalar1 / 200000;
17     return SkScalarAbs(a - b) <= tolerance;
18 }
19 
nearly_equal(const SkMatrix & a,const SkMatrix & b)20 static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
21     for (int i = 0; i < 9; i++) {
22         if (!nearly_equal_scalar(a[i], b[i])) {
23             SkDebugf("not equal %g %g\n", (float)a[i], (float)b[i]);
24             return false;
25         }
26     }
27     return true;
28 }
29 
are_equal(skiatest::Reporter * reporter,const SkMatrix & a,const SkMatrix & b)30 static bool are_equal(skiatest::Reporter* reporter,
31                       const SkMatrix& a,
32                       const SkMatrix& b) {
33     bool equal = a == b;
34     bool cheapEqual = a.cheapEqualTo(b);
35     if (equal != cheapEqual) {
36         if (equal) {
37             bool foundZeroSignDiff = false;
38             for (int i = 0; i < 9; ++i) {
39                 float aVal = a.get(i);
40                 float bVal = b.get(i);
41                 int aValI = *SkTCast<int*>(&aVal);
42                 int bValI = *SkTCast<int*>(&bVal);
43                 if (0 == aVal && 0 == bVal && aValI != bValI) {
44                     foundZeroSignDiff = true;
45                 } else {
46                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
47                 }
48             }
49             REPORTER_ASSERT(reporter, foundZeroSignDiff);
50         } else {
51             bool foundNaN = false;
52             for (int i = 0; i < 9; ++i) {
53                 float aVal = a.get(i);
54                 float bVal = b.get(i);
55                 int aValI = *SkTCast<int*>(&aVal);
56                 int bValI = *SkTCast<int*>(&bVal);
57                 if (sk_float_isnan(aVal) && aValI == bValI) {
58                     foundNaN = true;
59                 } else {
60                     REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
61                 }
62             }
63             REPORTER_ASSERT(reporter, foundNaN);
64         }
65     }
66     return equal;
67 }
68 
is_identity(const SkMatrix & m)69 static bool is_identity(const SkMatrix& m) {
70     SkMatrix identity;
71     identity.reset();
72     return nearly_equal(m, identity);
73 }
74 
assert9(skiatest::Reporter * reporter,const SkMatrix & m,SkScalar a,SkScalar b,SkScalar c,SkScalar d,SkScalar e,SkScalar f,SkScalar g,SkScalar h,SkScalar i)75 static void assert9(skiatest::Reporter* reporter, const SkMatrix& m,
76                     SkScalar a, SkScalar b, SkScalar c,
77                     SkScalar d, SkScalar e, SkScalar f,
78                     SkScalar g, SkScalar h, SkScalar i) {
79     SkScalar buffer[9];
80     m.get9(buffer);
81     REPORTER_ASSERT(reporter, buffer[0] == a);
82     REPORTER_ASSERT(reporter, buffer[1] == b);
83     REPORTER_ASSERT(reporter, buffer[2] == c);
84     REPORTER_ASSERT(reporter, buffer[3] == d);
85     REPORTER_ASSERT(reporter, buffer[4] == e);
86     REPORTER_ASSERT(reporter, buffer[5] == f);
87     REPORTER_ASSERT(reporter, buffer[6] == g);
88     REPORTER_ASSERT(reporter, buffer[7] == h);
89     REPORTER_ASSERT(reporter, buffer[8] == i);
90 }
91 
test_set9(skiatest::Reporter * reporter)92 static void test_set9(skiatest::Reporter* reporter) {
93 
94     SkMatrix m;
95     m.reset();
96     assert9(reporter, m, 1, 0, 0, 0, 1, 0, 0, 0, 1);
97 
98     m.setScale(2, 3);
99     assert9(reporter, m, 2, 0, 0, 0, 3, 0, 0, 0, 1);
100 
101     m.postTranslate(4, 5);
102     assert9(reporter, m, 2, 0, 4, 0, 3, 5, 0, 0, 1);
103 
104     SkScalar buffer[9];
105     sk_bzero(buffer, sizeof(buffer));
106     buffer[SkMatrix::kMScaleX] = 1;
107     buffer[SkMatrix::kMScaleY] = 1;
108     buffer[SkMatrix::kMPersp2] = 1;
109     REPORTER_ASSERT(reporter, !m.isIdentity());
110     m.set9(buffer);
111     REPORTER_ASSERT(reporter, m.isIdentity());
112 }
113 
test_matrix_recttorect(skiatest::Reporter * reporter)114 static void test_matrix_recttorect(skiatest::Reporter* reporter) {
115     SkRect src, dst;
116     SkMatrix matrix;
117 
118     src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10);
119     dst = src;
120     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
121     REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
122     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
123 
124     dst.offset(SK_Scalar1, SK_Scalar1);
125     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
126     REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
127     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
128 
129     dst.fRight += SK_Scalar1;
130     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
131     REPORTER_ASSERT(reporter,
132                     (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
133     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
134 
135     dst = src;
136     dst.fRight = src.fRight * 2;
137     matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
138     REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
139     REPORTER_ASSERT(reporter, matrix.rectStaysRect());
140 }
141 
test_flatten(skiatest::Reporter * reporter,const SkMatrix & m)142 static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
143     // add 100 in case we have a bug, I don't want to kill my stack in the test
144     static const size_t kBufferSize = SkMatrixPriv::kMaxFlattenSize + 100;
145     char buffer[kBufferSize];
146     size_t size1 = SkMatrixPriv::WriteToMemory(m, nullptr);
147     size_t size2 = SkMatrixPriv::WriteToMemory(m, buffer);
148     REPORTER_ASSERT(reporter, size1 == size2);
149     REPORTER_ASSERT(reporter, size1 <= SkMatrixPriv::kMaxFlattenSize);
150 
151     SkMatrix m2;
152     size_t size3 = SkMatrixPriv::ReadFromMemory(&m2, buffer, kBufferSize);
153     REPORTER_ASSERT(reporter, size1 == size3);
154     REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
155 
156     char buffer2[kBufferSize];
157     size3 = SkMatrixPriv::WriteToMemory(m2, buffer2);
158     REPORTER_ASSERT(reporter, size1 == size3);
159     REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
160 }
161 
test_matrix_min_max_scale(skiatest::Reporter * reporter)162 static void test_matrix_min_max_scale(skiatest::Reporter* reporter) {
163     SkScalar scales[2];
164     bool success;
165 
166     SkMatrix identity;
167     identity.reset();
168     REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinScale());
169     REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxScale());
170     success = identity.getMinMaxScales(scales);
171     REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
172 
173     SkMatrix scale;
174     scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
175     REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinScale());
176     REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxScale());
177     success = scale.getMinMaxScales(scales);
178     REPORTER_ASSERT(reporter, success && SK_Scalar1 * 2 == scales[0] && SK_Scalar1 * 4 == scales[1]);
179 
180     SkMatrix rot90Scale;
181     rot90Scale.setRotate(90 * SK_Scalar1);
182     rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
183     REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinScale());
184     REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxScale());
185     success = rot90Scale.getMinMaxScales(scales);
186     REPORTER_ASSERT(reporter, success && SK_Scalar1 / 4  == scales[0] && SK_Scalar1 / 2 == scales[1]);
187 
188     SkMatrix rotate;
189     rotate.setRotate(128 * SK_Scalar1);
190     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinScale(), SK_ScalarNearlyZero));
191     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxScale(), SK_ScalarNearlyZero));
192     success = rotate.getMinMaxScales(scales);
193     REPORTER_ASSERT(reporter, success);
194     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[0], SK_ScalarNearlyZero));
195     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[1], SK_ScalarNearlyZero));
196 
197     SkMatrix translate;
198     translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
199     REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinScale());
200     REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxScale());
201     success = translate.getMinMaxScales(scales);
202     REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
203 
204     SkMatrix perspX;
205     perspX.reset();
206     perspX.setPerspX(SK_Scalar1 / 1000);
207     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale());
208     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale());
209     success = perspX.getMinMaxScales(scales);
210     REPORTER_ASSERT(reporter, !success);
211 
212     // skbug.com/4718
213     SkMatrix big;
214     big.setAll(2.39394089e+36f, 8.85347779e+36f, 9.26526204e+36f,
215                3.9159619e+36f, 1.44823453e+37f, 1.51559342e+37f,
216                0.f, 0.f, 1.f);
217     success = big.getMinMaxScales(scales);
218     REPORTER_ASSERT(reporter, !success);
219 
220     // skbug.com/4718
221     SkMatrix givingNegativeNearlyZeros;
222     givingNegativeNearlyZeros.setAll(0.00436534f, 0.114138f, 0.37141f,
223                                      0.00358857f, 0.0936228f, -0.0174198f,
224                                      0.f, 0.f, 1.f);
225     success = givingNegativeNearlyZeros.getMinMaxScales(scales);
226     REPORTER_ASSERT(reporter, success && 0 == scales[0]);
227 
228     SkMatrix perspY;
229     perspY.reset();
230     perspY.setPerspY(-SK_Scalar1 / 500);
231     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale());
232     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxScale());
233     scales[0] = -5;
234     scales[1] = -5;
235     success = perspY.getMinMaxScales(scales);
236     REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1  == scales[1]);
237 
238     SkMatrix baseMats[] = {scale, rot90Scale, rotate,
239                            translate, perspX, perspY};
240     SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
241     for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
242         mats[i] = baseMats[i];
243         bool invertible = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
244         REPORTER_ASSERT(reporter, invertible);
245     }
246     SkRandom rand;
247     for (int m = 0; m < 1000; ++m) {
248         SkMatrix mat;
249         mat.reset();
250         for (int i = 0; i < 4; ++i) {
251             int x = rand.nextU() % SK_ARRAY_COUNT(mats);
252             mat.postConcat(mats[x]);
253         }
254 
255         SkScalar minScale = mat.getMinScale();
256         SkScalar maxScale = mat.getMaxScale();
257         REPORTER_ASSERT(reporter, (minScale < 0) == (maxScale < 0));
258         REPORTER_ASSERT(reporter, (maxScale < 0) == mat.hasPerspective());
259 
260         SkScalar scales[2];
261         bool success = mat.getMinMaxScales(scales);
262         REPORTER_ASSERT(reporter, success == !mat.hasPerspective());
263         REPORTER_ASSERT(reporter, !success || (scales[0] == minScale && scales[1] == maxScale));
264 
265         if (mat.hasPerspective()) {
266             m -= 1; // try another non-persp matrix
267             continue;
268         }
269 
270         // test a bunch of vectors. All should be scaled by between minScale and maxScale
271         // (modulo some error) and we should find a vector that is scaled by almost each.
272         static const SkScalar gVectorScaleTol = (105 * SK_Scalar1) / 100;
273         static const SkScalar gCloseScaleTol = (97 * SK_Scalar1) / 100;
274         SkScalar max = 0, min = SK_ScalarMax;
275         SkVector vectors[1000];
276         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
277             vectors[i].fX = rand.nextSScalar1();
278             vectors[i].fY = rand.nextSScalar1();
279             if (!vectors[i].normalize()) {
280                 i -= 1;
281                 continue;
282             }
283         }
284         mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
285         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
286             SkScalar d = vectors[i].length();
287             REPORTER_ASSERT(reporter, d / maxScale < gVectorScaleTol);
288             REPORTER_ASSERT(reporter, minScale / d < gVectorScaleTol);
289             if (max < d) {
290                 max = d;
291             }
292             if (min > d) {
293                 min = d;
294             }
295         }
296         REPORTER_ASSERT(reporter, max / maxScale >= gCloseScaleTol);
297         REPORTER_ASSERT(reporter, minScale / min >= gCloseScaleTol);
298     }
299 }
300 
test_matrix_preserve_shape(skiatest::Reporter * reporter)301 static void test_matrix_preserve_shape(skiatest::Reporter* reporter) {
302     SkMatrix mat;
303 
304     // identity
305     mat.setIdentity();
306     REPORTER_ASSERT(reporter, mat.isSimilarity());
307     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
308 
309     // translation only
310     mat.reset();
311     mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
312     REPORTER_ASSERT(reporter, mat.isSimilarity());
313     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
314 
315     // scale with same size
316     mat.reset();
317     mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
318     REPORTER_ASSERT(reporter, mat.isSimilarity());
319     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
320 
321     // scale with one negative
322     mat.reset();
323     mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
324     REPORTER_ASSERT(reporter, mat.isSimilarity());
325     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
326 
327     // scale with different size
328     mat.reset();
329     mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
330     REPORTER_ASSERT(reporter, !mat.isSimilarity());
331     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
332 
333     // scale with same size at a pivot point
334     mat.reset();
335     mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
336                  SkIntToScalar(2), SkIntToScalar(2));
337     REPORTER_ASSERT(reporter, mat.isSimilarity());
338     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
339 
340     // scale with different size at a pivot point
341     mat.reset();
342     mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
343                  SkIntToScalar(2), SkIntToScalar(2));
344     REPORTER_ASSERT(reporter, !mat.isSimilarity());
345     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
346 
347     // skew with same size
348     mat.reset();
349     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
350     REPORTER_ASSERT(reporter, !mat.isSimilarity());
351     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
352 
353     // skew with different size
354     mat.reset();
355     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
356     REPORTER_ASSERT(reporter, !mat.isSimilarity());
357     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
358 
359     // skew with same size at a pivot point
360     mat.reset();
361     mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
362                 SkIntToScalar(2), SkIntToScalar(2));
363     REPORTER_ASSERT(reporter, !mat.isSimilarity());
364     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
365 
366     // skew with different size at a pivot point
367     mat.reset();
368     mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
369                 SkIntToScalar(2), SkIntToScalar(2));
370     REPORTER_ASSERT(reporter, !mat.isSimilarity());
371     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
372 
373     // perspective x
374     mat.reset();
375     mat.setPerspX(SK_Scalar1 / 2);
376     REPORTER_ASSERT(reporter, !mat.isSimilarity());
377     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
378 
379     // perspective y
380     mat.reset();
381     mat.setPerspY(SK_Scalar1 / 2);
382     REPORTER_ASSERT(reporter, !mat.isSimilarity());
383     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
384 
385     // rotate
386     for (int angle = 0; angle < 360; ++angle) {
387         mat.reset();
388         mat.setRotate(SkIntToScalar(angle));
389         REPORTER_ASSERT(reporter, mat.isSimilarity());
390         REPORTER_ASSERT(reporter, mat.preservesRightAngles());
391     }
392 
393     // see if there are any accumulated precision issues
394     mat.reset();
395     for (int i = 1; i < 360; i++) {
396         mat.postRotate(SkIntToScalar(1));
397     }
398     REPORTER_ASSERT(reporter, mat.isSimilarity());
399     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
400 
401     // rotate + translate
402     mat.reset();
403     mat.setRotate(SkIntToScalar(30));
404     mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
405     REPORTER_ASSERT(reporter, mat.isSimilarity());
406     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
407 
408     // rotate + uniform scale
409     mat.reset();
410     mat.setRotate(SkIntToScalar(30));
411     mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
412     REPORTER_ASSERT(reporter, mat.isSimilarity());
413     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
414 
415     // rotate + non-uniform scale
416     mat.reset();
417     mat.setRotate(SkIntToScalar(30));
418     mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
419     REPORTER_ASSERT(reporter, !mat.isSimilarity());
420     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
421 
422     // non-uniform scale + rotate
423     mat.reset();
424     mat.setScale(SkIntToScalar(3), SkIntToScalar(2));
425     mat.postRotate(SkIntToScalar(30));
426     REPORTER_ASSERT(reporter, !mat.isSimilarity());
427     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
428 
429     // all zero
430     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
431     REPORTER_ASSERT(reporter, !mat.isSimilarity());
432     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
433 
434     // all zero except perspective
435     mat.reset();
436     mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
437     REPORTER_ASSERT(reporter, !mat.isSimilarity());
438     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
439 
440     // scales zero, only skews (rotation)
441     mat.setAll(0, SK_Scalar1, 0,
442                -SK_Scalar1, 0, 0,
443                0, 0, SkMatrix::I()[8]);
444     REPORTER_ASSERT(reporter, mat.isSimilarity());
445     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
446 
447     // scales zero, only skews (reflection)
448     mat.setAll(0, SK_Scalar1, 0,
449                SK_Scalar1, 0, 0,
450                0, 0, SkMatrix::I()[8]);
451     REPORTER_ASSERT(reporter, mat.isSimilarity());
452     REPORTER_ASSERT(reporter, mat.preservesRightAngles());
453 }
454 
455 // For test_matrix_decomposition, below.
scalar_nearly_equal_relative(SkScalar a,SkScalar b,SkScalar tolerance=SK_ScalarNearlyZero)456 static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
457                                          SkScalar tolerance = SK_ScalarNearlyZero) {
458     // from Bruce Dawson
459     // absolute check
460     SkScalar diff = SkScalarAbs(a - b);
461     if (diff < tolerance) {
462         return true;
463     }
464 
465     // relative check
466     a = SkScalarAbs(a);
467     b = SkScalarAbs(b);
468     SkScalar largest = (b > a) ? b : a;
469 
470     if (diff <= largest*tolerance) {
471         return true;
472     }
473 
474     return false;
475 }
476 
check_matrix_recomposition(const SkMatrix & mat,const SkPoint & rotation1,const SkPoint & scale,const SkPoint & rotation2)477 static bool check_matrix_recomposition(const SkMatrix& mat,
478                                        const SkPoint& rotation1,
479                                        const SkPoint& scale,
480                                        const SkPoint& rotation2) {
481     SkScalar c1 = rotation1.fX;
482     SkScalar s1 = rotation1.fY;
483     SkScalar scaleX = scale.fX;
484     SkScalar scaleY = scale.fY;
485     SkScalar c2 = rotation2.fX;
486     SkScalar s2 = rotation2.fY;
487 
488     // We do a relative check here because large scale factors cause problems with an absolute check
489     bool result = scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
490                                                scaleX*c1*c2 - scaleY*s1*s2) &&
491                   scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
492                                                -scaleX*s1*c2 - scaleY*c1*s2) &&
493                   scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
494                                                scaleX*c1*s2 + scaleY*s1*c2) &&
495                   scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
496                                                -scaleX*s1*s2 + scaleY*c1*c2);
497     return result;
498 }
499 
test_matrix_decomposition(skiatest::Reporter * reporter)500 static void test_matrix_decomposition(skiatest::Reporter* reporter) {
501     SkMatrix mat;
502     SkPoint rotation1, scale, rotation2;
503 
504     const float kRotation0 = 15.5f;
505     const float kRotation1 = -50.f;
506     const float kScale0 = 5000.f;
507     const float kScale1 = 0.001f;
508 
509     // identity
510     mat.reset();
511     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
512     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
513     // make sure it doesn't crash if we pass in NULLs
514     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, nullptr, nullptr, nullptr));
515 
516     // rotation only
517     mat.setRotate(kRotation0);
518     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
519     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
520 
521     // uniform scale only
522     mat.setScale(kScale0, kScale0);
523     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
524     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
525 
526     // anisotropic scale only
527     mat.setScale(kScale1, kScale0);
528     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
529     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
530 
531     // rotation then uniform scale
532     mat.setRotate(kRotation1);
533     mat.postScale(kScale0, kScale0);
534     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
535     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
536 
537     // uniform scale then rotation
538     mat.setScale(kScale0, kScale0);
539     mat.postRotate(kRotation1);
540     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
541     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
542 
543     // rotation then uniform scale+reflection
544     mat.setRotate(kRotation0);
545     mat.postScale(kScale1, -kScale1);
546     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
547     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
548 
549     // uniform scale+reflection, then rotate
550     mat.setScale(kScale0, -kScale0);
551     mat.postRotate(kRotation1);
552     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
553     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
554 
555     // rotation then anisotropic scale
556     mat.setRotate(kRotation1);
557     mat.postScale(kScale1, kScale0);
558     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
559     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
560 
561     // rotation then anisotropic scale
562     mat.setRotate(90);
563     mat.postScale(kScale1, kScale0);
564     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
565     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
566 
567     // anisotropic scale then rotation
568     mat.setScale(kScale1, kScale0);
569     mat.postRotate(kRotation0);
570     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
571     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
572 
573     // anisotropic scale then rotation
574     mat.setScale(kScale1, kScale0);
575     mat.postRotate(90);
576     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
577     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
578 
579     // rotation, uniform scale, then different rotation
580     mat.setRotate(kRotation1);
581     mat.postScale(kScale0, kScale0);
582     mat.postRotate(kRotation0);
583     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
584     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
585 
586     // rotation, anisotropic scale, then different rotation
587     mat.setRotate(kRotation0);
588     mat.postScale(kScale1, kScale0);
589     mat.postRotate(kRotation1);
590     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
591     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
592 
593     // rotation, anisotropic scale + reflection, then different rotation
594     mat.setRotate(kRotation0);
595     mat.postScale(-kScale1, kScale0);
596     mat.postRotate(kRotation1);
597     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
598     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
599 
600     // try some random matrices
601     SkRandom rand;
602     for (int m = 0; m < 1000; ++m) {
603         SkScalar rot0 = rand.nextRangeF(-180, 180);
604         SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
605         SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
606         SkScalar rot1 = rand.nextRangeF(-180, 180);
607         mat.setRotate(rot0);
608         mat.postScale(sx, sy);
609         mat.postRotate(rot1);
610 
611         if (SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)) {
612             REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
613         } else {
614             // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
615             SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
616                                mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
617             REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
618         }
619     }
620 
621     // translation shouldn't affect this
622     mat.postTranslate(-1000.f, 1000.f);
623     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
624     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
625 
626     // perspective shouldn't affect this
627     mat[SkMatrix::kMPersp0] = 12.f;
628     mat[SkMatrix::kMPersp1] = 4.f;
629     mat[SkMatrix::kMPersp2] = 1872.f;
630     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
631     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
632 
633     // degenerate matrices
634     // mostly zero entries
635     mat.reset();
636     mat[SkMatrix::kMScaleX] = 0.f;
637     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
638     mat.reset();
639     mat[SkMatrix::kMScaleY] = 0.f;
640     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
641     mat.reset();
642     // linearly dependent entries
643     mat[SkMatrix::kMScaleX] = 1.f;
644     mat[SkMatrix::kMSkewX] = 2.f;
645     mat[SkMatrix::kMSkewY] = 4.f;
646     mat[SkMatrix::kMScaleY] = 8.f;
647     REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
648 }
649 
650 // For test_matrix_homogeneous, below.
point3_array_nearly_equal_relative(const SkPoint3 a[],const SkPoint3 b[],int count)651 static bool point3_array_nearly_equal_relative(const SkPoint3 a[], const SkPoint3 b[], int count) {
652     for (int i = 0; i < count; ++i) {
653         if (!scalar_nearly_equal_relative(a[i].fX, b[i].fX)) {
654             return false;
655         }
656         if (!scalar_nearly_equal_relative(a[i].fY, b[i].fY)) {
657             return false;
658         }
659         if (!scalar_nearly_equal_relative(a[i].fZ, b[i].fZ)) {
660             return false;
661         }
662     }
663     return true;
664 }
665 
666 // For test_matrix_homogeneous, below.
667 // Maps a single triple in src using m and compares results to those in dst
naive_homogeneous_mapping(const SkMatrix & m,const SkPoint3 & src,const SkPoint3 & dst)668 static bool naive_homogeneous_mapping(const SkMatrix& m, const SkPoint3& src,
669                                       const SkPoint3& dst) {
670     SkPoint3 res;
671     SkScalar ms[9] = {m[0], m[1], m[2],
672                       m[3], m[4], m[5],
673                       m[6], m[7], m[8]};
674     res.fX = src.fX * ms[0] + src.fY * ms[1] + src.fZ * ms[2];
675     res.fY = src.fX * ms[3] + src.fY * ms[4] + src.fZ * ms[5];
676     res.fZ = src.fX * ms[6] + src.fY * ms[7] + src.fZ * ms[8];
677     return point3_array_nearly_equal_relative(&res, &dst, 1);
678 }
679 
test_matrix_homogeneous(skiatest::Reporter * reporter)680 static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
681     SkMatrix mat;
682 
683     const float kRotation0 = 15.5f;
684     const float kRotation1 = -50.f;
685     const float kScale0 = 5000.f;
686 
687 #if defined(SK_BUILD_FOR_GOOGLE3)
688     // Stack frame size is limited in SK_BUILD_FOR_GOOGLE3.
689     const int kTripleCount = 100;
690     const int kMatrixCount = 100;
691 #else
692     const int kTripleCount = 1000;
693     const int kMatrixCount = 1000;
694 #endif
695     SkRandom rand;
696 
697     SkPoint3 randTriples[kTripleCount];
698     for (int i = 0; i < kTripleCount; ++i) {
699         randTriples[i].fX = rand.nextRangeF(-3000.f, 3000.f);
700         randTriples[i].fY = rand.nextRangeF(-3000.f, 3000.f);
701         randTriples[i].fZ = rand.nextRangeF(-3000.f, 3000.f);
702     }
703 
704     SkMatrix mats[kMatrixCount];
705     for (int i = 0; i < kMatrixCount; ++i) {
706         for (int j = 0; j < 9; ++j) {
707             mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
708         }
709     }
710 
711     // identity
712     {
713     mat.reset();
714     SkPoint3 dst[kTripleCount];
715     mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
716     REPORTER_ASSERT(reporter, point3_array_nearly_equal_relative(randTriples, dst, kTripleCount));
717     }
718 
719     const SkPoint3 zeros = {0.f, 0.f, 0.f};
720     // zero matrix
721     {
722     mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
723     SkPoint3 dst[kTripleCount];
724     mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
725     for (int i = 0; i < kTripleCount; ++i) {
726         REPORTER_ASSERT(reporter, point3_array_nearly_equal_relative(&dst[i], &zeros, 1));
727     }
728     }
729 
730     // zero point
731     {
732     for (int i = 0; i < kMatrixCount; ++i) {
733         SkPoint3 dst;
734         mats[i].mapHomogeneousPoints(&dst, &zeros, 1);
735         REPORTER_ASSERT(reporter, point3_array_nearly_equal_relative(&dst, &zeros, 1));
736     }
737     }
738 
739     // doesn't crash with null dst, src, count == 0
740     {
741     mats[0].mapHomogeneousPoints(nullptr, nullptr, 0);
742     }
743 
744     // uniform scale of point
745     {
746     mat.setScale(kScale0, kScale0);
747     SkPoint3 dst;
748     SkPoint3 src = {randTriples[0].fX, randTriples[0].fY, 1.f};
749     SkPoint pnt;
750     pnt.set(src.fX, src.fY);
751     mat.mapHomogeneousPoints(&dst, &src, 1);
752     mat.mapPoints(&pnt, &pnt, 1);
753     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fX, pnt.fX));
754     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fY, pnt.fY));
755     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fZ, SK_Scalar1));
756     }
757 
758     // rotation of point
759     {
760     mat.setRotate(kRotation0);
761     SkPoint3 dst;
762     SkPoint3 src = {randTriples[0].fX, randTriples[0].fY, 1.f};
763     SkPoint pnt;
764     pnt.set(src.fX, src.fY);
765     mat.mapHomogeneousPoints(&dst, &src, 1);
766     mat.mapPoints(&pnt, &pnt, 1);
767     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fX, pnt.fX));
768     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fY, pnt.fY));
769     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fZ, SK_Scalar1));
770     }
771 
772     // rotation, scale, rotation of point
773     {
774     mat.setRotate(kRotation1);
775     mat.postScale(kScale0, kScale0);
776     mat.postRotate(kRotation0);
777     SkPoint3 dst;
778     SkPoint3 src = {randTriples[0].fX, randTriples[0].fY, 1.f};
779     SkPoint pnt;
780     pnt.set(src.fX, src.fY);
781     mat.mapHomogeneousPoints(&dst, &src, 1);
782     mat.mapPoints(&pnt, &pnt, 1);
783     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fX, pnt.fX));
784     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fY, pnt.fY));
785     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fZ, SK_Scalar1));
786     }
787 
788     // compare with naive approach
789     {
790     for (int i = 0; i < kMatrixCount; ++i) {
791         for (int j = 0; j < kTripleCount; ++j) {
792             SkPoint3 dst;
793             mats[i].mapHomogeneousPoints(&dst, &randTriples[j], 1);
794             REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], randTriples[j], dst));
795         }
796     }
797     }
798 
799 }
800 
check_decompScale(const SkMatrix & matrix)801 static bool check_decompScale(const SkMatrix& matrix) {
802     SkSize scale;
803     SkMatrix remaining;
804 
805     if (!matrix.decomposeScale(&scale, &remaining)) {
806         return false;
807     }
808     if (scale.width() <= 0 || scale.height() <= 0) {
809         return false;
810     }
811     remaining.preScale(scale.width(), scale.height());
812     return nearly_equal(matrix, remaining);
813 }
814 
test_decompScale(skiatest::Reporter * reporter)815 static void test_decompScale(skiatest::Reporter* reporter) {
816     SkMatrix m;
817 
818     m.reset();
819     REPORTER_ASSERT(reporter, check_decompScale(m));
820     m.setScale(2, 3);
821     REPORTER_ASSERT(reporter, check_decompScale(m));
822     m.setRotate(35, 0, 0);
823     REPORTER_ASSERT(reporter, check_decompScale(m));
824 
825     m.setScale(1, 0);
826     REPORTER_ASSERT(reporter, !check_decompScale(m));
827 }
828 
DEF_TEST(Matrix,reporter)829 DEF_TEST(Matrix, reporter) {
830     SkMatrix    mat, inverse, iden1, iden2;
831 
832     mat.reset();
833     mat.setTranslate(SK_Scalar1, SK_Scalar1);
834     REPORTER_ASSERT(reporter, mat.invert(&inverse));
835     iden1.setConcat(mat, inverse);
836     REPORTER_ASSERT(reporter, is_identity(iden1));
837 
838     mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
839     REPORTER_ASSERT(reporter, mat.invert(&inverse));
840     iden1.setConcat(mat, inverse);
841     REPORTER_ASSERT(reporter, is_identity(iden1));
842     test_flatten(reporter, mat);
843 
844     mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
845     REPORTER_ASSERT(reporter, mat.invert(&inverse));
846     iden1.setConcat(mat, inverse);
847     REPORTER_ASSERT(reporter, is_identity(iden1));
848     test_flatten(reporter, mat);
849 
850     mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
851     mat.postRotate(SkIntToScalar(25));
852     REPORTER_ASSERT(reporter, mat.invert(nullptr));
853     REPORTER_ASSERT(reporter, mat.invert(&inverse));
854     iden1.setConcat(mat, inverse);
855     REPORTER_ASSERT(reporter, is_identity(iden1));
856     iden2.setConcat(inverse, mat);
857     REPORTER_ASSERT(reporter, is_identity(iden2));
858     test_flatten(reporter, mat);
859     test_flatten(reporter, iden2);
860 
861     mat.setScale(0, SK_Scalar1);
862     REPORTER_ASSERT(reporter, !mat.invert(nullptr));
863     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
864     mat.setScale(SK_Scalar1, 0);
865     REPORTER_ASSERT(reporter, !mat.invert(nullptr));
866     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
867 
868     // Inverting this matrix results in a non-finite matrix
869     mat.setAll(0.0f, 1.0f, 2.0f,
870                0.0f, 1.0f, -3.40277175e+38f,
871                1.00003040f, 1.0f, 0.0f);
872     REPORTER_ASSERT(reporter, !mat.invert(nullptr));
873     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
874 
875     // rectStaysRect test
876     {
877         static const struct {
878             SkScalar    m00, m01, m10, m11;
879             bool        mStaysRect;
880         }
881         gRectStaysRectSamples[] = {
882             {          0,          0,          0,           0, false },
883             {          0,          0,          0,  SK_Scalar1, false },
884             {          0,          0, SK_Scalar1,           0, false },
885             {          0,          0, SK_Scalar1,  SK_Scalar1, false },
886             {          0, SK_Scalar1,          0,           0, false },
887             {          0, SK_Scalar1,          0,  SK_Scalar1, false },
888             {          0, SK_Scalar1, SK_Scalar1,           0, true },
889             {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
890             { SK_Scalar1,          0,          0,           0, false },
891             { SK_Scalar1,          0,          0,  SK_Scalar1, true },
892             { SK_Scalar1,          0, SK_Scalar1,           0, false },
893             { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
894             { SK_Scalar1, SK_Scalar1,          0,           0, false },
895             { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
896             { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
897             { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
898         };
899 
900         for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
901             SkMatrix    m;
902 
903             m.reset();
904             m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
905             m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
906             m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
907             m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
908             REPORTER_ASSERT(reporter,
909                     m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
910         }
911     }
912 
913     mat.reset();
914     mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
915     mat.set(SkMatrix::kMSkewX,  SkIntToScalar(2));
916     mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
917     mat.set(SkMatrix::kMSkewY,  SkIntToScalar(4));
918     mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
919     mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
920     SkScalar affine[6];
921     REPORTER_ASSERT(reporter, mat.asAffine(affine));
922 
923     #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
924     REPORTER_ASSERT(reporter, affineEqual(ScaleX));
925     REPORTER_ASSERT(reporter, affineEqual(SkewY));
926     REPORTER_ASSERT(reporter, affineEqual(SkewX));
927     REPORTER_ASSERT(reporter, affineEqual(ScaleY));
928     REPORTER_ASSERT(reporter, affineEqual(TransX));
929     REPORTER_ASSERT(reporter, affineEqual(TransY));
930     #undef affineEqual
931 
932     mat.set(SkMatrix::kMPersp1, SK_Scalar1 / 2);
933     REPORTER_ASSERT(reporter, !mat.asAffine(affine));
934 
935     SkMatrix mat2;
936     mat2.reset();
937     mat.reset();
938     SkScalar zero = 0;
939     mat.set(SkMatrix::kMSkewX, -zero);
940     REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
941 
942     mat2.reset();
943     mat.reset();
944     mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
945     mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
946     REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
947 
948     test_matrix_min_max_scale(reporter);
949     test_matrix_preserve_shape(reporter);
950     test_matrix_recttorect(reporter);
951     test_matrix_decomposition(reporter);
952     test_matrix_homogeneous(reporter);
953     test_set9(reporter);
954 
955     test_decompScale(reporter);
956 
957     mat.setScaleTranslate(2, 3, 1, 4);
958     mat2.setScale(2, 3);
959     mat2.postTranslate(1, 4);
960     REPORTER_ASSERT(reporter, mat == mat2);
961 }
962 
DEF_TEST(Matrix_Concat,r)963 DEF_TEST(Matrix_Concat, r) {
964     SkMatrix a;
965     a.setTranslate(10, 20);
966 
967     SkMatrix b;
968     b.setScale(3, 5);
969 
970     SkMatrix expected;
971     expected.setConcat(a,b);
972 
973     REPORTER_ASSERT(r, expected == SkMatrix::Concat(a, b));
974 }
975 
976 // Test that all variants of maprect are correct.
DEF_TEST(Matrix_maprects,r)977 DEF_TEST(Matrix_maprects, r) {
978     const SkScalar scale = 1000;
979 
980     SkMatrix mat;
981     mat.setScale(2, 3);
982     mat.postTranslate(1, 4);
983 
984     SkRandom rand;
985     for (int i = 0; i < 10000; ++i) {
986         SkRect src = SkRect::MakeLTRB(rand.nextSScalar1() * scale,
987                                       rand.nextSScalar1() * scale,
988                                       rand.nextSScalar1() * scale,
989                                       rand.nextSScalar1() * scale);
990         SkRect dst[3];
991 
992         mat.mapPoints((SkPoint*)&dst[0].fLeft, (SkPoint*)&src.fLeft, 2);
993         dst[0].sort();
994         mat.mapRect(&dst[1], src);
995         mat.mapRectScaleTranslate(&dst[2], src);
996 
997         REPORTER_ASSERT(r, dst[0] == dst[1]);
998         REPORTER_ASSERT(r, dst[0] == dst[2]);
999     }
1000 }
1001