blob: 9f4babfed91b0cd117ab37700111ca2060e21408 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.comed673312009-02-27 16:24:51 +00008#include "Test.h"
tomhudson@google.com889bd8b2011-09-27 17:38:17 +00009#include "SkMath.h"
reed@android.comed673312009-02-27 16:24:51 +000010#include "SkMatrix.h"
commit-bot@chromium.org08284e42013-07-24 18:08:08 +000011#include "SkMatrixUtils.h"
bsalomon@google.com38396322011-09-09 19:32:04 +000012#include "SkRandom.h"
reed@android.comed673312009-02-27 16:24:51 +000013
14static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
epoger@google.com2047f002011-05-17 17:36:59 +000015 // Note that we get more compounded error for multiple operations when
16 // SK_SCALAR_IS_FIXED.
reed@android.comed673312009-02-27 16:24:51 +000017#ifdef SK_SCALAR_IS_FLOAT
epoger@google.com2047f002011-05-17 17:36:59 +000018 const SkScalar tolerance = SK_Scalar1 / 200000;
reed@android.comed673312009-02-27 16:24:51 +000019#else
epoger@google.com2047f002011-05-17 17:36:59 +000020 const SkScalar tolerance = SK_Scalar1 / 1024;
reed@android.comed673312009-02-27 16:24:51 +000021#endif
22
23 return SkScalarAbs(a - b) <= tolerance;
24}
25
26static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
27 for (int i = 0; i < 9; i++) {
28 if (!nearly_equal_scalar(a[i], b[i])) {
bungeman@google.comfab44db2013-10-11 18:50:45 +000029 SkDebugf("not equal %g %g\n", (float)a[i], (float)b[i]);
reed@android.comed673312009-02-27 16:24:51 +000030 return false;
31 }
32 }
33 return true;
34}
35
bsalomon@google.com8fe84b52012-03-26 15:24:27 +000036static bool are_equal(skiatest::Reporter* reporter,
37 const SkMatrix& a,
38 const SkMatrix& b) {
39 bool equal = a == b;
40 bool cheapEqual = a.cheapEqualTo(b);
41 if (equal != cheapEqual) {
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +000042#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.com39d4f3a2012-03-26 17:25:45 +000043 if (equal) {
bsalomon@google.com8fe84b52012-03-26 15:24:27 +000044 bool foundZeroSignDiff = false;
45 for (int i = 0; i < 9; ++i) {
46 float aVal = a.get(i);
47 float bVal = b.get(i);
bsalomon@google.com373ebc62012-09-26 13:08:56 +000048 int aValI = *SkTCast<int*>(&aVal);
49 int bValI = *SkTCast<int*>(&bVal);
bsalomon@google.com8fe84b52012-03-26 15:24:27 +000050 if (0 == aVal && 0 == bVal && aValI != bValI) {
51 foundZeroSignDiff = true;
52 } else {
53 REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
54 }
55 }
56 REPORTER_ASSERT(reporter, foundZeroSignDiff);
57 } else {
58 bool foundNaN = false;
59 for (int i = 0; i < 9; ++i) {
60 float aVal = a.get(i);
61 float bVal = b.get(i);
bsalomon@google.com373ebc62012-09-26 13:08:56 +000062 int aValI = *SkTCast<int*>(&aVal);
63 int bValI = *SkTCast<int*>(&bVal);
bsalomon@google.com8fe84b52012-03-26 15:24:27 +000064 if (sk_float_isnan(aVal) && aValI == bValI) {
65 foundNaN = true;
66 } else {
67 REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
68 }
69 }
70 REPORTER_ASSERT(reporter, foundNaN);
71 }
72#else
73 REPORTER_ASSERT(reporter, false);
74#endif
75 }
76 return equal;
77}
78
reed@android.comed673312009-02-27 16:24:51 +000079static bool is_identity(const SkMatrix& m) {
80 SkMatrix identity;
reed@android.com80e39a72009-04-02 16:59:40 +000081 identity.reset();
reed@android.comed673312009-02-27 16:24:51 +000082 return nearly_equal(m, identity);
83}
84
reed@google.com97cd69c2012-10-12 14:35:48 +000085static void test_matrix_recttorect(skiatest::Reporter* reporter) {
86 SkRect src, dst;
87 SkMatrix matrix;
skia.committer@gmail.comf57c01b2012-10-13 02:01:56 +000088
reed@google.com97cd69c2012-10-12 14:35:48 +000089 src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10);
90 dst = src;
91 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
92 REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
93 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
skia.committer@gmail.comf57c01b2012-10-13 02:01:56 +000094
reed@google.com97cd69c2012-10-12 14:35:48 +000095 dst.offset(SK_Scalar1, SK_Scalar1);
96 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
97 REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
98 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
skia.committer@gmail.comf57c01b2012-10-13 02:01:56 +000099
reed@google.com97cd69c2012-10-12 14:35:48 +0000100 dst.fRight += SK_Scalar1;
101 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
skia.committer@gmail.come659c2e2012-12-04 02:01:25 +0000102 REPORTER_ASSERT(reporter,
robertphillips@google.com93f03322012-12-03 17:35:19 +0000103 (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
reed@google.com97cd69c2012-10-12 14:35:48 +0000104 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
105
106 dst = src;
107 dst.fRight = src.fRight * 2;
108 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
109 REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
110 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
111}
112
reed@android.com4b7577b2009-06-29 16:14:41 +0000113static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
114 // add 100 in case we have a bug, I don't want to kill my stack in the test
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000115 static const size_t kBufferSize = SkMatrix::kMaxFlattenSize + 100;
116 char buffer[kBufferSize];
117 size_t size1 = m.writeToMemory(NULL);
118 size_t size2 = m.writeToMemory(buffer);
reed@android.com4b7577b2009-06-29 16:14:41 +0000119 REPORTER_ASSERT(reporter, size1 == size2);
120 REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000121
reed@android.com4b7577b2009-06-29 16:14:41 +0000122 SkMatrix m2;
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000123 size_t size3 = m2.readFromMemory(buffer, kBufferSize);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000124 REPORTER_ASSERT(reporter, size1 == size3);
bsalomon@google.com8fe84b52012-03-26 15:24:27 +0000125 REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000126
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000127 char buffer2[kBufferSize];
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000128 size3 = m2.writeToMemory(buffer2);
129 REPORTER_ASSERT(reporter, size1 == size3);
reed@android.com4b7577b2009-06-29 16:14:41 +0000130 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
131}
132
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000133static void test_matrix_min_max_stretch(skiatest::Reporter* reporter) {
bsalomon@google.com38396322011-09-09 19:32:04 +0000134 SkMatrix identity;
135 identity.reset();
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000136 REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinStretch());
bsalomon@google.com38396322011-09-09 19:32:04 +0000137 REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
138
139 SkMatrix scale;
140 scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000141 REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinStretch());
bsalomon@google.com38396322011-09-09 19:32:04 +0000142 REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch());
143
144 SkMatrix rot90Scale;
145 rot90Scale.setRotate(90 * SK_Scalar1);
146 rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000147 REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinStretch());
bsalomon@google.com38396322011-09-09 19:32:04 +0000148 REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch());
149
150 SkMatrix rotate;
151 rotate.setRotate(128 * SK_Scalar1);
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000152 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinStretch() ,SK_ScalarNearlyZero));
153 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxStretch(), SK_ScalarNearlyZero));
bsalomon@google.com38396322011-09-09 19:32:04 +0000154
155 SkMatrix translate;
156 translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000157 REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinStretch());
bsalomon@google.com38396322011-09-09 19:32:04 +0000158 REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());
159
160 SkMatrix perspX;
161 perspX.reset();
bungeman@google.com07faed12011-10-07 21:55:56 +0000162 perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000163 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinStretch());
bsalomon@google.com38396322011-09-09 19:32:04 +0000164 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());
165
166 SkMatrix perspY;
167 perspY.reset();
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000168 perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
169 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinStretch());
bsalomon@google.com38396322011-09-09 19:32:04 +0000170 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());
171
172 SkMatrix baseMats[] = {scale, rot90Scale, rotate,
173 translate, perspX, perspY};
174 SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
tomhudson@google.com83a44462011-10-27 15:27:51 +0000175 for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
bsalomon@google.com38396322011-09-09 19:32:04 +0000176 mats[i] = baseMats[i];
177 bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
178 REPORTER_ASSERT(reporter, invertable);
179 }
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000180 SkRandom rand;
bsalomon@google.com38396322011-09-09 19:32:04 +0000181 for (int m = 0; m < 1000; ++m) {
182 SkMatrix mat;
183 mat.reset();
184 for (int i = 0; i < 4; ++i) {
185 int x = rand.nextU() % SK_ARRAY_COUNT(mats);
186 mat.postConcat(mats[x]);
187 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000188
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000189 SkScalar minStretch = mat.getMinStretch();
190 SkScalar maxStretch = mat.getMaxStretch();
191 REPORTER_ASSERT(reporter, (minStretch < 0) == (maxStretch < 0));
192 REPORTER_ASSERT(reporter, (maxStretch < 0) == mat.hasPerspective());
bsalomon@google.com38396322011-09-09 19:32:04 +0000193
194 if (mat.hasPerspective()) {
195 m -= 1; // try another non-persp matrix
196 continue;
197 }
198
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000199 // test a bunch of vectors. All should be scaled by between minStretch and maxStretch
200 // (modulo some error) and we should find a vector that is scaled by almost each.
201 static const SkScalar gVectorStretchTol = (105 * SK_Scalar1) / 100;
202 static const SkScalar gClosestStretchTol = (97 * SK_Scalar1) / 100;
203 SkScalar max = 0, min = SK_ScalarMax;
bsalomon@google.com38396322011-09-09 19:32:04 +0000204 SkVector vectors[1000];
tomhudson@google.com83a44462011-10-27 15:27:51 +0000205 for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
bsalomon@google.com38396322011-09-09 19:32:04 +0000206 vectors[i].fX = rand.nextSScalar1();
207 vectors[i].fY = rand.nextSScalar1();
208 if (!vectors[i].normalize()) {
209 i -= 1;
210 continue;
211 }
212 }
213 mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
tomhudson@google.com83a44462011-10-27 15:27:51 +0000214 for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
bsalomon@google.com38396322011-09-09 19:32:04 +0000215 SkScalar d = vectors[i].length();
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000216 REPORTER_ASSERT(reporter, SkScalarDiv(d, maxStretch) < gVectorStretchTol);
217 REPORTER_ASSERT(reporter, SkScalarDiv(minStretch, d) < gVectorStretchTol);
bsalomon@google.com38396322011-09-09 19:32:04 +0000218 if (max < d) {
219 max = d;
220 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000221 if (min > d) {
222 min = d;
223 }
bsalomon@google.com38396322011-09-09 19:32:04 +0000224 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000225 REPORTER_ASSERT(reporter, SkScalarDiv(max, maxStretch) >= gClosestStretchTol);
226 REPORTER_ASSERT(reporter, SkScalarDiv(minStretch, min) >= gClosestStretchTol);
bsalomon@google.com38396322011-09-09 19:32:04 +0000227 }
228}
229
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000230static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
bsalomon@google.com69afee12012-04-25 15:07:40 +0000231 SkMatrix mat;
232
233 // identity
234 mat.setIdentity();
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000235 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000236
237 // translation only
238 mat.reset();
239 mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000240 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000241
242 // scale with same size
243 mat.reset();
244 mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000245 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000246
247 // scale with one negative
248 mat.reset();
249 mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000250 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000251
252 // scale with different size
253 mat.reset();
254 mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000255 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000256
257 // scale with same size at a pivot point
258 mat.reset();
259 mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
260 SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000261 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000262
263 // scale with different size at a pivot point
264 mat.reset();
265 mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
266 SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000267 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000268
269 // skew with same size
270 mat.reset();
271 mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000272 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000273
274 // skew with different size
275 mat.reset();
276 mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000277 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000278
279 // skew with same size at a pivot point
280 mat.reset();
281 mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
282 SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000283 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000284
285 // skew with different size at a pivot point
286 mat.reset();
287 mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
288 SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000289 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000290
291 // perspective x
292 mat.reset();
293 mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000294 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000295
296 // perspective y
297 mat.reset();
298 mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000299 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000300
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000301#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.com69afee12012-04-25 15:07:40 +0000302 /* We bypass the following tests for SK_SCALAR_IS_FIXED build.
303 * The long discussion can be found in this issue:
304 * http://codereview.appspot.com/5999050/
305 * In short, we haven't found a perfect way to fix the precision
306 * issue, i.e. the way we use tolerance in isSimilarityTransformation
307 * is incorrect. The situation becomes worse in fixed build, so
308 * we disabled rotation related tests for fixed build.
309 */
310
311 // rotate
312 for (int angle = 0; angle < 360; ++angle) {
313 mat.reset();
314 mat.setRotate(SkIntToScalar(angle));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000315 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000316 }
317
318 // see if there are any accumulated precision issues
319 mat.reset();
320 for (int i = 1; i < 360; i++) {
321 mat.postRotate(SkIntToScalar(1));
322 }
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000323 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000324
325 // rotate + translate
326 mat.reset();
327 mat.setRotate(SkIntToScalar(30));
328 mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000329 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000330
331 // rotate + uniform scale
332 mat.reset();
333 mat.setRotate(SkIntToScalar(30));
334 mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000335 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000336
337 // rotate + non-uniform scale
338 mat.reset();
339 mat.setRotate(SkIntToScalar(30));
340 mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000341 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000342#endif
343
344 // all zero
345 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000346 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000347
348 // all zero except perspective
349 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000350 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000351
352 // scales zero, only skews
353 mat.setAll(0, SK_Scalar1, 0,
354 SK_Scalar1, 0, 0,
355 0, 0, SkMatrix::I()[8]);
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000356 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000357}
358
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000359// For test_matrix_decomposition, below.
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000360static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000361 SkScalar tolerance = SK_ScalarNearlyZero) {
362 // from Bruce Dawson
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000363 // absolute check
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000364 SkScalar diff = SkScalarAbs(a - b);
365 if (diff < tolerance) {
366 return true;
367 }
368
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000369 // relative check
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000370 a = SkScalarAbs(a);
371 b = SkScalarAbs(b);
372 SkScalar largest = (b > a) ? b : a;
373
374 if (diff <= largest*tolerance) {
375 return true;
376 }
377
378 return false;
379}
380
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000381static bool check_matrix_recomposition(const SkMatrix& mat,
382 const SkPoint& rotation1,
383 const SkPoint& scale,
384 const SkPoint& rotation2) {
385 SkScalar c1 = rotation1.fX;
386 SkScalar s1 = rotation1.fY;
387 SkScalar scaleX = scale.fX;
388 SkScalar scaleY = scale.fY;
389 SkScalar c2 = rotation2.fX;
390 SkScalar s2 = rotation2.fY;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +0000391
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000392 // We do a relative check here because large scale factors cause problems with an absolute check
393 bool result = scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
394 scaleX*c1*c2 - scaleY*s1*s2) &&
395 scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
396 -scaleX*s1*c2 - scaleY*c1*s2) &&
397 scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
398 scaleX*c1*s2 + scaleY*s1*c2) &&
399 scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
400 -scaleX*s1*s2 + scaleY*c1*c2);
401 return result;
402}
403
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000404static void test_matrix_decomposition(skiatest::Reporter* reporter) {
405 SkMatrix mat;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000406 SkPoint rotation1, scale, rotation2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000407
408 const float kRotation0 = 15.5f;
409 const float kRotation1 = -50.f;
410 const float kScale0 = 5000.f;
411 const float kScale1 = 0.001f;
412
413 // identity
414 mat.reset();
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000415 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
416 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000417 // make sure it doesn't crash if we pass in NULLs
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000418 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000419
420 // rotation only
421 mat.setRotate(kRotation0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000422 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
423 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000424
425 // uniform scale only
426 mat.setScale(kScale0, kScale0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000427 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
428 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000429
430 // anisotropic scale only
431 mat.setScale(kScale1, kScale0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000432 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
433 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000434
435 // rotation then uniform scale
436 mat.setRotate(kRotation1);
437 mat.postScale(kScale0, kScale0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000438 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
439 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000440
441 // uniform scale then rotation
442 mat.setScale(kScale0, kScale0);
443 mat.postRotate(kRotation1);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000444 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
445 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000446
447 // rotation then uniform scale+reflection
448 mat.setRotate(kRotation0);
449 mat.postScale(kScale1, -kScale1);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000450 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
451 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000452
453 // uniform scale+reflection, then rotate
454 mat.setScale(kScale0, -kScale0);
455 mat.postRotate(kRotation1);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000456 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
457 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000458
459 // rotation then anisotropic scale
460 mat.setRotate(kRotation1);
461 mat.postScale(kScale1, kScale0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000462 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
463 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000464
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000465 // rotation then anisotropic scale
466 mat.setRotate(90);
467 mat.postScale(kScale1, kScale0);
468 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
469 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
skia.committer@gmail.com85092f02013-09-04 07:01:39 +0000470
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000471 // anisotropic scale then rotation
472 mat.setScale(kScale1, kScale0);
473 mat.postRotate(kRotation0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000474 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
475 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
skia.committer@gmail.com85092f02013-09-04 07:01:39 +0000476
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000477 // anisotropic scale then rotation
478 mat.setScale(kScale1, kScale0);
479 mat.postRotate(90);
480 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
481 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000482
483 // rotation, uniform scale, then different rotation
484 mat.setRotate(kRotation1);
485 mat.postScale(kScale0, kScale0);
486 mat.postRotate(kRotation0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000487 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
488 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000489
490 // rotation, anisotropic scale, then different rotation
491 mat.setRotate(kRotation0);
492 mat.postScale(kScale1, kScale0);
493 mat.postRotate(kRotation1);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000494 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
495 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
skia.committer@gmail.com85092f02013-09-04 07:01:39 +0000496
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000497 // rotation, anisotropic scale + reflection, then different rotation
498 mat.setRotate(kRotation0);
499 mat.postScale(-kScale1, kScale0);
500 mat.postRotate(kRotation1);
501 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
502 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000503
504 // try some random matrices
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000505 SkRandom rand;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000506 for (int m = 0; m < 1000; ++m) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000507 SkScalar rot0 = rand.nextRangeF(-180, 180);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000508 SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
509 SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000510 SkScalar rot1 = rand.nextRangeF(-180, 180);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000511 mat.setRotate(rot0);
512 mat.postScale(sx, sy);
513 mat.postRotate(rot1);
514
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000515 if (SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)) {
516 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000517 } else {
518 // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
519 SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
520 mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
521 REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
522 }
523 }
524
525 // translation shouldn't affect this
526 mat.postTranslate(-1000.f, 1000.f);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000527 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
528 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000529
530 // perspective shouldn't affect this
jvanverth@google.com588f3d32013-07-24 18:44:10 +0000531 mat[SkMatrix::kMPersp0] = 12.f;
532 mat[SkMatrix::kMPersp1] = 4.f;
533 mat[SkMatrix::kMPersp2] = 1872.f;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000534 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
535 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000536
537 // degenerate matrices
538 // mostly zero entries
539 mat.reset();
540 mat[SkMatrix::kMScaleX] = 0.f;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000541 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000542 mat.reset();
543 mat[SkMatrix::kMScaleY] = 0.f;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000544 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000545 mat.reset();
546 // linearly dependent entries
547 mat[SkMatrix::kMScaleX] = 1.f;
548 mat[SkMatrix::kMSkewX] = 2.f;
549 mat[SkMatrix::kMSkewY] = 4.f;
550 mat[SkMatrix::kMScaleY] = 8.f;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000551 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000552}
553
egdaniel@google.com259fbaf2013-08-15 21:12:11 +0000554// For test_matrix_homogeneous, below.
555static bool scalar_array_nearly_equal_relative(const SkScalar a[], const SkScalar b[], int count) {
556 for (int i = 0; i < count; ++i) {
557 if (!scalar_nearly_equal_relative(a[i], b[i])) {
558 return false;
559 }
560 }
561 return true;
562}
563
564// For test_matrix_homogeneous, below.
565// Maps a single triple in src using m and compares results to those in dst
566static bool naive_homogeneous_mapping(const SkMatrix& m, const SkScalar src[3],
567 const SkScalar dst[3]) {
568 SkScalar res[3];
569 SkScalar ms[9] = {m[0], m[1], m[2],
570 m[3], m[4], m[5],
571 m[6], m[7], m[8]};
572 res[0] = src[0] * ms[0] + src[1] * ms[1] + src[2] * ms[2];
573 res[1] = src[0] * ms[3] + src[1] * ms[4] + src[2] * ms[5];
574 res[2] = src[0] * ms[6] + src[1] * ms[7] + src[2] * ms[8];
575 return scalar_array_nearly_equal_relative(res, dst, 3);
576}
577
578static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
579 SkMatrix mat;
580
581 const float kRotation0 = 15.5f;
582 const float kRotation1 = -50.f;
583 const float kScale0 = 5000.f;
584
585 const int kTripleCount = 1000;
586 const int kMatrixCount = 1000;
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000587 SkRandom rand;
egdaniel@google.com259fbaf2013-08-15 21:12:11 +0000588
589 SkScalar randTriples[3*kTripleCount];
590 for (int i = 0; i < 3*kTripleCount; ++i) {
591 randTriples[i] = rand.nextRangeF(-3000.f, 3000.f);
592 }
593
594 SkMatrix mats[kMatrixCount];
595 for (int i = 0; i < kMatrixCount; ++i) {
596 for (int j = 0; j < 9; ++j) {
597 mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
598 }
599 }
600
601 // identity
602 {
603 mat.reset();
604 SkScalar dst[3*kTripleCount];
605 mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
606 REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3));
607 }
608
609 // zero matrix
610 {
611 mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
612 SkScalar dst[3*kTripleCount];
613 mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
614 SkScalar zeros[3] = {0.f, 0.f, 0.f};
615 for (int i = 0; i < kTripleCount; ++i) {
616 REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3));
617 }
618 }
619
620 // zero point
621 {
622 SkScalar zeros[3] = {0.f, 0.f, 0.f};
623 for (int i = 0; i < kMatrixCount; ++i) {
624 SkScalar dst[3];
625 mats[i].mapHomogeneousPoints(dst, zeros, 1);
626 REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3));
627 }
628 }
629
630 // doesn't crash with null dst, src, count == 0
631 {
632 mats[0].mapHomogeneousPoints(NULL, NULL, 0);
633 }
634
635 // uniform scale of point
636 {
637 mat.setScale(kScale0, kScale0);
638 SkScalar dst[3];
639 SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
640 SkPoint pnt;
641 pnt.set(src[0], src[1]);
642 mat.mapHomogeneousPoints(dst, src, 1);
643 mat.mapPoints(&pnt, &pnt, 1);
644 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
645 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
646 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
647 }
648
649 // rotation of point
650 {
651 mat.setRotate(kRotation0);
652 SkScalar dst[3];
653 SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
654 SkPoint pnt;
655 pnt.set(src[0], src[1]);
656 mat.mapHomogeneousPoints(dst, src, 1);
657 mat.mapPoints(&pnt, &pnt, 1);
658 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
659 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
660 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
661 }
662
663 // rotation, scale, rotation of point
664 {
665 mat.setRotate(kRotation1);
666 mat.postScale(kScale0, kScale0);
667 mat.postRotate(kRotation0);
668 SkScalar dst[3];
669 SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
670 SkPoint pnt;
671 pnt.set(src[0], src[1]);
672 mat.mapHomogeneousPoints(dst, src, 1);
673 mat.mapPoints(&pnt, &pnt, 1);
674 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
675 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
676 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
677 }
678
679 // compare with naive approach
680 {
681 for (int i = 0; i < kMatrixCount; ++i) {
682 for (int j = 0; j < kTripleCount; ++j) {
683 SkScalar dst[3];
684 mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1);
685 REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst));
686 }
687 }
688 }
689
690}
691
caryclark@google.com42639cd2012-06-06 12:03:39 +0000692static void TestMatrix(skiatest::Reporter* reporter) {
reed@android.comed673312009-02-27 16:24:51 +0000693 SkMatrix mat, inverse, iden1, iden2;
694
695 mat.reset();
696 mat.setTranslate(SK_Scalar1, SK_Scalar1);
reed@google.com5bfa55b2012-04-19 18:59:25 +0000697 REPORTER_ASSERT(reporter, mat.invert(&inverse));
reed@android.comed673312009-02-27 16:24:51 +0000698 iden1.setConcat(mat, inverse);
699 REPORTER_ASSERT(reporter, is_identity(iden1));
700
reed@google.com2fb96cc2013-01-04 17:02:33 +0000701 mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
reed@google.com5bfa55b2012-04-19 18:59:25 +0000702 REPORTER_ASSERT(reporter, mat.invert(&inverse));
reed@android.comed673312009-02-27 16:24:51 +0000703 iden1.setConcat(mat, inverse);
704 REPORTER_ASSERT(reporter, is_identity(iden1));
reed@android.com4b7577b2009-06-29 16:14:41 +0000705 test_flatten(reporter, mat);
reed@android.comed673312009-02-27 16:24:51 +0000706
reed@google.com2fb96cc2013-01-04 17:02:33 +0000707 mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
reed@google.com5bfa55b2012-04-19 18:59:25 +0000708 REPORTER_ASSERT(reporter, mat.invert(&inverse));
reed@android.comed673312009-02-27 16:24:51 +0000709 iden1.setConcat(mat, inverse);
710 REPORTER_ASSERT(reporter, is_identity(iden1));
reed@android.com4b7577b2009-06-29 16:14:41 +0000711 test_flatten(reporter, mat);
reed@android.comed673312009-02-27 16:24:51 +0000712
713 mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
714 mat.postRotate(SkIntToScalar(25));
715 REPORTER_ASSERT(reporter, mat.invert(NULL));
reed@google.com5bfa55b2012-04-19 18:59:25 +0000716 REPORTER_ASSERT(reporter, mat.invert(&inverse));
reed@android.comed673312009-02-27 16:24:51 +0000717 iden1.setConcat(mat, inverse);
718 REPORTER_ASSERT(reporter, is_identity(iden1));
719 iden2.setConcat(inverse, mat);
720 REPORTER_ASSERT(reporter, is_identity(iden2));
reed@android.com4b7577b2009-06-29 16:14:41 +0000721 test_flatten(reporter, mat);
722 test_flatten(reporter, iden2);
reed@android.com80e39a72009-04-02 16:59:40 +0000723
reed@google.com2fb96cc2013-01-04 17:02:33 +0000724 mat.setScale(0, SK_Scalar1);
reed@google.come40591d2013-01-30 15:47:42 +0000725 REPORTER_ASSERT(reporter, !mat.invert(NULL));
reed@google.com2fb96cc2013-01-04 17:02:33 +0000726 REPORTER_ASSERT(reporter, !mat.invert(&inverse));
727 mat.setScale(SK_Scalar1, 0);
reed@google.come40591d2013-01-30 15:47:42 +0000728 REPORTER_ASSERT(reporter, !mat.invert(NULL));
reed@google.com2fb96cc2013-01-04 17:02:33 +0000729 REPORTER_ASSERT(reporter, !mat.invert(&inverse));
730
reed@android.comed673312009-02-27 16:24:51 +0000731 // rectStaysRect test
732 {
733 static const struct {
734 SkScalar m00, m01, m10, m11;
735 bool mStaysRect;
736 }
737 gRectStaysRectSamples[] = {
738 { 0, 0, 0, 0, false },
739 { 0, 0, 0, SK_Scalar1, false },
740 { 0, 0, SK_Scalar1, 0, false },
741 { 0, 0, SK_Scalar1, SK_Scalar1, false },
742 { 0, SK_Scalar1, 0, 0, false },
743 { 0, SK_Scalar1, 0, SK_Scalar1, false },
744 { 0, SK_Scalar1, SK_Scalar1, 0, true },
745 { 0, SK_Scalar1, SK_Scalar1, SK_Scalar1, false },
746 { SK_Scalar1, 0, 0, 0, false },
747 { SK_Scalar1, 0, 0, SK_Scalar1, true },
748 { SK_Scalar1, 0, SK_Scalar1, 0, false },
749 { SK_Scalar1, 0, SK_Scalar1, SK_Scalar1, false },
750 { SK_Scalar1, SK_Scalar1, 0, 0, false },
751 { SK_Scalar1, SK_Scalar1, 0, SK_Scalar1, false },
752 { SK_Scalar1, SK_Scalar1, SK_Scalar1, 0, false },
753 { SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, false }
754 };
reed@android.com80e39a72009-04-02 16:59:40 +0000755
reed@android.comed673312009-02-27 16:24:51 +0000756 for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
757 SkMatrix m;
reed@android.com80e39a72009-04-02 16:59:40 +0000758
reed@android.comed673312009-02-27 16:24:51 +0000759 m.reset();
760 m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
761 m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01);
762 m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10);
763 m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
764 REPORTER_ASSERT(reporter,
765 m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
766 }
767 }
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000768
bungeman@google.comba7983e2011-07-13 20:18:16 +0000769 mat.reset();
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000770 mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
771 mat.set(SkMatrix::kMSkewX, SkIntToScalar(2));
772 mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
773 mat.set(SkMatrix::kMSkewY, SkIntToScalar(4));
774 mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
775 mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000776 SkScalar affine[6];
777 REPORTER_ASSERT(reporter, mat.asAffine(affine));
778
779 #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
780 REPORTER_ASSERT(reporter, affineEqual(ScaleX));
781 REPORTER_ASSERT(reporter, affineEqual(SkewY));
782 REPORTER_ASSERT(reporter, affineEqual(SkewX));
783 REPORTER_ASSERT(reporter, affineEqual(ScaleY));
784 REPORTER_ASSERT(reporter, affineEqual(TransX));
785 REPORTER_ASSERT(reporter, affineEqual(TransY));
786 #undef affineEqual
787
bungeman@google.com07faed12011-10-07 21:55:56 +0000788 mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000789 REPORTER_ASSERT(reporter, !mat.asAffine(affine));
bsalomon@google.com38396322011-09-09 19:32:04 +0000790
bsalomon@google.com8fe84b52012-03-26 15:24:27 +0000791 SkMatrix mat2;
792 mat2.reset();
793 mat.reset();
794 SkScalar zero = 0;
795 mat.set(SkMatrix::kMSkewX, -zero);
796 REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
797
798 mat2.reset();
799 mat.reset();
800 mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
801 mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
bsalomon@google.com9ed2ecd2012-03-26 15:57:37 +0000802 // fixed pt doesn't have the property that NaN does not equal itself.
803#ifdef SK_SCALAR_IS_FIXED
804 REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
805#else
bsalomon@google.com8fe84b52012-03-26 15:24:27 +0000806 REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
bsalomon@google.com9ed2ecd2012-03-26 15:57:37 +0000807#endif
bsalomon@google.com8fe84b52012-03-26 15:24:27 +0000808
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000809 test_matrix_min_max_stretch(reporter);
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000810 test_matrix_is_similarity(reporter);
reed@google.com97cd69c2012-10-12 14:35:48 +0000811 test_matrix_recttorect(reporter);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000812 test_matrix_decomposition(reporter);
egdaniel@google.com259fbaf2013-08-15 21:12:11 +0000813 test_matrix_homogeneous(reporter);
reed@android.comed673312009-02-27 16:24:51 +0000814}
815
reed@android.comd8730ea2009-02-27 22:06:06 +0000816#include "TestClassDef.h"
817DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix)