• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkPoint3.h"
10 #include "Test.h"
11 
nearly_equal_double(double a,double b)12 static bool nearly_equal_double(double a, double b) {
13     const double tolerance = 1e-7;
14     double diff = a - b;
15     if (diff < 0)
16         diff = -diff;
17     return diff <= tolerance;
18 }
19 
nearly_equal_mscalar(SkMScalar a,SkMScalar b)20 static bool nearly_equal_mscalar(SkMScalar a, SkMScalar b) {
21     const SkMScalar tolerance = SK_MScalar1 / 200000;
22 
23     return SkTAbs<SkMScalar>(a - b) <= tolerance;
24 }
25 
nearly_equal_scalar(SkScalar a,SkScalar b)26 static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
27     const SkScalar tolerance = SK_Scalar1 / 200000;
28     return SkScalarAbs(a - b) <= tolerance;
29 }
30 
assert16(skiatest::Reporter * reporter,const T data[],T m0,T m1,T m2,T m3,T m4,T m5,T m6,T m7,T m8,T m9,T m10,T m11,T m12,T m13,T m14,T m15)31 template <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 
nearly_equal(const SkMatrix44 & a,const SkMatrix44 & b)57 static 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_mscalar(a.get(i, j), b.get(i, j))) {
61                 SkDebugf("not equal %g %g\n", a.get(i, j), b.get(i, j));
62                 return false;
63             }
64         }
65     }
66     return true;
67 }
68 
is_identity(const SkMatrix44 & m)69 static bool is_identity(const SkMatrix44& m) {
70     SkMatrix44 identity(SkMatrix44::kIdentity_Constructor);
71     return nearly_equal(m, identity);
72 }
73 
74 ///////////////////////////////////////////////////////////////////////////////
bits_isonly(int value,int mask)75 static bool bits_isonly(int value, int mask) {
76     return 0 == (value & ~mask);
77 }
78 
test_constructor(skiatest::Reporter * reporter)79 static void test_constructor(skiatest::Reporter* reporter) {
80     // Allocate a matrix on the heap
81     SkMatrix44* placeholderMatrix = new SkMatrix44;
82     std::unique_ptr<SkMatrix44> deleteMe(placeholderMatrix);
83 
84     for (int row = 0; row < 4; ++row) {
85         for (int col = 0; col < 4; ++col) {
86             placeholderMatrix->setDouble(row, col, row * col);
87         }
88     }
89 
90     // Use placement-new syntax to trigger the constructor on top of the heap
91     // address we already initialized. This allows us to check that the
92     // constructor did avoid initializing the matrix contents.
93     SkMatrix44* testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kUninitialized_Constructor);
94     REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
95     REPORTER_ASSERT(reporter, !testMatrix->isIdentity());
96     for (int row = 0; row < 4; ++row) {
97         for (int col = 0; col < 4; ++col) {
98             REPORTER_ASSERT(reporter, nearly_equal_double(row * col, testMatrix->getDouble(row, col)));
99         }
100     }
101 
102     // Verify that kIdentity_Constructor really does initialize to an identity matrix.
103     testMatrix = 0;
104     testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kIdentity_Constructor);
105     REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
106     REPORTER_ASSERT(reporter, testMatrix->isIdentity());
107     REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
108 
109     // Verify that that constructing from an SkMatrix initializes everything.
110     SkMatrix44 scaleMatrix;
111     scaleMatrix.setScale(3, 4, 5);
112     REPORTER_ASSERT(reporter, scaleMatrix.isScale());
113     testMatrix = new(&scaleMatrix) SkMatrix44(SkMatrix::I());
114     REPORTER_ASSERT(reporter, testMatrix->isIdentity());
115     REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
116 }
117 
test_translate(skiatest::Reporter * reporter)118 static void test_translate(skiatest::Reporter* reporter) {
119     SkMatrix44 mat;
120     SkMatrix44 inverse;
121 
122     mat.setTranslate(0, 0, 0);
123     REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
124     mat.setTranslate(1, 2, 3);
125     REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kTranslate_Mask));
126     REPORTER_ASSERT(reporter, mat.invert(&inverse));
127     REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kTranslate_Mask));
128 
129     SkMatrix44 a,b,c;
130     a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
131     b.setTranslate(10, 11, 12);
132 
133     c.setConcat(a, b);
134     mat = a;
135     mat.preTranslate(10, 11, 12);
136     REPORTER_ASSERT(reporter, mat == c);
137 
138     c.setConcat(b, a);
139     mat = a;
140     mat.postTranslate(10, 11, 12);
141     REPORTER_ASSERT(reporter, mat == c);
142 }
143 
test_scale(skiatest::Reporter * reporter)144 static void test_scale(skiatest::Reporter* reporter) {
145     SkMatrix44 mat;
146     SkMatrix44 inverse;
147 
148     mat.setScale(1, 1, 1);
149     REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
150     mat.setScale(1, 2, 3);
151     REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kScale_Mask));
152     REPORTER_ASSERT(reporter, mat.invert(&inverse));
153     REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kScale_Mask));
154 
155     SkMatrix44 a,b,c;
156     a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
157     b.setScale(10, 11, 12);
158 
159     c.setConcat(a, b);
160     mat = a;
161     mat.preScale(10, 11, 12);
162     REPORTER_ASSERT(reporter, mat == c);
163 
164     c.setConcat(b, a);
165     mat = a;
166     mat.postScale(10, 11, 12);
167     REPORTER_ASSERT(reporter, mat == c);
168 }
169 
make_i(SkMatrix44 * mat)170 static void make_i(SkMatrix44* mat) { mat->setIdentity(); }
make_t(SkMatrix44 * mat)171 static void make_t(SkMatrix44* mat) { mat->setTranslate(1, 2, 3); }
make_s(SkMatrix44 * mat)172 static void make_s(SkMatrix44* mat) { mat->setScale(1, 2, 3); }
make_st(SkMatrix44 * mat)173 static void make_st(SkMatrix44* mat) {
174     mat->setScale(1, 2, 3);
175     mat->postTranslate(1, 2, 3);
176 }
make_a(SkMatrix44 * mat)177 static void make_a(SkMatrix44* mat) {
178     mat->setRotateDegreesAbout(1, 2, 3, 45);
179 }
make_p(SkMatrix44 * mat)180 static void make_p(SkMatrix44* mat) {
181     SkMScalar data[] = {
182         1, 2, 3, 4, 5, 6, 7, 8,
183         1, 2, 3, 4, 5, 6, 7, 8,
184     };
185     mat->setRowMajor(data);
186 }
187 
188 typedef void (*Make44Proc)(SkMatrix44*);
189 
190 static const Make44Proc gMakeProcs[] = {
191     make_i, make_t, make_s, make_st, make_a, make_p
192 };
193 
test_map2(skiatest::Reporter * reporter,const SkMatrix44 & mat)194 static void test_map2(skiatest::Reporter* reporter, const SkMatrix44& mat) {
195     SkMScalar src2[] = { 1, 2 };
196     SkMScalar src4[] = { src2[0], src2[1], 0, 1 };
197     SkMScalar dstA[4], dstB[4];
198 
199     for (int i = 0; i < 4; ++i) {
200         dstA[i] = SkDoubleToMScalar(123456789);
201         dstB[i] = SkDoubleToMScalar(987654321);
202     }
203 
204     mat.map2(src2, 1, dstA);
205     mat.mapMScalars(src4, dstB);
206 
207     for (int i = 0; i < 4; ++i) {
208         REPORTER_ASSERT(reporter, dstA[i] == dstB[i]);
209     }
210 }
211 
test_map2(skiatest::Reporter * reporter)212 static void test_map2(skiatest::Reporter* reporter) {
213     SkMatrix44 mat;
214 
215     for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProcs); ++i) {
216         gMakeProcs[i](&mat);
217         test_map2(reporter, mat);
218     }
219 }
220 
test_gettype(skiatest::Reporter * reporter)221 static void test_gettype(skiatest::Reporter* reporter) {
222     SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor);
223 
224     REPORTER_ASSERT(reporter, matrix.isIdentity());
225     REPORTER_ASSERT(reporter, SkMatrix44::kIdentity_Mask == matrix.getType());
226 
227     int expectedMask;
228 
229     matrix.set(1, 1, 0);
230     expectedMask = SkMatrix44::kScale_Mask;
231     REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
232 
233     matrix.set(0, 3, 1);    // translate-x
234     expectedMask |= SkMatrix44::kTranslate_Mask;
235     REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
236 
237     matrix.set(2, 0, 1);
238     expectedMask |= SkMatrix44::kAffine_Mask;
239     REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
240 
241     matrix.set(3, 2, 1);
242     REPORTER_ASSERT(reporter, matrix.getType() & SkMatrix44::kPerspective_Mask);
243 
244     // ensure that negative zero is treated as zero
245     SkMScalar dx = 0;
246     SkMScalar dy = 0;
247     SkMScalar dz = 0;
248     matrix.setTranslate(-dx, -dy, -dz);
249     REPORTER_ASSERT(reporter, matrix.isIdentity());
250     matrix.preTranslate(-dx, -dy, -dz);
251     REPORTER_ASSERT(reporter, matrix.isIdentity());
252     matrix.postTranslate(-dx, -dy, -dz);
253     REPORTER_ASSERT(reporter, matrix.isIdentity());
254 }
255 
test_common_angles(skiatest::Reporter * reporter)256 static void test_common_angles(skiatest::Reporter* reporter) {
257     SkMatrix44 rot;
258     // Test precision of rotation in common cases
259     int common_angles[] = { 0, 90, -90, 180, -180, 270, -270, 360, -360 };
260     for (int i = 0; i < 9; ++i) {
261         rot.setRotateDegreesAbout(0, 0, -1, SkIntToScalar(common_angles[i]));
262 
263         SkMatrix rot3x3 = rot;
264         REPORTER_ASSERT(reporter, rot3x3.rectStaysRect());
265     }
266 }
267 
test_concat(skiatest::Reporter * reporter)268 static void test_concat(skiatest::Reporter* reporter) {
269     int i;
270     SkMatrix44 a,b,c,d;
271 
272     a.setTranslate(10, 10, 10);
273     b.setScale(2, 2, 2);
274 
275     SkScalar src[8] = {
276         0, 0, 0, 1,
277         1, 1, 1, 1
278     };
279     SkScalar dst[8];
280 
281     c.setConcat(a, b);
282 
283     d = a;
284     d.preConcat(b);
285     REPORTER_ASSERT(reporter, d == c);
286 
287     c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
288     for (i = 0; i < 3; ++i) {
289         REPORTER_ASSERT(reporter, 10 == dst[i]);
290         REPORTER_ASSERT(reporter, 12 == dst[i + 4]);
291     }
292 
293     c.setConcat(b, a);
294 
295     d = a;
296     d.postConcat(b);
297     REPORTER_ASSERT(reporter, d == c);
298 
299     c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
300     for (i = 0; i < 3; ++i) {
301         REPORTER_ASSERT(reporter, 20 == dst[i]);
302         REPORTER_ASSERT(reporter, 22 == dst[i + 4]);
303     }
304 }
305 
test_determinant(skiatest::Reporter * reporter)306 static void test_determinant(skiatest::Reporter* reporter) {
307     SkMatrix44 a(SkMatrix44::kIdentity_Constructor);
308     REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant()));
309     a.set(1, 1, 2);
310     REPORTER_ASSERT(reporter, nearly_equal_double(2, a.determinant()));
311     SkMatrix44 b;
312     REPORTER_ASSERT(reporter, a.invert(&b));
313     REPORTER_ASSERT(reporter, nearly_equal_double(0.5, b.determinant()));
314     SkMatrix44 c = b = a;
315     c.set(0, 1, 4);
316     b.set(1, 0, 4);
317     REPORTER_ASSERT(reporter,
318                     nearly_equal_double(a.determinant(),
319                                         b.determinant()));
320     SkMatrix44 d = a;
321     d.set(0, 0, 8);
322     REPORTER_ASSERT(reporter, nearly_equal_double(16, d.determinant()));
323 
324     SkMatrix44 e = a;
325     e.postConcat(d);
326     REPORTER_ASSERT(reporter, nearly_equal_double(32, e.determinant()));
327     e.set(0, 0, 0);
328     REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant()));
329 }
330 
test_invert(skiatest::Reporter * reporter)331 static void test_invert(skiatest::Reporter* reporter) {
332     SkMatrix44 inverse;
333     double inverseData[16];
334 
335     SkMatrix44 identity(SkMatrix44::kIdentity_Constructor);
336     identity.invert(&inverse);
337     inverse.asRowMajord(inverseData);
338     assert16<double>(reporter, inverseData,
339                      1, 0, 0, 0,
340                      0, 1, 0, 0,
341                      0, 0, 1, 0,
342                      0, 0, 0, 1);
343 
344     SkMatrix44 translation;
345     translation.setTranslate(2, 3, 4);
346     translation.invert(&inverse);
347     inverse.asRowMajord(inverseData);
348     assert16<double>(reporter, inverseData,
349                      1, 0, 0, -2,
350                      0, 1, 0, -3,
351                      0, 0, 1, -4,
352                      0, 0, 0, 1);
353 
354     SkMatrix44 scale;
355     scale.setScale(2, 4, 8);
356     scale.invert(&inverse);
357     inverse.asRowMajord(inverseData);
358     assert16<double>(reporter, inverseData,
359                      0.5, 0,    0,     0,
360                      0,   0.25, 0,     0,
361                      0,   0,    0.125, 0,
362                      0,   0,    0,     1);
363 
364     SkMatrix44 scaleTranslation;
365     scaleTranslation.setScale(32, 128, 1024);
366     scaleTranslation.preTranslate(2, 3, 4);
367     scaleTranslation.invert(&inverse);
368     inverse.asRowMajord(inverseData);
369     assert16<double>(reporter, inverseData,
370                      0.03125,  0,          0,            -2,
371                      0,        0.0078125,  0,            -3,
372                      0,        0,          0.0009765625, -4,
373                      0,        0,          0,             1);
374 
375     SkMatrix44 rotation;
376     rotation.setRotateDegreesAbout(0, 0, 1, 90);
377     rotation.invert(&inverse);
378     SkMatrix44 expected;
379     double expectedInverseRotation[16] =
380             {0,  1, 0, 0,
381              -1, 0, 0, 0,
382              0,  0, 1, 0,
383              0,  0, 0, 1};
384     expected.setRowMajord(expectedInverseRotation);
385     REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
386 
387     SkMatrix44 affine;
388     affine.setRotateDegreesAbout(0, 0, 1, 90);
389     affine.preScale(10, 20, 100);
390     affine.preTranslate(2, 3, 4);
391     affine.invert(&inverse);
392     double expectedInverseAffine[16] =
393             {0,    0.1,  0,   -2,
394              -0.05, 0,   0,   -3,
395              0,     0,  0.01, -4,
396              0,     0,   0,   1};
397     expected.setRowMajord(expectedInverseAffine);
398     REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
399 
400     SkMatrix44 perspective(SkMatrix44::kIdentity_Constructor);
401     perspective.setDouble(3, 2, 1.0);
402     perspective.invert(&inverse);
403     double expectedInversePerspective[16] =
404             {1, 0,  0, 0,
405              0, 1,  0, 0,
406              0, 0,  1, 0,
407              0, 0, -1, 1};
408     expected.setRowMajord(expectedInversePerspective);
409     REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
410 
411     SkMatrix44 affineAndPerspective(SkMatrix44::kIdentity_Constructor);
412     affineAndPerspective.setDouble(3, 2, 1.0);
413     affineAndPerspective.preScale(10, 20, 100);
414     affineAndPerspective.preTranslate(2, 3, 4);
415     affineAndPerspective.invert(&inverse);
416     double expectedInverseAffineAndPerspective[16] =
417             {0.1, 0,    2,   -2,
418              0,  0.05,  3,   -3,
419              0,   0,   4.01, -4,
420              0,   0,   -1,    1};
421     expected.setRowMajord(expectedInverseAffineAndPerspective);
422     REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
423 
424     SkMatrix44 tinyScale(SkMatrix44::kIdentity_Constructor);
425     tinyScale.setDouble(0, 0, 1e-39);
426     REPORTER_ASSERT(reporter, tinyScale.getType() == SkMatrix44::kScale_Mask);
427     REPORTER_ASSERT(reporter, !tinyScale.invert(nullptr));
428     REPORTER_ASSERT(reporter, !tinyScale.invert(&inverse));
429 
430     SkMatrix44 tinyScaleTranslate(SkMatrix44::kIdentity_Constructor);
431     tinyScaleTranslate.setDouble(0, 0, 1e-38);
432     REPORTER_ASSERT(reporter, tinyScaleTranslate.invert(nullptr));
433     tinyScaleTranslate.setDouble(0, 3, 10);
434     REPORTER_ASSERT(
435         reporter, tinyScaleTranslate.getType() ==
436                       (SkMatrix44::kScale_Mask | SkMatrix44::kTranslate_Mask));
437     REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(nullptr));
438     REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(&inverse));
439 
440     SkMatrix44 tinyScalePerspective(SkMatrix44::kIdentity_Constructor);
441     tinyScalePerspective.setDouble(0, 0, 1e-39);
442     tinyScalePerspective.setDouble(3, 2, -1);
443     REPORTER_ASSERT(reporter, (tinyScalePerspective.getType() &
444                                SkMatrix44::kPerspective_Mask) ==
445                                   SkMatrix44::kPerspective_Mask);
446     REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(nullptr));
447     REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(&inverse));
448 }
449 
test_transpose(skiatest::Reporter * reporter)450 static void test_transpose(skiatest::Reporter* reporter) {
451     SkMatrix44 a,b;
452 
453     int i = 0;
454     for (int row = 0; row < 4; ++row) {
455         for (int col = 0; col < 4; ++col) {
456             a.setDouble(row, col, i);
457             b.setDouble(col, row, i++);
458         }
459     }
460 
461     a.transpose();
462     REPORTER_ASSERT(reporter, nearly_equal(a, b));
463 }
464 
test_get_set_double(skiatest::Reporter * reporter)465 static void test_get_set_double(skiatest::Reporter* reporter) {
466     SkMatrix44 a;
467     for (int row = 0; row < 4; ++row) {
468         for (int col = 0; col < 4; ++col) {
469             a.setDouble(row, col, 3.141592653589793);
470             REPORTER_ASSERT(reporter,
471                             nearly_equal_double(3.141592653589793,
472                                                 a.getDouble(row, col)));
473             a.setDouble(row, col, 0);
474             REPORTER_ASSERT(reporter,
475                             nearly_equal_double(0, a.getDouble(row, col)));
476         }
477     }
478 }
479 
test_set_3x3(skiatest::Reporter * r)480 static void test_set_3x3(skiatest::Reporter* r) {
481     static float vals[9] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, };
482 
483     SkMatrix44 mat;
484     mat.set3x3RowMajorf(vals);
485 
486     REPORTER_ASSERT(r, 1.0f == mat.getFloat(0, 0));
487     REPORTER_ASSERT(r, 2.0f == mat.getFloat(0, 1));
488     REPORTER_ASSERT(r, 3.0f == mat.getFloat(0, 2));
489     REPORTER_ASSERT(r, 4.0f == mat.getFloat(1, 0));
490     REPORTER_ASSERT(r, 5.0f == mat.getFloat(1, 1));
491     REPORTER_ASSERT(r, 6.0f == mat.getFloat(1, 2));
492     REPORTER_ASSERT(r, 7.0f == mat.getFloat(2, 0));
493     REPORTER_ASSERT(r, 8.0f == mat.getFloat(2, 1));
494     REPORTER_ASSERT(r, 9.0f == mat.getFloat(2, 2));
495 }
496 
test_set_row_col_major(skiatest::Reporter * reporter)497 static void test_set_row_col_major(skiatest::Reporter* reporter) {
498     SkMatrix44 a,b;
499 
500     for (int row = 0; row < 4; ++row) {
501         for (int col = 0; col < 4; ++col) {
502             a.setDouble(row, col, row * 4 + col);
503         }
504     }
505 
506     double bufferd[16];
507     float bufferf[16];
508     a.asColMajord(bufferd);
509     b.setColMajord(bufferd);
510     REPORTER_ASSERT(reporter, nearly_equal(a, b));
511     b.setRowMajord(bufferd);
512     b.transpose();
513     REPORTER_ASSERT(reporter, nearly_equal(a, b));
514     a.asColMajorf(bufferf);
515     b.setColMajorf(bufferf);
516     REPORTER_ASSERT(reporter, nearly_equal(a, b));
517     b.setRowMajorf(bufferf);
518     b.transpose();
519     REPORTER_ASSERT(reporter, nearly_equal(a, b));
520 }
521 
test_3x3_conversion(skiatest::Reporter * reporter)522 static void test_3x3_conversion(skiatest::Reporter* reporter) {
523     SkMScalar values4x4[16] = { 1, 2, 3, 4,
524                                 5, 6, 7, 8,
525                                 9, 10, 11, 12,
526                                 13, 14, 15, 16 };
527     SkScalar values3x3[9] = { 1, 2, 4,
528                               5, 6, 8,
529                               13, 14, 16 };
530     SkMScalar values4x4flattened[16] = { 1, 2, 0, 4,
531                                          5, 6, 0, 8,
532                                          0, 0, 1, 0,
533                                          13, 14, 0, 16 };
534     SkMatrix44 a44;
535     a44.setRowMajor(values4x4);
536 
537     SkMatrix a33 = a44;
538     SkMatrix expected33;
539     for (int i = 0; i < 9; i++) expected33[i] = values3x3[i];
540     REPORTER_ASSERT(reporter, expected33 == a33);
541 
542     SkMatrix44 a44flattened = a33;
543     SkMatrix44 expected44flattened;
544     expected44flattened.setRowMajor(values4x4flattened);
545     REPORTER_ASSERT(reporter, nearly_equal(a44flattened, expected44flattened));
546 
547     // Test that a point with a Z value of 0 is transformed the same way.
548     SkScalar vec4[4] = { 2, 4, 0, 8 };
549     SkPoint3 vec3 = { 2, 4, 8 };
550 
551     SkScalar vec4transformed[4];
552     SkPoint3 vec3transformed;
553     SkScalar vec4transformed2[4];
554     a44.mapScalars(vec4, vec4transformed);
555     a33.mapHomogeneousPoints(&vec3transformed, &vec3, 1);
556     a44flattened.mapScalars(vec4, vec4transformed2);
557     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec3transformed.fX));
558     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec3transformed.fY));
559     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec3transformed.fZ));
560     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec4transformed2[0]));
561     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec4transformed2[1]));
562     REPORTER_ASSERT(reporter, !nearly_equal_scalar(vec4transformed[2], vec4transformed2[2]));
563     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec4transformed2[3]));
564 }
565 
test_has_perspective(skiatest::Reporter * reporter)566 static void test_has_perspective(skiatest::Reporter* reporter) {
567     SkMatrix44 transform(SkMatrix44::kIdentity_Constructor);
568 
569     transform.setDouble(3, 2, -0.1);
570     REPORTER_ASSERT(reporter, transform.hasPerspective());
571 
572     transform.reset();
573     REPORTER_ASSERT(reporter, !transform.hasPerspective());
574 
575     transform.setDouble(3, 0, -1.0);
576     REPORTER_ASSERT(reporter, transform.hasPerspective());
577 
578     transform.reset();
579     transform.setDouble(3, 1, -1.0);
580     REPORTER_ASSERT(reporter, transform.hasPerspective());
581 
582     transform.reset();
583     transform.setDouble(3, 2, -0.3);
584     REPORTER_ASSERT(reporter, transform.hasPerspective());
585 
586     transform.reset();
587     transform.setDouble(3, 3, 0.5);
588     REPORTER_ASSERT(reporter, transform.hasPerspective());
589 
590     transform.reset();
591     transform.setDouble(3, 3, 0.0);
592     REPORTER_ASSERT(reporter, transform.hasPerspective());
593 }
594 
is_rectilinear(SkVector4 & p1,SkVector4 & p2,SkVector4 & p3,SkVector4 & p4)595 static bool is_rectilinear (SkVector4& p1, SkVector4& p2, SkVector4& p3, SkVector4& p4) {
596     return (SkScalarNearlyEqual(p1.fData[0], p2.fData[0]) &&
597             SkScalarNearlyEqual(p2.fData[1], p3.fData[1]) &&
598             SkScalarNearlyEqual(p3.fData[0], p4.fData[0]) &&
599             SkScalarNearlyEqual(p4.fData[1], p1.fData[1])) ||
600            (SkScalarNearlyEqual(p1.fData[1], p2.fData[1]) &&
601             SkScalarNearlyEqual(p2.fData[0], p3.fData[0]) &&
602             SkScalarNearlyEqual(p3.fData[1], p4.fData[1]) &&
603             SkScalarNearlyEqual(p4.fData[0], p1.fData[0]));
604 }
605 
mul_with_persp_divide(const SkMatrix44 & transform,const SkVector4 & target)606 static SkVector4 mul_with_persp_divide(const SkMatrix44& transform, const SkVector4& target) {
607     SkVector4 result = transform * target;
608     if (result.fData[3] != 0.0f && result.fData[3] != SK_Scalar1) {
609         float wInverse = SK_Scalar1 / result.fData[3];
610         result.set(result.fData[0] * wInverse,
611                    result.fData[1] * wInverse,
612                    result.fData[2] * wInverse,
613                    SK_Scalar1);
614     }
615     return result;
616 }
617 
empirically_preserves_2d_axis_alignment(skiatest::Reporter * reporter,const SkMatrix44 & transform)618 static bool empirically_preserves_2d_axis_alignment(skiatest::Reporter* reporter,
619                                                     const SkMatrix44& transform) {
620   SkVector4 p1(5.0f, 5.0f, 0.0f);
621   SkVector4 p2(10.0f, 5.0f, 0.0f);
622   SkVector4 p3(10.0f, 20.0f, 0.0f);
623   SkVector4 p4(5.0f, 20.0f, 0.0f);
624 
625   REPORTER_ASSERT(reporter, is_rectilinear(p1, p2, p3, p4));
626 
627   p1 = mul_with_persp_divide(transform, p1);
628   p2 = mul_with_persp_divide(transform, p2);
629   p3 = mul_with_persp_divide(transform, p3);
630   p4 = mul_with_persp_divide(transform, p4);
631 
632   return is_rectilinear(p1, p2, p3, p4);
633 }
634 
test(bool expected,skiatest::Reporter * reporter,const SkMatrix44 & transform)635 static void test(bool expected, skiatest::Reporter* reporter, const SkMatrix44& transform) {
636     if (expected) {
637         REPORTER_ASSERT(reporter, empirically_preserves_2d_axis_alignment(reporter, transform));
638         REPORTER_ASSERT(reporter, transform.preserves2dAxisAlignment());
639     } else {
640         REPORTER_ASSERT(reporter, !empirically_preserves_2d_axis_alignment(reporter, transform));
641         REPORTER_ASSERT(reporter, !transform.preserves2dAxisAlignment());
642     }
643 }
644 
test_preserves_2d_axis_alignment(skiatest::Reporter * reporter)645 static void test_preserves_2d_axis_alignment(skiatest::Reporter* reporter) {
646   SkMatrix44 transform;
647   SkMatrix44 transform2;
648 
649   static const struct TestCase {
650     SkMScalar a; // row 1, column 1
651     SkMScalar b; // row 1, column 2
652     SkMScalar c; // row 2, column 1
653     SkMScalar d; // row 2, column 2
654     bool expected;
655   } test_cases[] = {
656     { 3.f, 0.f,
657       0.f, 4.f, true }, // basic case
658     { 0.f, 4.f,
659       3.f, 0.f, true }, // rotate by 90
660     { 0.f, 0.f,
661       0.f, 4.f, true }, // degenerate x
662     { 3.f, 0.f,
663       0.f, 0.f, true }, // degenerate y
664     { 0.f, 0.f,
665       3.f, 0.f, true }, // degenerate x + rotate by 90
666     { 0.f, 4.f,
667       0.f, 0.f, true }, // degenerate y + rotate by 90
668     { 3.f, 4.f,
669       0.f, 0.f, false },
670     { 0.f, 0.f,
671       3.f, 4.f, false },
672     { 0.f, 3.f,
673       0.f, 4.f, false },
674     { 3.f, 0.f,
675       4.f, 0.f, false },
676     { 3.f, 4.f,
677       5.f, 0.f, false },
678     { 3.f, 4.f,
679       0.f, 5.f, false },
680     { 3.f, 0.f,
681       4.f, 5.f, false },
682     { 0.f, 3.f,
683       4.f, 5.f, false },
684     { 2.f, 3.f,
685       4.f, 5.f, false },
686   };
687 
688   for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) {
689     const TestCase& value = test_cases[i];
690     transform.setIdentity();
691     transform.set(0, 0, value.a);
692     transform.set(0, 1, value.b);
693     transform.set(1, 0, value.c);
694     transform.set(1, 1, value.d);
695 
696     test(value.expected, reporter, transform);
697   }
698 
699   // Try the same test cases again, but this time make sure that other matrix
700   // elements (except perspective) have entries, to test that they are ignored.
701   for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) {
702     const TestCase& value = test_cases[i];
703     transform.setIdentity();
704     transform.set(0, 0, value.a);
705     transform.set(0, 1, value.b);
706     transform.set(1, 0, value.c);
707     transform.set(1, 1, value.d);
708 
709     transform.set(0, 2, 1.f);
710     transform.set(0, 3, 2.f);
711     transform.set(1, 2, 3.f);
712     transform.set(1, 3, 4.f);
713     transform.set(2, 0, 5.f);
714     transform.set(2, 1, 6.f);
715     transform.set(2, 2, 7.f);
716     transform.set(2, 3, 8.f);
717 
718     test(value.expected, reporter, transform);
719   }
720 
721   // Try the same test cases again, but this time add perspective which is
722   // always assumed to not-preserve axis alignment.
723   for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) {
724     const TestCase& value = test_cases[i];
725     transform.setIdentity();
726     transform.set(0, 0, value.a);
727     transform.set(0, 1, value.b);
728     transform.set(1, 0, value.c);
729     transform.set(1, 1, value.d);
730 
731     transform.set(0, 2, 1.f);
732     transform.set(0, 3, 2.f);
733     transform.set(1, 2, 3.f);
734     transform.set(1, 3, 4.f);
735     transform.set(2, 0, 5.f);
736     transform.set(2, 1, 6.f);
737     transform.set(2, 2, 7.f);
738     transform.set(2, 3, 8.f);
739     transform.set(3, 0, 9.f);
740     transform.set(3, 1, 10.f);
741     transform.set(3, 2, 11.f);
742     transform.set(3, 3, 12.f);
743 
744     test(false, reporter, transform);
745   }
746 
747   // Try a few more practical situations to check precision
748   // Reuse TestCase (a, b, c, d) as (x, y, z, degrees) axis to rotate about.
749   TestCase rotation_tests[] = {
750     { 0.0, 0.0, 1.0, 90.0, true },
751     { 0.0, 0.0, 1.0, 180.0, true },
752     { 0.0, 0.0, 1.0, 270.0, true },
753     { 0.0, 1.0, 0.0, 90.0, true },
754     { 1.0, 0.0, 0.0, 90.0, true },
755     { 0.0, 0.0, 1.0, 45.0, false },
756     // In 3d these next two are non-preserving, but we're testing in 2d after
757     // orthographic projection, where they are.
758     { 0.0, 1.0, 0.0, 45.0, true },
759     { 1.0, 0.0, 0.0, 45.0, true },
760   };
761 
762   for (size_t i = 0; i < sizeof(rotation_tests)/sizeof(TestCase); ++i) {
763     const TestCase& value = rotation_tests[i];
764     transform.setRotateDegreesAbout(value.a, value.b, value.c, value.d);
765     test(value.expected, reporter, transform);
766   }
767 
768   static const struct DoubleRotationCase {
769     SkMScalar x1;
770     SkMScalar y1;
771     SkMScalar z1;
772     SkMScalar degrees1;
773     SkMScalar x2;
774     SkMScalar y2;
775     SkMScalar z2;
776     SkMScalar degrees2;
777     bool expected;
778   } double_rotation_tests[] = {
779     { 0.0, 0.0, 1.0, 90.0, 0.0, 1.0, 0.0, 90.0, true },
780     { 0.0, 0.0, 1.0, 90.0, 1.0, 0.0, 0.0, 90.0, true },
781     { 0.0, 1.0, 0.0, 90.0, 0.0, 0.0, 1.0, 90.0, true },
782   };
783 
784   for (size_t i = 0; i < sizeof(double_rotation_tests)/sizeof(DoubleRotationCase); ++i) {
785     const DoubleRotationCase& value = double_rotation_tests[i];
786     transform.setRotateDegreesAbout(value.x1, value.y1, value.z1, value.degrees1);
787     transform2.setRotateDegreesAbout(value.x2, value.y2, value.z2, value.degrees2);
788     transform.postConcat(transform2);
789     test(value.expected, reporter, transform);
790   }
791 
792   // Perspective cases.
793   transform.setIdentity();
794   transform.setDouble(3, 2, -0.1); // Perspective depth 10
795   transform2.setRotateDegreesAbout(0.0, 1.0, 0.0, 45.0);
796   transform.preConcat(transform2);
797   test(false, reporter, transform);
798 
799   transform.setIdentity();
800   transform.setDouble(3, 2, -0.1); // Perspective depth 10
801   transform2.setRotateDegreesAbout(0.0, 0.0, 1.0, 90.0);
802   transform.preConcat(transform2);
803   test(true, reporter, transform);
804 }
805 
806 // just want to exercise the various converters for MScalar
test_toint(skiatest::Reporter * reporter)807 static void test_toint(skiatest::Reporter* reporter) {
808     SkMatrix44 mat;
809     mat.setScale(3, 3, 3);
810 
811     SkMScalar sum = SkMScalarFloor(mat.get(0, 0)) +
812                     SkMScalarRound(mat.get(1, 0)) +
813                     SkMScalarCeil(mat.get(2, 0));
814     int isum =      SkMScalarFloorToInt(mat.get(0, 1)) +
815                     SkMScalarRoundToInt(mat.get(1, 2)) +
816                     SkMScalarCeilToInt(mat.get(2, 3));
817     REPORTER_ASSERT(reporter, sum >= 0);
818     REPORTER_ASSERT(reporter, isum >= 0);
819     REPORTER_ASSERT(reporter, static_cast<SkMScalar>(isum) == SkIntToMScalar(isum));
820 }
821 
DEF_TEST(Matrix44,reporter)822 DEF_TEST(Matrix44, reporter) {
823     SkMatrix44 mat;
824     SkMatrix44 inverse;
825     SkMatrix44 iden1;
826     SkMatrix44 iden2;
827     SkMatrix44 rot;
828 
829     mat.setTranslate(1, 1, 1);
830     mat.invert(&inverse);
831     iden1.setConcat(mat, inverse);
832     REPORTER_ASSERT(reporter, is_identity(iden1));
833 
834     mat.setScale(2, 2, 2);
835     mat.invert(&inverse);
836     iden1.setConcat(mat, inverse);
837     REPORTER_ASSERT(reporter, is_identity(iden1));
838 
839     mat.setScale(SK_MScalar1/2, SK_MScalar1/2, SK_MScalar1/2);
840     mat.invert(&inverse);
841     iden1.setConcat(mat, inverse);
842     REPORTER_ASSERT(reporter, is_identity(iden1));
843 
844     mat.setScale(3, 3, 3);
845     rot.setRotateDegreesAbout(0, 0, -1, 90);
846     mat.postConcat(rot);
847     REPORTER_ASSERT(reporter, mat.invert(nullptr));
848     mat.invert(&inverse);
849     iden1.setConcat(mat, inverse);
850     REPORTER_ASSERT(reporter, is_identity(iden1));
851     iden2.setConcat(inverse, mat);
852     REPORTER_ASSERT(reporter, is_identity(iden2));
853 
854     // test tiny-valued matrix inverse
855     mat.reset();
856     auto v = SkDoubleToMScalar(1.0e-12);
857     mat.setScale(v,v,v);
858     rot.setRotateDegreesAbout(0, 0, -1, 90);
859     mat.postConcat(rot);
860     mat.postTranslate(v,v,v);
861     REPORTER_ASSERT(reporter, mat.invert(nullptr));
862     mat.invert(&inverse);
863     iden1.setConcat(mat, inverse);
864     REPORTER_ASSERT(reporter, is_identity(iden1));
865 
866     // test mixed-valued matrix inverse
867     mat.reset();
868     mat.setScale(SkDoubleToMScalar(1.0e-2),
869                  SkDoubleToMScalar(3.0),
870                  SkDoubleToMScalar(1.0e+2));
871     rot.setRotateDegreesAbout(0, 0, -1, 90);
872     mat.postConcat(rot);
873     mat.postTranslate(SkDoubleToMScalar(1.0e+2),
874                       SkDoubleToMScalar(3.0),
875                       SkDoubleToMScalar(1.0e-2));
876     REPORTER_ASSERT(reporter, mat.invert(nullptr));
877     mat.invert(&inverse);
878     iden1.setConcat(mat, inverse);
879     REPORTER_ASSERT(reporter, is_identity(iden1));
880 
881     // test degenerate matrix
882     mat.reset();
883     mat.set3x3(1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0);
884     REPORTER_ASSERT(reporter, !mat.invert(nullptr));
885 
886     // test rol/col Major getters
887     {
888         mat.setTranslate(2, 3, 4);
889         float dataf[16];
890         double datad[16];
891 
892         mat.asColMajorf(dataf);
893         assert16<float>(reporter, dataf,
894                  1, 0, 0, 0,
895                  0, 1, 0, 0,
896                  0, 0, 1, 0,
897                  2, 3, 4, 1);
898         mat.asColMajord(datad);
899         assert16<double>(reporter, datad, 1, 0, 0, 0,
900                         0, 1, 0, 0,
901                         0, 0, 1, 0,
902                         2, 3, 4, 1);
903         mat.asRowMajorf(dataf);
904         assert16<float>(reporter, dataf, 1, 0, 0, 2,
905                         0, 1, 0, 3,
906                         0, 0, 1, 4,
907                         0, 0, 0, 1);
908         mat.asRowMajord(datad);
909         assert16<double>(reporter, datad, 1, 0, 0, 2,
910                         0, 1, 0, 3,
911                         0, 0, 1, 4,
912                         0, 0, 0, 1);
913     }
914 
915     test_concat(reporter);
916 
917     if (false) { // avoid bit rot, suppress warning (working on making this pass)
918         test_common_angles(reporter);
919     }
920 
921     test_constructor(reporter);
922     test_gettype(reporter);
923     test_determinant(reporter);
924     test_invert(reporter);
925     test_transpose(reporter);
926     test_get_set_double(reporter);
927     test_set_row_col_major(reporter);
928     test_set_3x3(reporter);
929     test_translate(reporter);
930     test_scale(reporter);
931     test_map2(reporter);
932     test_3x3_conversion(reporter);
933     test_has_perspective(reporter);
934     test_preserves_2d_axis_alignment(reporter);
935     test_toint(reporter);
936 }
937