blob: 269e359022877bc1cc60624b56b0daa70e79d61c [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
reed@google.com7d683352012-12-03 21:19:52 +00007
reed@google.com125002a2011-06-09 19:13:41 +00008#include "Test.h"
9#include "SkMatrix44.h"
10
vollick@chromium.org3959a762012-11-13 15:08:22 +000011static 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
reed@google.comda9fac02011-06-13 14:46:52 +000019static bool nearly_equal_scalar(SkMScalar a, SkMScalar b) {
reed@google.com125002a2011-06-09 19:13:41 +000020 // 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
reed@google.com2b57dc62013-01-08 13:23:32 +000028 return SkTAbs<SkMScalar>(a - b) <= tolerance;
reed@google.com125002a2011-06-09 19:13:41 +000029}
30
reed@google.comda9fac02011-06-13 14:46:52 +000031template <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
reed@google.com125002a2011-06-09 19:13:41 +000057static 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))) {
reed@google.comda9fac02011-06-13 14:46:52 +000061 printf("not equal %g %g\n", a.get(i, j), b.get(i, j));
reed@google.com125002a2011-06-09 19:13:41 +000062 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
reed@google.com99b5c7f2012-12-05 22:13:59 +000075///////////////////////////////////////////////////////////////////////////////
76static bool bits_isonly(int value, int mask) {
77 return 0 == (value & ~mask);
78}
79
vollick@chromium.org57a54e32012-12-10 20:16:10 +000080static void test_constructor(skiatest::Reporter* reporter) {
81 // Allocate a matrix on the heap
82 SkMatrix44* placeholderMatrix = new SkMatrix44();
83 for (int row = 0; row < 4; ++row) {
84 for (int col = 0; col < 4; ++col) {
85 placeholderMatrix->setDouble(row, col, row * col);
86 }
87 }
88
89 // Use placement-new syntax to trigger the constructor on top of the heap
90 // address we already initialized. This allows us to check that the
91 // constructor did avoid initializing the matrix contents.
92 SkMatrix44* testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kUninitialized_Constructor);
93 REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
94 REPORTER_ASSERT(reporter, !testMatrix->isIdentity());
95 for (int row = 0; row < 4; ++row) {
96 for (int col = 0; col < 4; ++col) {
97 REPORTER_ASSERT(reporter, nearly_equal_double(row * col, testMatrix->getDouble(row, col)));
98 }
99 }
100
101 // Verify that kIdentity_Constructor really does initialize to an identity matrix.
102 testMatrix = 0;
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000103 testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kIdentity_Constructor);
vollick@chromium.org57a54e32012-12-10 20:16:10 +0000104 REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
105 REPORTER_ASSERT(reporter, testMatrix->isIdentity());
106 REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
107}
108
reed@google.com99b5c7f2012-12-05 22:13:59 +0000109static void test_translate(skiatest::Reporter* reporter) {
110 SkMatrix44 mat, inverse;
111
112 mat.setTranslate(0, 0, 0);
113 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
114 mat.setTranslate(1, 2, 3);
115 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kTranslate_Mask));
116 REPORTER_ASSERT(reporter, mat.invert(&inverse));
117 REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kTranslate_Mask));
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +0000118
reed@google.com99b5c7f2012-12-05 22:13:59 +0000119 SkMatrix44 a, b, c;
120 a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
121 b.setTranslate(10, 11, 12);
122
123 c.setConcat(a, b);
124 mat = a;
125 mat.preTranslate(10, 11, 12);
126 REPORTER_ASSERT(reporter, mat == c);
127
128 c.setConcat(b, a);
129 mat = a;
130 mat.postTranslate(10, 11, 12);
131 REPORTER_ASSERT(reporter, mat == c);
132}
133
134static void test_scale(skiatest::Reporter* reporter) {
135 SkMatrix44 mat, inverse;
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +0000136
reed@google.com99b5c7f2012-12-05 22:13:59 +0000137 mat.setScale(1, 1, 1);
138 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
139 mat.setScale(1, 2, 3);
140 REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kScale_Mask));
141 REPORTER_ASSERT(reporter, mat.invert(&inverse));
142 REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kScale_Mask));
143
144 SkMatrix44 a, b, c;
145 a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
146 b.setScale(10, 11, 12);
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +0000147
reed@google.com99b5c7f2012-12-05 22:13:59 +0000148 c.setConcat(a, b);
149 mat = a;
150 mat.preScale(10, 11, 12);
151 REPORTER_ASSERT(reporter, mat == c);
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +0000152
reed@google.com99b5c7f2012-12-05 22:13:59 +0000153 c.setConcat(b, a);
154 mat = a;
155 mat.postScale(10, 11, 12);
156 REPORTER_ASSERT(reporter, mat == c);
157}
158
159static void make_i(SkMatrix44* mat) { mat->setIdentity(); }
160static void make_t(SkMatrix44* mat) { mat->setTranslate(1, 2, 3); }
161static void make_s(SkMatrix44* mat) { mat->setScale(1, 2, 3); }
162static void make_st(SkMatrix44* mat) {
163 mat->setScale(1, 2, 3);
164 mat->postTranslate(1, 2, 3);
165}
166static void make_a(SkMatrix44* mat) {
167 mat->setRotateDegreesAbout(1, 2, 3, 45);
168}
169static void make_p(SkMatrix44* mat) {
170 SkMScalar data[] = {
171 1, 2, 3, 4, 5, 6, 7, 8,
172 1, 2, 3, 4, 5, 6, 7, 8,
173 };
174 mat->setRowMajor(data);
175}
176
177typedef void (*Make44Proc)(SkMatrix44*);
178
179static const Make44Proc gMakeProcs[] = {
180 make_i, make_t, make_s, make_st, make_a, make_p
181};
182
183static void test_map2(skiatest::Reporter* reporter, const SkMatrix44& mat) {
184 SkMScalar src2[] = { 1, 2 };
185 SkMScalar src4[] = { src2[0], src2[1], 0, 1 };
186 SkMScalar dstA[4], dstB[4];
187
188 for (int i = 0; i < 4; ++i) {
189 dstA[i] = 123456789;
190 dstB[i] = 987654321;
191 }
192
193 mat.map2(src2, 1, dstA);
194 mat.mapMScalars(src4, dstB);
skia.committer@gmail.com0264fb42012-12-06 02:01:25 +0000195
reed@google.com99b5c7f2012-12-05 22:13:59 +0000196 for (int i = 0; i < 4; ++i) {
197 REPORTER_ASSERT(reporter, dstA[i] == dstB[i]);
198 }
199}
200
201static void test_map2(skiatest::Reporter* reporter) {
202 SkMatrix44 mat;
203
204 for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProcs); ++i) {
205 gMakeProcs[i](&mat);
206 test_map2(reporter, mat);
207 }
208}
209
reed@google.com7d683352012-12-03 21:19:52 +0000210static void test_gettype(skiatest::Reporter* reporter) {
211 SkMatrix44 matrix;
skia.committer@gmail.come659c2e2012-12-04 02:01:25 +0000212
reed@google.com7d683352012-12-03 21:19:52 +0000213 REPORTER_ASSERT(reporter, matrix.isIdentity());
214 REPORTER_ASSERT(reporter, SkMatrix44::kIdentity_Mask == matrix.getType());
skia.committer@gmail.come659c2e2012-12-04 02:01:25 +0000215
reed@google.com7d683352012-12-03 21:19:52 +0000216 int expectedMask;
217
218 matrix.set(1, 1, 0);
219 expectedMask = SkMatrix44::kScale_Mask;
220 REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
221
222 matrix.set(0, 3, 1); // translate-x
223 expectedMask |= SkMatrix44::kTranslate_Mask;
224 REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
225
226 matrix.set(2, 0, 1);
227 expectedMask |= SkMatrix44::kAffine_Mask;
228 REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
skia.committer@gmail.come659c2e2012-12-04 02:01:25 +0000229
reed@google.com7d683352012-12-03 21:19:52 +0000230 matrix.set(3, 2, 1);
231 REPORTER_ASSERT(reporter, matrix.getType() & SkMatrix44::kPerspective_Mask);
232}
233
reed@google.com6f2b44d2011-06-24 18:13:39 +0000234static void test_common_angles(skiatest::Reporter* reporter) {
235 SkMatrix44 rot;
236 // Test precision of rotation in common cases
237 int common_angles[] = { 0, 90, -90, 180, -180, 270, -270, 360, -360 };
238 for (int i = 0; i < 9; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000239 rot.setRotateDegreesAbout(0, 0, -1, SkIntToScalar(common_angles[i]));
reed@google.com6f2b44d2011-06-24 18:13:39 +0000240
241 SkMatrix rot3x3 = rot;
242 REPORTER_ASSERT(reporter, rot3x3.rectStaysRect());
243 }
244}
245
reed@google.com80b577e2012-11-09 21:25:06 +0000246static void test_concat(skiatest::Reporter* reporter) {
247 int i;
248 SkMatrix44 a, b, c, d;
249
250 a.setTranslate(10, 10, 10);
251 b.setScale(2, 2, 2);
252
253 SkScalar src[8] = {
254 0, 0, 0, 1,
255 1, 1, 1, 1
256 };
257 SkScalar dst[8];
258
259 c.setConcat(a, b);
260
261 d = a;
262 d.preConcat(b);
263 REPORTER_ASSERT(reporter, d == c);
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000264
reed@google.com1ea95be2012-11-09 21:39:48 +0000265 c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
reed@google.com80b577e2012-11-09 21:25:06 +0000266 for (i = 0; i < 3; ++i) {
267 REPORTER_ASSERT(reporter, 10 == dst[i]);
268 REPORTER_ASSERT(reporter, 12 == dst[i + 4]);
269 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +0000270
reed@google.com80b577e2012-11-09 21:25:06 +0000271 c.setConcat(b, a);
272
273 d = a;
274 d.postConcat(b);
275 REPORTER_ASSERT(reporter, d == c);
276
reed@google.com1ea95be2012-11-09 21:39:48 +0000277 c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
reed@google.com80b577e2012-11-09 21:25:06 +0000278 for (i = 0; i < 3; ++i) {
279 REPORTER_ASSERT(reporter, 20 == dst[i]);
280 REPORTER_ASSERT(reporter, 22 == dst[i + 4]);
281 }
282}
283
vollick@chromium.org3959a762012-11-13 15:08:22 +0000284static void test_determinant(skiatest::Reporter* reporter) {
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000285 SkMatrix44 a;
286 REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant()));
reed@google.com7d683352012-12-03 21:19:52 +0000287 a.set(1, 1, 2);
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000288 REPORTER_ASSERT(reporter, nearly_equal_double(2, a.determinant()));
289 SkMatrix44 b;
290 REPORTER_ASSERT(reporter, a.invert(&b));
291 REPORTER_ASSERT(reporter, nearly_equal_double(0.5, b.determinant()));
292 SkMatrix44 c = b = a;
reed@google.com7d683352012-12-03 21:19:52 +0000293 c.set(0, 1, 4);
294 b.set(1, 0, 4);
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000295 REPORTER_ASSERT(reporter,
296 nearly_equal_double(a.determinant(),
297 b.determinant()));
298 SkMatrix44 d = a;
reed@google.com7d683352012-12-03 21:19:52 +0000299 d.set(0, 0, 8);
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000300 REPORTER_ASSERT(reporter, nearly_equal_double(16, d.determinant()));
vollick@chromium.org3959a762012-11-13 15:08:22 +0000301
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000302 SkMatrix44 e = a;
303 e.postConcat(d);
304 REPORTER_ASSERT(reporter, nearly_equal_double(32, e.determinant()));
reed@google.com7d683352012-12-03 21:19:52 +0000305 e.set(0, 0, 0);
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000306 REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant()));
vollick@chromium.org3959a762012-11-13 15:08:22 +0000307}
308
vollick@chromium.org9b21c252012-11-14 21:33:55 +0000309static void test_transpose(skiatest::Reporter* reporter) {
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000310 SkMatrix44 a;
311 SkMatrix44 b;
vollick@chromium.org9b21c252012-11-14 21:33:55 +0000312
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000313 int i = 0;
314 for (int row = 0; row < 4; ++row) {
315 for (int col = 0; col < 4; ++col) {
316 a.setDouble(row, col, i);
317 b.setDouble(col, row, i++);
318 }
vollick@chromium.org9b21c252012-11-14 21:33:55 +0000319 }
vollick@chromium.org9b21c252012-11-14 21:33:55 +0000320
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000321 a.transpose();
322 REPORTER_ASSERT(reporter, nearly_equal(a, b));
vollick@chromium.org9b21c252012-11-14 21:33:55 +0000323}
324
325static void test_get_set_double(skiatest::Reporter* reporter) {
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000326 SkMatrix44 a;
327 for (int row = 0; row < 4; ++row) {
328 for (int col = 0; col < 4; ++col) {
329 a.setDouble(row, col, 3.141592653589793);
330 REPORTER_ASSERT(reporter,
331 nearly_equal_double(3.141592653589793,
332 a.getDouble(row, col)));
333 a.setDouble(row, col, 0);
334 REPORTER_ASSERT(reporter,
335 nearly_equal_double(0, a.getDouble(row, col)));
336 }
vollick@chromium.org9b21c252012-11-14 21:33:55 +0000337 }
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000338}
339
340static void test_set_row_col_major(skiatest::Reporter* reporter) {
341 SkMatrix44 a, b, c, d;
reed@google.com7d683352012-12-03 21:19:52 +0000342 for (int row = 0; row < 4; ++row) {
343 for (int col = 0; col < 4; ++col) {
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000344 a.setDouble(row, col, row * 4 + col);
reed@google.com7d683352012-12-03 21:19:52 +0000345 }
346 }
skia.committer@gmail.come659c2e2012-12-04 02:01:25 +0000347
vollick@chromium.orgf11cf9f2012-11-19 21:02:06 +0000348 double bufferd[16];
349 float bufferf[16];
350 a.asColMajord(bufferd);
351 b.setColMajord(bufferd);
352 REPORTER_ASSERT(reporter, nearly_equal(a, b));
353 b.setRowMajord(bufferd);
354 b.transpose();
355 REPORTER_ASSERT(reporter, nearly_equal(a, b));
356 a.asColMajorf(bufferf);
357 b.setColMajorf(bufferf);
358 REPORTER_ASSERT(reporter, nearly_equal(a, b));
359 b.setRowMajorf(bufferf);
360 b.transpose();
361 REPORTER_ASSERT(reporter, nearly_equal(a, b));
vollick@chromium.org9b21c252012-11-14 21:33:55 +0000362}
363
caryclark@google.com42639cd2012-06-06 12:03:39 +0000364static void TestMatrix44(skiatest::Reporter* reporter) {
reed@google.com125002a2011-06-09 19:13:41 +0000365 SkMatrix44 mat, inverse, iden1, iden2, rot;
366
367 mat.reset();
reed@google.com7d683352012-12-03 21:19:52 +0000368 mat.setTranslate(1, 1, 1);
reed@google.com125002a2011-06-09 19:13:41 +0000369 mat.invert(&inverse);
370 iden1.setConcat(mat, inverse);
371 REPORTER_ASSERT(reporter, is_identity(iden1));
372
reed@google.com7d683352012-12-03 21:19:52 +0000373 mat.setScale(2, 2, 2);
reed@google.com125002a2011-06-09 19:13:41 +0000374 mat.invert(&inverse);
375 iden1.setConcat(mat, inverse);
376 REPORTER_ASSERT(reporter, is_identity(iden1));
377
reed@google.com7d683352012-12-03 21:19:52 +0000378 mat.setScale(SK_MScalar1/2, SK_MScalar1/2, SK_MScalar1/2);
reed@google.com125002a2011-06-09 19:13:41 +0000379 mat.invert(&inverse);
380 iden1.setConcat(mat, inverse);
381 REPORTER_ASSERT(reporter, is_identity(iden1));
382
reed@google.com7d683352012-12-03 21:19:52 +0000383 mat.setScale(3, 3, 3);
384 rot.setRotateDegreesAbout(0, 0, -1, 90);
reed@google.com125002a2011-06-09 19:13:41 +0000385 mat.postConcat(rot);
386 REPORTER_ASSERT(reporter, mat.invert(NULL));
387 mat.invert(&inverse);
388 iden1.setConcat(mat, inverse);
389 REPORTER_ASSERT(reporter, is_identity(iden1));
390 iden2.setConcat(inverse, mat);
391 REPORTER_ASSERT(reporter, is_identity(iden2));
reed@google.comda9fac02011-06-13 14:46:52 +0000392
393 // test rol/col Major getters
394 {
395 mat.setTranslate(2, 3, 4);
396 float dataf[16];
397 double datad[16];
rmistry@google.comd6176b02012-08-23 18:14:13 +0000398
reed@google.comda9fac02011-06-13 14:46:52 +0000399 mat.asColMajorf(dataf);
400 assert16<float>(reporter, dataf,
401 1, 0, 0, 0,
402 0, 1, 0, 0,
403 0, 0, 1, 0,
404 2, 3, 4, 1);
405 mat.asColMajord(datad);
406 assert16<double>(reporter, datad, 1, 0, 0, 0,
407 0, 1, 0, 0,
408 0, 0, 1, 0,
409 2, 3, 4, 1);
410 mat.asRowMajorf(dataf);
411 assert16<float>(reporter, dataf, 1, 0, 0, 2,
412 0, 1, 0, 3,
413 0, 0, 1, 4,
414 0, 0, 0, 1);
415 mat.asRowMajord(datad);
416 assert16<double>(reporter, datad, 1, 0, 0, 2,
417 0, 1, 0, 3,
418 0, 0, 1, 4,
419 0, 0, 0, 1);
420 }
reed@google.com6f2b44d2011-06-24 18:13:39 +0000421
reed@google.com80b577e2012-11-09 21:25:06 +0000422 test_concat(reporter);
423
caryclark@google.com42639cd2012-06-06 12:03:39 +0000424 if (false) { // avoid bit rot, suppress warning (working on making this pass)
425 test_common_angles(reporter);
426 }
vollick@chromium.org3959a762012-11-13 15:08:22 +0000427
vollick@chromium.org57a54e32012-12-10 20:16:10 +0000428 test_constructor(reporter);
reed@google.com7d683352012-12-03 21:19:52 +0000429 test_gettype(reporter);
vollick@chromium.org3959a762012-11-13 15:08:22 +0000430 test_determinant(reporter);
vollick@chromium.org9b21c252012-11-14 21:33:55 +0000431 test_transpose(reporter);
432 test_get_set_double(reporter);
reed@google.com7d683352012-12-03 21:19:52 +0000433 test_set_row_col_major(reporter);
reed@google.com99b5c7f2012-12-05 22:13:59 +0000434 test_translate(reporter);
435 test_scale(reporter);
436 test_map2(reporter);
reed@google.com125002a2011-06-09 19:13:41 +0000437}
438
439#include "TestClassDef.h"
440DEFINE_TESTCLASS("Matrix44", Matrix44TestClass, TestMatrix44)