1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2011 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkM44.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint3.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkFloatingPoint.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMatrixPriv.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMatrixUtils.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPointPriv.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
24*c8dee2aaSAndroid Build Coastguard Worker
25*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
26*c8dee2aaSAndroid Build Coastguard Worker #include <initializer_list>
27*c8dee2aaSAndroid Build Coastguard Worker #include <string>
28*c8dee2aaSAndroid Build Coastguard Worker
nearly_equal_scalar(SkScalar a,SkScalar b)29*c8dee2aaSAndroid Build Coastguard Worker static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
30*c8dee2aaSAndroid Build Coastguard Worker const SkScalar tolerance = SK_Scalar1 / 200000;
31*c8dee2aaSAndroid Build Coastguard Worker return SkScalarAbs(a - b) <= tolerance;
32*c8dee2aaSAndroid Build Coastguard Worker }
33*c8dee2aaSAndroid Build Coastguard Worker
nearly_equal(const SkMatrix & a,const SkMatrix & b)34*c8dee2aaSAndroid Build Coastguard Worker static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
35*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 9; i++) {
36*c8dee2aaSAndroid Build Coastguard Worker if (!nearly_equal_scalar(a[i], b[i])) {
37*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("matrices not equal [%d] %g %g\n", i, (float)a[i], (float)b[i]);
38*c8dee2aaSAndroid Build Coastguard Worker return false;
39*c8dee2aaSAndroid Build Coastguard Worker }
40*c8dee2aaSAndroid Build Coastguard Worker }
41*c8dee2aaSAndroid Build Coastguard Worker return true;
42*c8dee2aaSAndroid Build Coastguard Worker }
43*c8dee2aaSAndroid Build Coastguard Worker
float_bits(float f)44*c8dee2aaSAndroid Build Coastguard Worker static int float_bits(float f) {
45*c8dee2aaSAndroid Build Coastguard Worker int result;
46*c8dee2aaSAndroid Build Coastguard Worker memcpy(&result, &f, 4);
47*c8dee2aaSAndroid Build Coastguard Worker return result;
48*c8dee2aaSAndroid Build Coastguard Worker }
49*c8dee2aaSAndroid Build Coastguard Worker
are_equal(skiatest::Reporter * reporter,const SkMatrix & a,const SkMatrix & b)50*c8dee2aaSAndroid Build Coastguard Worker static bool are_equal(skiatest::Reporter* reporter,
51*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& a,
52*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& b) {
53*c8dee2aaSAndroid Build Coastguard Worker bool equal = a == b;
54*c8dee2aaSAndroid Build Coastguard Worker bool cheapEqual = SkMatrixPriv::CheapEqual(a, b);
55*c8dee2aaSAndroid Build Coastguard Worker if (equal != cheapEqual) {
56*c8dee2aaSAndroid Build Coastguard Worker if (equal) {
57*c8dee2aaSAndroid Build Coastguard Worker bool foundZeroSignDiff = false;
58*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 9; ++i) {
59*c8dee2aaSAndroid Build Coastguard Worker float aVal = a.get(i);
60*c8dee2aaSAndroid Build Coastguard Worker float bVal = b.get(i);
61*c8dee2aaSAndroid Build Coastguard Worker int aValI = float_bits(aVal);
62*c8dee2aaSAndroid Build Coastguard Worker int bValI = float_bits(bVal);
63*c8dee2aaSAndroid Build Coastguard Worker if (0 == aVal && 0 == bVal && aValI != bValI) {
64*c8dee2aaSAndroid Build Coastguard Worker foundZeroSignDiff = true;
65*c8dee2aaSAndroid Build Coastguard Worker } else {
66*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
67*c8dee2aaSAndroid Build Coastguard Worker }
68*c8dee2aaSAndroid Build Coastguard Worker }
69*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, foundZeroSignDiff);
70*c8dee2aaSAndroid Build Coastguard Worker } else {
71*c8dee2aaSAndroid Build Coastguard Worker bool foundNaN = false;
72*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 9; ++i) {
73*c8dee2aaSAndroid Build Coastguard Worker float aVal = a.get(i);
74*c8dee2aaSAndroid Build Coastguard Worker float bVal = b.get(i);
75*c8dee2aaSAndroid Build Coastguard Worker int aValI = float_bits(aVal);
76*c8dee2aaSAndroid Build Coastguard Worker int bValI = float_bits(bVal);
77*c8dee2aaSAndroid Build Coastguard Worker if (std::isnan(aVal) && aValI == bValI) {
78*c8dee2aaSAndroid Build Coastguard Worker foundNaN = true;
79*c8dee2aaSAndroid Build Coastguard Worker } else {
80*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
81*c8dee2aaSAndroid Build Coastguard Worker }
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, foundNaN);
84*c8dee2aaSAndroid Build Coastguard Worker }
85*c8dee2aaSAndroid Build Coastguard Worker }
86*c8dee2aaSAndroid Build Coastguard Worker return equal;
87*c8dee2aaSAndroid Build Coastguard Worker }
88*c8dee2aaSAndroid Build Coastguard Worker
is_identity(const SkMatrix & m)89*c8dee2aaSAndroid Build Coastguard Worker static bool is_identity(const SkMatrix& m) {
90*c8dee2aaSAndroid Build Coastguard Worker SkMatrix identity;
91*c8dee2aaSAndroid Build Coastguard Worker identity.reset();
92*c8dee2aaSAndroid Build Coastguard Worker return nearly_equal(m, identity);
93*c8dee2aaSAndroid Build Coastguard Worker }
94*c8dee2aaSAndroid Build Coastguard Worker
assert9(skiatest::Reporter * reporter,const SkMatrix & m,SkScalar a,SkScalar b,SkScalar c,SkScalar d,SkScalar e,SkScalar f,SkScalar g,SkScalar h,SkScalar i)95*c8dee2aaSAndroid Build Coastguard Worker static void assert9(skiatest::Reporter* reporter, const SkMatrix& m,
96*c8dee2aaSAndroid Build Coastguard Worker SkScalar a, SkScalar b, SkScalar c,
97*c8dee2aaSAndroid Build Coastguard Worker SkScalar d, SkScalar e, SkScalar f,
98*c8dee2aaSAndroid Build Coastguard Worker SkScalar g, SkScalar h, SkScalar i) {
99*c8dee2aaSAndroid Build Coastguard Worker SkScalar buffer[9];
100*c8dee2aaSAndroid Build Coastguard Worker m.get9(buffer);
101*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, buffer[0] == a);
102*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, buffer[1] == b);
103*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, buffer[2] == c);
104*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, buffer[3] == d);
105*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, buffer[4] == e);
106*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, buffer[5] == f);
107*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, buffer[6] == g);
108*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, buffer[7] == h);
109*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, buffer[8] == i);
110*c8dee2aaSAndroid Build Coastguard Worker
111*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.rc(0, 0) == a);
112*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.rc(0, 1) == b);
113*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.rc(0, 2) == c);
114*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.rc(1, 0) == d);
115*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.rc(1, 1) == e);
116*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.rc(1, 2) == f);
117*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.rc(2, 0) == g);
118*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.rc(2, 1) == h);
119*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.rc(2, 2) == i);
120*c8dee2aaSAndroid Build Coastguard Worker }
121*c8dee2aaSAndroid Build Coastguard Worker
test_set9(skiatest::Reporter * reporter)122*c8dee2aaSAndroid Build Coastguard Worker static void test_set9(skiatest::Reporter* reporter) {
123*c8dee2aaSAndroid Build Coastguard Worker
124*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
125*c8dee2aaSAndroid Build Coastguard Worker m.reset();
126*c8dee2aaSAndroid Build Coastguard Worker assert9(reporter, m, 1, 0, 0, 0, 1, 0, 0, 0, 1);
127*c8dee2aaSAndroid Build Coastguard Worker
128*c8dee2aaSAndroid Build Coastguard Worker m.setScale(2, 3);
129*c8dee2aaSAndroid Build Coastguard Worker assert9(reporter, m, 2, 0, 0, 0, 3, 0, 0, 0, 1);
130*c8dee2aaSAndroid Build Coastguard Worker
131*c8dee2aaSAndroid Build Coastguard Worker m.postTranslate(4, 5);
132*c8dee2aaSAndroid Build Coastguard Worker assert9(reporter, m, 2, 0, 4, 0, 3, 5, 0, 0, 1);
133*c8dee2aaSAndroid Build Coastguard Worker
134*c8dee2aaSAndroid Build Coastguard Worker SkScalar buffer[9];
135*c8dee2aaSAndroid Build Coastguard Worker sk_bzero(buffer, sizeof(buffer));
136*c8dee2aaSAndroid Build Coastguard Worker buffer[SkMatrix::kMScaleX] = 1;
137*c8dee2aaSAndroid Build Coastguard Worker buffer[SkMatrix::kMScaleY] = 1;
138*c8dee2aaSAndroid Build Coastguard Worker buffer[SkMatrix::kMPersp2] = 1;
139*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !m.isIdentity());
140*c8dee2aaSAndroid Build Coastguard Worker m.set9(buffer);
141*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, m.isIdentity());
142*c8dee2aaSAndroid Build Coastguard Worker }
143*c8dee2aaSAndroid Build Coastguard Worker
test_matrix_recttorect(skiatest::Reporter * reporter)144*c8dee2aaSAndroid Build Coastguard Worker static void test_matrix_recttorect(skiatest::Reporter* reporter) {
145*c8dee2aaSAndroid Build Coastguard Worker SkRect src, dst;
146*c8dee2aaSAndroid Build Coastguard Worker SkMatrix matrix;
147*c8dee2aaSAndroid Build Coastguard Worker
148*c8dee2aaSAndroid Build Coastguard Worker src.setLTRB(0, 0, 10, 10);
149*c8dee2aaSAndroid Build Coastguard Worker dst = src;
150*c8dee2aaSAndroid Build Coastguard Worker matrix = SkMatrix::RectToRect(src, dst);
151*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
152*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, matrix.rectStaysRect());
153*c8dee2aaSAndroid Build Coastguard Worker
154*c8dee2aaSAndroid Build Coastguard Worker dst.offset(1, 1);
155*c8dee2aaSAndroid Build Coastguard Worker matrix = SkMatrix::RectToRect(src, dst);
156*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
157*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, matrix.rectStaysRect());
158*c8dee2aaSAndroid Build Coastguard Worker
159*c8dee2aaSAndroid Build Coastguard Worker dst.fRight += 1;
160*c8dee2aaSAndroid Build Coastguard Worker matrix = SkMatrix::RectToRect(src, dst);
161*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter,
162*c8dee2aaSAndroid Build Coastguard Worker (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
163*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, matrix.rectStaysRect());
164*c8dee2aaSAndroid Build Coastguard Worker
165*c8dee2aaSAndroid Build Coastguard Worker dst = src;
166*c8dee2aaSAndroid Build Coastguard Worker dst.fRight = src.fRight * 2;
167*c8dee2aaSAndroid Build Coastguard Worker matrix = SkMatrix::RectToRect(src, dst);
168*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
169*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, matrix.rectStaysRect());
170*c8dee2aaSAndroid Build Coastguard Worker }
171*c8dee2aaSAndroid Build Coastguard Worker
test_flatten(skiatest::Reporter * reporter,const SkMatrix & m)172*c8dee2aaSAndroid Build Coastguard Worker static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
173*c8dee2aaSAndroid Build Coastguard Worker // add 100 in case we have a bug, I don't want to kill my stack in the test
174*c8dee2aaSAndroid Build Coastguard Worker static const size_t kBufferSize = SkMatrixPriv::kMaxFlattenSize + 100;
175*c8dee2aaSAndroid Build Coastguard Worker char buffer[kBufferSize];
176*c8dee2aaSAndroid Build Coastguard Worker size_t size1 = SkMatrixPriv::WriteToMemory(m, nullptr);
177*c8dee2aaSAndroid Build Coastguard Worker size_t size2 = SkMatrixPriv::WriteToMemory(m, buffer);
178*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, size1 == size2);
179*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, size1 <= SkMatrixPriv::kMaxFlattenSize);
180*c8dee2aaSAndroid Build Coastguard Worker
181*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m2;
182*c8dee2aaSAndroid Build Coastguard Worker size_t size3 = SkMatrixPriv::ReadFromMemory(&m2, buffer, kBufferSize);
183*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, size1 == size3);
184*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
185*c8dee2aaSAndroid Build Coastguard Worker
186*c8dee2aaSAndroid Build Coastguard Worker char buffer2[kBufferSize];
187*c8dee2aaSAndroid Build Coastguard Worker size3 = SkMatrixPriv::WriteToMemory(m2, buffer2);
188*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, size1 == size3);
189*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
190*c8dee2aaSAndroid Build Coastguard Worker }
191*c8dee2aaSAndroid Build Coastguard Worker
test_matrix_min_max_scale(skiatest::Reporter * reporter)192*c8dee2aaSAndroid Build Coastguard Worker static void test_matrix_min_max_scale(skiatest::Reporter* reporter) {
193*c8dee2aaSAndroid Build Coastguard Worker SkScalar scales[2];
194*c8dee2aaSAndroid Build Coastguard Worker bool success;
195*c8dee2aaSAndroid Build Coastguard Worker
196*c8dee2aaSAndroid Build Coastguard Worker SkMatrix identity;
197*c8dee2aaSAndroid Build Coastguard Worker identity.reset();
198*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 1 == identity.getMinScale());
199*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 1 == identity.getMaxScale());
200*c8dee2aaSAndroid Build Coastguard Worker success = identity.getMinMaxScales(scales);
201*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, success && 1 == scales[0] && 1 == scales[1]);
202*c8dee2aaSAndroid Build Coastguard Worker
203*c8dee2aaSAndroid Build Coastguard Worker SkMatrix scale;
204*c8dee2aaSAndroid Build Coastguard Worker scale.setScale(2, 4);
205*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 2 == scale.getMinScale());
206*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 4 == scale.getMaxScale());
207*c8dee2aaSAndroid Build Coastguard Worker success = scale.getMinMaxScales(scales);
208*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, success && 2 == scales[0] && 4 == scales[1]);
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker SkMatrix rot90Scale;
211*c8dee2aaSAndroid Build Coastguard Worker rot90Scale.setRotate(90).postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
212*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinScale());
213*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxScale());
214*c8dee2aaSAndroid Build Coastguard Worker success = rot90Scale.getMinMaxScales(scales);
215*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, success && SK_Scalar1 / 4 == scales[0] && SK_Scalar1 / 2 == scales[1]);
216*c8dee2aaSAndroid Build Coastguard Worker
217*c8dee2aaSAndroid Build Coastguard Worker SkMatrix rotate;
218*c8dee2aaSAndroid Build Coastguard Worker rotate.setRotate(128);
219*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(1, rotate.getMinScale(), SK_ScalarNearlyZero));
220*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(1, rotate.getMaxScale(), SK_ScalarNearlyZero));
221*c8dee2aaSAndroid Build Coastguard Worker success = rotate.getMinMaxScales(scales);
222*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, success);
223*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(1, scales[0], SK_ScalarNearlyZero));
224*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(1, scales[1], SK_ScalarNearlyZero));
225*c8dee2aaSAndroid Build Coastguard Worker
226*c8dee2aaSAndroid Build Coastguard Worker SkMatrix translate;
227*c8dee2aaSAndroid Build Coastguard Worker translate.setTranslate(10, -5);
228*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 1 == translate.getMinScale());
229*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, 1 == translate.getMaxScale());
230*c8dee2aaSAndroid Build Coastguard Worker success = translate.getMinMaxScales(scales);
231*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, success && 1 == scales[0] && 1 == scales[1]);
232*c8dee2aaSAndroid Build Coastguard Worker
233*c8dee2aaSAndroid Build Coastguard Worker SkMatrix perspX;
234*c8dee2aaSAndroid Build Coastguard Worker perspX.reset().setPerspX(SK_Scalar1 / 1000);
235*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, -1 == perspX.getMinScale());
236*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, -1 == perspX.getMaxScale());
237*c8dee2aaSAndroid Build Coastguard Worker success = perspX.getMinMaxScales(scales);
238*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !success);
239*c8dee2aaSAndroid Build Coastguard Worker
240*c8dee2aaSAndroid Build Coastguard Worker // skbug.com/4718
241*c8dee2aaSAndroid Build Coastguard Worker SkMatrix big;
242*c8dee2aaSAndroid Build Coastguard Worker big.setAll(2.39394089e+36f, 8.85347779e+36f, 9.26526204e+36f,
243*c8dee2aaSAndroid Build Coastguard Worker 3.9159619e+36f, 1.44823453e+37f, 1.51559342e+37f,
244*c8dee2aaSAndroid Build Coastguard Worker 0.f, 0.f, 1.f);
245*c8dee2aaSAndroid Build Coastguard Worker success = big.getMinMaxScales(scales);
246*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !success);
247*c8dee2aaSAndroid Build Coastguard Worker
248*c8dee2aaSAndroid Build Coastguard Worker // skbug.com/4718
249*c8dee2aaSAndroid Build Coastguard Worker SkMatrix givingNegativeNearlyZeros;
250*c8dee2aaSAndroid Build Coastguard Worker givingNegativeNearlyZeros.setAll(0.00436534f, 0.114138f, 0.37141f,
251*c8dee2aaSAndroid Build Coastguard Worker 0.00358857f, 0.0936228f, -0.0174198f,
252*c8dee2aaSAndroid Build Coastguard Worker 0.f, 0.f, 1.f);
253*c8dee2aaSAndroid Build Coastguard Worker success = givingNegativeNearlyZeros.getMinMaxScales(scales);
254*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, success && 0 == scales[0]);
255*c8dee2aaSAndroid Build Coastguard Worker
256*c8dee2aaSAndroid Build Coastguard Worker SkMatrix perspY;
257*c8dee2aaSAndroid Build Coastguard Worker perspY.reset().setPerspY(-SK_Scalar1 / 500);
258*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, -1 == perspY.getMinScale());
259*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, -1 == perspY.getMaxScale());
260*c8dee2aaSAndroid Build Coastguard Worker scales[0] = -5;
261*c8dee2aaSAndroid Build Coastguard Worker scales[1] = -5;
262*c8dee2aaSAndroid Build Coastguard Worker success = perspY.getMinMaxScales(scales);
263*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !success && -5 == scales[0] && -5 == scales[1]);
264*c8dee2aaSAndroid Build Coastguard Worker
265*c8dee2aaSAndroid Build Coastguard Worker SkMatrix baseMats[] = {scale, rot90Scale, rotate,
266*c8dee2aaSAndroid Build Coastguard Worker translate, perspX, perspY};
267*c8dee2aaSAndroid Build Coastguard Worker SkMatrix mats[2*std::size(baseMats)];
268*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(baseMats); ++i) {
269*c8dee2aaSAndroid Build Coastguard Worker mats[i] = baseMats[i];
270*c8dee2aaSAndroid Build Coastguard Worker bool invertible = mats[i].invert(&mats[i + std::size(baseMats)]);
271*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, invertible);
272*c8dee2aaSAndroid Build Coastguard Worker }
273*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand;
274*c8dee2aaSAndroid Build Coastguard Worker for (int m = 0; m < 1000; ++m) {
275*c8dee2aaSAndroid Build Coastguard Worker SkMatrix mat;
276*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
277*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
278*c8dee2aaSAndroid Build Coastguard Worker int x = rand.nextU() % std::size(mats);
279*c8dee2aaSAndroid Build Coastguard Worker mat.postConcat(mats[x]);
280*c8dee2aaSAndroid Build Coastguard Worker }
281*c8dee2aaSAndroid Build Coastguard Worker
282*c8dee2aaSAndroid Build Coastguard Worker SkScalar minScale = mat.getMinScale();
283*c8dee2aaSAndroid Build Coastguard Worker SkScalar maxScale = mat.getMaxScale();
284*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, (minScale < 0) == (maxScale < 0));
285*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, (maxScale < 0) == mat.hasPerspective());
286*c8dee2aaSAndroid Build Coastguard Worker
287*c8dee2aaSAndroid Build Coastguard Worker success = mat.getMinMaxScales(scales);
288*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, success == !mat.hasPerspective());
289*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !success || (scales[0] == minScale && scales[1] == maxScale));
290*c8dee2aaSAndroid Build Coastguard Worker
291*c8dee2aaSAndroid Build Coastguard Worker if (mat.hasPerspective()) {
292*c8dee2aaSAndroid Build Coastguard Worker m -= 1; // try another non-persp matrix
293*c8dee2aaSAndroid Build Coastguard Worker continue;
294*c8dee2aaSAndroid Build Coastguard Worker }
295*c8dee2aaSAndroid Build Coastguard Worker
296*c8dee2aaSAndroid Build Coastguard Worker // test a bunch of vectors. All should be scaled by between minScale and maxScale
297*c8dee2aaSAndroid Build Coastguard Worker // (modulo some error) and we should find a vector that is scaled by almost each.
298*c8dee2aaSAndroid Build Coastguard Worker static const SkScalar gVectorScaleTol = (105 * SK_Scalar1) / 100;
299*c8dee2aaSAndroid Build Coastguard Worker static const SkScalar gCloseScaleTol = (97 * SK_Scalar1) / 100;
300*c8dee2aaSAndroid Build Coastguard Worker SkScalar max = 0, min = SK_ScalarMax;
301*c8dee2aaSAndroid Build Coastguard Worker SkVector vectors[1000];
302*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(vectors); ++i) {
303*c8dee2aaSAndroid Build Coastguard Worker vectors[i].fX = rand.nextSScalar1();
304*c8dee2aaSAndroid Build Coastguard Worker vectors[i].fY = rand.nextSScalar1();
305*c8dee2aaSAndroid Build Coastguard Worker if (!vectors[i].normalize()) {
306*c8dee2aaSAndroid Build Coastguard Worker i -= 1;
307*c8dee2aaSAndroid Build Coastguard Worker continue;
308*c8dee2aaSAndroid Build Coastguard Worker }
309*c8dee2aaSAndroid Build Coastguard Worker }
310*c8dee2aaSAndroid Build Coastguard Worker mat.mapVectors(vectors, std::size(vectors));
311*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(vectors); ++i) {
312*c8dee2aaSAndroid Build Coastguard Worker SkScalar d = vectors[i].length();
313*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, d / maxScale < gVectorScaleTol);
314*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, minScale / d < gVectorScaleTol);
315*c8dee2aaSAndroid Build Coastguard Worker if (max < d) {
316*c8dee2aaSAndroid Build Coastguard Worker max = d;
317*c8dee2aaSAndroid Build Coastguard Worker }
318*c8dee2aaSAndroid Build Coastguard Worker if (min > d) {
319*c8dee2aaSAndroid Build Coastguard Worker min = d;
320*c8dee2aaSAndroid Build Coastguard Worker }
321*c8dee2aaSAndroid Build Coastguard Worker }
322*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, max / maxScale >= gCloseScaleTol);
323*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, minScale / min >= gCloseScaleTol);
324*c8dee2aaSAndroid Build Coastguard Worker }
325*c8dee2aaSAndroid Build Coastguard Worker }
326*c8dee2aaSAndroid Build Coastguard Worker
test_matrix_preserve_shape(skiatest::Reporter * reporter)327*c8dee2aaSAndroid Build Coastguard Worker static void test_matrix_preserve_shape(skiatest::Reporter* reporter) {
328*c8dee2aaSAndroid Build Coastguard Worker SkMatrix mat;
329*c8dee2aaSAndroid Build Coastguard Worker
330*c8dee2aaSAndroid Build Coastguard Worker // identity
331*c8dee2aaSAndroid Build Coastguard Worker mat.setIdentity();
332*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
333*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
334*c8dee2aaSAndroid Build Coastguard Worker
335*c8dee2aaSAndroid Build Coastguard Worker // translation only
336*c8dee2aaSAndroid Build Coastguard Worker mat.setTranslate(100, 100);
337*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
338*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
339*c8dee2aaSAndroid Build Coastguard Worker
340*c8dee2aaSAndroid Build Coastguard Worker // scale with same size
341*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(15, 15);
342*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
343*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
344*c8dee2aaSAndroid Build Coastguard Worker
345*c8dee2aaSAndroid Build Coastguard Worker // scale with one negative
346*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(-15, 15);
347*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
348*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
349*c8dee2aaSAndroid Build Coastguard Worker
350*c8dee2aaSAndroid Build Coastguard Worker // scale with different size
351*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(15, 20);
352*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
353*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
354*c8dee2aaSAndroid Build Coastguard Worker
355*c8dee2aaSAndroid Build Coastguard Worker // scale with same size at a pivot point
356*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(15, 15, 2, 2);
357*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
358*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
359*c8dee2aaSAndroid Build Coastguard Worker
360*c8dee2aaSAndroid Build Coastguard Worker // scale with different size at a pivot point
361*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(15, 20, 2, 2);
362*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
363*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
364*c8dee2aaSAndroid Build Coastguard Worker
365*c8dee2aaSAndroid Build Coastguard Worker // skew with same size
366*c8dee2aaSAndroid Build Coastguard Worker mat.setSkew(15, 15);
367*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
368*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
369*c8dee2aaSAndroid Build Coastguard Worker
370*c8dee2aaSAndroid Build Coastguard Worker // skew with different size
371*c8dee2aaSAndroid Build Coastguard Worker mat.setSkew(15, 20);
372*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
373*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
374*c8dee2aaSAndroid Build Coastguard Worker
375*c8dee2aaSAndroid Build Coastguard Worker // skew with same size at a pivot point
376*c8dee2aaSAndroid Build Coastguard Worker mat.setSkew(15, 15, 2, 2);
377*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
378*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
379*c8dee2aaSAndroid Build Coastguard Worker
380*c8dee2aaSAndroid Build Coastguard Worker // skew with different size at a pivot point
381*c8dee2aaSAndroid Build Coastguard Worker mat.setSkew(15, 20, 2, 2);
382*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
383*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
384*c8dee2aaSAndroid Build Coastguard Worker
385*c8dee2aaSAndroid Build Coastguard Worker // perspective x
386*c8dee2aaSAndroid Build Coastguard Worker mat.reset().setPerspX(SK_Scalar1 / 2);
387*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
388*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
389*c8dee2aaSAndroid Build Coastguard Worker
390*c8dee2aaSAndroid Build Coastguard Worker // perspective y
391*c8dee2aaSAndroid Build Coastguard Worker mat.reset().setPerspY(SK_Scalar1 / 2);
392*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
393*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
394*c8dee2aaSAndroid Build Coastguard Worker
395*c8dee2aaSAndroid Build Coastguard Worker // rotate
396*c8dee2aaSAndroid Build Coastguard Worker for (int angle = 0; angle < 360; ++angle) {
397*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(SkIntToScalar(angle));
398*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
399*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
400*c8dee2aaSAndroid Build Coastguard Worker }
401*c8dee2aaSAndroid Build Coastguard Worker
402*c8dee2aaSAndroid Build Coastguard Worker // see if there are any accumulated precision issues
403*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
404*c8dee2aaSAndroid Build Coastguard Worker for (int i = 1; i < 360; i++) {
405*c8dee2aaSAndroid Build Coastguard Worker mat.postRotate(1);
406*c8dee2aaSAndroid Build Coastguard Worker }
407*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
408*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
409*c8dee2aaSAndroid Build Coastguard Worker
410*c8dee2aaSAndroid Build Coastguard Worker // rotate + translate
411*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(30).postTranslate(10, 20);
412*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
413*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
414*c8dee2aaSAndroid Build Coastguard Worker
415*c8dee2aaSAndroid Build Coastguard Worker // rotate + uniform scale
416*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(30).postScale(2, 2);
417*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
418*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
419*c8dee2aaSAndroid Build Coastguard Worker
420*c8dee2aaSAndroid Build Coastguard Worker // rotate + non-uniform scale
421*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(30).postScale(3, 2);
422*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
423*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
424*c8dee2aaSAndroid Build Coastguard Worker
425*c8dee2aaSAndroid Build Coastguard Worker // non-uniform scale + rotate
426*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(3, 2).postRotate(30);
427*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
428*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
429*c8dee2aaSAndroid Build Coastguard Worker
430*c8dee2aaSAndroid Build Coastguard Worker // all zero
431*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
432*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
433*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
434*c8dee2aaSAndroid Build Coastguard Worker
435*c8dee2aaSAndroid Build Coastguard Worker // all zero except perspective
436*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 1);
437*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.isSimilarity());
438*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
439*c8dee2aaSAndroid Build Coastguard Worker
440*c8dee2aaSAndroid Build Coastguard Worker // scales zero, only skews (rotation)
441*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(0, 1, 0,
442*c8dee2aaSAndroid Build Coastguard Worker -1, 0, 0,
443*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 1);
444*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
445*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
446*c8dee2aaSAndroid Build Coastguard Worker
447*c8dee2aaSAndroid Build Coastguard Worker // scales zero, only skews (reflection)
448*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(0, 1, 0,
449*c8dee2aaSAndroid Build Coastguard Worker 1, 0, 0,
450*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 1);
451*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isSimilarity());
452*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.preservesRightAngles());
453*c8dee2aaSAndroid Build Coastguard Worker }
454*c8dee2aaSAndroid Build Coastguard Worker
455*c8dee2aaSAndroid Build Coastguard Worker // For test_matrix_decomposition, below.
scalar_nearly_equal_relative(SkScalar a,SkScalar b,SkScalar tolerance=SK_ScalarNearlyZero)456*c8dee2aaSAndroid Build Coastguard Worker static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
457*c8dee2aaSAndroid Build Coastguard Worker SkScalar tolerance = SK_ScalarNearlyZero) {
458*c8dee2aaSAndroid Build Coastguard Worker // from Bruce Dawson
459*c8dee2aaSAndroid Build Coastguard Worker // absolute check
460*c8dee2aaSAndroid Build Coastguard Worker SkScalar diff = SkScalarAbs(a - b);
461*c8dee2aaSAndroid Build Coastguard Worker if (diff < tolerance) {
462*c8dee2aaSAndroid Build Coastguard Worker return true;
463*c8dee2aaSAndroid Build Coastguard Worker }
464*c8dee2aaSAndroid Build Coastguard Worker
465*c8dee2aaSAndroid Build Coastguard Worker // relative check
466*c8dee2aaSAndroid Build Coastguard Worker a = SkScalarAbs(a);
467*c8dee2aaSAndroid Build Coastguard Worker b = SkScalarAbs(b);
468*c8dee2aaSAndroid Build Coastguard Worker SkScalar largest = (b > a) ? b : a;
469*c8dee2aaSAndroid Build Coastguard Worker
470*c8dee2aaSAndroid Build Coastguard Worker if (diff <= largest*tolerance) {
471*c8dee2aaSAndroid Build Coastguard Worker return true;
472*c8dee2aaSAndroid Build Coastguard Worker }
473*c8dee2aaSAndroid Build Coastguard Worker
474*c8dee2aaSAndroid Build Coastguard Worker return false;
475*c8dee2aaSAndroid Build Coastguard Worker }
476*c8dee2aaSAndroid Build Coastguard Worker
check_matrix_recomposition(const SkMatrix & mat,const SkPoint & rotation1,const SkPoint & scale,const SkPoint & rotation2)477*c8dee2aaSAndroid Build Coastguard Worker static bool check_matrix_recomposition(const SkMatrix& mat,
478*c8dee2aaSAndroid Build Coastguard Worker const SkPoint& rotation1,
479*c8dee2aaSAndroid Build Coastguard Worker const SkPoint& scale,
480*c8dee2aaSAndroid Build Coastguard Worker const SkPoint& rotation2) {
481*c8dee2aaSAndroid Build Coastguard Worker SkScalar c1 = rotation1.fX;
482*c8dee2aaSAndroid Build Coastguard Worker SkScalar s1 = rotation1.fY;
483*c8dee2aaSAndroid Build Coastguard Worker SkScalar scaleX = scale.fX;
484*c8dee2aaSAndroid Build Coastguard Worker SkScalar scaleY = scale.fY;
485*c8dee2aaSAndroid Build Coastguard Worker SkScalar c2 = rotation2.fX;
486*c8dee2aaSAndroid Build Coastguard Worker SkScalar s2 = rotation2.fY;
487*c8dee2aaSAndroid Build Coastguard Worker
488*c8dee2aaSAndroid Build Coastguard Worker // We do a relative check here because large scale factors cause problems with an absolute check
489*c8dee2aaSAndroid Build Coastguard Worker bool result = scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
490*c8dee2aaSAndroid Build Coastguard Worker scaleX*c1*c2 - scaleY*s1*s2) &&
491*c8dee2aaSAndroid Build Coastguard Worker scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
492*c8dee2aaSAndroid Build Coastguard Worker -scaleX*s1*c2 - scaleY*c1*s2) &&
493*c8dee2aaSAndroid Build Coastguard Worker scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
494*c8dee2aaSAndroid Build Coastguard Worker scaleX*c1*s2 + scaleY*s1*c2) &&
495*c8dee2aaSAndroid Build Coastguard Worker scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
496*c8dee2aaSAndroid Build Coastguard Worker -scaleX*s1*s2 + scaleY*c1*c2);
497*c8dee2aaSAndroid Build Coastguard Worker return result;
498*c8dee2aaSAndroid Build Coastguard Worker }
499*c8dee2aaSAndroid Build Coastguard Worker
test_matrix_decomposition(skiatest::Reporter * reporter)500*c8dee2aaSAndroid Build Coastguard Worker static void test_matrix_decomposition(skiatest::Reporter* reporter) {
501*c8dee2aaSAndroid Build Coastguard Worker SkMatrix mat;
502*c8dee2aaSAndroid Build Coastguard Worker SkPoint rotation1, scale, rotation2;
503*c8dee2aaSAndroid Build Coastguard Worker
504*c8dee2aaSAndroid Build Coastguard Worker const float kRotation0 = 15.5f;
505*c8dee2aaSAndroid Build Coastguard Worker const float kRotation1 = -50.f;
506*c8dee2aaSAndroid Build Coastguard Worker const float kScale0 = 5000.f;
507*c8dee2aaSAndroid Build Coastguard Worker const float kScale1 = 0.001f;
508*c8dee2aaSAndroid Build Coastguard Worker
509*c8dee2aaSAndroid Build Coastguard Worker // identity
510*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
511*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
512*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
513*c8dee2aaSAndroid Build Coastguard Worker // make sure it doesn't crash if we pass in NULLs
514*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, nullptr, nullptr, nullptr));
515*c8dee2aaSAndroid Build Coastguard Worker
516*c8dee2aaSAndroid Build Coastguard Worker // rotation only
517*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(kRotation0);
518*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
519*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
520*c8dee2aaSAndroid Build Coastguard Worker
521*c8dee2aaSAndroid Build Coastguard Worker // uniform scale only
522*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(kScale0, kScale0);
523*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
524*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
525*c8dee2aaSAndroid Build Coastguard Worker
526*c8dee2aaSAndroid Build Coastguard Worker // anisotropic scale only
527*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(kScale1, kScale0);
528*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
529*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
530*c8dee2aaSAndroid Build Coastguard Worker
531*c8dee2aaSAndroid Build Coastguard Worker // rotation then uniform scale
532*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(kRotation1).postScale(kScale0, kScale0);
533*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
534*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
535*c8dee2aaSAndroid Build Coastguard Worker
536*c8dee2aaSAndroid Build Coastguard Worker // uniform scale then rotation
537*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(kScale0, kScale0).postRotate(kRotation1);
538*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
539*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
540*c8dee2aaSAndroid Build Coastguard Worker
541*c8dee2aaSAndroid Build Coastguard Worker // rotation then uniform scale+reflection
542*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(kRotation0).postScale(kScale1, -kScale1);
543*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
544*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
545*c8dee2aaSAndroid Build Coastguard Worker
546*c8dee2aaSAndroid Build Coastguard Worker // uniform scale+reflection, then rotate
547*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(kScale0, -kScale0).postRotate(kRotation1);
548*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
549*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
550*c8dee2aaSAndroid Build Coastguard Worker
551*c8dee2aaSAndroid Build Coastguard Worker // rotation then anisotropic scale
552*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(kRotation1).postScale(kScale1, kScale0);
553*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
554*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
555*c8dee2aaSAndroid Build Coastguard Worker
556*c8dee2aaSAndroid Build Coastguard Worker // rotation then anisotropic scale
557*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(90).postScale(kScale1, kScale0);
558*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
559*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
560*c8dee2aaSAndroid Build Coastguard Worker
561*c8dee2aaSAndroid Build Coastguard Worker // anisotropic scale then rotation
562*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(kScale1, kScale0).postRotate(kRotation0);
563*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
564*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
565*c8dee2aaSAndroid Build Coastguard Worker
566*c8dee2aaSAndroid Build Coastguard Worker // anisotropic scale then rotation
567*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(kScale1, kScale0).postRotate(90);
568*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
569*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
570*c8dee2aaSAndroid Build Coastguard Worker
571*c8dee2aaSAndroid Build Coastguard Worker // rotation, uniform scale, then different rotation
572*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(kRotation1).postScale(kScale0, kScale0).postRotate(kRotation0);
573*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
574*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
575*c8dee2aaSAndroid Build Coastguard Worker
576*c8dee2aaSAndroid Build Coastguard Worker // rotation, anisotropic scale, then different rotation
577*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(kRotation0).postScale(kScale1, kScale0).postRotate(kRotation1);
578*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
579*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
580*c8dee2aaSAndroid Build Coastguard Worker
581*c8dee2aaSAndroid Build Coastguard Worker // rotation, anisotropic scale + reflection, then different rotation
582*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(kRotation0).postScale(-kScale1, kScale0).postRotate(kRotation1);
583*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
584*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
585*c8dee2aaSAndroid Build Coastguard Worker
586*c8dee2aaSAndroid Build Coastguard Worker // try some random matrices
587*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand;
588*c8dee2aaSAndroid Build Coastguard Worker for (int m = 0; m < 1000; ++m) {
589*c8dee2aaSAndroid Build Coastguard Worker SkScalar rot0 = rand.nextRangeF(-180, 180);
590*c8dee2aaSAndroid Build Coastguard Worker SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
591*c8dee2aaSAndroid Build Coastguard Worker SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
592*c8dee2aaSAndroid Build Coastguard Worker SkScalar rot1 = rand.nextRangeF(-180, 180);
593*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(rot0).postScale(sx, sy).postRotate(rot1);
594*c8dee2aaSAndroid Build Coastguard Worker
595*c8dee2aaSAndroid Build Coastguard Worker if (SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)) {
596*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
597*c8dee2aaSAndroid Build Coastguard Worker } else {
598*c8dee2aaSAndroid Build Coastguard Worker // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
599*c8dee2aaSAndroid Build Coastguard Worker SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
600*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
601*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
602*c8dee2aaSAndroid Build Coastguard Worker }
603*c8dee2aaSAndroid Build Coastguard Worker }
604*c8dee2aaSAndroid Build Coastguard Worker
605*c8dee2aaSAndroid Build Coastguard Worker // translation shouldn't affect this
606*c8dee2aaSAndroid Build Coastguard Worker mat.postTranslate(-1000.f, 1000.f);
607*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
608*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
609*c8dee2aaSAndroid Build Coastguard Worker
610*c8dee2aaSAndroid Build Coastguard Worker // perspective shouldn't affect this
611*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMPersp0] = 12.f;
612*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMPersp1] = 4.f;
613*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMPersp2] = 1872.f;
614*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
615*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
616*c8dee2aaSAndroid Build Coastguard Worker
617*c8dee2aaSAndroid Build Coastguard Worker // degenerate matrices
618*c8dee2aaSAndroid Build Coastguard Worker // mostly zero entries
619*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
620*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMScaleX] = 0.f;
621*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
622*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
623*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMScaleY] = 0.f;
624*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
625*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
626*c8dee2aaSAndroid Build Coastguard Worker // linearly dependent entries
627*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMScaleX] = 1.f;
628*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMSkewX] = 2.f;
629*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMSkewY] = 4.f;
630*c8dee2aaSAndroid Build Coastguard Worker mat[SkMatrix::kMScaleY] = 8.f;
631*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
632*c8dee2aaSAndroid Build Coastguard Worker }
633*c8dee2aaSAndroid Build Coastguard Worker
634*c8dee2aaSAndroid Build Coastguard Worker // For test_matrix_homogeneous, below.
point3_array_nearly_equal_relative(const SkPoint3 a[],const SkPoint3 b[],int count)635*c8dee2aaSAndroid Build Coastguard Worker static bool point3_array_nearly_equal_relative(const SkPoint3 a[], const SkPoint3 b[], int count) {
636*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < count; ++i) {
637*c8dee2aaSAndroid Build Coastguard Worker if (!scalar_nearly_equal_relative(a[i].fX, b[i].fX)) {
638*c8dee2aaSAndroid Build Coastguard Worker return false;
639*c8dee2aaSAndroid Build Coastguard Worker }
640*c8dee2aaSAndroid Build Coastguard Worker if (!scalar_nearly_equal_relative(a[i].fY, b[i].fY)) {
641*c8dee2aaSAndroid Build Coastguard Worker return false;
642*c8dee2aaSAndroid Build Coastguard Worker }
643*c8dee2aaSAndroid Build Coastguard Worker if (!scalar_nearly_equal_relative(a[i].fZ, b[i].fZ)) {
644*c8dee2aaSAndroid Build Coastguard Worker return false;
645*c8dee2aaSAndroid Build Coastguard Worker }
646*c8dee2aaSAndroid Build Coastguard Worker }
647*c8dee2aaSAndroid Build Coastguard Worker return true;
648*c8dee2aaSAndroid Build Coastguard Worker }
649*c8dee2aaSAndroid Build Coastguard Worker
650*c8dee2aaSAndroid Build Coastguard Worker // For test_matrix_homogeneous, below.
651*c8dee2aaSAndroid Build Coastguard Worker // Maps a single triple in src using m and compares results to those in dst
naive_homogeneous_mapping(const SkMatrix & m,const SkPoint3 & src,const SkPoint3 & dst)652*c8dee2aaSAndroid Build Coastguard Worker static bool naive_homogeneous_mapping(const SkMatrix& m, const SkPoint3& src,
653*c8dee2aaSAndroid Build Coastguard Worker const SkPoint3& dst) {
654*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 res;
655*c8dee2aaSAndroid Build Coastguard Worker SkScalar ms[9] = {m[0], m[1], m[2],
656*c8dee2aaSAndroid Build Coastguard Worker m[3], m[4], m[5],
657*c8dee2aaSAndroid Build Coastguard Worker m[6], m[7], m[8]};
658*c8dee2aaSAndroid Build Coastguard Worker res.fX = src.fX * ms[0] + src.fY * ms[1] + src.fZ * ms[2];
659*c8dee2aaSAndroid Build Coastguard Worker res.fY = src.fX * ms[3] + src.fY * ms[4] + src.fZ * ms[5];
660*c8dee2aaSAndroid Build Coastguard Worker res.fZ = src.fX * ms[6] + src.fY * ms[7] + src.fZ * ms[8];
661*c8dee2aaSAndroid Build Coastguard Worker return point3_array_nearly_equal_relative(&res, &dst, 1);
662*c8dee2aaSAndroid Build Coastguard Worker }
663*c8dee2aaSAndroid Build Coastguard Worker
test_matrix_homogeneous(skiatest::Reporter * reporter)664*c8dee2aaSAndroid Build Coastguard Worker static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
665*c8dee2aaSAndroid Build Coastguard Worker SkMatrix mat;
666*c8dee2aaSAndroid Build Coastguard Worker
667*c8dee2aaSAndroid Build Coastguard Worker const float kRotation0 = 15.5f;
668*c8dee2aaSAndroid Build Coastguard Worker const float kRotation1 = -50.f;
669*c8dee2aaSAndroid Build Coastguard Worker const float kScale0 = 5000.f;
670*c8dee2aaSAndroid Build Coastguard Worker
671*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_GOOGLE3)
672*c8dee2aaSAndroid Build Coastguard Worker // Stack frame size is limited in SK_BUILD_FOR_GOOGLE3.
673*c8dee2aaSAndroid Build Coastguard Worker const int kTripleCount = 100;
674*c8dee2aaSAndroid Build Coastguard Worker const int kMatrixCount = 100;
675*c8dee2aaSAndroid Build Coastguard Worker #else
676*c8dee2aaSAndroid Build Coastguard Worker const int kTripleCount = 1000;
677*c8dee2aaSAndroid Build Coastguard Worker const int kMatrixCount = 1000;
678*c8dee2aaSAndroid Build Coastguard Worker #endif
679*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand;
680*c8dee2aaSAndroid Build Coastguard Worker
681*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 randTriples[kTripleCount];
682*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kTripleCount; ++i) {
683*c8dee2aaSAndroid Build Coastguard Worker randTriples[i].fX = rand.nextRangeF(-3000.f, 3000.f);
684*c8dee2aaSAndroid Build Coastguard Worker randTriples[i].fY = rand.nextRangeF(-3000.f, 3000.f);
685*c8dee2aaSAndroid Build Coastguard Worker randTriples[i].fZ = rand.nextRangeF(-3000.f, 3000.f);
686*c8dee2aaSAndroid Build Coastguard Worker }
687*c8dee2aaSAndroid Build Coastguard Worker
688*c8dee2aaSAndroid Build Coastguard Worker SkMatrix mats[kMatrixCount];
689*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kMatrixCount; ++i) {
690*c8dee2aaSAndroid Build Coastguard Worker for (int j = 0; j < 9; ++j) {
691*c8dee2aaSAndroid Build Coastguard Worker mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
692*c8dee2aaSAndroid Build Coastguard Worker }
693*c8dee2aaSAndroid Build Coastguard Worker }
694*c8dee2aaSAndroid Build Coastguard Worker
695*c8dee2aaSAndroid Build Coastguard Worker // identity
696*c8dee2aaSAndroid Build Coastguard Worker {
697*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
698*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 dst[kTripleCount];
699*c8dee2aaSAndroid Build Coastguard Worker mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
700*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, point3_array_nearly_equal_relative(randTriples, dst, kTripleCount));
701*c8dee2aaSAndroid Build Coastguard Worker }
702*c8dee2aaSAndroid Build Coastguard Worker
703*c8dee2aaSAndroid Build Coastguard Worker const SkPoint3 zeros = {0.f, 0.f, 0.f};
704*c8dee2aaSAndroid Build Coastguard Worker // zero matrix
705*c8dee2aaSAndroid Build Coastguard Worker {
706*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
707*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 dst[kTripleCount];
708*c8dee2aaSAndroid Build Coastguard Worker mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
709*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kTripleCount; ++i) {
710*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, point3_array_nearly_equal_relative(&dst[i], &zeros, 1));
711*c8dee2aaSAndroid Build Coastguard Worker }
712*c8dee2aaSAndroid Build Coastguard Worker }
713*c8dee2aaSAndroid Build Coastguard Worker
714*c8dee2aaSAndroid Build Coastguard Worker // zero point
715*c8dee2aaSAndroid Build Coastguard Worker {
716*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kMatrixCount; ++i) {
717*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 dst;
718*c8dee2aaSAndroid Build Coastguard Worker mats[i].mapHomogeneousPoints(&dst, &zeros, 1);
719*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, point3_array_nearly_equal_relative(&dst, &zeros, 1));
720*c8dee2aaSAndroid Build Coastguard Worker }
721*c8dee2aaSAndroid Build Coastguard Worker }
722*c8dee2aaSAndroid Build Coastguard Worker
723*c8dee2aaSAndroid Build Coastguard Worker // doesn't crash with null dst, src, count == 0
724*c8dee2aaSAndroid Build Coastguard Worker {
725*c8dee2aaSAndroid Build Coastguard Worker mats[0].mapHomogeneousPoints(nullptr, (const SkPoint3*)nullptr, 0);
726*c8dee2aaSAndroid Build Coastguard Worker }
727*c8dee2aaSAndroid Build Coastguard Worker
728*c8dee2aaSAndroid Build Coastguard Worker // uniform scale of point
729*c8dee2aaSAndroid Build Coastguard Worker {
730*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(kScale0, kScale0);
731*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 dst;
732*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 src = {randTriples[0].fX, randTriples[0].fY, 1.f};
733*c8dee2aaSAndroid Build Coastguard Worker SkPoint pnt;
734*c8dee2aaSAndroid Build Coastguard Worker pnt.set(src.fX, src.fY);
735*c8dee2aaSAndroid Build Coastguard Worker mat.mapHomogeneousPoints(&dst, &src, 1);
736*c8dee2aaSAndroid Build Coastguard Worker mat.mapPoints(&pnt, &pnt, 1);
737*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fX, pnt.fX));
738*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fY, pnt.fY));
739*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fZ, 1));
740*c8dee2aaSAndroid Build Coastguard Worker }
741*c8dee2aaSAndroid Build Coastguard Worker
742*c8dee2aaSAndroid Build Coastguard Worker // rotation of point
743*c8dee2aaSAndroid Build Coastguard Worker {
744*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(kRotation0);
745*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 dst;
746*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 src = {randTriples[0].fX, randTriples[0].fY, 1.f};
747*c8dee2aaSAndroid Build Coastguard Worker SkPoint pnt;
748*c8dee2aaSAndroid Build Coastguard Worker pnt.set(src.fX, src.fY);
749*c8dee2aaSAndroid Build Coastguard Worker mat.mapHomogeneousPoints(&dst, &src, 1);
750*c8dee2aaSAndroid Build Coastguard Worker mat.mapPoints(&pnt, &pnt, 1);
751*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fX, pnt.fX));
752*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fY, pnt.fY));
753*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fZ, 1));
754*c8dee2aaSAndroid Build Coastguard Worker }
755*c8dee2aaSAndroid Build Coastguard Worker
756*c8dee2aaSAndroid Build Coastguard Worker // rotation, scale, rotation of point
757*c8dee2aaSAndroid Build Coastguard Worker {
758*c8dee2aaSAndroid Build Coastguard Worker mat.setRotate(kRotation1);
759*c8dee2aaSAndroid Build Coastguard Worker mat.postScale(kScale0, kScale0);
760*c8dee2aaSAndroid Build Coastguard Worker mat.postRotate(kRotation0);
761*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 dst;
762*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 src = {randTriples[0].fX, randTriples[0].fY, 1.f};
763*c8dee2aaSAndroid Build Coastguard Worker SkPoint pnt;
764*c8dee2aaSAndroid Build Coastguard Worker pnt.set(src.fX, src.fY);
765*c8dee2aaSAndroid Build Coastguard Worker mat.mapHomogeneousPoints(&dst, &src, 1);
766*c8dee2aaSAndroid Build Coastguard Worker mat.mapPoints(&pnt, &pnt, 1);
767*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fX, pnt.fX));
768*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fY, pnt.fY));
769*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.fZ, 1));
770*c8dee2aaSAndroid Build Coastguard Worker }
771*c8dee2aaSAndroid Build Coastguard Worker
772*c8dee2aaSAndroid Build Coastguard Worker // compare with naive approach
773*c8dee2aaSAndroid Build Coastguard Worker {
774*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kMatrixCount; ++i) {
775*c8dee2aaSAndroid Build Coastguard Worker for (int j = 0; j < kTripleCount; ++j) {
776*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 dst;
777*c8dee2aaSAndroid Build Coastguard Worker mats[i].mapHomogeneousPoints(&dst, &randTriples[j], 1);
778*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], randTriples[j], dst));
779*c8dee2aaSAndroid Build Coastguard Worker }
780*c8dee2aaSAndroid Build Coastguard Worker }
781*c8dee2aaSAndroid Build Coastguard Worker }
782*c8dee2aaSAndroid Build Coastguard Worker
783*c8dee2aaSAndroid Build Coastguard Worker }
784*c8dee2aaSAndroid Build Coastguard Worker
check_decompScale(const SkMatrix & original)785*c8dee2aaSAndroid Build Coastguard Worker static bool check_decompScale(const SkMatrix& original) {
786*c8dee2aaSAndroid Build Coastguard Worker SkSize scale;
787*c8dee2aaSAndroid Build Coastguard Worker SkMatrix remaining;
788*c8dee2aaSAndroid Build Coastguard Worker
789*c8dee2aaSAndroid Build Coastguard Worker if (!original.decomposeScale(&scale, &remaining)) {
790*c8dee2aaSAndroid Build Coastguard Worker return false;
791*c8dee2aaSAndroid Build Coastguard Worker }
792*c8dee2aaSAndroid Build Coastguard Worker if (scale.width() <= 0 || scale.height() <= 0) {
793*c8dee2aaSAndroid Build Coastguard Worker return false;
794*c8dee2aaSAndroid Build Coastguard Worker }
795*c8dee2aaSAndroid Build Coastguard Worker
796*c8dee2aaSAndroid Build Coastguard Worker // First ensure that the decomposition reconstitutes back to the original
797*c8dee2aaSAndroid Build Coastguard Worker {
798*c8dee2aaSAndroid Build Coastguard Worker SkMatrix reconstituted = remaining;
799*c8dee2aaSAndroid Build Coastguard Worker
800*c8dee2aaSAndroid Build Coastguard Worker reconstituted.preScale(scale.width(), scale.height());
801*c8dee2aaSAndroid Build Coastguard Worker if (!nearly_equal(original, reconstituted)) {
802*c8dee2aaSAndroid Build Coastguard Worker return false;
803*c8dee2aaSAndroid Build Coastguard Worker }
804*c8dee2aaSAndroid Build Coastguard Worker }
805*c8dee2aaSAndroid Build Coastguard Worker
806*c8dee2aaSAndroid Build Coastguard Worker // Then push some points through both paths and make sure they are the same.
807*c8dee2aaSAndroid Build Coastguard Worker static const int kNumPoints = 5;
808*c8dee2aaSAndroid Build Coastguard Worker const SkPoint testPts[kNumPoints] = {
809*c8dee2aaSAndroid Build Coastguard Worker { 0.0f, 0.0f },
810*c8dee2aaSAndroid Build Coastguard Worker { 1.0f, 1.0f },
811*c8dee2aaSAndroid Build Coastguard Worker { 1.0f, 0.5f },
812*c8dee2aaSAndroid Build Coastguard Worker { -1.0f, -0.5f },
813*c8dee2aaSAndroid Build Coastguard Worker { -1.0f, 2.0f }
814*c8dee2aaSAndroid Build Coastguard Worker };
815*c8dee2aaSAndroid Build Coastguard Worker
816*c8dee2aaSAndroid Build Coastguard Worker SkPoint v1[kNumPoints];
817*c8dee2aaSAndroid Build Coastguard Worker original.mapPoints(v1, testPts, kNumPoints);
818*c8dee2aaSAndroid Build Coastguard Worker
819*c8dee2aaSAndroid Build Coastguard Worker SkPoint v2[kNumPoints];
820*c8dee2aaSAndroid Build Coastguard Worker SkMatrix scaleMat = SkMatrix::Scale(scale.width(), scale.height());
821*c8dee2aaSAndroid Build Coastguard Worker
822*c8dee2aaSAndroid Build Coastguard Worker // Note, we intend the decomposition to be applied in the order scale and then remainder but,
823*c8dee2aaSAndroid Build Coastguard Worker // due to skbug.com/7211, the order is reversed!
824*c8dee2aaSAndroid Build Coastguard Worker scaleMat.mapPoints(v2, testPts, kNumPoints);
825*c8dee2aaSAndroid Build Coastguard Worker remaining.mapPoints(v2, kNumPoints);
826*c8dee2aaSAndroid Build Coastguard Worker
827*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kNumPoints; ++i) {
828*c8dee2aaSAndroid Build Coastguard Worker if (!SkPointPriv::EqualsWithinTolerance(v1[i], v2[i], 0.00001f)) {
829*c8dee2aaSAndroid Build Coastguard Worker return false;
830*c8dee2aaSAndroid Build Coastguard Worker }
831*c8dee2aaSAndroid Build Coastguard Worker }
832*c8dee2aaSAndroid Build Coastguard Worker
833*c8dee2aaSAndroid Build Coastguard Worker return true;
834*c8dee2aaSAndroid Build Coastguard Worker }
835*c8dee2aaSAndroid Build Coastguard Worker
test_decompScale(skiatest::Reporter * reporter)836*c8dee2aaSAndroid Build Coastguard Worker static void test_decompScale(skiatest::Reporter* reporter) {
837*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
838*c8dee2aaSAndroid Build Coastguard Worker
839*c8dee2aaSAndroid Build Coastguard Worker m.reset();
840*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_decompScale(m));
841*c8dee2aaSAndroid Build Coastguard Worker m.setScale(2, 3);
842*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_decompScale(m));
843*c8dee2aaSAndroid Build Coastguard Worker m.setRotate(35, 0, 0);
844*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_decompScale(m));
845*c8dee2aaSAndroid Build Coastguard Worker
846*c8dee2aaSAndroid Build Coastguard Worker m.setScale(1, 0);
847*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !check_decompScale(m));
848*c8dee2aaSAndroid Build Coastguard Worker
849*c8dee2aaSAndroid Build Coastguard Worker m.setRotate(35, 0, 0).preScale(2, 3);
850*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_decompScale(m));
851*c8dee2aaSAndroid Build Coastguard Worker
852*c8dee2aaSAndroid Build Coastguard Worker m.setRotate(35, 0, 0).postScale(2, 3);
853*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, check_decompScale(m));
854*c8dee2aaSAndroid Build Coastguard Worker }
855*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Matrix,reporter)856*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Matrix, reporter) {
857*c8dee2aaSAndroid Build Coastguard Worker SkMatrix mat, inverse, iden1, iden2;
858*c8dee2aaSAndroid Build Coastguard Worker
859*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
860*c8dee2aaSAndroid Build Coastguard Worker mat.setTranslate(1, 1);
861*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.invert(&inverse));
862*c8dee2aaSAndroid Build Coastguard Worker iden1.setConcat(mat, inverse);
863*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, is_identity(iden1));
864*c8dee2aaSAndroid Build Coastguard Worker
865*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(2, 4);
866*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.invert(&inverse));
867*c8dee2aaSAndroid Build Coastguard Worker iden1.setConcat(mat, inverse);
868*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, is_identity(iden1));
869*c8dee2aaSAndroid Build Coastguard Worker test_flatten(reporter, mat);
870*c8dee2aaSAndroid Build Coastguard Worker
871*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(SK_Scalar1/2, 2);
872*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.invert(&inverse));
873*c8dee2aaSAndroid Build Coastguard Worker iden1.setConcat(mat, inverse);
874*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, is_identity(iden1));
875*c8dee2aaSAndroid Build Coastguard Worker test_flatten(reporter, mat);
876*c8dee2aaSAndroid Build Coastguard Worker
877*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(3, 5, 20, 0).postRotate(25);
878*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.invert(nullptr));
879*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.invert(&inverse));
880*c8dee2aaSAndroid Build Coastguard Worker iden1.setConcat(mat, inverse);
881*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, is_identity(iden1));
882*c8dee2aaSAndroid Build Coastguard Worker iden2.setConcat(inverse, mat);
883*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, is_identity(iden2));
884*c8dee2aaSAndroid Build Coastguard Worker test_flatten(reporter, mat);
885*c8dee2aaSAndroid Build Coastguard Worker test_flatten(reporter, iden2);
886*c8dee2aaSAndroid Build Coastguard Worker
887*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(0, 1);
888*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(nullptr));
889*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(&inverse));
890*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(1, 0);
891*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(nullptr));
892*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(&inverse));
893*c8dee2aaSAndroid Build Coastguard Worker
894*c8dee2aaSAndroid Build Coastguard Worker // Inverting this matrix results in a non-finite matrix
895*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(0.0f, 1.0f, 2.0f,
896*c8dee2aaSAndroid Build Coastguard Worker 0.0f, 1.0f, -3.40277175e+38f,
897*c8dee2aaSAndroid Build Coastguard Worker 1.00003040f, 1.0f, 0.0f);
898*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(nullptr));
899*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(&inverse));
900*c8dee2aaSAndroid Build Coastguard Worker
901*c8dee2aaSAndroid Build Coastguard Worker // Inverting this matrix results in a non-finite matrix (1/scale overflows to infinity)
902*c8dee2aaSAndroid Build Coastguard Worker // b/378231198: Previously this would pass invert() if a null inverse pointer was passed in.
903*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(std::numeric_limits<float>::denorm_min(), 0.f, 0.f,
904*c8dee2aaSAndroid Build Coastguard Worker 0.f, 1.f, 0.f,
905*c8dee2aaSAndroid Build Coastguard Worker 0.f, 0.f, 1.f);
906*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isScaleTranslate());
907*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(nullptr));
908*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(&inverse));
909*c8dee2aaSAndroid Build Coastguard Worker
910*c8dee2aaSAndroid Build Coastguard Worker // b/378231198: This matrix shouldn't be invertible, but previously the translation wasn't being
911*c8dee2aaSAndroid Build Coastguard Worker // validated when taking the optimized scale+translate paths.
912*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(2.f, 0.f, std::numeric_limits<float>::quiet_NaN(),
913*c8dee2aaSAndroid Build Coastguard Worker 0.f, 2.f, 0.f,
914*c8dee2aaSAndroid Build Coastguard Worker 0.f, 0.f, 1.f);
915*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isScaleTranslate());
916*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(nullptr));
917*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(&inverse));
918*c8dee2aaSAndroid Build Coastguard Worker // Variant that tests the translate-only optimized invert()
919*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(1.f, 0.f, std::numeric_limits<float>::quiet_NaN(),
920*c8dee2aaSAndroid Build Coastguard Worker 0.f, 1.f, 0.f,
921*c8dee2aaSAndroid Build Coastguard Worker 0.f, 0.f, 1.f);
922*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isTranslate());
923*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(nullptr));
924*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(&inverse));
925*c8dee2aaSAndroid Build Coastguard Worker
926*c8dee2aaSAndroid Build Coastguard Worker // A finite scale+translate matrix whose inverse can't be calculated because trans/scale
927*c8dee2aaSAndroid Build Coastguard Worker // becomes non-finite.
928*c8dee2aaSAndroid Build Coastguard Worker mat.setAll(std::numeric_limits<float>::min(), 0.f, std::numeric_limits<float>::max(),
929*c8dee2aaSAndroid Build Coastguard Worker 0.f, 1.f, 0.f,
930*c8dee2aaSAndroid Build Coastguard Worker 0.f, 0.f, 1.f);
931*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isScaleTranslate());
932*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.isFinite());
933*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(nullptr));
934*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.invert(&inverse));
935*c8dee2aaSAndroid Build Coastguard Worker
936*c8dee2aaSAndroid Build Coastguard Worker // rectStaysRect test
937*c8dee2aaSAndroid Build Coastguard Worker {
938*c8dee2aaSAndroid Build Coastguard Worker static const struct {
939*c8dee2aaSAndroid Build Coastguard Worker SkScalar m00, m01, m10, m11;
940*c8dee2aaSAndroid Build Coastguard Worker bool mStaysRect;
941*c8dee2aaSAndroid Build Coastguard Worker }
942*c8dee2aaSAndroid Build Coastguard Worker gRectStaysRectSamples[] = {
943*c8dee2aaSAndroid Build Coastguard Worker { 0, 0, 0, 0, false },
944*c8dee2aaSAndroid Build Coastguard Worker { 0, 0, 0, 1, false },
945*c8dee2aaSAndroid Build Coastguard Worker { 0, 0, 1, 0, false },
946*c8dee2aaSAndroid Build Coastguard Worker { 0, 0, 1, 1, false },
947*c8dee2aaSAndroid Build Coastguard Worker { 0, 1, 0, 0, false },
948*c8dee2aaSAndroid Build Coastguard Worker { 0, 1, 0, 1, false },
949*c8dee2aaSAndroid Build Coastguard Worker { 0, 1, 1, 0, true },
950*c8dee2aaSAndroid Build Coastguard Worker { 0, 1, 1, 1, false },
951*c8dee2aaSAndroid Build Coastguard Worker { 1, 0, 0, 0, false },
952*c8dee2aaSAndroid Build Coastguard Worker { 1, 0, 0, 1, true },
953*c8dee2aaSAndroid Build Coastguard Worker { 1, 0, 1, 0, false },
954*c8dee2aaSAndroid Build Coastguard Worker { 1, 0, 1, 1, false },
955*c8dee2aaSAndroid Build Coastguard Worker { 1, 1, 0, 0, false },
956*c8dee2aaSAndroid Build Coastguard Worker { 1, 1, 0, 1, false },
957*c8dee2aaSAndroid Build Coastguard Worker { 1, 1, 1, 0, false },
958*c8dee2aaSAndroid Build Coastguard Worker { 1, 1, 1, 1, false }
959*c8dee2aaSAndroid Build Coastguard Worker };
960*c8dee2aaSAndroid Build Coastguard Worker
961*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(gRectStaysRectSamples); i++) {
962*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
963*c8dee2aaSAndroid Build Coastguard Worker
964*c8dee2aaSAndroid Build Coastguard Worker m.reset();
965*c8dee2aaSAndroid Build Coastguard Worker m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
966*c8dee2aaSAndroid Build Coastguard Worker m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01);
967*c8dee2aaSAndroid Build Coastguard Worker m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10);
968*c8dee2aaSAndroid Build Coastguard Worker m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
969*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter,
970*c8dee2aaSAndroid Build Coastguard Worker m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
971*c8dee2aaSAndroid Build Coastguard Worker }
972*c8dee2aaSAndroid Build Coastguard Worker }
973*c8dee2aaSAndroid Build Coastguard Worker
974*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
975*c8dee2aaSAndroid Build Coastguard Worker mat.set(SkMatrix::kMScaleX, 1)
976*c8dee2aaSAndroid Build Coastguard Worker .set(SkMatrix::kMSkewX, 2)
977*c8dee2aaSAndroid Build Coastguard Worker .set(SkMatrix::kMTransX, 3)
978*c8dee2aaSAndroid Build Coastguard Worker .set(SkMatrix::kMSkewY, 4)
979*c8dee2aaSAndroid Build Coastguard Worker .set(SkMatrix::kMScaleY, 5)
980*c8dee2aaSAndroid Build Coastguard Worker .set(SkMatrix::kMTransY, 6);
981*c8dee2aaSAndroid Build Coastguard Worker SkScalar affine[6];
982*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat.asAffine(affine));
983*c8dee2aaSAndroid Build Coastguard Worker
984*c8dee2aaSAndroid Build Coastguard Worker #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
985*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, affineEqual(ScaleX));
986*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, affineEqual(SkewY));
987*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, affineEqual(SkewX));
988*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, affineEqual(ScaleY));
989*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, affineEqual(TransX));
990*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, affineEqual(TransY));
991*c8dee2aaSAndroid Build Coastguard Worker #undef affineEqual
992*c8dee2aaSAndroid Build Coastguard Worker
993*c8dee2aaSAndroid Build Coastguard Worker mat.set(SkMatrix::kMPersp1, SK_Scalar1 / 2);
994*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !mat.asAffine(affine));
995*c8dee2aaSAndroid Build Coastguard Worker
996*c8dee2aaSAndroid Build Coastguard Worker SkMatrix mat2;
997*c8dee2aaSAndroid Build Coastguard Worker mat2.reset();
998*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
999*c8dee2aaSAndroid Build Coastguard Worker SkScalar zero = 0;
1000*c8dee2aaSAndroid Build Coastguard Worker mat.set(SkMatrix::kMSkewX, -zero);
1001*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
1002*c8dee2aaSAndroid Build Coastguard Worker
1003*c8dee2aaSAndroid Build Coastguard Worker mat2.reset();
1004*c8dee2aaSAndroid Build Coastguard Worker mat.reset();
1005*c8dee2aaSAndroid Build Coastguard Worker mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
1006*c8dee2aaSAndroid Build Coastguard Worker mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
1007*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
1008*c8dee2aaSAndroid Build Coastguard Worker
1009*c8dee2aaSAndroid Build Coastguard Worker test_matrix_min_max_scale(reporter);
1010*c8dee2aaSAndroid Build Coastguard Worker test_matrix_preserve_shape(reporter);
1011*c8dee2aaSAndroid Build Coastguard Worker test_matrix_recttorect(reporter);
1012*c8dee2aaSAndroid Build Coastguard Worker test_matrix_decomposition(reporter);
1013*c8dee2aaSAndroid Build Coastguard Worker test_matrix_homogeneous(reporter);
1014*c8dee2aaSAndroid Build Coastguard Worker test_set9(reporter);
1015*c8dee2aaSAndroid Build Coastguard Worker
1016*c8dee2aaSAndroid Build Coastguard Worker test_decompScale(reporter);
1017*c8dee2aaSAndroid Build Coastguard Worker
1018*c8dee2aaSAndroid Build Coastguard Worker mat.setScaleTranslate(2, 3, 1, 4);
1019*c8dee2aaSAndroid Build Coastguard Worker mat2.setScale(2, 3).postTranslate(1, 4);
1020*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, mat == mat2);
1021*c8dee2aaSAndroid Build Coastguard Worker }
1022*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Matrix_Concat,r)1023*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Matrix_Concat, r) {
1024*c8dee2aaSAndroid Build Coastguard Worker SkMatrix a;
1025*c8dee2aaSAndroid Build Coastguard Worker a.setTranslate(10, 20);
1026*c8dee2aaSAndroid Build Coastguard Worker
1027*c8dee2aaSAndroid Build Coastguard Worker SkMatrix b;
1028*c8dee2aaSAndroid Build Coastguard Worker b.setScale(3, 5);
1029*c8dee2aaSAndroid Build Coastguard Worker
1030*c8dee2aaSAndroid Build Coastguard Worker SkMatrix expected;
1031*c8dee2aaSAndroid Build Coastguard Worker expected.setConcat(a,b);
1032*c8dee2aaSAndroid Build Coastguard Worker
1033*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, expected == SkMatrix::Concat(a, b));
1034*c8dee2aaSAndroid Build Coastguard Worker }
1035*c8dee2aaSAndroid Build Coastguard Worker
1036*c8dee2aaSAndroid Build Coastguard Worker // Test that all variants of maprect are correct.
DEF_TEST(Matrix_maprects,r)1037*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Matrix_maprects, r) {
1038*c8dee2aaSAndroid Build Coastguard Worker const SkScalar scale = 1000;
1039*c8dee2aaSAndroid Build Coastguard Worker
1040*c8dee2aaSAndroid Build Coastguard Worker SkMatrix mat;
1041*c8dee2aaSAndroid Build Coastguard Worker mat.setScale(2, 3).postTranslate(1, 4);
1042*c8dee2aaSAndroid Build Coastguard Worker
1043*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand;
1044*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 10000; ++i) {
1045*c8dee2aaSAndroid Build Coastguard Worker SkRect src = SkRect::MakeLTRB(rand.nextSScalar1() * scale,
1046*c8dee2aaSAndroid Build Coastguard Worker rand.nextSScalar1() * scale,
1047*c8dee2aaSAndroid Build Coastguard Worker rand.nextSScalar1() * scale,
1048*c8dee2aaSAndroid Build Coastguard Worker rand.nextSScalar1() * scale);
1049*c8dee2aaSAndroid Build Coastguard Worker SkRect dst[4];
1050*c8dee2aaSAndroid Build Coastguard Worker
1051*c8dee2aaSAndroid Build Coastguard Worker mat.mapPoints((SkPoint*)&dst[0].fLeft, (SkPoint*)&src.fLeft, 2);
1052*c8dee2aaSAndroid Build Coastguard Worker dst[0].sort();
1053*c8dee2aaSAndroid Build Coastguard Worker mat.mapRect(&dst[1], src);
1054*c8dee2aaSAndroid Build Coastguard Worker mat.mapRectScaleTranslate(&dst[2], src);
1055*c8dee2aaSAndroid Build Coastguard Worker dst[3] = mat.mapRect(src);
1056*c8dee2aaSAndroid Build Coastguard Worker
1057*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, dst[0] == dst[1]);
1058*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, dst[0] == dst[2]);
1059*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, dst[0] == dst[3]);
1060*c8dee2aaSAndroid Build Coastguard Worker }
1061*c8dee2aaSAndroid Build Coastguard Worker
1062*c8dee2aaSAndroid Build Coastguard Worker // We should report nonfinite-ness after a mapping
1063*c8dee2aaSAndroid Build Coastguard Worker {
1064*c8dee2aaSAndroid Build Coastguard Worker // We have special-cases in mapRect for different matrix types
1065*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m0 = SkMatrix::Scale(1e20f, 1e20f);
1066*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m1; m1.setRotate(30); m1.postScale(1e20f, 1e20f);
1067*c8dee2aaSAndroid Build Coastguard Worker
1068*c8dee2aaSAndroid Build Coastguard Worker for (const auto& m : { m0, m1 }) {
1069*c8dee2aaSAndroid Build Coastguard Worker SkRect rect = { 0, 0, 1e20f, 1e20f };
1070*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, rect.isFinite());
1071*c8dee2aaSAndroid Build Coastguard Worker rect = m.mapRect(rect);
1072*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !rect.isFinite());
1073*c8dee2aaSAndroid Build Coastguard Worker }
1074*c8dee2aaSAndroid Build Coastguard Worker }
1075*c8dee2aaSAndroid Build Coastguard Worker }
1076*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Matrix_mapRect_skbug12335,r)1077*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Matrix_mapRect_skbug12335, r) {
1078*c8dee2aaSAndroid Build Coastguard Worker // Stripped down test case from skbug.com/12335. Essentially, the corners of this rect would
1079*c8dee2aaSAndroid Build Coastguard Worker // map to homogoneous coords with very small w's (below the old value of kW0PlaneDistance) and
1080*c8dee2aaSAndroid Build Coastguard Worker // so they would be clipped "behind" the plane, resulting in an empty mapped rect. Coordinates
1081*c8dee2aaSAndroid Build Coastguard Worker // with positive that wouldn't overflow when divided by w should still be included in the mapped
1082*c8dee2aaSAndroid Build Coastguard Worker // rectangle.
1083*c8dee2aaSAndroid Build Coastguard Worker SkRect rect = SkRect::MakeLTRB(0, 0, 319, 620);
1084*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m = SkMatrix::MakeAll( 0.000152695269f, 0.00000000f, -6.53848401e-05f,
1085*c8dee2aaSAndroid Build Coastguard Worker -1.75697533e-05f, 0.000157153074f, -1.10847975e-06f,
1086*c8dee2aaSAndroid Build Coastguard Worker -6.00415362e-08f, 0.00000000f, 0.000169880834f);
1087*c8dee2aaSAndroid Build Coastguard Worker SkRect out = m.mapRect(rect);
1088*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !out.isEmpty());
1089*c8dee2aaSAndroid Build Coastguard Worker }
1090*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Matrix_Ctor,r)1091*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Matrix_Ctor, r) {
1092*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, SkMatrix{} == SkMatrix::I());
1093*c8dee2aaSAndroid Build Coastguard Worker }
1094*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Matrix_LookAt,r)1095*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Matrix_LookAt, r) {
1096*c8dee2aaSAndroid Build Coastguard Worker // Degenerate inputs should not trigger *SAN errors.
1097*c8dee2aaSAndroid Build Coastguard Worker const auto m = SkM44::LookAt({0,0,0}, {0,0,0}, {0,0,0});
1098*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, m == SkM44());
1099*c8dee2aaSAndroid Build Coastguard Worker }
1100*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Matrix_SetRotateSnap,r)1101*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Matrix_SetRotateSnap, r) {
1102*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
1103*c8dee2aaSAndroid Build Coastguard Worker
1104*c8dee2aaSAndroid Build Coastguard Worker // We need to snap sin & cos when we call setRotate, or rotations by multiples of 90 degrees
1105*c8dee2aaSAndroid Build Coastguard Worker // will end up with slight drift (and we won't consider them to satisfy rectStaysRect, which
1106*c8dee2aaSAndroid Build Coastguard Worker // is an important performance constraint). We test up to +-1080 degrees.
1107*c8dee2aaSAndroid Build Coastguard Worker for (float deg = 90.0f; deg <= 1080.0f; deg += 90.0f) {
1108*c8dee2aaSAndroid Build Coastguard Worker m.setRotate(deg);
1109*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, m.rectStaysRect());
1110*c8dee2aaSAndroid Build Coastguard Worker m.setRotate(-deg);
1111*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, m.rectStaysRect());
1112*c8dee2aaSAndroid Build Coastguard Worker }
1113*c8dee2aaSAndroid Build Coastguard Worker
1114*c8dee2aaSAndroid Build Coastguard Worker // But: we don't want to be too lenient with snapping. That prevents small rotations from being
1115*c8dee2aaSAndroid Build Coastguard Worker // registered at all. Ensure that .01 degrees produces an actual rotation. (crbug.com/1345038)
1116*c8dee2aaSAndroid Build Coastguard Worker m.setRotate(0.01f);
1117*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !m.rectStaysRect());
1118*c8dee2aaSAndroid Build Coastguard Worker }
1119*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Matrix_rectStaysRect_zeroScale,r)1120*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Matrix_rectStaysRect_zeroScale, r) {
1121*c8dee2aaSAndroid Build Coastguard Worker // rectStaysRect() returns true if the scale factors are non-zero, so preScale(0,0),
1122*c8dee2aaSAndroid Build Coastguard Worker // setScale(0,0), setScaleTranslate(0,0,...), ::Scale(), should not have the flag set.
1123*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !SkMatrix::Scale(0.f, 0.f).rectStaysRect());
1124*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !SkMatrix::Scale(0.f, 2.f).rectStaysRect());
1125*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !SkMatrix::Scale(2.f, 0.f).rectStaysRect());
1126*c8dee2aaSAndroid Build Coastguard Worker
1127*c8dee2aaSAndroid Build Coastguard Worker // RectToRect() is like scaling. It fails if the source rect is empty, but if the dst rect is
1128*c8dee2aaSAndroid Build Coastguard Worker // empty it's as if it had a zero scale factor, so it's type mask should reflect that.
1129*c8dee2aaSAndroid Build Coastguard Worker const SkRect src = {0.f,0.f,10.f,10.f};
1130*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !SkMatrix::RectToRect(src, {0.f,0.f,0.f,0.f}).rectStaysRect());
1131*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !SkMatrix::RectToRect(src, {0.f,0.f,0.f,20.f}).rectStaysRect());
1132*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !SkMatrix::RectToRect(src, {0.f,0.f,20.f,0.f}).rectStaysRect());
1133*c8dee2aaSAndroid Build Coastguard Worker
1134*c8dee2aaSAndroid Build Coastguard Worker {
1135*c8dee2aaSAndroid Build Coastguard Worker SkMatrix rectMatrix = SkMatrix::I(); // trivially
1136*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, rectMatrix.rectStaysRect());
1137*c8dee2aaSAndroid Build Coastguard Worker
1138*c8dee2aaSAndroid Build Coastguard Worker SkMatrix nonRectMatrix = rectMatrix;
1139*c8dee2aaSAndroid Build Coastguard Worker nonRectMatrix.preScale(0.f, 0.f);
1140*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !nonRectMatrix.rectStaysRect());
1141*c8dee2aaSAndroid Build Coastguard Worker
1142*c8dee2aaSAndroid Build Coastguard Worker nonRectMatrix = rectMatrix;
1143*c8dee2aaSAndroid Build Coastguard Worker nonRectMatrix.preScale(0.f, 2.f);
1144*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !nonRectMatrix.rectStaysRect());
1145*c8dee2aaSAndroid Build Coastguard Worker
1146*c8dee2aaSAndroid Build Coastguard Worker nonRectMatrix = rectMatrix;
1147*c8dee2aaSAndroid Build Coastguard Worker nonRectMatrix.preScale(2.f, 0.f);
1148*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !nonRectMatrix.rectStaysRect());
1149*c8dee2aaSAndroid Build Coastguard Worker }
1150*c8dee2aaSAndroid Build Coastguard Worker
1151*c8dee2aaSAndroid Build Coastguard Worker {
1152*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
1153*c8dee2aaSAndroid Build Coastguard Worker m.setScale(0.f, 0.f);
1154*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !m.rectStaysRect());
1155*c8dee2aaSAndroid Build Coastguard Worker }
1156*c8dee2aaSAndroid Build Coastguard Worker
1157*c8dee2aaSAndroid Build Coastguard Worker {
1158*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
1159*c8dee2aaSAndroid Build Coastguard Worker m.setScale(0.f, 2.f);
1160*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !m.rectStaysRect());
1161*c8dee2aaSAndroid Build Coastguard Worker }
1162*c8dee2aaSAndroid Build Coastguard Worker
1163*c8dee2aaSAndroid Build Coastguard Worker {
1164*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
1165*c8dee2aaSAndroid Build Coastguard Worker m.setScale(2.f, 0.f);
1166*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !m.rectStaysRect());
1167*c8dee2aaSAndroid Build Coastguard Worker }
1168*c8dee2aaSAndroid Build Coastguard Worker
1169*c8dee2aaSAndroid Build Coastguard Worker {
1170*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
1171*c8dee2aaSAndroid Build Coastguard Worker m.setScaleTranslate(0.f, 0.f, 10.f, 10.f);
1172*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !m.rectStaysRect());
1173*c8dee2aaSAndroid Build Coastguard Worker }
1174*c8dee2aaSAndroid Build Coastguard Worker
1175*c8dee2aaSAndroid Build Coastguard Worker {
1176*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
1177*c8dee2aaSAndroid Build Coastguard Worker m.setScaleTranslate(0.f, 2.f, 10.f, 10.f);
1178*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !m.rectStaysRect());
1179*c8dee2aaSAndroid Build Coastguard Worker }
1180*c8dee2aaSAndroid Build Coastguard Worker
1181*c8dee2aaSAndroid Build Coastguard Worker {
1182*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m;
1183*c8dee2aaSAndroid Build Coastguard Worker m.setScaleTranslate(2.f, 0.f, 10.f, 10.f);
1184*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !m.rectStaysRect());
1185*c8dee2aaSAndroid Build Coastguard Worker }
1186*c8dee2aaSAndroid Build Coastguard Worker
1187*c8dee2aaSAndroid Build Coastguard Worker }
1188