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    SkScalar scales[2];
124    bool success;
125
126    SkMatrix identity;
127    identity.reset();
128    REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinScale());
129    REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxScale());
130    success = identity.getMinMaxScales(scales);
131    REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
132
133    SkMatrix scale;
134    scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
135    REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinScale());
136    REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxScale());
137    success = scale.getMinMaxScales(scales);
138    REPORTER_ASSERT(reporter, success && SK_Scalar1 * 2 == scales[0] && SK_Scalar1 * 4 == scales[1]);
139
140    SkMatrix rot90Scale;
141    rot90Scale.setRotate(90 * SK_Scalar1);
142    rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
143    REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinScale());
144    REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxScale());
145    success = rot90Scale.getMinMaxScales(scales);
146    REPORTER_ASSERT(reporter, success && SK_Scalar1 / 4  == scales[0] && SK_Scalar1 / 2 == scales[1]);
147
148    SkMatrix rotate;
149    rotate.setRotate(128 * SK_Scalar1);
150    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinScale(), SK_ScalarNearlyZero));
151    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxScale(), SK_ScalarNearlyZero));
152    success = rotate.getMinMaxScales(scales);
153    REPORTER_ASSERT(reporter, success);
154    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[0], SK_ScalarNearlyZero));
155    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[1], SK_ScalarNearlyZero));
156
157    SkMatrix translate;
158    translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
159    REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinScale());
160    REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxScale());
161    success = translate.getMinMaxScales(scales);
162    REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
163
164    SkMatrix perspX;
165    perspX.reset();
166    perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
167    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale());
168    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale());
169    // Verify that getMinMaxScales() doesn't update the scales array on failure.
170    scales[0] = -5;
171    scales[1] = -5;
172    success = perspX.getMinMaxScales(scales);
173    REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1  == scales[1]);
174
175    SkMatrix perspY;
176    perspY.reset();
177    perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
178    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale());
179    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxScale());
180    scales[0] = -5;
181    scales[1] = -5;
182    success = perspY.getMinMaxScales(scales);
183    REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1  == scales[1]);
184
185    SkMatrix baseMats[] = {scale, rot90Scale, rotate,
186                           translate, perspX, perspY};
187    SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
188    for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
189        mats[i] = baseMats[i];
190        bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
191        REPORTER_ASSERT(reporter, invertable);
192    }
193    SkRandom rand;
194    for (int m = 0; m < 1000; ++m) {
195        SkMatrix mat;
196        mat.reset();
197        for (int i = 0; i < 4; ++i) {
198            int x = rand.nextU() % SK_ARRAY_COUNT(mats);
199            mat.postConcat(mats[x]);
200        }
201
202        SkScalar minScale = mat.getMinScale();
203        SkScalar maxScale = mat.getMaxScale();
204        REPORTER_ASSERT(reporter, (minScale < 0) == (maxScale < 0));
205        REPORTER_ASSERT(reporter, (maxScale < 0) == mat.hasPerspective());
206
207        SkScalar scales[2];
208        bool success = mat.getMinMaxScales(scales);
209        REPORTER_ASSERT(reporter, success == !mat.hasPerspective());
210        REPORTER_ASSERT(reporter, !success || (scales[0] == minScale && scales[1] == maxScale));
211
212        if (mat.hasPerspective()) {
213            m -= 1; // try another non-persp matrix
214            continue;
215        }
216
217        // test a bunch of vectors. All should be scaled by between minScale and maxScale
218        // (modulo some error) and we should find a vector that is scaled by almost each.
219        static const SkScalar gVectorScaleTol = (105 * SK_Scalar1) / 100;
220        static const SkScalar gCloseScaleTol = (97 * SK_Scalar1) / 100;
221        SkScalar max = 0, min = SK_ScalarMax;
222        SkVector vectors[1000];
223        for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
224            vectors[i].fX = rand.nextSScalar1();
225            vectors[i].fY = rand.nextSScalar1();
226            if (!vectors[i].normalize()) {
227                i -= 1;
228                continue;
229            }
230        }
231        mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
232        for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
233            SkScalar d = vectors[i].length();
234            REPORTER_ASSERT(reporter, SkScalarDiv(d, maxScale) < gVectorScaleTol);
235            REPORTER_ASSERT(reporter, SkScalarDiv(minScale, d) < gVectorScaleTol);
236            if (max < d) {
237                max = d;
238            }
239            if (min > d) {
240                min = d;
241            }
242        }
243        REPORTER_ASSERT(reporter, SkScalarDiv(max, maxScale) >= gCloseScaleTol);
244        REPORTER_ASSERT(reporter, SkScalarDiv(minScale, min) >= gCloseScaleTol);
245    }
246}
247
248static void test_matrix_preserve_shape(skiatest::Reporter* reporter) {
249    SkMatrix mat;
250
251    // identity
252    mat.setIdentity();
253    REPORTER_ASSERT(reporter, mat.isSimilarity());
254    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
255
256    // translation only
257    mat.reset();
258    mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
259    REPORTER_ASSERT(reporter, mat.isSimilarity());
260    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
261
262    // scale with same size
263    mat.reset();
264    mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
265    REPORTER_ASSERT(reporter, mat.isSimilarity());
266    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
267
268    // scale with one negative
269    mat.reset();
270    mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
271    REPORTER_ASSERT(reporter, mat.isSimilarity());
272    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
273
274    // scale with different size
275    mat.reset();
276    mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
277    REPORTER_ASSERT(reporter, !mat.isSimilarity());
278    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
279
280    // scale with same size at a pivot point
281    mat.reset();
282    mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
283                 SkIntToScalar(2), SkIntToScalar(2));
284    REPORTER_ASSERT(reporter, mat.isSimilarity());
285    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
286
287    // scale with different size at a pivot point
288    mat.reset();
289    mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
290                 SkIntToScalar(2), SkIntToScalar(2));
291    REPORTER_ASSERT(reporter, !mat.isSimilarity());
292    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
293
294    // skew with same size
295    mat.reset();
296    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
297    REPORTER_ASSERT(reporter, !mat.isSimilarity());
298    REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
299
300    // skew with different size
301    mat.reset();
302    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
303    REPORTER_ASSERT(reporter, !mat.isSimilarity());
304    REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
305
306    // skew with same size at a pivot point
307    mat.reset();
308    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
309                SkIntToScalar(2), SkIntToScalar(2));
310    REPORTER_ASSERT(reporter, !mat.isSimilarity());
311    REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
312
313    // skew with different size at a pivot point
314    mat.reset();
315    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
316                SkIntToScalar(2), SkIntToScalar(2));
317    REPORTER_ASSERT(reporter, !mat.isSimilarity());
318    REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
319
320    // perspective x
321    mat.reset();
322    mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
323    REPORTER_ASSERT(reporter, !mat.isSimilarity());
324    REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
325
326    // perspective y
327    mat.reset();
328    mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
329    REPORTER_ASSERT(reporter, !mat.isSimilarity());
330    REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
331
332    // rotate
333    for (int angle = 0; angle < 360; ++angle) {
334        mat.reset();
335        mat.setRotate(SkIntToScalar(angle));
336        REPORTER_ASSERT(reporter, mat.isSimilarity());
337        REPORTER_ASSERT(reporter, mat.preservesRightAngles());
338    }
339
340    // see if there are any accumulated precision issues
341    mat.reset();
342    for (int i = 1; i < 360; i++) {
343        mat.postRotate(SkIntToScalar(1));
344    }
345    REPORTER_ASSERT(reporter, mat.isSimilarity());
346    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
347
348    // rotate + translate
349    mat.reset();
350    mat.setRotate(SkIntToScalar(30));
351    mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
352    REPORTER_ASSERT(reporter, mat.isSimilarity());
353    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
354
355    // rotate + uniform scale
356    mat.reset();
357    mat.setRotate(SkIntToScalar(30));
358    mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
359    REPORTER_ASSERT(reporter, mat.isSimilarity());
360    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
361
362    // rotate + non-uniform scale
363    mat.reset();
364    mat.setRotate(SkIntToScalar(30));
365    mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
366    REPORTER_ASSERT(reporter, !mat.isSimilarity());
367    REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
368
369    // non-uniform scale + rotate
370    mat.reset();
371    mat.setScale(SkIntToScalar(3), SkIntToScalar(2));
372    mat.postRotate(SkIntToScalar(30));
373    REPORTER_ASSERT(reporter, !mat.isSimilarity());
374    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
375
376    // all zero
377    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
378    REPORTER_ASSERT(reporter, !mat.isSimilarity());
379    REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
380
381    // all zero except perspective
382    mat.reset();
383    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
384    REPORTER_ASSERT(reporter, !mat.isSimilarity());
385    REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
386
387    // scales zero, only skews (rotation)
388    mat.setAll(0, SK_Scalar1, 0,
389               -SK_Scalar1, 0, 0,
390               0, 0, SkMatrix::I()[8]);
391    REPORTER_ASSERT(reporter, mat.isSimilarity());
392    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
393
394    // scales zero, only skews (reflection)
395    mat.setAll(0, SK_Scalar1, 0,
396               SK_Scalar1, 0, 0,
397               0, 0, SkMatrix::I()[8]);
398    REPORTER_ASSERT(reporter, mat.isSimilarity());
399    REPORTER_ASSERT(reporter, mat.preservesRightAngles());
400}
401
402// For test_matrix_decomposition, below.
403static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
404                                         SkScalar tolerance = SK_ScalarNearlyZero) {
405    // from Bruce Dawson
406    // absolute check
407    SkScalar diff = SkScalarAbs(a - b);
408    if (diff < tolerance) {
409        return true;
410    }
411
412    // relative check
413    a = SkScalarAbs(a);
414    b = SkScalarAbs(b);
415    SkScalar largest = (b > a) ? b : a;
416
417    if (diff <= largest*tolerance) {
418        return true;
419    }
420
421    return false;
422}
423
424static bool check_matrix_recomposition(const SkMatrix& mat,
425                                       const SkPoint& rotation1,
426                                       const SkPoint& scale,
427                                       const SkPoint& rotation2) {
428    SkScalar c1 = rotation1.fX;
429    SkScalar s1 = rotation1.fY;
430    SkScalar scaleX = scale.fX;
431    SkScalar scaleY = scale.fY;
432    SkScalar c2 = rotation2.fX;
433    SkScalar s2 = rotation2.fY;
434
435    // We do a relative check here because large scale factors cause problems with an absolute check
436    bool result = scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
437                                               scaleX*c1*c2 - scaleY*s1*s2) &&
438                  scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
439                                               -scaleX*s1*c2 - scaleY*c1*s2) &&
440                  scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
441                                               scaleX*c1*s2 + scaleY*s1*c2) &&
442                  scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
443                                               -scaleX*s1*s2 + scaleY*c1*c2);
444    return result;
445}
446
447static void test_matrix_decomposition(skiatest::Reporter* reporter) {
448    SkMatrix mat;
449    SkPoint rotation1, scale, rotation2;
450
451    const float kRotation0 = 15.5f;
452    const float kRotation1 = -50.f;
453    const float kScale0 = 5000.f;
454    const float kScale1 = 0.001f;
455
456    // identity
457    mat.reset();
458    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
459    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
460    // make sure it doesn't crash if we pass in NULLs
461    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL));
462
463    // rotation only
464    mat.setRotate(kRotation0);
465    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
466    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
467
468    // uniform scale only
469    mat.setScale(kScale0, kScale0);
470    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
471    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
472
473    // anisotropic scale only
474    mat.setScale(kScale1, kScale0);
475    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
476    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
477
478    // rotation then uniform scale
479    mat.setRotate(kRotation1);
480    mat.postScale(kScale0, kScale0);
481    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
482    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
483
484    // uniform scale then rotation
485    mat.setScale(kScale0, kScale0);
486    mat.postRotate(kRotation1);
487    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
488    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
489
490    // rotation then uniform scale+reflection
491    mat.setRotate(kRotation0);
492    mat.postScale(kScale1, -kScale1);
493    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
494    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
495
496    // uniform scale+reflection, then rotate
497    mat.setScale(kScale0, -kScale0);
498    mat.postRotate(kRotation1);
499    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
500    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
501
502    // rotation then anisotropic scale
503    mat.setRotate(kRotation1);
504    mat.postScale(kScale1, kScale0);
505    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
506    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
507
508    // rotation then anisotropic scale
509    mat.setRotate(90);
510    mat.postScale(kScale1, kScale0);
511    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
512    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
513
514    // anisotropic scale then rotation
515    mat.setScale(kScale1, kScale0);
516    mat.postRotate(kRotation0);
517    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
518    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
519
520    // anisotropic scale then rotation
521    mat.setScale(kScale1, kScale0);
522    mat.postRotate(90);
523    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
524    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
525
526    // rotation, uniform scale, then different rotation
527    mat.setRotate(kRotation1);
528    mat.postScale(kScale0, kScale0);
529    mat.postRotate(kRotation0);
530    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
531    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
532
533    // rotation, anisotropic scale, then different rotation
534    mat.setRotate(kRotation0);
535    mat.postScale(kScale1, kScale0);
536    mat.postRotate(kRotation1);
537    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
538    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
539
540    // rotation, anisotropic scale + reflection, then different rotation
541    mat.setRotate(kRotation0);
542    mat.postScale(-kScale1, kScale0);
543    mat.postRotate(kRotation1);
544    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
545    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
546
547    // try some random matrices
548    SkRandom rand;
549    for (int m = 0; m < 1000; ++m) {
550        SkScalar rot0 = rand.nextRangeF(-180, 180);
551        SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
552        SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
553        SkScalar rot1 = rand.nextRangeF(-180, 180);
554        mat.setRotate(rot0);
555        mat.postScale(sx, sy);
556        mat.postRotate(rot1);
557
558        if (SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)) {
559            REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
560        } else {
561            // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
562            SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
563                               mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
564            REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
565        }
566    }
567
568    // translation shouldn't affect this
569    mat.postTranslate(-1000.f, 1000.f);
570    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
571    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
572
573    // perspective shouldn't affect this
574    mat[SkMatrix::kMPersp0] = 12.f;
575    mat[SkMatrix::kMPersp1] = 4.f;
576    mat[SkMatrix::kMPersp2] = 1872.f;
577    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
578    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
579
580    // degenerate matrices
581    // mostly zero entries
582    mat.reset();
583    mat[SkMatrix::kMScaleX] = 0.f;
584    REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
585    mat.reset();
586    mat[SkMatrix::kMScaleY] = 0.f;
587    REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
588    mat.reset();
589    // linearly dependent entries
590    mat[SkMatrix::kMScaleX] = 1.f;
591    mat[SkMatrix::kMSkewX] = 2.f;
592    mat[SkMatrix::kMSkewY] = 4.f;
593    mat[SkMatrix::kMScaleY] = 8.f;
594    REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
595}
596
597// For test_matrix_homogeneous, below.
598static bool scalar_array_nearly_equal_relative(const SkScalar a[], const SkScalar b[], int count) {
599    for (int i = 0; i < count; ++i) {
600        if (!scalar_nearly_equal_relative(a[i], b[i])) {
601            return false;
602        }
603    }
604    return true;
605}
606
607// For test_matrix_homogeneous, below.
608// Maps a single triple in src using m and compares results to those in dst
609static bool naive_homogeneous_mapping(const SkMatrix& m, const SkScalar src[3],
610                                      const SkScalar dst[3]) {
611    SkScalar res[3];
612    SkScalar ms[9] = {m[0], m[1], m[2],
613                      m[3], m[4], m[5],
614                      m[6], m[7], m[8]};
615    res[0] = src[0] * ms[0] + src[1] * ms[1] + src[2] * ms[2];
616    res[1] = src[0] * ms[3] + src[1] * ms[4] + src[2] * ms[5];
617    res[2] = src[0] * ms[6] + src[1] * ms[7] + src[2] * ms[8];
618    return scalar_array_nearly_equal_relative(res, dst, 3);
619}
620
621static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
622    SkMatrix mat;
623
624    const float kRotation0 = 15.5f;
625    const float kRotation1 = -50.f;
626    const float kScale0 = 5000.f;
627
628    const int kTripleCount = 1000;
629    const int kMatrixCount = 1000;
630    SkRandom rand;
631
632    SkScalar randTriples[3*kTripleCount];
633    for (int i = 0; i < 3*kTripleCount; ++i) {
634        randTriples[i] = rand.nextRangeF(-3000.f, 3000.f);
635    }
636
637    SkMatrix mats[kMatrixCount];
638    for (int i = 0; i < kMatrixCount; ++i) {
639        for (int j = 0; j < 9; ++j) {
640            mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
641        }
642    }
643
644    // identity
645    {
646    mat.reset();
647    SkScalar dst[3*kTripleCount];
648    mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
649    REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3));
650    }
651
652    // zero matrix
653    {
654    mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
655    SkScalar dst[3*kTripleCount];
656    mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
657    SkScalar zeros[3] = {0.f, 0.f, 0.f};
658    for (int i = 0; i < kTripleCount; ++i) {
659        REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3));
660    }
661    }
662
663    // zero point
664    {
665    SkScalar zeros[3] = {0.f, 0.f, 0.f};
666    for (int i = 0; i < kMatrixCount; ++i) {
667        SkScalar dst[3];
668        mats[i].mapHomogeneousPoints(dst, zeros, 1);
669        REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3));
670    }
671    }
672
673    // doesn't crash with null dst, src, count == 0
674    {
675    mats[0].mapHomogeneousPoints(NULL, NULL, 0);
676    }
677
678    // uniform scale of point
679    {
680    mat.setScale(kScale0, kScale0);
681    SkScalar dst[3];
682    SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
683    SkPoint pnt;
684    pnt.set(src[0], src[1]);
685    mat.mapHomogeneousPoints(dst, src, 1);
686    mat.mapPoints(&pnt, &pnt, 1);
687    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
688    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
689    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
690    }
691
692    // rotation of point
693    {
694    mat.setRotate(kRotation0);
695    SkScalar dst[3];
696    SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
697    SkPoint pnt;
698    pnt.set(src[0], src[1]);
699    mat.mapHomogeneousPoints(dst, src, 1);
700    mat.mapPoints(&pnt, &pnt, 1);
701    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
702    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
703    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
704    }
705
706    // rotation, scale, rotation of point
707    {
708    mat.setRotate(kRotation1);
709    mat.postScale(kScale0, kScale0);
710    mat.postRotate(kRotation0);
711    SkScalar dst[3];
712    SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
713    SkPoint pnt;
714    pnt.set(src[0], src[1]);
715    mat.mapHomogeneousPoints(dst, src, 1);
716    mat.mapPoints(&pnt, &pnt, 1);
717    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
718    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
719    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
720    }
721
722    // compare with naive approach
723    {
724    for (int i = 0; i < kMatrixCount; ++i) {
725        for (int j = 0; j < kTripleCount; ++j) {
726            SkScalar dst[3];
727            mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1);
728            REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst));
729        }
730    }
731    }
732
733}
734
735DEF_TEST(Matrix, reporter) {
736    SkMatrix    mat, inverse, iden1, iden2;
737
738    mat.reset();
739    mat.setTranslate(SK_Scalar1, SK_Scalar1);
740    REPORTER_ASSERT(reporter, mat.invert(&inverse));
741    iden1.setConcat(mat, inverse);
742    REPORTER_ASSERT(reporter, is_identity(iden1));
743
744    mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
745    REPORTER_ASSERT(reporter, mat.invert(&inverse));
746    iden1.setConcat(mat, inverse);
747    REPORTER_ASSERT(reporter, is_identity(iden1));
748    test_flatten(reporter, mat);
749
750    mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
751    REPORTER_ASSERT(reporter, mat.invert(&inverse));
752    iden1.setConcat(mat, inverse);
753    REPORTER_ASSERT(reporter, is_identity(iden1));
754    test_flatten(reporter, mat);
755
756    mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
757    mat.postRotate(SkIntToScalar(25));
758    REPORTER_ASSERT(reporter, mat.invert(NULL));
759    REPORTER_ASSERT(reporter, mat.invert(&inverse));
760    iden1.setConcat(mat, inverse);
761    REPORTER_ASSERT(reporter, is_identity(iden1));
762    iden2.setConcat(inverse, mat);
763    REPORTER_ASSERT(reporter, is_identity(iden2));
764    test_flatten(reporter, mat);
765    test_flatten(reporter, iden2);
766
767    mat.setScale(0, SK_Scalar1);
768    REPORTER_ASSERT(reporter, !mat.invert(NULL));
769    REPORTER_ASSERT(reporter, !mat.invert(&inverse));
770    mat.setScale(SK_Scalar1, 0);
771    REPORTER_ASSERT(reporter, !mat.invert(NULL));
772    REPORTER_ASSERT(reporter, !mat.invert(&inverse));
773
774    // rectStaysRect test
775    {
776        static const struct {
777            SkScalar    m00, m01, m10, m11;
778            bool        mStaysRect;
779        }
780        gRectStaysRectSamples[] = {
781            {          0,          0,          0,           0, false },
782            {          0,          0,          0,  SK_Scalar1, false },
783            {          0,          0, SK_Scalar1,           0, false },
784            {          0,          0, SK_Scalar1,  SK_Scalar1, false },
785            {          0, SK_Scalar1,          0,           0, false },
786            {          0, SK_Scalar1,          0,  SK_Scalar1, false },
787            {          0, SK_Scalar1, SK_Scalar1,           0, true },
788            {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
789            { SK_Scalar1,          0,          0,           0, false },
790            { SK_Scalar1,          0,          0,  SK_Scalar1, true },
791            { SK_Scalar1,          0, SK_Scalar1,           0, false },
792            { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
793            { SK_Scalar1, SK_Scalar1,          0,           0, false },
794            { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
795            { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
796            { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
797        };
798
799        for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
800            SkMatrix    m;
801
802            m.reset();
803            m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
804            m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
805            m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
806            m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
807            REPORTER_ASSERT(reporter,
808                    m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
809        }
810    }
811
812    mat.reset();
813    mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
814    mat.set(SkMatrix::kMSkewX,  SkIntToScalar(2));
815    mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
816    mat.set(SkMatrix::kMSkewY,  SkIntToScalar(4));
817    mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
818    mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
819    SkScalar affine[6];
820    REPORTER_ASSERT(reporter, mat.asAffine(affine));
821
822    #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
823    REPORTER_ASSERT(reporter, affineEqual(ScaleX));
824    REPORTER_ASSERT(reporter, affineEqual(SkewY));
825    REPORTER_ASSERT(reporter, affineEqual(SkewX));
826    REPORTER_ASSERT(reporter, affineEqual(ScaleY));
827    REPORTER_ASSERT(reporter, affineEqual(TransX));
828    REPORTER_ASSERT(reporter, affineEqual(TransY));
829    #undef affineEqual
830
831    mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
832    REPORTER_ASSERT(reporter, !mat.asAffine(affine));
833
834    SkMatrix mat2;
835    mat2.reset();
836    mat.reset();
837    SkScalar zero = 0;
838    mat.set(SkMatrix::kMSkewX, -zero);
839    REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
840
841    mat2.reset();
842    mat.reset();
843    mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
844    mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
845    REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
846
847    test_matrix_min_max_scale(reporter);
848    test_matrix_preserve_shape(reporter);
849    test_matrix_recttorect(reporter);
850    test_matrix_decomposition(reporter);
851    test_matrix_homogeneous(reporter);
852}
853
854DEF_TEST(Matrix_Concat, r) {
855    SkMatrix a;
856    a.setTranslate(10, 20);
857
858    SkMatrix b;
859    b.setScale(3, 5);
860
861    SkMatrix expected;
862    expected.setConcat(a,b);
863
864    REPORTER_ASSERT(r, expected == SkMatrix::Concat(a, b));
865}
866