• 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 "include/core/SkM44.h"
9 #include "include/core/SkMatrix44.h"
10 #include "include/core/SkPoint3.h"
11 #include "tests/Test.h"
12 
nearly_equal_double(double a,double b)13 static bool nearly_equal_double(double a, double b) {
14     const double tolerance = 1e-7;
15     double diff = a - b;
16     if (diff < 0)
17         diff = -diff;
18     return diff <= tolerance;
19 }
20 
nearly_equal_scalar(SkScalar a,SkScalar b)21 static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
22     const SkScalar tolerance = SK_Scalar1 / 200000;
23     return SkScalarAbs(a - b) <= tolerance;
24 }
25 
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)26 template <typename T> void assert16(skiatest::Reporter* reporter, const T data[],
27                                     T m0,  T m1,  T m2,  T m3,
28                                     T m4,  T m5,  T m6,  T m7,
29                                     T m8,  T m9,  T m10, T m11,
30                                     T m12, T m13, T m14, T m15) {
31     REPORTER_ASSERT(reporter, data[0] == m0);
32     REPORTER_ASSERT(reporter, data[1] == m1);
33     REPORTER_ASSERT(reporter, data[2] == m2);
34     REPORTER_ASSERT(reporter, data[3] == m3);
35 
36     REPORTER_ASSERT(reporter, data[4] == m4);
37     REPORTER_ASSERT(reporter, data[5] == m5);
38     REPORTER_ASSERT(reporter, data[6] == m6);
39     REPORTER_ASSERT(reporter, data[7] == m7);
40 
41     REPORTER_ASSERT(reporter, data[8] == m8);
42     REPORTER_ASSERT(reporter, data[9] == m9);
43     REPORTER_ASSERT(reporter, data[10] == m10);
44     REPORTER_ASSERT(reporter, data[11] == m11);
45 
46     REPORTER_ASSERT(reporter, data[12] == m12);
47     REPORTER_ASSERT(reporter, data[13] == m13);
48     REPORTER_ASSERT(reporter, data[14] == m14);
49     REPORTER_ASSERT(reporter, data[15] == m15);
50 }
51 
nearly_equal(const SkMatrix44 & a,const SkMatrix44 & b)52 static bool nearly_equal(const SkMatrix44& a, const SkMatrix44& b) {
53     for (int i = 0; i < 4; ++i) {
54         for (int j = 0; j < 4; ++j) {
55             if (!SkScalarNearlyEqual(a.get(i, j), b.get(i, j))) {
56                 SkDebugf("not equal %g %g\n", a.get(i, j), b.get(i, j));
57                 return false;
58             }
59         }
60     }
61     return true;
62 }
63 
is_identity(const SkMatrix44 & m)64 static bool is_identity(const SkMatrix44& m) {
65     SkMatrix44 identity(SkMatrix44::kIdentity_Constructor);
66     return nearly_equal(m, identity);
67 }
68 
69 ///////////////////////////////////////////////////////////////////////////////
bits_isonly(int value,int mask)70 static bool bits_isonly(int value, int mask) {
71     return 0 == (value & ~mask);
72 }
73 
test_constructor(skiatest::Reporter * reporter)74 static void test_constructor(skiatest::Reporter* reporter) {
75     // Allocate a matrix on the heap
76     SkMatrix44* placeholderMatrix = new SkMatrix44;
77     std::unique_ptr<SkMatrix44> deleteMe(placeholderMatrix);
78 
79     for (int row = 0; row < 4; ++row) {
80         for (int col = 0; col < 4; ++col) {
81             placeholderMatrix->setDouble(row, col, row * col);
82         }
83     }
84 
85     // Use placement-new syntax to trigger the constructor on top of the heap
86     // address we already initialized. This allows us to check that the
87     // constructor did avoid initializing the matrix contents.
88     SkMatrix44* testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kUninitialized_Constructor);
89     REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
90     REPORTER_ASSERT(reporter, !testMatrix->isIdentity());
91     for (int row = 0; row < 4; ++row) {
92         for (int col = 0; col < 4; ++col) {
93             REPORTER_ASSERT(reporter, nearly_equal_double(row * col, testMatrix->getDouble(row, col)));
94         }
95     }
96 
97     // Verify that kIdentity_Constructor really does initialize to an identity matrix.
98     testMatrix = 0;
99     testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kIdentity_Constructor);
100     REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
101     REPORTER_ASSERT(reporter, testMatrix->isIdentity());
102     REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
103 
104     // Verify that that constructing from an SkMatrix initializes everything.
105     SkMatrix44 scaleMatrix;
106     scaleMatrix.setScale(3, 4, 5);
107     REPORTER_ASSERT(reporter, scaleMatrix.isScale());
108     testMatrix = new(&scaleMatrix) SkMatrix44(SkMatrix::I());
109     REPORTER_ASSERT(reporter, testMatrix->isIdentity());
110     REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
111 }
112 
test_translate(skiatest::Reporter * reporter)113 static void test_translate(skiatest::Reporter* reporter) {
114     SkMatrix44 mat;
115     SkMatrix44 inverse;
116 
117     mat.setTranslate(0, 0, 0);
118     REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
119     mat.setTranslate(1, 2, 3);
120     REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kTranslate_Mask));
121     REPORTER_ASSERT(reporter, mat.invert(&inverse));
122     REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kTranslate_Mask));
123 
124     SkMatrix44 a,b,c;
125     a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
126     b.setTranslate(10, 11, 12);
127 
128     c.setConcat(a, b);
129     mat = a;
130     mat.preTranslate(10, 11, 12);
131     REPORTER_ASSERT(reporter, mat == c);
132 
133     c.setConcat(b, a);
134     mat = a;
135     mat.postTranslate(10, 11, 12);
136     REPORTER_ASSERT(reporter, mat == c);
137 }
138 
test_scale(skiatest::Reporter * reporter)139 static void test_scale(skiatest::Reporter* reporter) {
140     SkMatrix44 mat;
141     SkMatrix44 inverse;
142 
143     mat.setScale(1, 1, 1);
144     REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
145     mat.setScale(1, 2, 3);
146     REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kScale_Mask));
147     REPORTER_ASSERT(reporter, mat.invert(&inverse));
148     REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kScale_Mask));
149 
150     SkMatrix44 a,b,c;
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 
make_i(SkMatrix44 * mat)165 static void make_i(SkMatrix44* mat) { mat->setIdentity(); }
make_t(SkMatrix44 * mat)166 static void make_t(SkMatrix44* mat) { mat->setTranslate(1, 2, 3); }
make_s(SkMatrix44 * mat)167 static void make_s(SkMatrix44* mat) { mat->setScale(1, 2, 3); }
make_st(SkMatrix44 * mat)168 static void make_st(SkMatrix44* mat) {
169     mat->setScale(1, 2, 3);
170     mat->postTranslate(1, 2, 3);
171 }
make_a(SkMatrix44 * mat)172 static void make_a(SkMatrix44* mat) {
173     mat->setRotateDegreesAbout(1, 2, 3, 45);
174 }
make_p(SkMatrix44 * mat)175 static void make_p(SkMatrix44* mat) {
176     SkScalar 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 
183 typedef void (*Make44Proc)(SkMatrix44*);
184 
185 static const Make44Proc gMakeProcs[] = {
186     make_i, make_t, make_s, make_st, make_a, make_p
187 };
188 
test_map2(skiatest::Reporter * reporter,const SkMatrix44 & mat)189 static void test_map2(skiatest::Reporter* reporter, const SkMatrix44& mat) {
190     SkScalar src2[] = { 1, 2 };
191     SkScalar src4[] = { src2[0], src2[1], 0, 1 };
192     SkScalar dstA[4], dstB[4];
193 
194     for (int i = 0; i < 4; ++i) {
195         dstA[i] = SkScalar(123456789);
196         dstB[i] = SkScalar(987654321);
197     }
198 
199     mat.map2(src2, 1, dstA);
200     mat.mapScalars(src4, dstB);
201 
202     for (int i = 0; i < 4; ++i) {
203         REPORTER_ASSERT(reporter, dstA[i] == dstB[i]);
204     }
205 }
206 
test_map2(skiatest::Reporter * reporter)207 static void test_map2(skiatest::Reporter* reporter) {
208     SkMatrix44 mat;
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 
test_gettype(skiatest::Reporter * reporter)216 static 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     SkScalar dx = 0;
241     SkScalar dy = 0;
242     SkScalar 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 
test_common_angles(skiatest::Reporter * reporter)251 static void test_common_angles(skiatest::Reporter* reporter) {
252     SkMatrix44 rot;
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 = SkMatrix(rot);
259         REPORTER_ASSERT(reporter, rot3x3.rectStaysRect());
260     }
261 }
262 
test_concat(skiatest::Reporter * reporter)263 static void test_concat(skiatest::Reporter* reporter) {
264     int i;
265     SkMatrix44 a,b,c,d;
266 
267     a.setTranslate(10, 10, 10);
268     b.setScale(2, 2, 2);
269 
270     SkScalar src[8] = {
271         0, 0, 0, 1,
272         1, 1, 1, 1
273     };
274     SkScalar dst[8];
275 
276     c.setConcat(a, b);
277 
278     d = a;
279     d.preConcat(b);
280     REPORTER_ASSERT(reporter, d == c);
281 
282     c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
283     for (i = 0; i < 3; ++i) {
284         REPORTER_ASSERT(reporter, 10 == dst[i]);
285         REPORTER_ASSERT(reporter, 12 == dst[i + 4]);
286     }
287 
288     c.setConcat(b, a);
289 
290     d = a;
291     d.postConcat(b);
292     REPORTER_ASSERT(reporter, d == c);
293 
294     c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
295     for (i = 0; i < 3; ++i) {
296         REPORTER_ASSERT(reporter, 20 == dst[i]);
297         REPORTER_ASSERT(reporter, 22 == dst[i + 4]);
298     }
299 }
300 
test_determinant(skiatest::Reporter * reporter)301 static void test_determinant(skiatest::Reporter* reporter) {
302     SkMatrix44 a(SkMatrix44::kIdentity_Constructor);
303     REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant()));
304     a.set(1, 1, 2);
305     REPORTER_ASSERT(reporter, nearly_equal_double(2, a.determinant()));
306     SkMatrix44 b;
307     REPORTER_ASSERT(reporter, a.invert(&b));
308     REPORTER_ASSERT(reporter, nearly_equal_double(0.5, b.determinant()));
309     SkMatrix44 c = b = a;
310     c.set(0, 1, 4);
311     b.set(1, 0, 4);
312     REPORTER_ASSERT(reporter,
313                     nearly_equal_double(a.determinant(),
314                                         b.determinant()));
315     SkMatrix44 d = a;
316     d.set(0, 0, 8);
317     REPORTER_ASSERT(reporter, nearly_equal_double(16, d.determinant()));
318 
319     SkMatrix44 e = a;
320     e.postConcat(d);
321     REPORTER_ASSERT(reporter, nearly_equal_double(32, e.determinant()));
322     e.set(0, 0, 0);
323     REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant()));
324 }
325 
test_invert(skiatest::Reporter * reporter)326 static void test_invert(skiatest::Reporter* reporter) {
327     SkMatrix44 inverse;
328     double inverseData[16];
329 
330     SkMatrix44 identity(SkMatrix44::kIdentity_Constructor);
331     identity.invert(&inverse);
332     inverse.asRowMajord(inverseData);
333     assert16<double>(reporter, inverseData,
334                      1, 0, 0, 0,
335                      0, 1, 0, 0,
336                      0, 0, 1, 0,
337                      0, 0, 0, 1);
338 
339     SkMatrix44 translation;
340     translation.setTranslate(2, 3, 4);
341     translation.invert(&inverse);
342     inverse.asRowMajord(inverseData);
343     assert16<double>(reporter, inverseData,
344                      1, 0, 0, -2,
345                      0, 1, 0, -3,
346                      0, 0, 1, -4,
347                      0, 0, 0, 1);
348 
349     SkMatrix44 scale;
350     scale.setScale(2, 4, 8);
351     scale.invert(&inverse);
352     inverse.asRowMajord(inverseData);
353     assert16<double>(reporter, inverseData,
354                      0.5, 0,    0,     0,
355                      0,   0.25, 0,     0,
356                      0,   0,    0.125, 0,
357                      0,   0,    0,     1);
358 
359     SkMatrix44 scaleTranslation;
360     scaleTranslation.setScale(32, 128, 1024);
361     scaleTranslation.preTranslate(2, 3, 4);
362     scaleTranslation.invert(&inverse);
363     inverse.asRowMajord(inverseData);
364     assert16<double>(reporter, inverseData,
365                      0.03125,  0,          0,            -2,
366                      0,        0.0078125,  0,            -3,
367                      0,        0,          0.0009765625, -4,
368                      0,        0,          0,             1);
369 
370     SkMatrix44 rotation;
371     rotation.setRotateDegreesAbout(0, 0, 1, 90);
372     rotation.invert(&inverse);
373     SkMatrix44 expected;
374     double expectedInverseRotation[16] =
375             {0,  1, 0, 0,
376              -1, 0, 0, 0,
377              0,  0, 1, 0,
378              0,  0, 0, 1};
379     expected.setRowMajord(expectedInverseRotation);
380     REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
381 
382     SkMatrix44 affine;
383     affine.setRotateDegreesAbout(0, 0, 1, 90);
384     affine.preScale(10, 20, 100);
385     affine.preTranslate(2, 3, 4);
386     affine.invert(&inverse);
387     double expectedInverseAffine[16] =
388             {0,    0.1,  0,   -2,
389              -0.05, 0,   0,   -3,
390              0,     0,  0.01, -4,
391              0,     0,   0,   1};
392     expected.setRowMajord(expectedInverseAffine);
393     REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
394 
395     SkMatrix44 perspective(SkMatrix44::kIdentity_Constructor);
396     perspective.setDouble(3, 2, 1.0);
397     perspective.invert(&inverse);
398     double expectedInversePerspective[16] =
399             {1, 0,  0, 0,
400              0, 1,  0, 0,
401              0, 0,  1, 0,
402              0, 0, -1, 1};
403     expected.setRowMajord(expectedInversePerspective);
404     REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
405 
406     SkMatrix44 affineAndPerspective(SkMatrix44::kIdentity_Constructor);
407     affineAndPerspective.setDouble(3, 2, 1.0);
408     affineAndPerspective.preScale(10, 20, 100);
409     affineAndPerspective.preTranslate(2, 3, 4);
410     affineAndPerspective.invert(&inverse);
411     double expectedInverseAffineAndPerspective[16] =
412             {0.1, 0,    2,   -2,
413              0,  0.05,  3,   -3,
414              0,   0,   4.01, -4,
415              0,   0,   -1,    1};
416     expected.setRowMajord(expectedInverseAffineAndPerspective);
417     REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
418 
419     SkMatrix44 tinyScale(SkMatrix44::kIdentity_Constructor);
420     tinyScale.setDouble(0, 0, 1e-39);
421     REPORTER_ASSERT(reporter, tinyScale.getType() == SkMatrix44::kScale_Mask);
422     REPORTER_ASSERT(reporter, !tinyScale.invert(nullptr));
423     REPORTER_ASSERT(reporter, !tinyScale.invert(&inverse));
424 
425     SkMatrix44 tinyScaleTranslate(SkMatrix44::kIdentity_Constructor);
426     tinyScaleTranslate.setDouble(0, 0, 1e-38);
427     REPORTER_ASSERT(reporter, tinyScaleTranslate.invert(nullptr));
428     tinyScaleTranslate.setDouble(0, 3, 10);
429     REPORTER_ASSERT(
430         reporter, tinyScaleTranslate.getType() ==
431                       (SkMatrix44::kScale_Mask | SkMatrix44::kTranslate_Mask));
432     REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(nullptr));
433     REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(&inverse));
434 
435     SkMatrix44 tinyScalePerspective(SkMatrix44::kIdentity_Constructor);
436     tinyScalePerspective.setDouble(0, 0, 1e-39);
437     tinyScalePerspective.setDouble(3, 2, -1);
438     REPORTER_ASSERT(reporter, (tinyScalePerspective.getType() &
439                                SkMatrix44::kPerspective_Mask) ==
440                                   SkMatrix44::kPerspective_Mask);
441     REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(nullptr));
442     REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(&inverse));
443 }
444 
test_transpose(skiatest::Reporter * reporter)445 static void test_transpose(skiatest::Reporter* reporter) {
446     SkMatrix44 a,b;
447 
448     int i = 0;
449     for (int row = 0; row < 4; ++row) {
450         for (int col = 0; col < 4; ++col) {
451             a.setDouble(row, col, i);
452             b.setDouble(col, row, i++);
453         }
454     }
455 
456     a.transpose();
457     REPORTER_ASSERT(reporter, nearly_equal(a, b));
458 }
459 
test_get_set_double(skiatest::Reporter * reporter)460 static void test_get_set_double(skiatest::Reporter* reporter) {
461     SkMatrix44 a;
462     for (int row = 0; row < 4; ++row) {
463         for (int col = 0; col < 4; ++col) {
464             a.setDouble(row, col, 3.141592653589793);
465             REPORTER_ASSERT(reporter,
466                             nearly_equal_double(3.141592653589793,
467                                                 a.getDouble(row, col)));
468             a.setDouble(row, col, 0);
469             REPORTER_ASSERT(reporter,
470                             nearly_equal_double(0, a.getDouble(row, col)));
471         }
472     }
473 }
474 
test_set_3x3(skiatest::Reporter * r)475 static void test_set_3x3(skiatest::Reporter* r) {
476     static float vals[9] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, };
477 
478     SkMatrix44 mat;
479     mat.set3x3RowMajorf(vals);
480 
481     REPORTER_ASSERT(r, 1.0f == mat.getFloat(0, 0));
482     REPORTER_ASSERT(r, 2.0f == mat.getFloat(0, 1));
483     REPORTER_ASSERT(r, 3.0f == mat.getFloat(0, 2));
484     REPORTER_ASSERT(r, 4.0f == mat.getFloat(1, 0));
485     REPORTER_ASSERT(r, 5.0f == mat.getFloat(1, 1));
486     REPORTER_ASSERT(r, 6.0f == mat.getFloat(1, 2));
487     REPORTER_ASSERT(r, 7.0f == mat.getFloat(2, 0));
488     REPORTER_ASSERT(r, 8.0f == mat.getFloat(2, 1));
489     REPORTER_ASSERT(r, 9.0f == mat.getFloat(2, 2));
490 }
491 
test_set_row_col_major(skiatest::Reporter * reporter)492 static void test_set_row_col_major(skiatest::Reporter* reporter) {
493     SkMatrix44 a,b;
494 
495     for (int row = 0; row < 4; ++row) {
496         for (int col = 0; col < 4; ++col) {
497             a.setDouble(row, col, row * 4 + col);
498         }
499     }
500 
501     double bufferd[16];
502     float bufferf[16];
503     a.asColMajord(bufferd);
504     b.setColMajord(bufferd);
505     REPORTER_ASSERT(reporter, nearly_equal(a, b));
506     b.setRowMajord(bufferd);
507     b.transpose();
508     REPORTER_ASSERT(reporter, nearly_equal(a, b));
509     a.asColMajorf(bufferf);
510     b.setColMajorf(bufferf);
511     REPORTER_ASSERT(reporter, nearly_equal(a, b));
512     b.setRowMajorf(bufferf);
513     b.transpose();
514     REPORTER_ASSERT(reporter, nearly_equal(a, b));
515 }
516 
test_3x3_conversion(skiatest::Reporter * reporter)517 static void test_3x3_conversion(skiatest::Reporter* reporter) {
518     SkScalar values4x4[16] = { 1, 2, 3, 4,
519                                5, 6, 7, 8,
520                                9, 10, 11, 12,
521                                13, 14, 15, 16 };
522     SkScalar values3x3[9] = { 1, 2, 4,
523                               5, 6, 8,
524                               13, 14, 16 };
525     SkScalar values4x4flattened[16] = { 1, 2, 0, 4,
526                                         5, 6, 0, 8,
527                                         0, 0, 1, 0,
528                                         13, 14, 0, 16 };
529     SkMatrix44 a44;
530     a44.setRowMajor(values4x4);
531 
532     SkMatrix a33 = SkMatrix(a44);
533     SkMatrix expected33;
534     for (int i = 0; i < 9; i++) expected33[i] = values3x3[i];
535     REPORTER_ASSERT(reporter, expected33 == a33);
536 
537     SkMatrix44 a44flattened = a33;
538     SkMatrix44 expected44flattened;
539     expected44flattened.setRowMajor(values4x4flattened);
540     REPORTER_ASSERT(reporter, nearly_equal(a44flattened, expected44flattened));
541 
542     // Test that a point with a Z value of 0 is transformed the same way.
543     SkScalar vec4[4] = { 2, 4, 0, 8 };
544     SkPoint3 vec3 = { 2, 4, 8 };
545 
546     SkScalar vec4transformed[4];
547     SkPoint3 vec3transformed;
548     SkScalar vec4transformed2[4];
549     a44.mapScalars(vec4, vec4transformed);
550     a33.mapHomogeneousPoints(&vec3transformed, &vec3, 1);
551     a44flattened.mapScalars(vec4, vec4transformed2);
552     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec3transformed.fX));
553     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec3transformed.fY));
554     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec3transformed.fZ));
555     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec4transformed2[0]));
556     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec4transformed2[1]));
557     REPORTER_ASSERT(reporter, !nearly_equal_scalar(vec4transformed[2], vec4transformed2[2]));
558     REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec4transformed2[3]));
559 }
560 
test_has_perspective(skiatest::Reporter * reporter)561 static void test_has_perspective(skiatest::Reporter* reporter) {
562     SkMatrix44 transform(SkMatrix44::kIdentity_Constructor);
563 
564     transform.setDouble(3, 2, -0.1);
565     REPORTER_ASSERT(reporter, transform.hasPerspective());
566 
567     transform.reset();
568     REPORTER_ASSERT(reporter, !transform.hasPerspective());
569 
570     transform.setDouble(3, 0, -1.0);
571     REPORTER_ASSERT(reporter, transform.hasPerspective());
572 
573     transform.reset();
574     transform.setDouble(3, 1, -1.0);
575     REPORTER_ASSERT(reporter, transform.hasPerspective());
576 
577     transform.reset();
578     transform.setDouble(3, 2, -0.3);
579     REPORTER_ASSERT(reporter, transform.hasPerspective());
580 
581     transform.reset();
582     transform.setDouble(3, 3, 0.5);
583     REPORTER_ASSERT(reporter, transform.hasPerspective());
584 
585     transform.reset();
586     transform.setDouble(3, 3, 0.0);
587     REPORTER_ASSERT(reporter, transform.hasPerspective());
588 }
589 
is_rectilinear(SkVector4 & p1,SkVector4 & p2,SkVector4 & p3,SkVector4 & p4)590 static bool is_rectilinear (SkVector4& p1, SkVector4& p2, SkVector4& p3, SkVector4& p4) {
591     return (SkScalarNearlyEqual(p1.fData[0], p2.fData[0]) &&
592             SkScalarNearlyEqual(p2.fData[1], p3.fData[1]) &&
593             SkScalarNearlyEqual(p3.fData[0], p4.fData[0]) &&
594             SkScalarNearlyEqual(p4.fData[1], p1.fData[1])) ||
595            (SkScalarNearlyEqual(p1.fData[1], p2.fData[1]) &&
596             SkScalarNearlyEqual(p2.fData[0], p3.fData[0]) &&
597             SkScalarNearlyEqual(p3.fData[1], p4.fData[1]) &&
598             SkScalarNearlyEqual(p4.fData[0], p1.fData[0]));
599 }
600 
mul_with_persp_divide(const SkMatrix44 & transform,const SkVector4 & target)601 static SkVector4 mul_with_persp_divide(const SkMatrix44& transform, const SkVector4& target) {
602     SkVector4 result = transform * target;
603     if (result.fData[3] != 0.0f && result.fData[3] != SK_Scalar1) {
604         float wInverse = SK_Scalar1 / result.fData[3];
605         result.set(result.fData[0] * wInverse,
606                    result.fData[1] * wInverse,
607                    result.fData[2] * wInverse,
608                    SK_Scalar1);
609     }
610     return result;
611 }
612 
empirically_preserves_2d_axis_alignment(skiatest::Reporter * reporter,const SkMatrix44 & transform)613 static bool empirically_preserves_2d_axis_alignment(skiatest::Reporter* reporter,
614                                                     const SkMatrix44& transform) {
615   SkVector4 p1(5.0f, 5.0f, 0.0f);
616   SkVector4 p2(10.0f, 5.0f, 0.0f);
617   SkVector4 p3(10.0f, 20.0f, 0.0f);
618   SkVector4 p4(5.0f, 20.0f, 0.0f);
619 
620   REPORTER_ASSERT(reporter, is_rectilinear(p1, p2, p3, p4));
621 
622   p1 = mul_with_persp_divide(transform, p1);
623   p2 = mul_with_persp_divide(transform, p2);
624   p3 = mul_with_persp_divide(transform, p3);
625   p4 = mul_with_persp_divide(transform, p4);
626 
627   return is_rectilinear(p1, p2, p3, p4);
628 }
629 
test(bool expected,skiatest::Reporter * reporter,const SkMatrix44 & transform)630 static void test(bool expected, skiatest::Reporter* reporter, const SkMatrix44& transform) {
631     if (expected) {
632         REPORTER_ASSERT(reporter, empirically_preserves_2d_axis_alignment(reporter, transform));
633         REPORTER_ASSERT(reporter, transform.preserves2dAxisAlignment());
634     } else {
635         REPORTER_ASSERT(reporter, !empirically_preserves_2d_axis_alignment(reporter, transform));
636         REPORTER_ASSERT(reporter, !transform.preserves2dAxisAlignment());
637     }
638 }
639 
test_preserves_2d_axis_alignment(skiatest::Reporter * reporter)640 static void test_preserves_2d_axis_alignment(skiatest::Reporter* reporter) {
641   SkMatrix44 transform;
642   SkMatrix44 transform2;
643 
644   static const struct TestCase {
645     SkScalar a; // row 1, column 1
646     SkScalar b; // row 1, column 2
647     SkScalar c; // row 2, column 1
648     SkScalar d; // row 2, column 2
649     bool expected;
650   } test_cases[] = {
651     { 3.f, 0.f,
652       0.f, 4.f, true }, // basic case
653     { 0.f, 4.f,
654       3.f, 0.f, true }, // rotate by 90
655     { 0.f, 0.f,
656       0.f, 4.f, true }, // degenerate x
657     { 3.f, 0.f,
658       0.f, 0.f, true }, // degenerate y
659     { 0.f, 0.f,
660       3.f, 0.f, true }, // degenerate x + rotate by 90
661     { 0.f, 4.f,
662       0.f, 0.f, true }, // degenerate y + rotate by 90
663     { 3.f, 4.f,
664       0.f, 0.f, false },
665     { 0.f, 0.f,
666       3.f, 4.f, false },
667     { 0.f, 3.f,
668       0.f, 4.f, false },
669     { 3.f, 0.f,
670       4.f, 0.f, false },
671     { 3.f, 4.f,
672       5.f, 0.f, false },
673     { 3.f, 4.f,
674       0.f, 5.f, false },
675     { 3.f, 0.f,
676       4.f, 5.f, false },
677     { 0.f, 3.f,
678       4.f, 5.f, false },
679     { 2.f, 3.f,
680       4.f, 5.f, false },
681   };
682 
683   for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) {
684     const TestCase& value = test_cases[i];
685     transform.setIdentity();
686     transform.set(0, 0, value.a);
687     transform.set(0, 1, value.b);
688     transform.set(1, 0, value.c);
689     transform.set(1, 1, value.d);
690 
691     test(value.expected, reporter, transform);
692   }
693 
694   // Try the same test cases again, but this time make sure that other matrix
695   // elements (except perspective) have entries, to test that they are ignored.
696   for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) {
697     const TestCase& value = test_cases[i];
698     transform.setIdentity();
699     transform.set(0, 0, value.a);
700     transform.set(0, 1, value.b);
701     transform.set(1, 0, value.c);
702     transform.set(1, 1, value.d);
703 
704     transform.set(0, 2, 1.f);
705     transform.set(0, 3, 2.f);
706     transform.set(1, 2, 3.f);
707     transform.set(1, 3, 4.f);
708     transform.set(2, 0, 5.f);
709     transform.set(2, 1, 6.f);
710     transform.set(2, 2, 7.f);
711     transform.set(2, 3, 8.f);
712 
713     test(value.expected, reporter, transform);
714   }
715 
716   // Try the same test cases again, but this time add perspective which is
717   // always assumed to not-preserve axis alignment.
718   for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) {
719     const TestCase& value = test_cases[i];
720     transform.setIdentity();
721     transform.set(0, 0, value.a);
722     transform.set(0, 1, value.b);
723     transform.set(1, 0, value.c);
724     transform.set(1, 1, value.d);
725 
726     transform.set(0, 2, 1.f);
727     transform.set(0, 3, 2.f);
728     transform.set(1, 2, 3.f);
729     transform.set(1, 3, 4.f);
730     transform.set(2, 0, 5.f);
731     transform.set(2, 1, 6.f);
732     transform.set(2, 2, 7.f);
733     transform.set(2, 3, 8.f);
734     transform.set(3, 0, 9.f);
735     transform.set(3, 1, 10.f);
736     transform.set(3, 2, 11.f);
737     transform.set(3, 3, 12.f);
738 
739     test(false, reporter, transform);
740   }
741 
742   // Try a few more practical situations to check precision
743   // Reuse TestCase (a, b, c, d) as (x, y, z, degrees) axis to rotate about.
744   TestCase rotation_tests[] = {
745     { 0.0, 0.0, 1.0, 90.0, true },
746     { 0.0, 0.0, 1.0, 180.0, true },
747     { 0.0, 0.0, 1.0, 270.0, true },
748     { 0.0, 1.0, 0.0, 90.0, true },
749     { 1.0, 0.0, 0.0, 90.0, true },
750     { 0.0, 0.0, 1.0, 45.0, false },
751     // In 3d these next two are non-preserving, but we're testing in 2d after
752     // orthographic projection, where they are.
753     { 0.0, 1.0, 0.0, 45.0, true },
754     { 1.0, 0.0, 0.0, 45.0, true },
755   };
756 
757   for (size_t i = 0; i < sizeof(rotation_tests)/sizeof(TestCase); ++i) {
758     const TestCase& value = rotation_tests[i];
759     transform.setRotateDegreesAbout(value.a, value.b, value.c, value.d);
760     test(value.expected, reporter, transform);
761   }
762 
763   static const struct DoubleRotationCase {
764     SkScalar x1;
765     SkScalar y1;
766     SkScalar z1;
767     SkScalar degrees1;
768     SkScalar x2;
769     SkScalar y2;
770     SkScalar z2;
771     SkScalar degrees2;
772     bool expected;
773   } double_rotation_tests[] = {
774     { 0.0, 0.0, 1.0, 90.0, 0.0, 1.0, 0.0, 90.0, true },
775     { 0.0, 0.0, 1.0, 90.0, 1.0, 0.0, 0.0, 90.0, true },
776     { 0.0, 1.0, 0.0, 90.0, 0.0, 0.0, 1.0, 90.0, true },
777   };
778 
779   for (size_t i = 0; i < sizeof(double_rotation_tests)/sizeof(DoubleRotationCase); ++i) {
780     const DoubleRotationCase& value = double_rotation_tests[i];
781     transform.setRotateDegreesAbout(value.x1, value.y1, value.z1, value.degrees1);
782     transform2.setRotateDegreesAbout(value.x2, value.y2, value.z2, value.degrees2);
783     transform.postConcat(transform2);
784     test(value.expected, reporter, transform);
785   }
786 
787   // Perspective cases.
788   transform.setIdentity();
789   transform.setDouble(3, 2, -0.1); // Perspective depth 10
790   transform2.setRotateDegreesAbout(0.0, 1.0, 0.0, 45.0);
791   transform.preConcat(transform2);
792   test(false, reporter, transform);
793 
794   transform.setIdentity();
795   transform.setDouble(3, 2, -0.1); // Perspective depth 10
796   transform2.setRotateDegreesAbout(0.0, 0.0, 1.0, 90.0);
797   transform.preConcat(transform2);
798   test(true, reporter, transform);
799 }
800 
801 // just want to exercise the various converters for Scalar
test_toint(skiatest::Reporter * reporter)802 static void test_toint(skiatest::Reporter* reporter) {
803     SkMatrix44 mat;
804     mat.setScale(3, 3, 3);
805 
806     SkScalar sum = SkScalarFloorToScalar(mat.get(0, 0)) +
807                    SkScalarRoundToScalar(mat.get(1, 0)) +
808                    SkScalarCeilToScalar(mat.get(2, 0));
809     int isum =     SkScalarFloorToInt(mat.get(0, 1)) +
810                    SkScalarRoundToInt(mat.get(1, 2)) +
811                    SkScalarCeilToInt(mat.get(2, 3));
812     REPORTER_ASSERT(reporter, sum >= 0);
813     REPORTER_ASSERT(reporter, isum >= 0);
814     REPORTER_ASSERT(reporter, static_cast<SkScalar>(isum) == SkIntToScalar(isum));
815 }
816 
DEF_TEST(Matrix44,reporter)817 DEF_TEST(Matrix44, reporter) {
818     SkMatrix44 mat;
819     SkMatrix44 inverse;
820     SkMatrix44 iden1;
821     SkMatrix44 iden2;
822     SkMatrix44 rot;
823 
824     mat.setTranslate(1, 1, 1);
825     mat.invert(&inverse);
826     iden1.setConcat(mat, inverse);
827     REPORTER_ASSERT(reporter, is_identity(iden1));
828 
829     mat.setScale(2, 2, 2);
830     mat.invert(&inverse);
831     iden1.setConcat(mat, inverse);
832     REPORTER_ASSERT(reporter, is_identity(iden1));
833 
834     mat.setScale(SK_Scalar1/2, SK_Scalar1/2, SK_Scalar1/2);
835     mat.invert(&inverse);
836     iden1.setConcat(mat, inverse);
837     REPORTER_ASSERT(reporter, is_identity(iden1));
838 
839     mat.setScale(3, 3, 3);
840     rot.setRotateDegreesAbout(0, 0, -1, 90);
841     mat.postConcat(rot);
842     REPORTER_ASSERT(reporter, mat.invert(nullptr));
843     mat.invert(&inverse);
844     iden1.setConcat(mat, inverse);
845     REPORTER_ASSERT(reporter, is_identity(iden1));
846     iden2.setConcat(inverse, mat);
847     REPORTER_ASSERT(reporter, is_identity(iden2));
848 
849     // test tiny-valued matrix inverse
850     mat.reset();
851     auto v = 1.0e-12f;
852     mat.setScale(v,v,v);
853     rot.setRotateDegreesAbout(0, 0, -1, 90);
854     mat.postConcat(rot);
855     mat.postTranslate(v,v,v);
856     REPORTER_ASSERT(reporter, mat.invert(nullptr));
857     mat.invert(&inverse);
858     iden1.setConcat(mat, inverse);
859     REPORTER_ASSERT(reporter, is_identity(iden1));
860 
861     // test mixed-valued matrix inverse
862     mat.reset();
863     mat.setScale(1.0e-2f, 3.0f, 1.0e+2f);
864     rot.setRotateDegreesAbout(0, 0, -1, 90);
865     mat.postConcat(rot);
866     mat.postTranslate(1.0e+2f, 3.0f, 1.0e-2f);
867     REPORTER_ASSERT(reporter, mat.invert(nullptr));
868     mat.invert(&inverse);
869     iden1.setConcat(mat, inverse);
870     REPORTER_ASSERT(reporter, is_identity(iden1));
871 
872     // test degenerate matrix
873     mat.reset();
874     mat.set3x3(1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0);
875     REPORTER_ASSERT(reporter, !mat.invert(nullptr));
876 
877     // test rol/col Major getters
878     {
879         mat.setTranslate(2, 3, 4);
880         float dataf[16];
881         double datad[16];
882 
883         mat.asColMajorf(dataf);
884         assert16<float>(reporter, dataf,
885                  1, 0, 0, 0,
886                  0, 1, 0, 0,
887                  0, 0, 1, 0,
888                  2, 3, 4, 1);
889         mat.asColMajord(datad);
890         assert16<double>(reporter, datad, 1, 0, 0, 0,
891                         0, 1, 0, 0,
892                         0, 0, 1, 0,
893                         2, 3, 4, 1);
894         mat.asRowMajorf(dataf);
895         assert16<float>(reporter, dataf, 1, 0, 0, 2,
896                         0, 1, 0, 3,
897                         0, 0, 1, 4,
898                         0, 0, 0, 1);
899         mat.asRowMajord(datad);
900         assert16<double>(reporter, datad, 1, 0, 0, 2,
901                         0, 1, 0, 3,
902                         0, 0, 1, 4,
903                         0, 0, 0, 1);
904     }
905 
906     test_concat(reporter);
907 
908     if (false) { // avoid bit rot, suppress warning (working on making this pass)
909         test_common_angles(reporter);
910     }
911 
912     test_constructor(reporter);
913     test_gettype(reporter);
914     test_determinant(reporter);
915     test_invert(reporter);
916     test_transpose(reporter);
917     test_get_set_double(reporter);
918     test_set_row_col_major(reporter);
919     test_set_3x3(reporter);
920     test_translate(reporter);
921     test_scale(reporter);
922     test_map2(reporter);
923     test_3x3_conversion(reporter);
924     test_has_perspective(reporter);
925     test_preserves_2d_axis_alignment(reporter);
926     test_toint(reporter);
927 }
928 
eq(const SkMatrix44 & a,const SkM44 & b,float tol)929 static bool eq(const SkMatrix44& a, const SkM44& b, float tol) {
930     float fa[16], fb[16];
931     a.asColMajorf(fa);
932     b.getColMajor(fb);
933     for (int i = 0; i < 16; ++i) {
934         if (!SkScalarNearlyEqual(fa[i], fb[i], tol)) {
935             return false;
936         }
937     }
938     return true;
939 }
940 
eq(const SkM44 & a,const SkM44 & b,float tol)941 static bool eq(const SkM44& a, const SkM44& b, float tol) {
942     float fa[16], fb[16];
943     a.getColMajor(fa);
944     b.getColMajor(fb);
945     for (int i = 0; i < 16; ++i) {
946         if (!SkScalarNearlyEqual(fa[i], fb[i], tol)) {
947             return false;
948         }
949     }
950     return true;
951 }
952 
DEF_TEST(M44,reporter)953 DEF_TEST(M44, reporter) {
954     SkM44 m, im;
955     SkMatrix44 m44, im44;
956 
957     REPORTER_ASSERT(reporter, eq(m44, m, 0));
958     REPORTER_ASSERT(reporter, SkM44() == m);
959     REPORTER_ASSERT(reporter, m.invert(&im));
960     REPORTER_ASSERT(reporter, SkM44() == im);
961 
962     m.setTranslate(3, 4, 2);
963     m44.setTranslate(3, 4, 2);
964     REPORTER_ASSERT(reporter, eq(m44, m, 0));
965     REPORTER_ASSERT(reporter, SkM44(1, 0, 0, 3,
966                                     0, 1, 0, 4,
967                                     0, 0, 1, 2,
968                                     0, 0, 0, 1) == m);
969 
970     const float f[] = { 1, 0, 0, 2, 3, 1, 2, 5, 0, 5, 3, 0, 0, 1, 0, 2 };
971     m.setColMajor(f);
972     m44.setColMajorf(f);
973     REPORTER_ASSERT(reporter, eq(m44, m, 0));
974 
975     {
976         SkM44 t = m.transpose();
977         REPORTER_ASSERT(reporter, t != m);
978         REPORTER_ASSERT(reporter, t.rc(1,0) == m.rc(0,1));
979         SkM44 tt = t.transpose();
980         REPORTER_ASSERT(reporter, tt == m);
981     }
982 
983     m.setRowMajor(f);
984     m44.setRowMajorf(f);
985     REPORTER_ASSERT(reporter, eq(m44, m, 0));
986 
987     REPORTER_ASSERT(reporter, m.invert(&im));
988     REPORTER_ASSERT(reporter, m44.invert(&im44));
989     REPORTER_ASSERT(reporter, eq(im44, im, 0));
990 
991     m = m * im;
992     // m should be identity now, but our calc is not perfect...
993     REPORTER_ASSERT(reporter, eq(SkM44(), m, 0.0000005f));
994     REPORTER_ASSERT(reporter, SkM44() != m);
995 }
996 
DEF_TEST(M44_v3,reporter)997 DEF_TEST(M44_v3, reporter) {
998     SkV3 a = {1, 2, 3},
999          b = {1, 2, 2};
1000 
1001     REPORTER_ASSERT(reporter, a.lengthSquared() == 1 + 4 + 9);
1002     REPORTER_ASSERT(reporter, b.length() == 3);
1003     REPORTER_ASSERT(reporter, a.dot(b) == 1 + 4 + 6);
1004     REPORTER_ASSERT(reporter, b.dot(a) == 1 + 4 + 6);
1005     REPORTER_ASSERT(reporter, (a.cross(b) == SkV3{-2,  1, 0}));
1006     REPORTER_ASSERT(reporter, (b.cross(a) == SkV3{ 2, -1, 0}));
1007 
1008     SkM44 m = {
1009         2, 0, 0, 3,
1010         0, 1, 0, 5,
1011         0, 0, 3, 1,
1012         0, 0, 0, 1
1013     };
1014 
1015     SkV3 c = m * a;
1016     REPORTER_ASSERT(reporter, (c == SkV3{2, 2, 9}));
1017     SkV4 d = m.map(4, 3, 2, 1);
1018     REPORTER_ASSERT(reporter, (d == SkV4{11, 8, 7, 1}));
1019 }
1020 
DEF_TEST(M44_v4,reporter)1021 DEF_TEST(M44_v4, reporter) {
1022     SkM44 m( 1,  2,  3,  4,
1023              5,  6,  7,  8,
1024              9, 10, 11, 12,
1025             13, 14, 15, 16);
1026 
1027     SkV4 r0 = m.row(0),
1028          r1 = m.row(1),
1029          r2 = m.row(2),
1030          r3 = m.row(3);
1031 
1032     REPORTER_ASSERT(reporter, (r0 == SkV4{ 1,  2,  3,  4}));
1033     REPORTER_ASSERT(reporter, (r1 == SkV4{ 5,  6,  7,  8}));
1034     REPORTER_ASSERT(reporter, (r2 == SkV4{ 9, 10, 11, 12}));
1035     REPORTER_ASSERT(reporter, (r3 == SkV4{13, 14, 15, 16}));
1036 
1037     REPORTER_ASSERT(reporter, SkM44::Rows(r0, r1, r2, r3) == m);
1038 
1039     SkV4 c0 = m.col(0),
1040          c1 = m.col(1),
1041          c2 = m.col(2),
1042          c3 = m.col(3);
1043 
1044     REPORTER_ASSERT(reporter, (c0 == SkV4{1, 5,  9, 13}));
1045     REPORTER_ASSERT(reporter, (c1 == SkV4{2, 6, 10, 14}));
1046     REPORTER_ASSERT(reporter, (c2 == SkV4{3, 7, 11, 15}));
1047     REPORTER_ASSERT(reporter, (c3 == SkV4{4, 8, 12, 16}));
1048 
1049     REPORTER_ASSERT(reporter, SkM44::Cols(c0, c1, c2, c3) == m);
1050 
1051     // implement matrix * vector using column vectors
1052     SkV4 v = {1, 2, 3, 4};
1053     SkV4 v1 = m * v;
1054     SkV4 v2 = c0 * v.x + c1 * v.y + c2 * v.z + c3 * v.w;
1055     REPORTER_ASSERT(reporter, v1 == v2);
1056 
1057     REPORTER_ASSERT(reporter, (c0 + r0 == SkV4{c0.x+r0.x, c0.y+r0.y, c0.z+r0.z, c0.w+r0.w}));
1058     REPORTER_ASSERT(reporter, (c0 - r0 == SkV4{c0.x-r0.x, c0.y-r0.y, c0.z-r0.z, c0.w-r0.w}));
1059     REPORTER_ASSERT(reporter, (c0 * r0 == SkV4{c0.x*r0.x, c0.y*r0.y, c0.z*r0.z, c0.w*r0.w}));
1060 }
1061 
DEF_TEST(M44_rotate,reporter)1062 DEF_TEST(M44_rotate, reporter) {
1063     const SkV3 x = {1, 0, 0},
1064                y = {0, 1, 0},
1065                z = {0, 0, 1};
1066 
1067     // We have radians version of setRotateAbout methods, but even with our best approx
1068     // for PI, sin(SK_ScalarPI) != 0, so to make the comparisons in the unittest clear,
1069     // I'm using the variants that explicitly take the sin,cos values.
1070 
1071     struct {
1072         SkScalar sinAngle, cosAngle;
1073         SkV3 aboutAxis;
1074         SkV3 expectedX, expectedY, expectedZ;
1075     } recs[] = {
1076         { 0, 1,    x,   x, y, z},    // angle = 0
1077         { 0, 1,    y,   x, y, z},    // angle = 0
1078         { 0, 1,    z,   x, y, z},    // angle = 0
1079 
1080         { 0,-1,    x,   x,-y,-z},    // angle = 180
1081         { 0,-1,    y,  -x, y,-z},    // angle = 180
1082         { 0,-1,    z,  -x,-y, z},    // angle = 180
1083 
1084         // Skia coordinate system is right-handed
1085 
1086         { 1, 0,    x,   x, z,-y},    // angle = 90
1087         { 1, 0,    y,  -z, y, x},    // angle = 90
1088         { 1, 0,    z,   y,-x, z},    // angle = 90
1089 
1090         {-1, 0,    x,   x,-z, y},    // angle = -90
1091         {-1, 0,    y,   z, y,-x},    // angle = -90
1092         {-1, 0,    z,  -y, x, z},    // angle = -90
1093     };
1094 
1095     for (const auto& r : recs) {
1096         SkM44 m(SkM44::kNaN_Constructor);
1097         m.setRotateUnitSinCos(r.aboutAxis, r.sinAngle, r.cosAngle);
1098 
1099         auto mx = m * x;
1100         auto my = m * y;
1101         auto mz = m * z;
1102         REPORTER_ASSERT(reporter, mx == r.expectedX);
1103         REPORTER_ASSERT(reporter, my == r.expectedY);
1104         REPORTER_ASSERT(reporter, mz == r.expectedZ);
1105 
1106         // flipping the axis-of-rotation should flip the results
1107         mx = m * -x;
1108         my = m * -y;
1109         mz = m * -z;
1110         REPORTER_ASSERT(reporter, mx == -r.expectedX);
1111         REPORTER_ASSERT(reporter, my == -r.expectedY);
1112         REPORTER_ASSERT(reporter, mz == -r.expectedZ);
1113     }
1114 }
1115