Flutter Engine
The Flutter Engine
Functions
M44Test.cpp File Reference
#include "include/core/SkM44.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPath.h"
#include "include/core/SkRect.h"
#include "include/core/SkScalar.h"
#include "include/core/SkTypes.h"
#include "src/base/SkRandom.h"
#include "src/core/SkMatrixPriv.h"
#include "tests/Test.h"

Go to the source code of this file.

Functions

static bool eq (const SkM44 &a, const SkM44 &b, float tol)
 
 DEF_TEST (M44, reporter)
 
 DEF_TEST (M44_v3, reporter)
 
 DEF_TEST (M44_v4, reporter)
 
 DEF_TEST (M44_rotate, reporter)
 
 DEF_TEST (M44_rectToRect, reporter)
 
 DEF_TEST (M44_mapRect, reporter)
 
 DEF_TEST (M44_mapRect_skbug12335, r)
 

Function Documentation

◆ DEF_TEST() [1/7]

DEF_TEST ( M44  ,
reporter   
)

Definition at line 30 of file M44Test.cpp.

30 {
31 SkM44 m, im;
32
33 REPORTER_ASSERT(reporter, SkM44(1, 0, 0, 0,
34 0, 1, 0, 0,
35 0, 0, 1, 0,
36 0, 0, 0, 1) == m);
38 REPORTER_ASSERT(reporter, m.invert(&im));
40
41 m.setTranslate(3, 4, 2);
42 REPORTER_ASSERT(reporter, SkM44(1, 0, 0, 3,
43 0, 1, 0, 4,
44 0, 0, 1, 2,
45 0, 0, 0, 1) == m);
46
47 const float f[] = { 1, 0, 0, 2, 3, 1, 2, 5, 0, 5, 3, 0, 0, 1, 0, 2 };
49 REPORTER_ASSERT(reporter, SkM44(f[0], f[4], f[ 8], f[12],
50 f[1], f[5], f[ 9], f[13],
51 f[2], f[6], f[10], f[14],
52 f[3], f[7], f[11], f[15]) == m);
53
54 {
55 SkM44 t = m.transpose();
57 REPORTER_ASSERT(reporter, t.rc(1,0) == m.rc(0,1));
58 SkM44 tt = t.transpose();
60 }
61
63 REPORTER_ASSERT(reporter, SkM44(f[ 0], f[ 1], f[ 2], f[ 3],
64 f[ 4], f[ 5], f[ 6], f[ 7],
65 f[ 8], f[ 9], f[10], f[14],
66 f[12], f[13], f[14], f[15]) == m);
67
68 REPORTER_ASSERT(reporter, m.invert(&im));
69
70 m = m * im;
71 // m should be identity now, but our calc is not perfect...
72 REPORTER_ASSERT(reporter, eq(SkM44(), m, 0.0000005f));
74}
reporter
Definition: FontMgrTest.cpp:39
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
Definition: M44Test.cpp:18
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
Definition: SkM44.h:150
static SkM44 RowMajor(const SkScalar r[16])
Definition: SkM44.h:212
static SkM44 ColMajor(const SkScalar c[16])
Definition: SkM44.h:218
SkScalar rc(int r, int c) const
Definition: SkM44.h:261
SkM44 transpose() const
Definition: SkM44.cpp:256

◆ DEF_TEST() [2/7]

DEF_TEST ( M44_mapRect  ,
reporter   
)

Definition at line 259 of file M44Test.cpp.

259 {
260 auto assertRectsNearlyEqual = [&](const SkRect& actual, const SkRect& expected,
261 const SkRect& e) {
262 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(actual.fLeft, expected.fLeft, e.fLeft),
263 "Expected %g == %g", actual.fLeft, expected.fLeft);
264 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(actual.fTop, expected.fTop, e.fTop),
265 "Expected %g == %g", actual.fTop, expected.fTop);
266 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(actual.fRight, expected.fRight, e.fRight),
267 "Expected %g == %g", actual.fRight, expected.fRight);
268 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(actual.fBottom, expected.fBottom, e.fBottom),
269 "Expected %g == %g", actual.fBottom, expected.fBottom);
270 };
271 auto assertMapRect = [&](const SkM44& m, const SkRect& src, const SkRect* expected) {
272 SkRect epsilon = {1e-5f, 1e-5f, 1e-5f, 1e-5f};
273
275 REPORTER_ASSERT(reporter, !actual.isEmpty());
276
277 if (expected) {
278 assertRectsNearlyEqual(actual, *expected, epsilon);
279 }
280
281 SkV4 corners[4] = {{src.fLeft, src.fTop, 0.f, 1.f},
282 {src.fRight, src.fTop, 0.f, 1.f},
283 {src.fRight, src.fBottom, 0.f, 1.f},
284 {src.fLeft, src.fBottom, 0.f, 1.f}};
285 bool leftFound = false;
286 bool topFound = false;
287 bool rightFound = false;
288 bool bottomFound = false;
289 bool clipped = false;
290 for (int i = 0; i < 4; ++i) {
291 SkV4 mapped = m * corners[i];
292 if (mapped.w > 0.f) {
293 // Should be contained in actual and might be on one or two of actual's edges
294 float x = mapped.x / mapped.w;
295 float y = mapped.y / mapped.w;
296
297 // Can't use SkRect::contains() since it treats right and bottom edges as exclusive
298 REPORTER_ASSERT(reporter, actual.fLeft <= x && x <= actual.fRight,
299 "Expected %g contained in [%g, %g]",
300 x, actual.fLeft, actual.fRight);
301 REPORTER_ASSERT(reporter, actual.fTop <= y && y <= actual.fBottom,
302 "Expected %g contained in [%g, %g]",
303 y, actual.fTop, actual.fBottom);
304
305 leftFound |= SkScalarNearlyEqual(x, actual.fLeft);
306 topFound |= SkScalarNearlyEqual(y, actual.fTop);
307 rightFound |= SkScalarNearlyEqual(x, actual.fRight);
308 bottomFound |= SkScalarNearlyEqual(y, actual.fBottom);
309 } else {
310 // The mapped point would be clipped so the clipped mapped bounds don't necessarily
311 // contain it
312 clipped = true;
313 }
314 }
315
316 if (clipped) {
317 // At least one of the mapped corners should have contributed to the rect
318 REPORTER_ASSERT(reporter, leftFound || topFound || rightFound || bottomFound);
319 // For any edge that came from a clipped corner, increase its error tolerance relative
320 // to what SkPath::ApplyPerspectiveClip calculates.
321 // TODO(michaelludwig): skbug.com/12335 required updating the w epsilon distance which
322 // greatly increased noise for coords projecting to infinity. They aren't "wrong", since
323 // the intent was clearly to pick a big number that's definitely offscreen, but
324 // MapRect should have a more robust solution than a fixed w > epsilon and when it does,
325 // these expectations for clipped points should be more accurate.
326 if (!leftFound) { epsilon.fLeft = .01f * actual.fLeft; }
327 if (!topFound) { epsilon.fTop = .01f * actual.fTop; }
328 if (!rightFound) { epsilon.fRight = .01f * actual.fRight; }
329 if (!bottomFound) { epsilon.fBottom = .01f * actual.fBottom; }
330 } else {
331 // The mapped corners should have contributed to all four edges of the returned rect
332 REPORTER_ASSERT(reporter, leftFound && topFound && rightFound && bottomFound);
333 }
334
336 path.transform(m.asM33(), SkApplyPerspectiveClip::kYes);
337 assertRectsNearlyEqual(actual, path.getBounds(), epsilon);
338 };
339
340 // src chosen arbitrarily
341 const SkRect src = SkRect::MakeLTRB(4.83f, -0.48f, 5.53f, 30.68f);
342
343 // Identity maps src to src
344 assertMapRect(SkM44(), src, &src);
345 // Scale+Translate just offsets src
346 SkRect st = SkRect::MakeLTRB(10.f + 2.f * src.fLeft, 8.f + 4.f * src.fTop,
347 10.f + 2.f * src.fRight, 8.f + 4.f * src.fBottom);
348 assertMapRect(SkM44::Scale(2.f, 4.f).postTranslate(10.f, 8.f), src, &st);
349 // Rotate 45 degrees about center
350 assertMapRect(SkM44::Rotate({0.f, 0.f, 1.f}, SK_ScalarPI / 4.f)
351 .preTranslate(-src.centerX(), -src.centerY())
352 .postTranslate(src.centerX(), src.centerY()),
353 src, nullptr);
354
355 // Perspective matrix where src does not need to be clipped w > 0
356 SkM44 p = SkM44::Perspective(0.01f, 10.f, SK_ScalarPI / 3.f);
357 p.preTranslate(0.f, 5.f, -0.1f);
358 p.preConcat(SkM44::Rotate({0.f, 1.f, 0.f}, 0.008f /* radians */));
359 assertMapRect(p, src, nullptr);
360
361 // Perspective matrix where src *does* need to be clipped w > 0
362 p.setIdentity();
363 p.setRow(3, {-.2f, -.6f, 0.f, 8.f});
364 assertMapRect(p, src, nullptr);
365}
@ kYes
Do pre-clip the geometry before applying the (perspective) matrix.
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:107
#define SK_ScalarPI
Definition: SkScalar.h:21
SkM44 & postTranslate(SkScalar x, SkScalar y, SkScalar z=0)
Definition: SkM44.cpp:100
static SkM44 Rotate(SkV3 axis, SkScalar radians)
Definition: SkM44.h:239
static SkM44 Perspective(float near, float far, float angle)
Definition: SkM44.cpp:343
static SkM44 Scale(SkScalar x, SkScalar y, SkScalar z=1)
Definition: SkM44.h:232
static SkRect MapRect(const SkM44 &m, const SkRect &r)
Definition: SkM44.cpp:216
Definition: SkPath.h:59
static SkPath Rect(const SkRect &, SkPathDirection=SkPathDirection::kCW, unsigned startIndex=0)
Definition: SkPath.cpp:3586
double y
double x
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
bool isEmpty() const
Definition: SkRect.h:693
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15
Definition: SkM44.h:98
float w
Definition: SkM44.h:99
float y
Definition: SkM44.h:99
float x
Definition: SkM44.h:99

◆ DEF_TEST() [3/7]

DEF_TEST ( M44_mapRect_skbug12335  ,
 
)

Definition at line 367 of file M44Test.cpp.

367 {
368 // Stripped down test case from skbug.com/12335. Essentially, the corners of this rect would
369 // map to homogoneous coords with very small w's (below the old value of kW0PlaneDistance) and
370 // so they would be clipped "behind" the plane, resulting in an empty mapped rect. Coordinates
371 // with positive that wouldn't overflow when divided by w should still be included in the mapped
372 // rectangle.
373 SkRect rect = SkRect::MakeLTRB(0, 0, 319, 620);
374 SkM44 m(SkMatrix::MakeAll( 0.000152695269f, 0.00000000f, -6.53848401e-05f,
375 -1.75697533e-05f, 0.000157153074f, -1.10847975e-06f,
376 -6.00415362e-08f, 0.00000000f, 0.000169880834f));
378 REPORTER_ASSERT(r, !out.isEmpty());
379}
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition: SkMatrix.h:179
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350

◆ DEF_TEST() [4/7]

DEF_TEST ( M44_rectToRect  ,
reporter   
)

Definition at line 195 of file M44Test.cpp.

195 {
196 SkV2 dstScales[] = {
197 {1.f, 1.f}, // no aspect ratio change, nor up/down scaling
198 {0.25f, 0.5f}, // aspect ratio narrows, downscale x and y
199 {0.5f, 0.25f}, // aspect ratio widens, downscale x and y
200 {0.5f, 0.5f}, // no aspect ratio change, downscale x and y
201 {2.f, 3.f}, // aspect ratio narrows, upscale x and y
202 {3.f, 2.f}, // aspect ratio widens, upscale x and y
203 {2.f, 2.f}, // no aspect ratio change, upscale x and y
204 {0.5f, 2.f}, // aspect ratio narrows, downscale x and upscale y
205 {2.f, 0.5f} // aspect ratio widens, upscale x and downscale y
206 };
207
208 auto map2d = [&](const SkM44& m, SkV2 p) {
209 SkV4 mapped = m.map(p.x, p.y, 0.f, 1.f);
210 REPORTER_ASSERT(reporter, mapped.z == 0.f);
211 REPORTER_ASSERT(reporter, mapped.w == 1.f);
212 return SkV2{mapped.x, mapped.y};
213 };
214 auto assertNearlyEqual = [&](float actual, float expected) {
216 "Expected %g == %g", actual, expected);
217 };
218 auto assertEdges = [&](float actualLow, float actualHigh,
219 float expectedLow, float expectedHigh) {
220 SkASSERT(expectedLow < expectedHigh);
221 REPORTER_ASSERT(reporter, actualLow < actualHigh,
222 "Expected %g < %g", actualLow, actualHigh);
223
224 assertNearlyEqual(actualLow, expectedLow);
225 assertNearlyEqual(actualHigh, expectedHigh);
226 };
227
228 SkRandom rand;
229 for (const auto& r : dstScales) {
230 SkRect src = SkRect::MakeXYWH(rand.nextRangeF(-10.f, 10.f),
231 rand.nextRangeF(-10.f, 10.f),
232 rand.nextRangeF(1.f, 10.f),
233 rand.nextRangeF(1.f, 10.f));
234 SkRect dst = SkRect::MakeXYWH(rand.nextRangeF(-10.f, 10.f),
235 rand.nextRangeF(-10.f, 10.f),
236 r.x * src.width(),
237 r.y * src.height());
238
240
241 // Regardless of the factory, center of src maps to center of dst
242 SkV2 center = map2d(m, {src.centerX(), src.centerY()});
243 assertNearlyEqual(center.x, dst.centerX());
244 assertNearlyEqual(center.y, dst.centerY());
245
246 // Map the four corners of src and validate against expected edge mapping
247 SkV2 tl = map2d(m, {src.fLeft, src.fTop});
248 SkV2 tr = map2d(m, {src.fRight, src.fTop});
249 SkV2 br = map2d(m, {src.fRight, src.fBottom});
250 SkV2 bl = map2d(m, {src.fLeft, src.fBottom});
251
252 assertEdges(tl.x, tr.x, dst.fLeft, dst.fRight);
253 assertEdges(bl.x, br.x, dst.fLeft, dst.fRight);
254 assertEdges(tl.y, bl.y, dst.fTop, dst.fBottom);
255 assertEdges(tr.y, br.y, dst.fTop, dst.fBottom);
256 }
257}
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkScalar center(float pos0, float pos1)
static SkM44 RectToRect(const SkRect &src, const SkRect &dst)
Definition: SkM44.cpp:304
float nextRangeF(float min, float max)
Definition: SkRandom.h:64
dst
Definition: cp.py:12
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
Definition: SkM44.h:19
float x
Definition: SkM44.h:20
float y
Definition: SkM44.h:20
float z
Definition: SkM44.h:99

◆ DEF_TEST() [5/7]

DEF_TEST ( M44_rotate  ,
reporter   
)

Definition at line 141 of file M44Test.cpp.

141 {
142 const SkV3 x = {1, 0, 0},
143 y = {0, 1, 0},
144 z = {0, 0, 1};
145
146 // We have radians version of setRotateAbout methods, but even with our best approx
147 // for PI, sin(SK_ScalarPI) != 0, so to make the comparisons in the unittest clear,
148 // I'm using the variants that explicitly take the sin,cos values.
149
150 struct {
151 SkScalar sinAngle, cosAngle;
152 SkV3 aboutAxis;
153 SkV3 expectedX, expectedY, expectedZ;
154 } recs[] = {
155 { 0, 1, x, x, y, z}, // angle = 0
156 { 0, 1, y, x, y, z}, // angle = 0
157 { 0, 1, z, x, y, z}, // angle = 0
158
159 { 0,-1, x, x,-y,-z}, // angle = 180
160 { 0,-1, y, -x, y,-z}, // angle = 180
161 { 0,-1, z, -x,-y, z}, // angle = 180
162
163 // Skia coordinate system is right-handed
164
165 { 1, 0, x, x, z,-y}, // angle = 90
166 { 1, 0, y, -z, y, x}, // angle = 90
167 { 1, 0, z, y,-x, z}, // angle = 90
168
169 {-1, 0, x, x,-z, y}, // angle = -90
170 {-1, 0, y, z, y,-x}, // angle = -90
171 {-1, 0, z, -y, x, z}, // angle = -90
172 };
173
174 for (const auto& r : recs) {
176 m.setRotateUnitSinCos(r.aboutAxis, r.sinAngle, r.cosAngle);
177
178 auto mx = m * x;
179 auto my = m * y;
180 auto mz = m * z;
181 REPORTER_ASSERT(reporter, mx == r.expectedX);
182 REPORTER_ASSERT(reporter, my == r.expectedY);
183 REPORTER_ASSERT(reporter, mz == r.expectedZ);
184
185 // flipping the axis-of-rotation should flip the results
186 mx = m * -x;
187 my = m * -y;
188 mz = m * -z;
189 REPORTER_ASSERT(reporter, mx == -r.expectedX);
190 REPORTER_ASSERT(reporter, my == -r.expectedY);
191 REPORTER_ASSERT(reporter, mz == -r.expectedZ);
192 }
193}
@ kNaN_Constructor
Definition: SkM44.h:172
float SkScalar
Definition: extension.cpp:12
Definition: SkM44.h:56

◆ DEF_TEST() [6/7]

DEF_TEST ( M44_v3  ,
reporter   
)

Definition at line 76 of file M44Test.cpp.

76 {
77 SkV3 a = {1, 2, 3},
78 b = {1, 2, 2};
79
80 REPORTER_ASSERT(reporter, a.lengthSquared() == 1 + 4 + 9);
81 REPORTER_ASSERT(reporter, b.length() == 3);
82 REPORTER_ASSERT(reporter, a.dot(b) == 1 + 4 + 6);
83 REPORTER_ASSERT(reporter, b.dot(a) == 1 + 4 + 6);
84 REPORTER_ASSERT(reporter, (a.cross(b) == SkV3{-2, 1, 0}));
85 REPORTER_ASSERT(reporter, (b.cross(a) == SkV3{ 2, -1, 0}));
86
87 SkM44 m = {
88 2, 0, 0, 3,
89 0, 1, 0, 5,
90 0, 0, 3, 1,
91 0, 0, 0, 1
92 };
93
94 SkV3 c = m * a;
95 REPORTER_ASSERT(reporter, (c == SkV3{2, 2, 9}));
96 SkV4 d = m.map(4, 3, 2, 1);
97 REPORTER_ASSERT(reporter, (d == SkV4{11, 8, 7, 1}));
98}
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
static bool b
struct MyStruct a[10]

◆ DEF_TEST() [7/7]

DEF_TEST ( M44_v4  ,
reporter   
)

Definition at line 100 of file M44Test.cpp.

100 {
101 SkM44 m( 1, 2, 3, 4,
102 5, 6, 7, 8,
103 9, 10, 11, 12,
104 13, 14, 15, 16);
105
106 SkV4 r0 = m.row(0),
107 r1 = m.row(1),
108 r2 = m.row(2),
109 r3 = m.row(3);
110
111 REPORTER_ASSERT(reporter, (r0 == SkV4{ 1, 2, 3, 4}));
112 REPORTER_ASSERT(reporter, (r1 == SkV4{ 5, 6, 7, 8}));
113 REPORTER_ASSERT(reporter, (r2 == SkV4{ 9, 10, 11, 12}));
114 REPORTER_ASSERT(reporter, (r3 == SkV4{13, 14, 15, 16}));
115
116 REPORTER_ASSERT(reporter, SkM44::Rows(r0, r1, r2, r3) == m);
117
118 SkV4 c0 = m.col(0),
119 c1 = m.col(1),
120 c2 = m.col(2),
121 c3 = m.col(3);
122
123 REPORTER_ASSERT(reporter, (c0 == SkV4{1, 5, 9, 13}));
124 REPORTER_ASSERT(reporter, (c1 == SkV4{2, 6, 10, 14}));
125 REPORTER_ASSERT(reporter, (c2 == SkV4{3, 7, 11, 15}));
126 REPORTER_ASSERT(reporter, (c3 == SkV4{4, 8, 12, 16}));
127
128 REPORTER_ASSERT(reporter, SkM44::Cols(c0, c1, c2, c3) == m);
129
130 // implement matrix * vector using column vectors
131 SkV4 v = {1, 2, 3, 4};
132 SkV4 v1 = m * v;
133 SkV4 v2 = c0 * v.x + c1 * v.y + c2 * v.z + c3 * v.w;
135
136 REPORTER_ASSERT(reporter, (c0 + r0 == SkV4{c0.x+r0.x, c0.y+r0.y, c0.z+r0.z, c0.w+r0.w}));
137 REPORTER_ASSERT(reporter, (c0 - r0 == SkV4{c0.x-r0.x, c0.y-r0.y, c0.z-r0.z, c0.w-r0.w}));
138 REPORTER_ASSERT(reporter, (c0 * r0 == SkV4{c0.x*r0.x, c0.y*r0.y, c0.z*r0.z, c0.w*r0.w}));
139}
Vec2Value v2
static SkM44 Rows(const SkV4 &r0, const SkV4 &r1, const SkV4 &r2, const SkV4 &r3)
Definition: SkM44.h:195
static SkM44 Cols(const SkV4 &c0, const SkV4 &c1, const SkV4 &c2, const SkV4 &c3)
Definition: SkM44.h:203

◆ eq()

static bool eq ( const SkM44 a,
const SkM44 b,
float  tol 
)
static

Definition at line 18 of file M44Test.cpp.

18 {
19 float fa[16], fb[16];
20 a.getColMajor(fa);
21 b.getColMajor(fb);
22 for (int i = 0; i < 16; ++i) {
23 if (!SkScalarNearlyEqual(fa[i], fb[i], tol)) {
24 return false;
25 }
26 }
27 return true;
28}