1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "Test.h"
9#include "SkMatrix44.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_scalar(SkMScalar a, SkMScalar b) {
20    // Note that we get more compounded error for multiple operations when
21    // SK_SCALAR_IS_FIXED.
22#ifdef SK_SCALAR_IS_FLOAT
23    const SkScalar tolerance = SK_Scalar1 / 200000;
24#else
25    const SkScalar tolerance = SK_Scalar1 / 1024;
26#endif
27
28    return SkTAbs<SkMScalar>(a - b) <= tolerance;
29}
30
31template <typename T> void assert16(skiatest::Reporter* reporter, const T data[],
32                                    T m0,  T m1,  T m2,  T m3,
33                                    T m4,  T m5,  T m6,  T m7,
34                                    T m8,  T m9,  T m10, T m11,
35                                    T m12, T m13, T m14, T m15) {
36    REPORTER_ASSERT(reporter, data[0] == m0);
37    REPORTER_ASSERT(reporter, data[1] == m1);
38    REPORTER_ASSERT(reporter, data[2] == m2);
39    REPORTER_ASSERT(reporter, data[3] == m3);
40
41    REPORTER_ASSERT(reporter, data[4] == m4);
42    REPORTER_ASSERT(reporter, data[5] == m5);
43    REPORTER_ASSERT(reporter, data[6] == m6);
44    REPORTER_ASSERT(reporter, data[7] == m7);
45
46    REPORTER_ASSERT(reporter, data[8] == m8);
47    REPORTER_ASSERT(reporter, data[9] == m9);
48    REPORTER_ASSERT(reporter, data[10] == m10);
49    REPORTER_ASSERT(reporter, data[11] == m11);
50
51    REPORTER_ASSERT(reporter, data[12] == m12);
52    REPORTER_ASSERT(reporter, data[13] == m13);
53    REPORTER_ASSERT(reporter, data[14] == m14);
54    REPORTER_ASSERT(reporter, data[15] == m15);
55}
56
57static bool nearly_equal(const SkMatrix44& a, const SkMatrix44& b) {
58    for (int i = 0; i < 4; ++i) {
59        for (int j = 0; j < 4; ++j) {
60            if (!nearly_equal_scalar(a.get(i, j), b.get(i, j))) {
61                printf("not equal %g %g\n", a.get(i, j), b.get(i, j));
62                return false;
63            }
64        }
65    }
66    return true;
67}
68
69static bool is_identity(const SkMatrix44& m) {
70    SkMatrix44 identity;
71    identity.reset();
72    return nearly_equal(m, identity);
73}
74
75///////////////////////////////////////////////////////////////////////////////
76static bool bits_isonly(int value, int mask) {
77    return 0 == (value & ~mask);
78}
79
80static void test_constructor(skiatest::Reporter* reporter) {
81    // Allocate a matrix on the heap
82    SkMatrix44* placeholderMatrix = new SkMatrix44();
83    SkAutoTDelete<SkMatrix44> deleteMe(placeholderMatrix);
84
85    for (int row = 0; row < 4; ++row) {
86        for (int col = 0; col < 4; ++col) {
87            placeholderMatrix->setDouble(row, col, row * col);
88        }
89    }
90
91    // Use placement-new syntax to trigger the constructor on top of the heap
92    // address we already initialized. This allows us to check that the
93    // constructor did avoid initializing the matrix contents.
94    SkMatrix44* testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kUninitialized_Constructor);
95    REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
96    REPORTER_ASSERT(reporter, !testMatrix->isIdentity());
97    for (int row = 0; row < 4; ++row) {
98        for (int col = 0; col < 4; ++col) {
99            REPORTER_ASSERT(reporter, nearly_equal_double(row * col, testMatrix->getDouble(row, col)));
100        }
101    }
102
103    // Verify that kIdentity_Constructor really does initialize to an identity matrix.
104    testMatrix = 0;
105    testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kIdentity_Constructor);
106    REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
107    REPORTER_ASSERT(reporter, testMatrix->isIdentity());
108    REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
109}
110
111static void test_translate(skiatest::Reporter* reporter) {
112    SkMatrix44 mat, inverse;
113
114    mat.setTranslate(0, 0, 0);
115    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
116    mat.setTranslate(1, 2, 3);
117    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kTranslate_Mask));
118    REPORTER_ASSERT(reporter, mat.invert(&inverse));
119    REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kTranslate_Mask));
120
121    SkMatrix44 a, b, c;
122    a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
123    b.setTranslate(10, 11, 12);
124
125    c.setConcat(a, b);
126    mat = a;
127    mat.preTranslate(10, 11, 12);
128    REPORTER_ASSERT(reporter, mat == c);
129
130    c.setConcat(b, a);
131    mat = a;
132    mat.postTranslate(10, 11, 12);
133    REPORTER_ASSERT(reporter, mat == c);
134}
135
136static void test_scale(skiatest::Reporter* reporter) {
137    SkMatrix44 mat, inverse;
138
139    mat.setScale(1, 1, 1);
140    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
141    mat.setScale(1, 2, 3);
142    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kScale_Mask));
143    REPORTER_ASSERT(reporter, mat.invert(&inverse));
144    REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kScale_Mask));
145
146    SkMatrix44 a, b, c;
147    a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
148    b.setScale(10, 11, 12);
149
150    c.setConcat(a, b);
151    mat = a;
152    mat.preScale(10, 11, 12);
153    REPORTER_ASSERT(reporter, mat == c);
154
155    c.setConcat(b, a);
156    mat = a;
157    mat.postScale(10, 11, 12);
158    REPORTER_ASSERT(reporter, mat == c);
159}
160
161static void make_i(SkMatrix44* mat) { mat->setIdentity(); }
162static void make_t(SkMatrix44* mat) { mat->setTranslate(1, 2, 3); }
163static void make_s(SkMatrix44* mat) { mat->setScale(1, 2, 3); }
164static void make_st(SkMatrix44* mat) {
165    mat->setScale(1, 2, 3);
166    mat->postTranslate(1, 2, 3);
167}
168static void make_a(SkMatrix44* mat) {
169    mat->setRotateDegreesAbout(1, 2, 3, 45);
170}
171static void make_p(SkMatrix44* mat) {
172    SkMScalar data[] = {
173        1, 2, 3, 4, 5, 6, 7, 8,
174        1, 2, 3, 4, 5, 6, 7, 8,
175    };
176    mat->setRowMajor(data);
177}
178
179typedef void (*Make44Proc)(SkMatrix44*);
180
181static const Make44Proc gMakeProcs[] = {
182    make_i, make_t, make_s, make_st, make_a, make_p
183};
184
185static void test_map2(skiatest::Reporter* reporter, const SkMatrix44& mat) {
186    SkMScalar src2[] = { 1, 2 };
187    SkMScalar src4[] = { src2[0], src2[1], 0, 1 };
188    SkMScalar dstA[4], dstB[4];
189
190    for (int i = 0; i < 4; ++i) {
191        dstA[i] = 123456789;
192        dstB[i] = 987654321;
193    }
194
195    mat.map2(src2, 1, dstA);
196    mat.mapMScalars(src4, dstB);
197
198    for (int i = 0; i < 4; ++i) {
199        REPORTER_ASSERT(reporter, dstA[i] == dstB[i]);
200    }
201}
202
203static void test_map2(skiatest::Reporter* reporter) {
204    SkMatrix44 mat;
205
206    for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProcs); ++i) {
207        gMakeProcs[i](&mat);
208        test_map2(reporter, mat);
209    }
210}
211
212static void test_gettype(skiatest::Reporter* reporter) {
213    SkMatrix44 matrix;
214
215    REPORTER_ASSERT(reporter, matrix.isIdentity());
216    REPORTER_ASSERT(reporter, SkMatrix44::kIdentity_Mask == matrix.getType());
217
218    int expectedMask;
219
220    matrix.set(1, 1, 0);
221    expectedMask = SkMatrix44::kScale_Mask;
222    REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
223
224    matrix.set(0, 3, 1);    // translate-x
225    expectedMask |= SkMatrix44::kTranslate_Mask;
226    REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
227
228    matrix.set(2, 0, 1);
229    expectedMask |= SkMatrix44::kAffine_Mask;
230    REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
231
232    matrix.set(3, 2, 1);
233    REPORTER_ASSERT(reporter, matrix.getType() & SkMatrix44::kPerspective_Mask);
234
235    // ensure that negative zero is treated as zero
236    SkMScalar dx = 0;
237    SkMScalar dy = 0;
238    SkMScalar dz = 0;
239    matrix.setTranslate(-dx, -dy, -dz);
240    REPORTER_ASSERT(reporter, matrix.isIdentity());
241    matrix.preTranslate(-dx, -dy, -dz);
242    REPORTER_ASSERT(reporter, matrix.isIdentity());
243    matrix.postTranslate(-dx, -dy, -dz);
244    REPORTER_ASSERT(reporter, matrix.isIdentity());
245}
246
247static void test_common_angles(skiatest::Reporter* reporter) {
248    SkMatrix44 rot;
249    // Test precision of rotation in common cases
250    int common_angles[] = { 0, 90, -90, 180, -180, 270, -270, 360, -360 };
251    for (int i = 0; i < 9; ++i) {
252        rot.setRotateDegreesAbout(0, 0, -1, SkIntToScalar(common_angles[i]));
253
254        SkMatrix rot3x3 = rot;
255        REPORTER_ASSERT(reporter, rot3x3.rectStaysRect());
256    }
257}
258
259static void test_concat(skiatest::Reporter* reporter) {
260    int i;
261    SkMatrix44 a, b, c, d;
262
263    a.setTranslate(10, 10, 10);
264    b.setScale(2, 2, 2);
265
266    SkScalar src[8] = {
267        0, 0, 0, 1,
268        1, 1, 1, 1
269    };
270    SkScalar dst[8];
271
272    c.setConcat(a, b);
273
274    d = a;
275    d.preConcat(b);
276    REPORTER_ASSERT(reporter, d == c);
277
278    c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
279    for (i = 0; i < 3; ++i) {
280        REPORTER_ASSERT(reporter, 10 == dst[i]);
281        REPORTER_ASSERT(reporter, 12 == dst[i + 4]);
282    }
283
284    c.setConcat(b, a);
285
286    d = a;
287    d.postConcat(b);
288    REPORTER_ASSERT(reporter, d == c);
289
290    c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
291    for (i = 0; i < 3; ++i) {
292        REPORTER_ASSERT(reporter, 20 == dst[i]);
293        REPORTER_ASSERT(reporter, 22 == dst[i + 4]);
294    }
295}
296
297static void test_determinant(skiatest::Reporter* reporter) {
298    SkMatrix44 a;
299    REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant()));
300    a.set(1, 1, 2);
301    REPORTER_ASSERT(reporter, nearly_equal_double(2, a.determinant()));
302    SkMatrix44 b;
303    REPORTER_ASSERT(reporter, a.invert(&b));
304    REPORTER_ASSERT(reporter, nearly_equal_double(0.5, b.determinant()));
305    SkMatrix44 c = b = a;
306    c.set(0, 1, 4);
307    b.set(1, 0, 4);
308    REPORTER_ASSERT(reporter,
309                    nearly_equal_double(a.determinant(),
310                                        b.determinant()));
311    SkMatrix44 d = a;
312    d.set(0, 0, 8);
313    REPORTER_ASSERT(reporter, nearly_equal_double(16, d.determinant()));
314
315    SkMatrix44 e = a;
316    e.postConcat(d);
317    REPORTER_ASSERT(reporter, nearly_equal_double(32, e.determinant()));
318    e.set(0, 0, 0);
319    REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant()));
320}
321
322static void test_transpose(skiatest::Reporter* reporter) {
323    SkMatrix44 a;
324    SkMatrix44 b;
325
326    int i = 0;
327    for (int row = 0; row < 4; ++row) {
328        for (int col = 0; col < 4; ++col) {
329            a.setDouble(row, col, i);
330            b.setDouble(col, row, i++);
331        }
332    }
333
334    a.transpose();
335    REPORTER_ASSERT(reporter, nearly_equal(a, b));
336}
337
338static void test_get_set_double(skiatest::Reporter* reporter) {
339    SkMatrix44 a;
340    for (int row = 0; row < 4; ++row) {
341        for (int col = 0; col < 4; ++col) {
342            a.setDouble(row, col, 3.141592653589793);
343            REPORTER_ASSERT(reporter,
344                            nearly_equal_double(3.141592653589793,
345                                                a.getDouble(row, col)));
346            a.setDouble(row, col, 0);
347            REPORTER_ASSERT(reporter,
348                            nearly_equal_double(0, a.getDouble(row, col)));
349        }
350    }
351}
352
353static void test_set_row_col_major(skiatest::Reporter* reporter) {
354    SkMatrix44 a, b, c, d;
355    for (int row = 0; row < 4; ++row) {
356        for (int col = 0; col < 4; ++col) {
357            a.setDouble(row, col, row * 4 + col);
358        }
359    }
360
361    double bufferd[16];
362    float bufferf[16];
363    a.asColMajord(bufferd);
364    b.setColMajord(bufferd);
365    REPORTER_ASSERT(reporter, nearly_equal(a, b));
366    b.setRowMajord(bufferd);
367    b.transpose();
368    REPORTER_ASSERT(reporter, nearly_equal(a, b));
369    a.asColMajorf(bufferf);
370    b.setColMajorf(bufferf);
371    REPORTER_ASSERT(reporter, nearly_equal(a, b));
372    b.setRowMajorf(bufferf);
373    b.transpose();
374    REPORTER_ASSERT(reporter, nearly_equal(a, b));
375}
376
377static void TestMatrix44(skiatest::Reporter* reporter) {
378    SkMatrix44 mat, inverse, iden1, iden2, rot;
379
380    mat.reset();
381    mat.setTranslate(1, 1, 1);
382    mat.invert(&inverse);
383    iden1.setConcat(mat, inverse);
384    REPORTER_ASSERT(reporter, is_identity(iden1));
385
386    mat.setScale(2, 2, 2);
387    mat.invert(&inverse);
388    iden1.setConcat(mat, inverse);
389    REPORTER_ASSERT(reporter, is_identity(iden1));
390
391    mat.setScale(SK_MScalar1/2, SK_MScalar1/2, SK_MScalar1/2);
392    mat.invert(&inverse);
393    iden1.setConcat(mat, inverse);
394    REPORTER_ASSERT(reporter, is_identity(iden1));
395
396    mat.setScale(3, 3, 3);
397    rot.setRotateDegreesAbout(0, 0, -1, 90);
398    mat.postConcat(rot);
399    REPORTER_ASSERT(reporter, mat.invert(NULL));
400    mat.invert(&inverse);
401    iden1.setConcat(mat, inverse);
402    REPORTER_ASSERT(reporter, is_identity(iden1));
403    iden2.setConcat(inverse, mat);
404    REPORTER_ASSERT(reporter, is_identity(iden2));
405
406    // test rol/col Major getters
407    {
408        mat.setTranslate(2, 3, 4);
409        float dataf[16];
410        double datad[16];
411
412        mat.asColMajorf(dataf);
413        assert16<float>(reporter, dataf,
414                 1, 0, 0, 0,
415                 0, 1, 0, 0,
416                 0, 0, 1, 0,
417                 2, 3, 4, 1);
418        mat.asColMajord(datad);
419        assert16<double>(reporter, datad, 1, 0, 0, 0,
420                        0, 1, 0, 0,
421                        0, 0, 1, 0,
422                        2, 3, 4, 1);
423        mat.asRowMajorf(dataf);
424        assert16<float>(reporter, dataf, 1, 0, 0, 2,
425                        0, 1, 0, 3,
426                        0, 0, 1, 4,
427                        0, 0, 0, 1);
428        mat.asRowMajord(datad);
429        assert16<double>(reporter, datad, 1, 0, 0, 2,
430                        0, 1, 0, 3,
431                        0, 0, 1, 4,
432                        0, 0, 0, 1);
433    }
434
435    test_concat(reporter);
436
437    if (false) { // avoid bit rot, suppress warning (working on making this pass)
438        test_common_angles(reporter);
439    }
440
441    test_constructor(reporter);
442    test_gettype(reporter);
443    test_determinant(reporter);
444    test_transpose(reporter);
445    test_get_set_double(reporter);
446    test_set_row_col_major(reporter);
447    test_translate(reporter);
448    test_scale(reporter);
449    test_map2(reporter);
450}
451
452#include "TestClassDef.h"
453DEFINE_TESTCLASS("Matrix44", Matrix44TestClass, TestMatrix44)
454