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 "SkMatrix44.h"
9#include "Test.h"
10
11static bool nearly_equal_double(double a, double b) {
12    const double tolerance = 1e-7;
13    double diff = a - b;
14    if (diff < 0)
15        diff = -diff;
16    return diff <= tolerance;
17}
18
19static bool nearly_equal_mscalar(SkMScalar a, SkMScalar b) {
20    const SkMScalar tolerance = SK_MScalar1 / 200000;
21
22    return SkTAbs<SkMScalar>(a - b) <= tolerance;
23}
24
25static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
26    const SkScalar tolerance = SK_Scalar1 / 200000;
27    return SkScalarAbs(a - b) <= tolerance;
28}
29
30template <typename T> void assert16(skiatest::Reporter* reporter, const T data[],
31                                    T m0,  T m1,  T m2,  T m3,
32                                    T m4,  T m5,  T m6,  T m7,
33                                    T m8,  T m9,  T m10, T m11,
34                                    T m12, T m13, T m14, T m15) {
35    REPORTER_ASSERT(reporter, data[0] == m0);
36    REPORTER_ASSERT(reporter, data[1] == m1);
37    REPORTER_ASSERT(reporter, data[2] == m2);
38    REPORTER_ASSERT(reporter, data[3] == m3);
39
40    REPORTER_ASSERT(reporter, data[4] == m4);
41    REPORTER_ASSERT(reporter, data[5] == m5);
42    REPORTER_ASSERT(reporter, data[6] == m6);
43    REPORTER_ASSERT(reporter, data[7] == m7);
44
45    REPORTER_ASSERT(reporter, data[8] == m8);
46    REPORTER_ASSERT(reporter, data[9] == m9);
47    REPORTER_ASSERT(reporter, data[10] == m10);
48    REPORTER_ASSERT(reporter, data[11] == m11);
49
50    REPORTER_ASSERT(reporter, data[12] == m12);
51    REPORTER_ASSERT(reporter, data[13] == m13);
52    REPORTER_ASSERT(reporter, data[14] == m14);
53    REPORTER_ASSERT(reporter, data[15] == m15);
54}
55
56static bool nearly_equal(const SkMatrix44& a, const SkMatrix44& b) {
57    for (int i = 0; i < 4; ++i) {
58        for (int j = 0; j < 4; ++j) {
59            if (!nearly_equal_mscalar(a.get(i, j), b.get(i, j))) {
60                SkDebugf("not equal %g %g\n", a.get(i, j), b.get(i, j));
61                return false;
62            }
63        }
64    }
65    return true;
66}
67
68static bool is_identity(const SkMatrix44& m) {
69    SkMatrix44 identity(SkMatrix44::kIdentity_Constructor);
70    return nearly_equal(m, identity);
71}
72
73///////////////////////////////////////////////////////////////////////////////
74static bool bits_isonly(int value, int mask) {
75    return 0 == (value & ~mask);
76}
77
78static void test_constructor(skiatest::Reporter* reporter) {
79    // Allocate a matrix on the heap
80    SkMatrix44* placeholderMatrix = new SkMatrix44(SkMatrix44::kUninitialized_Constructor);
81    SkAutoTDelete<SkMatrix44> deleteMe(placeholderMatrix);
82
83    for (int row = 0; row < 4; ++row) {
84        for (int col = 0; col < 4; ++col) {
85            placeholderMatrix->setDouble(row, col, row * col);
86        }
87    }
88
89    // Use placement-new syntax to trigger the constructor on top of the heap
90    // address we already initialized. This allows us to check that the
91    // constructor did avoid initializing the matrix contents.
92    SkMatrix44* testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kUninitialized_Constructor);
93    REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
94    REPORTER_ASSERT(reporter, !testMatrix->isIdentity());
95    for (int row = 0; row < 4; ++row) {
96        for (int col = 0; col < 4; ++col) {
97            REPORTER_ASSERT(reporter, nearly_equal_double(row * col, testMatrix->getDouble(row, col)));
98        }
99    }
100
101    // Verify that kIdentity_Constructor really does initialize to an identity matrix.
102    testMatrix = 0;
103    testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kIdentity_Constructor);
104    REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
105    REPORTER_ASSERT(reporter, testMatrix->isIdentity());
106    REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
107}
108
109static void test_translate(skiatest::Reporter* reporter) {
110    SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
111    SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
112
113    mat.setTranslate(0, 0, 0);
114    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
115    mat.setTranslate(1, 2, 3);
116    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kTranslate_Mask));
117    REPORTER_ASSERT(reporter, mat.invert(&inverse));
118    REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kTranslate_Mask));
119
120    SkMatrix44 a(SkMatrix44::kUninitialized_Constructor);
121    SkMatrix44 b(SkMatrix44::kUninitialized_Constructor);
122    SkMatrix44 c(SkMatrix44::kUninitialized_Constructor);
123    a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
124    b.setTranslate(10, 11, 12);
125
126    c.setConcat(a, b);
127    mat = a;
128    mat.preTranslate(10, 11, 12);
129    REPORTER_ASSERT(reporter, mat == c);
130
131    c.setConcat(b, a);
132    mat = a;
133    mat.postTranslate(10, 11, 12);
134    REPORTER_ASSERT(reporter, mat == c);
135}
136
137static void test_scale(skiatest::Reporter* reporter) {
138    SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
139    SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
140
141    mat.setScale(1, 1, 1);
142    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
143    mat.setScale(1, 2, 3);
144    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kScale_Mask));
145    REPORTER_ASSERT(reporter, mat.invert(&inverse));
146    REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kScale_Mask));
147
148    SkMatrix44 a(SkMatrix44::kUninitialized_Constructor);
149    SkMatrix44 b(SkMatrix44::kUninitialized_Constructor);
150    SkMatrix44 c(SkMatrix44::kUninitialized_Constructor);
151    a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
152    b.setScale(10, 11, 12);
153
154    c.setConcat(a, b);
155    mat = a;
156    mat.preScale(10, 11, 12);
157    REPORTER_ASSERT(reporter, mat == c);
158
159    c.setConcat(b, a);
160    mat = a;
161    mat.postScale(10, 11, 12);
162    REPORTER_ASSERT(reporter, mat == c);
163}
164
165static void make_i(SkMatrix44* mat) { mat->setIdentity(); }
166static void make_t(SkMatrix44* mat) { mat->setTranslate(1, 2, 3); }
167static void make_s(SkMatrix44* mat) { mat->setScale(1, 2, 3); }
168static void make_st(SkMatrix44* mat) {
169    mat->setScale(1, 2, 3);
170    mat->postTranslate(1, 2, 3);
171}
172static void make_a(SkMatrix44* mat) {
173    mat->setRotateDegreesAbout(1, 2, 3, 45);
174}
175static void make_p(SkMatrix44* mat) {
176    SkMScalar data[] = {
177        1, 2, 3, 4, 5, 6, 7, 8,
178        1, 2, 3, 4, 5, 6, 7, 8,
179    };
180    mat->setRowMajor(data);
181}
182
183typedef void (*Make44Proc)(SkMatrix44*);
184
185static const Make44Proc gMakeProcs[] = {
186    make_i, make_t, make_s, make_st, make_a, make_p
187};
188
189static void test_map2(skiatest::Reporter* reporter, const SkMatrix44& mat) {
190    SkMScalar src2[] = { 1, 2 };
191    SkMScalar src4[] = { src2[0], src2[1], 0, 1 };
192    SkMScalar dstA[4], dstB[4];
193
194    for (int i = 0; i < 4; ++i) {
195        dstA[i] = 123456789;
196        dstB[i] = 987654321;
197    }
198
199    mat.map2(src2, 1, dstA);
200    mat.mapMScalars(src4, dstB);
201
202    for (int i = 0; i < 4; ++i) {
203        REPORTER_ASSERT(reporter, dstA[i] == dstB[i]);
204    }
205}
206
207static void test_map2(skiatest::Reporter* reporter) {
208    SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
209
210    for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProcs); ++i) {
211        gMakeProcs[i](&mat);
212        test_map2(reporter, mat);
213    }
214}
215
216static void test_gettype(skiatest::Reporter* reporter) {
217    SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor);
218
219    REPORTER_ASSERT(reporter, matrix.isIdentity());
220    REPORTER_ASSERT(reporter, SkMatrix44::kIdentity_Mask == matrix.getType());
221
222    int expectedMask;
223
224    matrix.set(1, 1, 0);
225    expectedMask = SkMatrix44::kScale_Mask;
226    REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
227
228    matrix.set(0, 3, 1);    // translate-x
229    expectedMask |= SkMatrix44::kTranslate_Mask;
230    REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
231
232    matrix.set(2, 0, 1);
233    expectedMask |= SkMatrix44::kAffine_Mask;
234    REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
235
236    matrix.set(3, 2, 1);
237    REPORTER_ASSERT(reporter, matrix.getType() & SkMatrix44::kPerspective_Mask);
238
239    // ensure that negative zero is treated as zero
240    SkMScalar dx = 0;
241    SkMScalar dy = 0;
242    SkMScalar dz = 0;
243    matrix.setTranslate(-dx, -dy, -dz);
244    REPORTER_ASSERT(reporter, matrix.isIdentity());
245    matrix.preTranslate(-dx, -dy, -dz);
246    REPORTER_ASSERT(reporter, matrix.isIdentity());
247    matrix.postTranslate(-dx, -dy, -dz);
248    REPORTER_ASSERT(reporter, matrix.isIdentity());
249}
250
251static void test_common_angles(skiatest::Reporter* reporter) {
252    SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
253    // Test precision of rotation in common cases
254    int common_angles[] = { 0, 90, -90, 180, -180, 270, -270, 360, -360 };
255    for (int i = 0; i < 9; ++i) {
256        rot.setRotateDegreesAbout(0, 0, -1, SkIntToScalar(common_angles[i]));
257
258        SkMatrix rot3x3 = rot;
259        REPORTER_ASSERT(reporter, rot3x3.rectStaysRect());
260    }
261}
262
263static void test_concat(skiatest::Reporter* reporter) {
264    int i;
265    SkMatrix44 a(SkMatrix44::kUninitialized_Constructor);
266    SkMatrix44 b(SkMatrix44::kUninitialized_Constructor);
267    SkMatrix44 c(SkMatrix44::kUninitialized_Constructor);
268    SkMatrix44 d(SkMatrix44::kUninitialized_Constructor);
269
270    a.setTranslate(10, 10, 10);
271    b.setScale(2, 2, 2);
272
273    SkScalar src[8] = {
274        0, 0, 0, 1,
275        1, 1, 1, 1
276    };
277    SkScalar dst[8];
278
279    c.setConcat(a, b);
280
281    d = a;
282    d.preConcat(b);
283    REPORTER_ASSERT(reporter, d == c);
284
285    c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
286    for (i = 0; i < 3; ++i) {
287        REPORTER_ASSERT(reporter, 10 == dst[i]);
288        REPORTER_ASSERT(reporter, 12 == dst[i + 4]);
289    }
290
291    c.setConcat(b, a);
292
293    d = a;
294    d.postConcat(b);
295    REPORTER_ASSERT(reporter, d == c);
296
297    c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
298    for (i = 0; i < 3; ++i) {
299        REPORTER_ASSERT(reporter, 20 == dst[i]);
300        REPORTER_ASSERT(reporter, 22 == dst[i + 4]);
301    }
302}
303
304static void test_determinant(skiatest::Reporter* reporter) {
305    SkMatrix44 a(SkMatrix44::kIdentity_Constructor);
306    REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant()));
307    a.set(1, 1, 2);
308    REPORTER_ASSERT(reporter, nearly_equal_double(2, a.determinant()));
309    SkMatrix44 b(SkMatrix44::kUninitialized_Constructor);
310    REPORTER_ASSERT(reporter, a.invert(&b));
311    REPORTER_ASSERT(reporter, nearly_equal_double(0.5, b.determinant()));
312    SkMatrix44 c = b = a;
313    c.set(0, 1, 4);
314    b.set(1, 0, 4);
315    REPORTER_ASSERT(reporter,
316                    nearly_equal_double(a.determinant(),
317                                        b.determinant()));
318    SkMatrix44 d = a;
319    d.set(0, 0, 8);
320    REPORTER_ASSERT(reporter, nearly_equal_double(16, d.determinant()));
321
322    SkMatrix44 e = a;
323    e.postConcat(d);
324    REPORTER_ASSERT(reporter, nearly_equal_double(32, e.determinant()));
325    e.set(0, 0, 0);
326    REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant()));
327}
328
329static void test_invert(skiatest::Reporter* reporter) {
330    SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
331    double inverseData[16];
332
333    SkMatrix44 identity(SkMatrix44::kIdentity_Constructor);
334    identity.invert(&inverse);
335    inverse.asRowMajord(inverseData);
336    assert16<double>(reporter, inverseData,
337                     1, 0, 0, 0,
338                     0, 1, 0, 0,
339                     0, 0, 1, 0,
340                     0, 0, 0, 1);
341
342    SkMatrix44 translation(SkMatrix44::kUninitialized_Constructor);
343    translation.setTranslate(2, 3, 4);
344    translation.invert(&inverse);
345    inverse.asRowMajord(inverseData);
346    assert16<double>(reporter, inverseData,
347                     1, 0, 0, -2,
348                     0, 1, 0, -3,
349                     0, 0, 1, -4,
350                     0, 0, 0, 1);
351
352    SkMatrix44 scale(SkMatrix44::kUninitialized_Constructor);
353    scale.setScale(2, 4, 8);
354    scale.invert(&inverse);
355    inverse.asRowMajord(inverseData);
356    assert16<double>(reporter, inverseData,
357                     0.5, 0,    0,     0,
358                     0,   0.25, 0,     0,
359                     0,   0,    0.125, 0,
360                     0,   0,    0,     1);
361
362    SkMatrix44 scaleTranslation(SkMatrix44::kUninitialized_Constructor);
363    scaleTranslation.setScale(10, 100, 1000);
364    scaleTranslation.preTranslate(2, 3, 4);
365    scaleTranslation.invert(&inverse);
366    inverse.asRowMajord(inverseData);
367    assert16<double>(reporter, inverseData,
368                     0.1,  0,    0,   -2,
369                     0,   0.01,  0,   -3,
370                     0,    0,  0.001, -4,
371                     0,    0,    0,   1);
372
373    SkMatrix44 rotation(SkMatrix44::kUninitialized_Constructor);
374    rotation.setRotateDegreesAbout(0, 0, 1, 90);
375    rotation.invert(&inverse);
376    SkMatrix44 expected(SkMatrix44::kUninitialized_Constructor);
377    double expectedInverseRotation[16] =
378            {0,  1, 0, 0,
379             -1, 0, 0, 0,
380             0,  0, 1, 0,
381             0,  0, 0, 1};
382    expected.setRowMajord(expectedInverseRotation);
383    REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
384
385    SkMatrix44 affine(SkMatrix44::kUninitialized_Constructor);
386    affine.setRotateDegreesAbout(0, 0, 1, 90);
387    affine.preScale(10, 20, 100);
388    affine.preTranslate(2, 3, 4);
389    affine.invert(&inverse);
390    double expectedInverseAffine[16] =
391            {0,    0.1,  0,   -2,
392             -0.05, 0,   0,   -3,
393             0,     0,  0.01, -4,
394             0,     0,   0,   1};
395    expected.setRowMajord(expectedInverseAffine);
396    REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
397
398    SkMatrix44 perspective(SkMatrix44::kIdentity_Constructor);
399    perspective.setDouble(3, 2, 1.0);
400    perspective.invert(&inverse);
401    double expectedInversePerspective[16] =
402            {1, 0,  0, 0,
403             0, 1,  0, 0,
404             0, 0,  1, 0,
405             0, 0, -1, 1};
406    expected.setRowMajord(expectedInversePerspective);
407    REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
408
409    SkMatrix44 affineAndPerspective(SkMatrix44::kIdentity_Constructor);
410    affineAndPerspective.setDouble(3, 2, 1.0);
411    affineAndPerspective.preScale(10, 20, 100);
412    affineAndPerspective.preTranslate(2, 3, 4);
413    affineAndPerspective.invert(&inverse);
414    double expectedInverseAffineAndPerspective[16] =
415            {0.1, 0,    2,   -2,
416             0,  0.05,  3,   -3,
417             0,   0,   4.01, -4,
418             0,   0,   -1,    1};
419    expected.setRowMajord(expectedInverseAffineAndPerspective);
420    REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
421}
422
423static void test_transpose(skiatest::Reporter* reporter) {
424    SkMatrix44 a(SkMatrix44::kUninitialized_Constructor);
425    SkMatrix44 b(SkMatrix44::kUninitialized_Constructor);
426
427    int i = 0;
428    for (int row = 0; row < 4; ++row) {
429        for (int col = 0; col < 4; ++col) {
430            a.setDouble(row, col, i);
431            b.setDouble(col, row, i++);
432        }
433    }
434
435    a.transpose();
436    REPORTER_ASSERT(reporter, nearly_equal(a, b));
437}
438
439static void test_get_set_double(skiatest::Reporter* reporter) {
440    SkMatrix44 a(SkMatrix44::kUninitialized_Constructor);
441    for (int row = 0; row < 4; ++row) {
442        for (int col = 0; col < 4; ++col) {
443            a.setDouble(row, col, 3.141592653589793);
444            REPORTER_ASSERT(reporter,
445                            nearly_equal_double(3.141592653589793,
446                                                a.getDouble(row, col)));
447            a.setDouble(row, col, 0);
448            REPORTER_ASSERT(reporter,
449                            nearly_equal_double(0, a.getDouble(row, col)));
450        }
451    }
452}
453
454static void test_set_row_col_major(skiatest::Reporter* reporter) {
455    SkMatrix44 a(SkMatrix44::kUninitialized_Constructor);
456    SkMatrix44 b(SkMatrix44::kUninitialized_Constructor);
457
458    for (int row = 0; row < 4; ++row) {
459        for (int col = 0; col < 4; ++col) {
460            a.setDouble(row, col, row * 4 + col);
461        }
462    }
463
464    double bufferd[16];
465    float bufferf[16];
466    a.asColMajord(bufferd);
467    b.setColMajord(bufferd);
468    REPORTER_ASSERT(reporter, nearly_equal(a, b));
469    b.setRowMajord(bufferd);
470    b.transpose();
471    REPORTER_ASSERT(reporter, nearly_equal(a, b));
472    a.asColMajorf(bufferf);
473    b.setColMajorf(bufferf);
474    REPORTER_ASSERT(reporter, nearly_equal(a, b));
475    b.setRowMajorf(bufferf);
476    b.transpose();
477    REPORTER_ASSERT(reporter, nearly_equal(a, b));
478}
479
480static void test_3x3_conversion(skiatest::Reporter* reporter) {
481    SkMScalar values4x4[16] = { 1, 2, 3, 4,
482                                5, 6, 7, 8,
483                                9, 10, 11, 12,
484                                13, 14, 15, 16 };
485    SkScalar values3x3[9] = { 1, 2, 4,
486                              5, 6, 8,
487                              13, 14, 16 };
488    SkMScalar values4x4flattened[16] = { 1, 2, 0, 4,
489                                         5, 6, 0, 8,
490                                         0, 0, 1, 0,
491                                         13, 14, 0, 16 };
492    SkMatrix44 a44(SkMatrix44::kUninitialized_Constructor);
493    a44.setRowMajor(values4x4);
494
495    SkMatrix a33 = a44;
496    SkMatrix expected33;
497    for (int i = 0; i < 9; i++) expected33[i] = values3x3[i];
498    REPORTER_ASSERT(reporter, expected33 == a33);
499
500    SkMatrix44 a44flattened = a33;
501    SkMatrix44 expected44flattened(SkMatrix44::kUninitialized_Constructor);
502    expected44flattened.setRowMajor(values4x4flattened);
503    REPORTER_ASSERT(reporter, nearly_equal(a44flattened, expected44flattened));
504
505    // Test that a point with a Z value of 0 is transformed the same way.
506    SkScalar vec4[4] = { 2, 4, 0, 8 };
507    SkScalar vec3[3] = { 2, 4, 8 };
508
509    SkScalar vec4transformed[4];
510    SkScalar vec3transformed[3];
511    SkScalar vec4transformed2[4];
512    a44.mapScalars(vec4, vec4transformed);
513    a33.mapHomogeneousPoints(vec3transformed, vec3, 1);
514    a44flattened.mapScalars(vec4, vec4transformed2);
515    REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec3transformed[0]));
516    REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec3transformed[1]));
517    REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec3transformed[2]));
518    REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec4transformed2[0]));
519    REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec4transformed2[1]));
520    REPORTER_ASSERT(reporter, !nearly_equal_scalar(vec4transformed[2], vec4transformed2[2]));
521    REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec4transformed2[3]));
522}
523
524static void test_has_perspective(skiatest::Reporter* reporter) {
525    SkMatrix44 transform(SkMatrix44::kIdentity_Constructor);
526
527    transform.set(3, 2, -0.1);
528    REPORTER_ASSERT(reporter, transform.hasPerspective());
529
530    transform.reset();
531    REPORTER_ASSERT(reporter, !transform.hasPerspective());
532
533    transform.set(3, 0, -1.0);
534    REPORTER_ASSERT(reporter, transform.hasPerspective());
535
536    transform.reset();
537    transform.set(3, 1, -1.0);
538    REPORTER_ASSERT(reporter, transform.hasPerspective());
539
540    transform.reset();
541    transform.set(3, 2, -0.3);
542    REPORTER_ASSERT(reporter, transform.hasPerspective());
543
544    transform.reset();
545    transform.set(3, 3, 0.5);
546    REPORTER_ASSERT(reporter, transform.hasPerspective());
547
548    transform.reset();
549    transform.set(3, 3, 0.0);
550    REPORTER_ASSERT(reporter, transform.hasPerspective());
551}
552
553DEF_TEST(Matrix44, reporter) {
554    SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
555    SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
556    SkMatrix44 iden1(SkMatrix44::kUninitialized_Constructor);
557    SkMatrix44 iden2(SkMatrix44::kUninitialized_Constructor);
558    SkMatrix44 rot(SkMatrix44::kUninitialized_Constructor);
559
560    mat.setTranslate(1, 1, 1);
561    mat.invert(&inverse);
562    iden1.setConcat(mat, inverse);
563    REPORTER_ASSERT(reporter, is_identity(iden1));
564
565    mat.setScale(2, 2, 2);
566    mat.invert(&inverse);
567    iden1.setConcat(mat, inverse);
568    REPORTER_ASSERT(reporter, is_identity(iden1));
569
570    mat.setScale(SK_MScalar1/2, SK_MScalar1/2, SK_MScalar1/2);
571    mat.invert(&inverse);
572    iden1.setConcat(mat, inverse);
573    REPORTER_ASSERT(reporter, is_identity(iden1));
574
575    mat.setScale(3, 3, 3);
576    rot.setRotateDegreesAbout(0, 0, -1, 90);
577    mat.postConcat(rot);
578    REPORTER_ASSERT(reporter, mat.invert(NULL));
579    mat.invert(&inverse);
580    iden1.setConcat(mat, inverse);
581    REPORTER_ASSERT(reporter, is_identity(iden1));
582    iden2.setConcat(inverse, mat);
583    REPORTER_ASSERT(reporter, is_identity(iden2));
584
585    // test tiny-valued matrix inverse
586    mat.reset();
587    mat.setScale(1.0e-12, 1.0e-12, 1.0e-12);
588    rot.setRotateDegreesAbout(0, 0, -1, 90);
589    mat.postConcat(rot);
590    mat.postTranslate(1.0e-12, 1.0e-12, 1.0e-12);
591    REPORTER_ASSERT(reporter, mat.invert(NULL));
592    mat.invert(&inverse);
593    iden1.setConcat(mat, inverse);
594    REPORTER_ASSERT(reporter, is_identity(iden1));
595
596    // test mixed-valued matrix inverse
597    mat.reset();
598    mat.setScale(1.0e-10, 3.0, 1.0e+10);
599    rot.setRotateDegreesAbout(0, 0, -1, 90);
600    mat.postConcat(rot);
601    mat.postTranslate(1.0e+10, 3.0, 1.0e-10);
602    REPORTER_ASSERT(reporter, mat.invert(NULL));
603    mat.invert(&inverse);
604    iden1.setConcat(mat, inverse);
605    REPORTER_ASSERT(reporter, is_identity(iden1));
606
607    // test degenerate matrix
608    mat.reset();
609    mat.set3x3(1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0);
610    REPORTER_ASSERT(reporter, !mat.invert(NULL));
611
612    // test rol/col Major getters
613    {
614        mat.setTranslate(2, 3, 4);
615        float dataf[16];
616        double datad[16];
617
618        mat.asColMajorf(dataf);
619        assert16<float>(reporter, dataf,
620                 1, 0, 0, 0,
621                 0, 1, 0, 0,
622                 0, 0, 1, 0,
623                 2, 3, 4, 1);
624        mat.asColMajord(datad);
625        assert16<double>(reporter, datad, 1, 0, 0, 0,
626                        0, 1, 0, 0,
627                        0, 0, 1, 0,
628                        2, 3, 4, 1);
629        mat.asRowMajorf(dataf);
630        assert16<float>(reporter, dataf, 1, 0, 0, 2,
631                        0, 1, 0, 3,
632                        0, 0, 1, 4,
633                        0, 0, 0, 1);
634        mat.asRowMajord(datad);
635        assert16<double>(reporter, datad, 1, 0, 0, 2,
636                        0, 1, 0, 3,
637                        0, 0, 1, 4,
638                        0, 0, 0, 1);
639    }
640
641    test_concat(reporter);
642
643    if (false) { // avoid bit rot, suppress warning (working on making this pass)
644        test_common_angles(reporter);
645    }
646
647    test_constructor(reporter);
648    test_gettype(reporter);
649    test_determinant(reporter);
650    test_invert(reporter);
651    test_transpose(reporter);
652    test_get_set_double(reporter);
653    test_set_row_col_major(reporter);
654    test_translate(reporter);
655    test_scale(reporter);
656    test_map2(reporter);
657    test_3x3_conversion(reporter);
658    test_has_perspective(reporter);
659}
660