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