blob: cbec021b405273c2301a3629c790906f11d6b5d3 [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 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
tomhudson@google.com889bd8b2011-09-27 17:38:17 +00008#include "SkMath.h"
reed@android.comed673312009-02-27 16:24:51 +00009#include "SkMatrix.h"
commit-bot@chromium.org08284e42013-07-24 18:08:08 +000010#include "SkMatrixUtils.h"
bsalomon@google.com38396322011-09-09 19:32:04 +000011#include "SkRandom.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000012#include "Test.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 const SkScalar tolerance = SK_Scalar1 / 200000;
reed@android.comed673312009-02-27 16:24:51 +000016 return SkScalarAbs(a - b) <= tolerance;
17}
18
19static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
20 for (int i = 0; i < 9; i++) {
21 if (!nearly_equal_scalar(a[i], b[i])) {
bungeman@google.comfab44db2013-10-11 18:50:45 +000022 SkDebugf("not equal %g %g\n", (float)a[i], (float)b[i]);
reed@android.comed673312009-02-27 16:24:51 +000023 return false;
24 }
25 }
26 return true;
27}
28
bsalomon@google.com8fe84b52012-03-26 15:24:27 +000029static bool are_equal(skiatest::Reporter* reporter,
30 const SkMatrix& a,
31 const SkMatrix& b) {
32 bool equal = a == b;
33 bool cheapEqual = a.cheapEqualTo(b);
34 if (equal != cheapEqual) {
bsalomon@google.com39d4f3a2012-03-26 17:25:45 +000035 if (equal) {
bsalomon@google.com8fe84b52012-03-26 15:24:27 +000036 bool foundZeroSignDiff = false;
37 for (int i = 0; i < 9; ++i) {
38 float aVal = a.get(i);
39 float bVal = b.get(i);
bsalomon@google.com373ebc62012-09-26 13:08:56 +000040 int aValI = *SkTCast<int*>(&aVal);
41 int bValI = *SkTCast<int*>(&bVal);
bsalomon@google.com8fe84b52012-03-26 15:24:27 +000042 if (0 == aVal && 0 == bVal && aValI != bValI) {
43 foundZeroSignDiff = true;
44 } else {
45 REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
46 }
47 }
48 REPORTER_ASSERT(reporter, foundZeroSignDiff);
49 } else {
50 bool foundNaN = false;
51 for (int i = 0; i < 9; ++i) {
52 float aVal = a.get(i);
53 float bVal = b.get(i);
bsalomon@google.com373ebc62012-09-26 13:08:56 +000054 int aValI = *SkTCast<int*>(&aVal);
55 int bValI = *SkTCast<int*>(&bVal);
bsalomon@google.com8fe84b52012-03-26 15:24:27 +000056 if (sk_float_isnan(aVal) && aValI == bValI) {
57 foundNaN = true;
58 } else {
59 REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
60 }
61 }
62 REPORTER_ASSERT(reporter, foundNaN);
63 }
bsalomon@google.com8fe84b52012-03-26 15:24:27 +000064 }
65 return equal;
66}
67
reed@android.comed673312009-02-27 16:24:51 +000068static bool is_identity(const SkMatrix& m) {
69 SkMatrix identity;
reed@android.com80e39a72009-04-02 16:59:40 +000070 identity.reset();
reed@android.comed673312009-02-27 16:24:51 +000071 return nearly_equal(m, identity);
72}
73
reed@google.com97cd69c2012-10-12 14:35:48 +000074static void test_matrix_recttorect(skiatest::Reporter* reporter) {
75 SkRect src, dst;
76 SkMatrix matrix;
skia.committer@gmail.comf57c01b2012-10-13 02:01:56 +000077
reed@google.com97cd69c2012-10-12 14:35:48 +000078 src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10);
79 dst = src;
80 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
81 REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
82 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
skia.committer@gmail.comf57c01b2012-10-13 02:01:56 +000083
reed@google.com97cd69c2012-10-12 14:35:48 +000084 dst.offset(SK_Scalar1, SK_Scalar1);
85 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
86 REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
87 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
skia.committer@gmail.comf57c01b2012-10-13 02:01:56 +000088
reed@google.com97cd69c2012-10-12 14:35:48 +000089 dst.fRight += SK_Scalar1;
90 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
skia.committer@gmail.come659c2e2012-12-04 02:01:25 +000091 REPORTER_ASSERT(reporter,
robertphillips@google.com93f03322012-12-03 17:35:19 +000092 (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
reed@google.com97cd69c2012-10-12 14:35:48 +000093 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
94
95 dst = src;
96 dst.fRight = src.fRight * 2;
97 matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
98 REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
99 REPORTER_ASSERT(reporter, matrix.rectStaysRect());
100}
101
reed@android.com4b7577b2009-06-29 16:14:41 +0000102static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
103 // 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 +0000104 static const size_t kBufferSize = SkMatrix::kMaxFlattenSize + 100;
105 char buffer[kBufferSize];
106 size_t size1 = m.writeToMemory(NULL);
107 size_t size2 = m.writeToMemory(buffer);
reed@android.com4b7577b2009-06-29 16:14:41 +0000108 REPORTER_ASSERT(reporter, size1 == size2);
109 REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000110
reed@android.com4b7577b2009-06-29 16:14:41 +0000111 SkMatrix m2;
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000112 size_t size3 = m2.readFromMemory(buffer, kBufferSize);
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000113 REPORTER_ASSERT(reporter, size1 == size3);
bsalomon@google.com8fe84b52012-03-26 15:24:27 +0000114 REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000115
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000116 char buffer2[kBufferSize];
djsollen@google.com94e75ee2012-06-08 18:30:46 +0000117 size3 = m2.writeToMemory(buffer2);
118 REPORTER_ASSERT(reporter, size1 == size3);
reed@android.com4b7577b2009-06-29 16:14:41 +0000119 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
120}
121
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000122static void test_matrix_min_max_scale(skiatest::Reporter* reporter) {
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000123 SkScalar scales[2];
124 bool success;
125
bsalomon@google.com38396322011-09-09 19:32:04 +0000126 SkMatrix identity;
127 identity.reset();
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000128 REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinScale());
129 REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxScale());
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000130 success = identity.getMinMaxScales(scales);
131 REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
bsalomon@google.com38396322011-09-09 19:32:04 +0000132
133 SkMatrix scale;
134 scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000135 REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinScale());
136 REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxScale());
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000137 success = scale.getMinMaxScales(scales);
138 REPORTER_ASSERT(reporter, success && SK_Scalar1 * 2 == scales[0] && SK_Scalar1 * 4 == scales[1]);
bsalomon@google.com38396322011-09-09 19:32:04 +0000139
140 SkMatrix rot90Scale;
141 rot90Scale.setRotate(90 * SK_Scalar1);
142 rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000143 REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinScale());
144 REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxScale());
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000145 success = rot90Scale.getMinMaxScales(scales);
146 REPORTER_ASSERT(reporter, success && SK_Scalar1 / 4 == scales[0] && SK_Scalar1 / 2 == scales[1]);
bsalomon@google.com38396322011-09-09 19:32:04 +0000147
148 SkMatrix rotate;
149 rotate.setRotate(128 * SK_Scalar1);
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000150 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinScale(), SK_ScalarNearlyZero));
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000151 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxScale(), SK_ScalarNearlyZero));
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000152 success = rotate.getMinMaxScales(scales);
153 REPORTER_ASSERT(reporter, success);
154 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[0], SK_ScalarNearlyZero));
155 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[1], SK_ScalarNearlyZero));
bsalomon@google.com38396322011-09-09 19:32:04 +0000156
157 SkMatrix translate;
158 translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000159 REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinScale());
160 REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxScale());
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000161 success = translate.getMinMaxScales(scales);
162 REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
bsalomon@google.com38396322011-09-09 19:32:04 +0000163
164 SkMatrix perspX;
165 perspX.reset();
bungeman@google.com07faed12011-10-07 21:55:56 +0000166 perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000167 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale());
168 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale());
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000169 // Verify that getMinMaxScales() doesn't update the scales array on failure.
170 scales[0] = -5;
171 scales[1] = -5;
172 success = perspX.getMinMaxScales(scales);
173 REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1 == scales[1]);
bsalomon@google.com38396322011-09-09 19:32:04 +0000174
175 SkMatrix perspY;
176 perspY.reset();
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000177 perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000178 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale());
179 REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxScale());
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000180 scales[0] = -5;
181 scales[1] = -5;
182 success = perspY.getMinMaxScales(scales);
183 REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1 == scales[1]);
bsalomon@google.com38396322011-09-09 19:32:04 +0000184
185 SkMatrix baseMats[] = {scale, rot90Scale, rotate,
186 translate, perspX, perspY};
187 SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
tomhudson@google.com83a44462011-10-27 15:27:51 +0000188 for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
bsalomon@google.com38396322011-09-09 19:32:04 +0000189 mats[i] = baseMats[i];
190 bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
191 REPORTER_ASSERT(reporter, invertable);
192 }
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000193 SkRandom rand;
bsalomon@google.com38396322011-09-09 19:32:04 +0000194 for (int m = 0; m < 1000; ++m) {
195 SkMatrix mat;
196 mat.reset();
197 for (int i = 0; i < 4; ++i) {
198 int x = rand.nextU() % SK_ARRAY_COUNT(mats);
199 mat.postConcat(mats[x]);
200 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000201
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000202 SkScalar minScale = mat.getMinScale();
203 SkScalar maxScale = mat.getMaxScale();
204 REPORTER_ASSERT(reporter, (minScale < 0) == (maxScale < 0));
205 REPORTER_ASSERT(reporter, (maxScale < 0) == mat.hasPerspective());
bsalomon@google.com38396322011-09-09 19:32:04 +0000206
commit-bot@chromium.org311a3cd2014-05-20 17:02:03 +0000207 SkScalar scales[2];
208 bool success = mat.getMinMaxScales(scales);
209 REPORTER_ASSERT(reporter, success == !mat.hasPerspective());
210 REPORTER_ASSERT(reporter, !success || (scales[0] == minScale && scales[1] == maxScale));
211
bsalomon@google.com38396322011-09-09 19:32:04 +0000212 if (mat.hasPerspective()) {
213 m -= 1; // try another non-persp matrix
214 continue;
215 }
216
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000217 // test a bunch of vectors. All should be scaled by between minScale and maxScale
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000218 // (modulo some error) and we should find a vector that is scaled by almost each.
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000219 static const SkScalar gVectorScaleTol = (105 * SK_Scalar1) / 100;
220 static const SkScalar gCloseScaleTol = (97 * SK_Scalar1) / 100;
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000221 SkScalar max = 0, min = SK_ScalarMax;
bsalomon@google.com38396322011-09-09 19:32:04 +0000222 SkVector vectors[1000];
tomhudson@google.com83a44462011-10-27 15:27:51 +0000223 for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
bsalomon@google.com38396322011-09-09 19:32:04 +0000224 vectors[i].fX = rand.nextSScalar1();
225 vectors[i].fY = rand.nextSScalar1();
226 if (!vectors[i].normalize()) {
227 i -= 1;
228 continue;
229 }
230 }
231 mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
tomhudson@google.com83a44462011-10-27 15:27:51 +0000232 for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
bsalomon@google.com38396322011-09-09 19:32:04 +0000233 SkScalar d = vectors[i].length();
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000234 REPORTER_ASSERT(reporter, SkScalarDiv(d, maxScale) < gVectorScaleTol);
235 REPORTER_ASSERT(reporter, SkScalarDiv(minScale, d) < gVectorScaleTol);
bsalomon@google.com38396322011-09-09 19:32:04 +0000236 if (max < d) {
237 max = d;
238 }
commit-bot@chromium.orgcea9abb2013-12-09 19:15:37 +0000239 if (min > d) {
240 min = d;
241 }
bsalomon@google.com38396322011-09-09 19:32:04 +0000242 }
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000243 REPORTER_ASSERT(reporter, SkScalarDiv(max, maxScale) >= gCloseScaleTol);
244 REPORTER_ASSERT(reporter, SkScalarDiv(minScale, min) >= gCloseScaleTol);
bsalomon@google.com38396322011-09-09 19:32:04 +0000245 }
246}
247
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000248static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
bsalomon@google.com69afee12012-04-25 15:07:40 +0000249 SkMatrix mat;
250
251 // identity
252 mat.setIdentity();
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000253 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000254
255 // translation only
256 mat.reset();
257 mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000258 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000259
260 // scale with same size
261 mat.reset();
262 mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000263 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000264
265 // scale with one negative
266 mat.reset();
267 mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000268 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000269
270 // scale with different size
271 mat.reset();
272 mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000273 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000274
275 // scale with same size at a pivot point
276 mat.reset();
277 mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
278 SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000279 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000280
281 // scale with different size at a pivot point
282 mat.reset();
283 mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
284 SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000285 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000286
287 // skew with same size
288 mat.reset();
289 mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000290 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000291
292 // skew with different size
293 mat.reset();
294 mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000295 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000296
297 // skew with same size at a pivot point
298 mat.reset();
299 mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
300 SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000301 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000302
303 // skew with different size at a pivot point
304 mat.reset();
305 mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
306 SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000307 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000308
309 // perspective x
310 mat.reset();
311 mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000312 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000313
314 // perspective y
315 mat.reset();
316 mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000317 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000318
bsalomon@google.com69afee12012-04-25 15:07:40 +0000319 // rotate
320 for (int angle = 0; angle < 360; ++angle) {
321 mat.reset();
322 mat.setRotate(SkIntToScalar(angle));
djsollena8d2c4a2014-08-21 07:40:42 -0700323#ifndef SK_CPU_ARM64
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000324 REPORTER_ASSERT(reporter, mat.isSimilarity());
djsollena8d2c4a2014-08-21 07:40:42 -0700325#else
326 // 64-bit ARM devices built with -O2 and -ffp-contract=fast have a loss
327 // of precision and require that we have a higher tolerance
328 REPORTER_ASSERT(reporter, mat.isSimilarity(SK_ScalarNearlyZero + 0.00010113f));
329#endif
bsalomon@google.com69afee12012-04-25 15:07:40 +0000330 }
331
332 // see if there are any accumulated precision issues
333 mat.reset();
334 for (int i = 1; i < 360; i++) {
335 mat.postRotate(SkIntToScalar(1));
336 }
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000337 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000338
339 // rotate + translate
340 mat.reset();
341 mat.setRotate(SkIntToScalar(30));
342 mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000343 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000344
345 // rotate + uniform scale
346 mat.reset();
347 mat.setRotate(SkIntToScalar(30));
348 mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000349 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000350
351 // rotate + non-uniform scale
352 mat.reset();
353 mat.setRotate(SkIntToScalar(30));
354 mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000355 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000356
357 // all zero
358 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000359 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000360
361 // all zero except perspective
362 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000363 REPORTER_ASSERT(reporter, !mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000364
365 // scales zero, only skews
366 mat.setAll(0, SK_Scalar1, 0,
367 SK_Scalar1, 0, 0,
368 0, 0, SkMatrix::I()[8]);
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000369 REPORTER_ASSERT(reporter, mat.isSimilarity());
bsalomon@google.com69afee12012-04-25 15:07:40 +0000370}
371
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000372// For test_matrix_decomposition, below.
skia.committer@gmail.com5c561cb2013-07-25 07:01:00 +0000373static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000374 SkScalar tolerance = SK_ScalarNearlyZero) {
375 // from Bruce Dawson
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000376 // absolute check
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000377 SkScalar diff = SkScalarAbs(a - b);
378 if (diff < tolerance) {
379 return true;
380 }
381
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000382 // relative check
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000383 a = SkScalarAbs(a);
384 b = SkScalarAbs(b);
385 SkScalar largest = (b > a) ? b : a;
386
387 if (diff <= largest*tolerance) {
388 return true;
389 }
390
391 return false;
392}
393
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000394static bool check_matrix_recomposition(const SkMatrix& mat,
395 const SkPoint& rotation1,
396 const SkPoint& scale,
397 const SkPoint& rotation2) {
398 SkScalar c1 = rotation1.fX;
399 SkScalar s1 = rotation1.fY;
400 SkScalar scaleX = scale.fX;
401 SkScalar scaleY = scale.fY;
402 SkScalar c2 = rotation2.fX;
403 SkScalar s2 = rotation2.fY;
skia.committer@gmail.com85092f02013-09-04 07:01:39 +0000404
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000405 // We do a relative check here because large scale factors cause problems with an absolute check
406 bool result = scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
407 scaleX*c1*c2 - scaleY*s1*s2) &&
408 scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
409 -scaleX*s1*c2 - scaleY*c1*s2) &&
410 scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
411 scaleX*c1*s2 + scaleY*s1*c2) &&
412 scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
413 -scaleX*s1*s2 + scaleY*c1*c2);
414 return result;
415}
416
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000417static void test_matrix_decomposition(skiatest::Reporter* reporter) {
418 SkMatrix mat;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000419 SkPoint rotation1, scale, rotation2;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000420
421 const float kRotation0 = 15.5f;
422 const float kRotation1 = -50.f;
423 const float kScale0 = 5000.f;
424 const float kScale1 = 0.001f;
425
426 // identity
427 mat.reset();
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000428 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
429 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000430 // make sure it doesn't crash if we pass in NULLs
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000431 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000432
433 // rotation only
434 mat.setRotate(kRotation0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000435 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
436 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000437
438 // uniform scale only
439 mat.setScale(kScale0, kScale0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000440 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
441 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000442
443 // anisotropic scale only
444 mat.setScale(kScale1, kScale0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000445 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
446 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000447
448 // rotation then uniform scale
449 mat.setRotate(kRotation1);
450 mat.postScale(kScale0, kScale0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000451 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
452 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000453
454 // uniform scale then rotation
455 mat.setScale(kScale0, kScale0);
456 mat.postRotate(kRotation1);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000457 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
458 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000459
460 // rotation then uniform scale+reflection
461 mat.setRotate(kRotation0);
462 mat.postScale(kScale1, -kScale1);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000463 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
464 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000465
466 // uniform scale+reflection, then rotate
467 mat.setScale(kScale0, -kScale0);
468 mat.postRotate(kRotation1);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000469 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
470 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000471
472 // rotation then anisotropic scale
473 mat.setRotate(kRotation1);
474 mat.postScale(kScale1, kScale0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000475 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
476 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000477
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000478 // rotation then anisotropic scale
479 mat.setRotate(90);
480 mat.postScale(kScale1, kScale0);
481 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
482 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
skia.committer@gmail.com85092f02013-09-04 07:01:39 +0000483
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000484 // anisotropic scale then rotation
485 mat.setScale(kScale1, 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));
skia.committer@gmail.com85092f02013-09-04 07:01:39 +0000489
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000490 // anisotropic scale then rotation
491 mat.setScale(kScale1, kScale0);
492 mat.postRotate(90);
493 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
494 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000495
496 // rotation, uniform scale, then different rotation
497 mat.setRotate(kRotation1);
498 mat.postScale(kScale0, kScale0);
499 mat.postRotate(kRotation0);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000500 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
501 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000502
503 // rotation, anisotropic scale, then different rotation
504 mat.setRotate(kRotation0);
505 mat.postScale(kScale1, kScale0);
506 mat.postRotate(kRotation1);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000507 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
508 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
skia.committer@gmail.com85092f02013-09-04 07:01:39 +0000509
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000510 // rotation, anisotropic scale + reflection, then different rotation
511 mat.setRotate(kRotation0);
512 mat.postScale(-kScale1, kScale0);
513 mat.postRotate(kRotation1);
514 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
515 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000516
517 // try some random matrices
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000518 SkRandom rand;
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000519 for (int m = 0; m < 1000; ++m) {
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000520 SkScalar rot0 = rand.nextRangeF(-180, 180);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000521 SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
522 SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000523 SkScalar rot1 = rand.nextRangeF(-180, 180);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000524 mat.setRotate(rot0);
525 mat.postScale(sx, sy);
526 mat.postRotate(rot1);
527
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000528 if (SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)) {
529 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000530 } else {
531 // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
532 SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
533 mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
534 REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
535 }
536 }
537
538 // translation shouldn't affect this
539 mat.postTranslate(-1000.f, 1000.f);
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000540 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
541 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000542
543 // perspective shouldn't affect this
jvanverth@google.com588f3d32013-07-24 18:44:10 +0000544 mat[SkMatrix::kMPersp0] = 12.f;
545 mat[SkMatrix::kMPersp1] = 4.f;
546 mat[SkMatrix::kMPersp2] = 1872.f;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000547 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
548 REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000549
550 // degenerate matrices
551 // mostly zero entries
552 mat.reset();
553 mat[SkMatrix::kMScaleX] = 0.f;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000554 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000555 mat.reset();
556 mat[SkMatrix::kMScaleY] = 0.f;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000557 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000558 mat.reset();
559 // linearly dependent entries
560 mat[SkMatrix::kMScaleX] = 1.f;
561 mat[SkMatrix::kMSkewX] = 2.f;
562 mat[SkMatrix::kMSkewY] = 4.f;
563 mat[SkMatrix::kMScaleY] = 8.f;
commit-bot@chromium.org5b2e2642013-09-03 19:08:14 +0000564 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000565}
566
egdaniel@google.com259fbaf2013-08-15 21:12:11 +0000567// For test_matrix_homogeneous, below.
568static bool scalar_array_nearly_equal_relative(const SkScalar a[], const SkScalar b[], int count) {
569 for (int i = 0; i < count; ++i) {
570 if (!scalar_nearly_equal_relative(a[i], b[i])) {
571 return false;
572 }
573 }
574 return true;
575}
576
577// For test_matrix_homogeneous, below.
578// Maps a single triple in src using m and compares results to those in dst
579static bool naive_homogeneous_mapping(const SkMatrix& m, const SkScalar src[3],
580 const SkScalar dst[3]) {
581 SkScalar res[3];
582 SkScalar ms[9] = {m[0], m[1], m[2],
583 m[3], m[4], m[5],
584 m[6], m[7], m[8]};
585 res[0] = src[0] * ms[0] + src[1] * ms[1] + src[2] * ms[2];
586 res[1] = src[0] * ms[3] + src[1] * ms[4] + src[2] * ms[5];
587 res[2] = src[0] * ms[6] + src[1] * ms[7] + src[2] * ms[8];
588 return scalar_array_nearly_equal_relative(res, dst, 3);
589}
590
591static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
592 SkMatrix mat;
593
594 const float kRotation0 = 15.5f;
595 const float kRotation1 = -50.f;
596 const float kScale0 = 5000.f;
597
598 const int kTripleCount = 1000;
599 const int kMatrixCount = 1000;
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000600 SkRandom rand;
egdaniel@google.com259fbaf2013-08-15 21:12:11 +0000601
602 SkScalar randTriples[3*kTripleCount];
603 for (int i = 0; i < 3*kTripleCount; ++i) {
604 randTriples[i] = rand.nextRangeF(-3000.f, 3000.f);
605 }
606
607 SkMatrix mats[kMatrixCount];
608 for (int i = 0; i < kMatrixCount; ++i) {
609 for (int j = 0; j < 9; ++j) {
610 mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
611 }
612 }
613
614 // identity
615 {
616 mat.reset();
617 SkScalar dst[3*kTripleCount];
618 mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
619 REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3));
620 }
621
622 // zero matrix
623 {
624 mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
625 SkScalar dst[3*kTripleCount];
626 mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
627 SkScalar zeros[3] = {0.f, 0.f, 0.f};
628 for (int i = 0; i < kTripleCount; ++i) {
629 REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3));
630 }
631 }
632
633 // zero point
634 {
635 SkScalar zeros[3] = {0.f, 0.f, 0.f};
636 for (int i = 0; i < kMatrixCount; ++i) {
637 SkScalar dst[3];
638 mats[i].mapHomogeneousPoints(dst, zeros, 1);
639 REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3));
640 }
641 }
642
643 // doesn't crash with null dst, src, count == 0
644 {
645 mats[0].mapHomogeneousPoints(NULL, NULL, 0);
646 }
647
648 // uniform scale of point
649 {
650 mat.setScale(kScale0, kScale0);
651 SkScalar dst[3];
652 SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
653 SkPoint pnt;
654 pnt.set(src[0], src[1]);
655 mat.mapHomogeneousPoints(dst, src, 1);
656 mat.mapPoints(&pnt, &pnt, 1);
657 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
658 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
659 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
660 }
661
662 // rotation of point
663 {
664 mat.setRotate(kRotation0);
665 SkScalar dst[3];
666 SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
667 SkPoint pnt;
668 pnt.set(src[0], src[1]);
669 mat.mapHomogeneousPoints(dst, src, 1);
670 mat.mapPoints(&pnt, &pnt, 1);
671 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
672 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
673 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
674 }
675
676 // rotation, scale, rotation of point
677 {
678 mat.setRotate(kRotation1);
679 mat.postScale(kScale0, kScale0);
680 mat.postRotate(kRotation0);
681 SkScalar dst[3];
682 SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
683 SkPoint pnt;
684 pnt.set(src[0], src[1]);
685 mat.mapHomogeneousPoints(dst, src, 1);
686 mat.mapPoints(&pnt, &pnt, 1);
687 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
688 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
689 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
690 }
691
692 // compare with naive approach
693 {
694 for (int i = 0; i < kMatrixCount; ++i) {
695 for (int j = 0; j < kTripleCount; ++j) {
696 SkScalar dst[3];
697 mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1);
698 REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst));
699 }
700 }
701 }
702
703}
704
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000705DEF_TEST(Matrix, reporter) {
reed@android.comed673312009-02-27 16:24:51 +0000706 SkMatrix mat, inverse, iden1, iden2;
707
708 mat.reset();
709 mat.setTranslate(SK_Scalar1, SK_Scalar1);
reed@google.com5bfa55b2012-04-19 18:59:25 +0000710 REPORTER_ASSERT(reporter, mat.invert(&inverse));
reed@android.comed673312009-02-27 16:24:51 +0000711 iden1.setConcat(mat, inverse);
712 REPORTER_ASSERT(reporter, is_identity(iden1));
713
reed@google.com2fb96cc2013-01-04 17:02:33 +0000714 mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
reed@google.com5bfa55b2012-04-19 18:59:25 +0000715 REPORTER_ASSERT(reporter, mat.invert(&inverse));
reed@android.comed673312009-02-27 16:24:51 +0000716 iden1.setConcat(mat, inverse);
717 REPORTER_ASSERT(reporter, is_identity(iden1));
reed@android.com4b7577b2009-06-29 16:14:41 +0000718 test_flatten(reporter, mat);
reed@android.comed673312009-02-27 16:24:51 +0000719
reed@google.com2fb96cc2013-01-04 17:02:33 +0000720 mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
reed@google.com5bfa55b2012-04-19 18:59:25 +0000721 REPORTER_ASSERT(reporter, mat.invert(&inverse));
reed@android.comed673312009-02-27 16:24:51 +0000722 iden1.setConcat(mat, inverse);
723 REPORTER_ASSERT(reporter, is_identity(iden1));
reed@android.com4b7577b2009-06-29 16:14:41 +0000724 test_flatten(reporter, mat);
reed@android.comed673312009-02-27 16:24:51 +0000725
726 mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
727 mat.postRotate(SkIntToScalar(25));
728 REPORTER_ASSERT(reporter, mat.invert(NULL));
reed@google.com5bfa55b2012-04-19 18:59:25 +0000729 REPORTER_ASSERT(reporter, mat.invert(&inverse));
reed@android.comed673312009-02-27 16:24:51 +0000730 iden1.setConcat(mat, inverse);
731 REPORTER_ASSERT(reporter, is_identity(iden1));
732 iden2.setConcat(inverse, mat);
733 REPORTER_ASSERT(reporter, is_identity(iden2));
reed@android.com4b7577b2009-06-29 16:14:41 +0000734 test_flatten(reporter, mat);
735 test_flatten(reporter, iden2);
reed@android.com80e39a72009-04-02 16:59:40 +0000736
reed@google.com2fb96cc2013-01-04 17:02:33 +0000737 mat.setScale(0, SK_Scalar1);
reed@google.come40591d2013-01-30 15:47:42 +0000738 REPORTER_ASSERT(reporter, !mat.invert(NULL));
reed@google.com2fb96cc2013-01-04 17:02:33 +0000739 REPORTER_ASSERT(reporter, !mat.invert(&inverse));
740 mat.setScale(SK_Scalar1, 0);
reed@google.come40591d2013-01-30 15:47:42 +0000741 REPORTER_ASSERT(reporter, !mat.invert(NULL));
reed@google.com2fb96cc2013-01-04 17:02:33 +0000742 REPORTER_ASSERT(reporter, !mat.invert(&inverse));
743
reed@android.comed673312009-02-27 16:24:51 +0000744 // rectStaysRect test
745 {
746 static const struct {
747 SkScalar m00, m01, m10, m11;
748 bool mStaysRect;
749 }
750 gRectStaysRectSamples[] = {
751 { 0, 0, 0, 0, false },
752 { 0, 0, 0, SK_Scalar1, false },
753 { 0, 0, SK_Scalar1, 0, false },
754 { 0, 0, SK_Scalar1, SK_Scalar1, false },
755 { 0, SK_Scalar1, 0, 0, false },
756 { 0, SK_Scalar1, 0, SK_Scalar1, false },
757 { 0, SK_Scalar1, SK_Scalar1, 0, true },
758 { 0, SK_Scalar1, SK_Scalar1, SK_Scalar1, false },
759 { SK_Scalar1, 0, 0, 0, false },
760 { SK_Scalar1, 0, 0, SK_Scalar1, true },
761 { SK_Scalar1, 0, SK_Scalar1, 0, false },
762 { SK_Scalar1, 0, SK_Scalar1, SK_Scalar1, false },
763 { SK_Scalar1, SK_Scalar1, 0, 0, false },
764 { SK_Scalar1, SK_Scalar1, 0, SK_Scalar1, false },
765 { SK_Scalar1, SK_Scalar1, SK_Scalar1, 0, false },
766 { SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, false }
767 };
reed@android.com80e39a72009-04-02 16:59:40 +0000768
reed@android.comed673312009-02-27 16:24:51 +0000769 for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
770 SkMatrix m;
reed@android.com80e39a72009-04-02 16:59:40 +0000771
reed@android.comed673312009-02-27 16:24:51 +0000772 m.reset();
773 m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
774 m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01);
775 m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10);
776 m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
777 REPORTER_ASSERT(reporter,
778 m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
779 }
780 }
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000781
bungeman@google.comba7983e2011-07-13 20:18:16 +0000782 mat.reset();
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000783 mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
784 mat.set(SkMatrix::kMSkewX, SkIntToScalar(2));
785 mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
786 mat.set(SkMatrix::kMSkewY, SkIntToScalar(4));
787 mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
788 mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000789 SkScalar affine[6];
790 REPORTER_ASSERT(reporter, mat.asAffine(affine));
791
792 #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
793 REPORTER_ASSERT(reporter, affineEqual(ScaleX));
794 REPORTER_ASSERT(reporter, affineEqual(SkewY));
795 REPORTER_ASSERT(reporter, affineEqual(SkewX));
796 REPORTER_ASSERT(reporter, affineEqual(ScaleY));
797 REPORTER_ASSERT(reporter, affineEqual(TransX));
798 REPORTER_ASSERT(reporter, affineEqual(TransY));
799 #undef affineEqual
800
bungeman@google.com07faed12011-10-07 21:55:56 +0000801 mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
bungeman@google.com1ddd7c32011-07-13 19:41:55 +0000802 REPORTER_ASSERT(reporter, !mat.asAffine(affine));
bsalomon@google.com38396322011-09-09 19:32:04 +0000803
bsalomon@google.com8fe84b52012-03-26 15:24:27 +0000804 SkMatrix mat2;
805 mat2.reset();
806 mat.reset();
807 SkScalar zero = 0;
808 mat.set(SkMatrix::kMSkewX, -zero);
809 REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
810
811 mat2.reset();
812 mat.reset();
813 mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
814 mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
815 REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
816
commit-bot@chromium.org18786512014-05-20 14:53:45 +0000817 test_matrix_min_max_scale(reporter);
jvanverth@google.com46d3d392013-01-22 13:34:01 +0000818 test_matrix_is_similarity(reporter);
reed@google.com97cd69c2012-10-12 14:35:48 +0000819 test_matrix_recttorect(reporter);
commit-bot@chromium.org08284e42013-07-24 18:08:08 +0000820 test_matrix_decomposition(reporter);
egdaniel@google.com259fbaf2013-08-15 21:12:11 +0000821 test_matrix_homogeneous(reporter);
reed@android.comed673312009-02-27 16:24:51 +0000822}
commit-bot@chromium.org99bd7d82014-05-19 15:51:12 +0000823
824DEF_TEST(Matrix_Concat, r) {
825 SkMatrix a;
826 a.setTranslate(10, 20);
827
828 SkMatrix b;
829 b.setScale(3, 5);
830
831 SkMatrix expected;
832 expected.setConcat(a,b);
833
834 REPORTER_ASSERT(r, expected == SkMatrix::Concat(a, b));
835}