Flutter Engine
 
Loading...
Searching...
No Matches
dl_matrix_clip_tracker_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
7#include "gtest/gtest.h"
8
9namespace flutter {
10namespace testing {
11
13 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
14 const DlMatrix matrix = DlMatrix::MakeScale({4.0, 4.0, 1.0});
15 const DlRect local_cull_rect = DlRect::MakeLTRB(5, 10, 15, 20);
16
17 DisplayListMatrixClipState state(cull_rect, matrix);
18
19 EXPECT_FALSE(state.using_4x4_matrix());
20 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
21 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
22 EXPECT_EQ(state.matrix(), matrix);
23}
24
26 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
27 // clang-format off
28 const DlMatrix matrix = DlMatrix::MakeRow(4, 0, 0.5, 0,
29 0, 4, 0.5, 0,
30 0, 0, 4.0, 0,
31 0, 0, 0.0, 1);
32 // clang-format on
33 const DlRect local_cull_rect = DlRect::MakeLTRB(5, 10, 15, 20);
34
35 DisplayListMatrixClipState state(cull_rect, DlMatrix());
36 EXPECT_FALSE(state.using_4x4_matrix());
37
38 state.transform(matrix);
39 EXPECT_TRUE(state.using_4x4_matrix());
40 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
41 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
42 EXPECT_EQ(state.matrix(), matrix);
43}
44
46 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
47 // clang-format off
48 const DlMatrix matrix = DlMatrix::MakeRow(4, 0, 0.5, 0,
49 0, 4, 0.5, 0,
50 0, 0, 4.0, 0,
51 0, 0, 0.0, 1);
52 // clang-format on
53 const DlRect local_cull_rect = DlRect::MakeLTRB(5, 10, 15, 20);
54
55 DisplayListMatrixClipState state(cull_rect, DlMatrix());
56 EXPECT_FALSE(state.using_4x4_matrix());
57
58 state.setTransform(matrix);
59 EXPECT_TRUE(state.using_4x4_matrix());
60 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
61 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
62 EXPECT_EQ(state.matrix(), matrix);
63}
64
66 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
67 const DlMatrix matrix = DlMatrix::MakeScale({4.0, 4.0, 1.0});
68 const DlMatrix translated_matrix =
69 matrix * DlMatrix::MakeTranslation({5.0, 1.0});
70 const DlRect local_cull_rect = DlRect::MakeLTRB(0, 9, 10, 19);
71
72 DisplayListMatrixClipState state(cull_rect, matrix);
73 state.translate(5, 1);
74
75 EXPECT_FALSE(state.using_4x4_matrix());
76 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
77 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
78 EXPECT_EQ(state.matrix(), translated_matrix);
79}
80
82 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
83 const DlMatrix matrix = DlMatrix::MakeScale({4.0, 4.0, 1.0});
84 // Scale factor carefully chosen to multiply cleanly and invert
85 // without any non-binary-power-of-2 approximation errors.
86 const DlMatrix scaled_matrix = matrix.Scale({0.5, 2, 1});
87 const DlRect local_cull_rect = DlRect::MakeLTRB(10, 5, 30, 10);
88
89 DisplayListMatrixClipState state(cull_rect, matrix);
90 state.scale(0.5, 2);
91
92 EXPECT_FALSE(state.using_4x4_matrix());
93 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
94 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
95 EXPECT_EQ(state.matrix(), scaled_matrix);
96}
97
99 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
100 const DlMatrix matrix = DlMatrix::MakeScale({4, 4, 1});
101 const DlMatrix skewed_matrix = matrix * DlMatrix::MakeSkew(0.25, 0);
102 const DlRect local_cull_rect = DlRect::MakeLTRB(0, 10, 12.5, 20);
103
104 DisplayListMatrixClipState state(cull_rect, matrix);
105 state.skew(.25, 0);
106
107 EXPECT_FALSE(state.using_4x4_matrix());
108 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
109 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
110 EXPECT_EQ(state.matrix(), skewed_matrix);
111}
112
114 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
115 const DlMatrix matrix = DlMatrix::MakeScale({4, 4, 1});
116 const DlMatrix rotated_matrix =
118 const DlRect local_cull_rect = DlRect::MakeLTRB(10, -15, 20, -5);
119
120 DisplayListMatrixClipState state(cull_rect, matrix);
121 state.rotate(DlDegrees(90));
122
123 EXPECT_FALSE(state.using_4x4_matrix());
124 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
125 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
126 EXPECT_EQ(state.matrix(), rotated_matrix);
127}
128
129TEST(DisplayListMatrixClipState, Transform2DAffine) {
130 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
131 const DlMatrix matrix = DlMatrix::MakeScale({4, 4, 1});
132
133 const DlMatrix transformed_matrix = //
134 matrix * DlMatrix::MakeRow(2, 0, 0, 5, //
135 0, 2, 0, 6, //
136 0, 0, 1, 0, //
137 0, 0, 0, 1);
138 const DlRect local_cull_rect = DlRect::MakeLTRB(0, 2, 5, 7);
139
140 DisplayListMatrixClipState state(cull_rect, matrix);
141 state.transform2DAffine(2, 0, 5, //
142 0, 2, 6);
143
144 EXPECT_FALSE(state.using_4x4_matrix());
145 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
146 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
147 EXPECT_EQ(state.matrix(), transformed_matrix);
148}
149
150TEST(DisplayListMatrixClipState, TransformFullPerspectiveUsing3x3Matrix) {
151 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
152 const DlMatrix matrix = DlMatrix::MakeScale({4, 4, 1});
153
154 const DlMatrix transformed_matrix = //
155 matrix * DlMatrix::MakeRow(2, 0, 0, 5, //
156 0, 2, 0, 6, //
157 0, 0, 1, 0, //
158 0, 0, 0, 1);
159 const DlRect local_cull_rect = DlRect::MakeLTRB(0, 2, 5, 7);
160
161 DisplayListMatrixClipState state(cull_rect, matrix);
162 state.transformFullPerspective(2, 0, 0, 5, //
163 0, 2, 0, 6, //
164 0, 0, 1, 0, //
165 0, 0, 0, 1);
166
167 EXPECT_FALSE(state.using_4x4_matrix());
168 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
169 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
170 EXPECT_EQ(state.matrix(), transformed_matrix);
171}
172
173TEST(DisplayListMatrixClipState, TransformFullPerspectiveUsing4x4Matrix) {
174 const DlRect cull_rect = DlRect::MakeLTRB(20, 40, 60, 80);
175 const DlMatrix matrix = DlMatrix::MakeScale({4, 4, 1});
176
177 const DlMatrix transformed_matrix = //
178 matrix * DlMatrix::MakeRow(2, 0, 0, 5, //
179 0, 2, 0, 6, //
180 0, 0, 1, 7, //
181 0, 0, 0, 1);
182 const DlRect local_cull_rect = DlRect::MakeLTRB(0, 2, 5, 7);
183
184 DisplayListMatrixClipState state(cull_rect, matrix);
185 state.transformFullPerspective(2, 0, 0, 5, //
186 0, 2, 0, 6, //
187 0, 0, 1, 7, //
188 0, 0, 0, 1);
189
190 EXPECT_TRUE(state.using_4x4_matrix());
191 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect);
192 EXPECT_EQ(state.GetLocalCullCoverage(), local_cull_rect);
193 EXPECT_EQ(state.matrix(), transformed_matrix);
194}
195
197 DlRect cull_rect = DlRect::MakeLTRB(20, 20, 40, 40);
198
199 auto non_reducing = [&cull_rect](const DlRect& diff_rect,
200 const std::string& label) {
201 {
202 DisplayListMatrixClipState state(cull_rect, DlMatrix());
203 state.clipRect(diff_rect, DlClipOp::kDifference, false);
204 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect) << label;
205 }
206 {
207 DisplayListMatrixClipState state(cull_rect, DlMatrix());
208 const DlRoundRect diff_rrect = DlRoundRect::MakeRect(diff_rect);
209 state.clipRRect(diff_rrect, DlClipOp::kDifference, false);
210 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect)
211 << label << " (RRect)";
212 }
213 {
214 DisplayListMatrixClipState state(cull_rect, DlMatrix());
215 const DlPath diff_path = DlPath::MakeRect(diff_rect);
216 state.clipPath(diff_path, DlClipOp::kDifference, false);
217 EXPECT_EQ(state.GetDeviceCullCoverage(), cull_rect)
218 << label << " (RRect)";
219 }
220 };
221
222 auto reducing = [&cull_rect](const DlRect& diff_rect,
223 const DlRect& result_rect,
224 const std::string& label) {
225 EXPECT_TRUE(result_rect.IsEmpty() || cull_rect.Contains(result_rect));
226 {
227 DisplayListMatrixClipState state(cull_rect, DlMatrix());
228 state.clipRect(diff_rect, DlClipOp::kDifference, false);
229 EXPECT_EQ(state.GetDeviceCullCoverage(), result_rect) << label;
230 }
231 {
232 DisplayListMatrixClipState state(cull_rect, DlMatrix());
233 const DlRoundRect diff_rrect = DlRoundRect::MakeRect(diff_rect);
234 state.clipRRect(diff_rrect, DlClipOp::kDifference, false);
235 EXPECT_EQ(state.GetDeviceCullCoverage(), result_rect)
236 << label << " (RRect)";
237 }
238 {
239 DisplayListMatrixClipState state(cull_rect, DlMatrix());
240 const DlPath diff_path = DlPath::MakeRect(diff_rect);
241 state.clipPath(diff_path, DlClipOp::kDifference, false);
242 EXPECT_EQ(state.GetDeviceCullCoverage(), result_rect)
243 << label << " (RRect)";
244 }
245 };
246
247 // Skim the corners and edge
248 non_reducing(DlRect::MakeLTRB(10, 10, 20, 20), "outside UL corner");
249 non_reducing(DlRect::MakeLTRB(20, 10, 40, 20), "Above");
250 non_reducing(DlRect::MakeLTRB(40, 10, 50, 20), "outside UR corner");
251 non_reducing(DlRect::MakeLTRB(40, 20, 50, 40), "Right");
252 non_reducing(DlRect::MakeLTRB(40, 40, 50, 50), "outside LR corner");
253 non_reducing(DlRect::MakeLTRB(20, 40, 40, 50), "Below");
254 non_reducing(DlRect::MakeLTRB(10, 40, 20, 50), "outside LR corner");
255 non_reducing(DlRect::MakeLTRB(10, 20, 20, 40), "Left");
256
257 // Overlap corners
258 non_reducing(DlRect::MakeLTRB(15, 15, 25, 25), "covering UL corner");
259 non_reducing(DlRect::MakeLTRB(35, 15, 45, 25), "covering UR corner");
260 non_reducing(DlRect::MakeLTRB(35, 35, 45, 45), "covering LR corner");
261 non_reducing(DlRect::MakeLTRB(15, 35, 25, 45), "covering LL corner");
262
263 // Overlap edges, but not across an entire side
264 non_reducing(DlRect::MakeLTRB(20, 15, 39, 25), "Top edge left-biased");
265 non_reducing(DlRect::MakeLTRB(21, 15, 40, 25), "Top edge, right biased");
266 non_reducing(DlRect::MakeLTRB(35, 20, 45, 39), "Right edge, top-biased");
267 non_reducing(DlRect::MakeLTRB(35, 21, 45, 40), "Right edge, bottom-biased");
268 non_reducing(DlRect::MakeLTRB(20, 35, 39, 45), "Bottom edge, left-biased");
269 non_reducing(DlRect::MakeLTRB(21, 35, 40, 45), "Bottom edge, right-biased");
270 non_reducing(DlRect::MakeLTRB(15, 20, 25, 39), "Left edge, top-biased");
271 non_reducing(DlRect::MakeLTRB(15, 21, 25, 40), "Left edge, bottom-biased");
272
273 // Slice all the way through the middle
274 non_reducing(DlRect::MakeLTRB(25, 15, 35, 45), "Vertical interior slice");
275 non_reducing(DlRect::MakeLTRB(15, 25, 45, 35), "Horizontal interior slice");
276
277 // Slice off each edge
278 reducing(DlRect::MakeLTRB(20, 15, 40, 25), //
279 DlRect::MakeLTRB(20, 25, 40, 40), //
280 "Slice off top");
281 reducing(DlRect::MakeLTRB(35, 20, 45, 40), //
282 DlRect::MakeLTRB(20, 20, 35, 40), //
283 "Slice off right");
284 reducing(DlRect::MakeLTRB(20, 35, 40, 45), //
285 DlRect::MakeLTRB(20, 20, 40, 35), //
286 "Slice off bottom");
287 reducing(DlRect::MakeLTRB(15, 20, 25, 40), //
288 DlRect::MakeLTRB(25, 20, 40, 40), //
289 "Slice off left");
290
291 // cull rect contains diff rect
292 non_reducing(DlRect::MakeLTRB(21, 21, 39, 39), "Contained, non-covering");
293
294 // cull rect equals diff rect results in empty
295 reducing(cull_rect, DlRect(), "Perfectly covering");
296
297 // diff rect contains cull rect results in empty
298 reducing(DlRect::MakeLTRB(15, 15, 45, 45), DlRect(), "Smothering");
299}
300
301TEST(DisplayListMatrixClipState, MapAndClipRectTranslation) {
302 DlRect cull_rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
303 DlMatrix matrix = DlMatrix::MakeTranslation({10.0f, 20.0f, 1.0f});
304 DisplayListMatrixClipState state(cull_rect, matrix);
305
306 {
307 // Empty width in src rect (before and after translation)
308 DlRect rect = DlRect::MakeLTRB(150.0f, 150.0f, 150.0f, 160.0f);
309 EXPECT_FALSE(state.mapAndClipRect(&rect));
310 EXPECT_TRUE(rect.IsEmpty());
311 }
312
313 {
314 // Empty height in src rect (before and after translation)
315 DlRect rect = DlRect::MakeLTRB(150.0f, 150.0f, 160.0f, 150.0f);
316 EXPECT_FALSE(state.mapAndClipRect(&rect));
317 EXPECT_TRUE(rect.IsEmpty());
318 }
319
320 {
321 // rect far outside of clip, even after translation
322 DlRect rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
323 EXPECT_FALSE(state.mapAndClipRect(&rect));
324 EXPECT_TRUE(rect.IsEmpty());
325 }
326
327 {
328 // Rect abuts clip left side after translation
329 DlRect rect = DlRect::MakeLTRB(80.0f, 100.0f, 90.0f, 110.0f);
330 EXPECT_FALSE(state.mapAndClipRect(&rect));
331 EXPECT_TRUE(rect.IsEmpty());
332 }
333
334 {
335 // Rect barely grazes clip left side after translation
336 DlRect rect = DlRect::MakeLTRB(80.0f, 100.0f, 91.0f, 110.0f);
337 EXPECT_TRUE(state.mapAndClipRect(&rect));
338 EXPECT_EQ(rect, DlRect::MakeLTRB(100.0f, 120.0f, 101.0f, 130.0f));
339 }
340
341 {
342 // Rect abuts clip top after translation
343 DlRect rect = DlRect::MakeLTRB(100.0f, 70.0f, 110.0f, 80.0f);
344 EXPECT_FALSE(state.mapAndClipRect(&rect));
345 EXPECT_TRUE(rect.IsEmpty());
346 }
347
348 {
349 // Rect barely grazes clip top after translation
350 DlRect rect = DlRect::MakeLTRB(100.0f, 70.0f, 110.0f, 81.0f);
351 EXPECT_TRUE(state.mapAndClipRect(&rect));
352 EXPECT_EQ(rect, DlRect::MakeLTRB(110.0f, 100.0f, 120.0f, 101.0f));
353 }
354
355 {
356 // Rect abuts clip right side after translation
357 DlRect rect = DlRect::MakeLTRB(190.0f, 100.0f, 200.0f, 110.0f);
358 EXPECT_FALSE(state.mapAndClipRect(&rect));
359 EXPECT_TRUE(rect.IsEmpty());
360 }
361
362 {
363 // Rect barely grazes clip right side after translation
364 DlRect rect = DlRect::MakeLTRB(189.0f, 100.0f, 200.0f, 110.0f);
365 EXPECT_TRUE(state.mapAndClipRect(&rect));
366 EXPECT_EQ(rect, DlRect::MakeLTRB(199.0f, 120.0f, 200.0f, 130.0f));
367 }
368
369 {
370 // Rect abuts clip bottom after translation
371 DlRect rect = DlRect::MakeLTRB(100.0f, 180.0f, 110.0f, 190.0f);
372 EXPECT_FALSE(state.mapAndClipRect(&rect));
373 EXPECT_TRUE(rect.IsEmpty());
374 }
375
376 {
377 // Rect barely grazes clip bottom after translation
378 DlRect rect = DlRect::MakeLTRB(100.0f, 179.0f, 110.0f, 190.0f);
379 EXPECT_TRUE(state.mapAndClipRect(&rect));
380 EXPECT_EQ(rect, DlRect::MakeLTRB(110.0f, 199.0f, 120.0f, 200.0f));
381 }
382}
383
384TEST(DisplayListMatrixClipState, MapAndClipRectScale) {
385 DlRect cull_rect = DlRect::MakeLTRB(100.0f, 100.0f, 500.0f, 500.0f);
386 DlMatrix matrix = DlMatrix::MakeScale({2.0f, 4.0f, 1.0f});
387 DisplayListMatrixClipState state(cull_rect, matrix);
388
389 {
390 // Empty width in src rect (before and after scaling)
391 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 100.0f, 110.0f);
392 EXPECT_FALSE(state.mapAndClipRect(&rect));
393 EXPECT_TRUE(rect.IsEmpty());
394 }
395
396 {
397 // Empty height in src rect (before and after scaling)
398 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 110.0f, 100.0f);
399 EXPECT_FALSE(state.mapAndClipRect(&rect));
400 EXPECT_TRUE(rect.IsEmpty());
401 }
402
403 {
404 // rect far outside of clip, even after scaling
405 DlRect rect = DlRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
406 EXPECT_FALSE(state.mapAndClipRect(&rect));
407 EXPECT_TRUE(rect.IsEmpty());
408 }
409
410 {
411 // Rect abuts clip left side after scaling
412 DlRect rect = DlRect::MakeLTRB(40.0f, 100.0f, 50.0f, 110.0f);
413 EXPECT_FALSE(state.mapAndClipRect(&rect));
414 EXPECT_TRUE(rect.IsEmpty());
415 }
416
417 {
418 // Rect barely grazes clip left side after scaling
419 DlRect rect = DlRect::MakeLTRB(40.0f, 100.0f, 51.0f, 110.0f);
420 EXPECT_TRUE(state.mapAndClipRect(&rect));
421 EXPECT_EQ(rect, DlRect::MakeLTRB(100.0f, 400.0f, 102.0f, 440.0f));
422 }
423
424 {
425 // Rect abuts clip top after scaling
426 DlRect rect = DlRect::MakeLTRB(100.0f, 15.0f, 110.0f, 25.0f);
427 EXPECT_FALSE(state.mapAndClipRect(&rect));
428 EXPECT_TRUE(rect.IsEmpty());
429 }
430
431 {
432 // Rect barely grazes clip top after scaling
433 DlRect rect = DlRect::MakeLTRB(100.0f, 15.0f, 110.0f, 26.0f);
434 EXPECT_TRUE(state.mapAndClipRect(&rect));
435 EXPECT_EQ(rect, DlRect::MakeLTRB(200.0f, 100.0f, 220.0f, 104.0f));
436 }
437
438 {
439 // Rect abuts clip right side after scaling
440 DlRect rect = DlRect::MakeLTRB(250.0f, 100.0f, 260.0f, 110.0f);
441 EXPECT_FALSE(state.mapAndClipRect(&rect));
442 EXPECT_TRUE(rect.IsEmpty());
443 }
444
445 {
446 // Rect barely grazes clip right side after scaling
447 DlRect rect = DlRect::MakeLTRB(249.0f, 100.0f, 260.0f, 110.0f);
448 EXPECT_TRUE(state.mapAndClipRect(&rect));
449 EXPECT_EQ(rect, DlRect::MakeLTRB(498.0f, 400.0f, 500.0f, 440.0f));
450 }
451
452 {
453 // Rect abuts clip bottom after scaling
454 DlRect rect = DlRect::MakeLTRB(100.0f, 125.0f, 110.0f, 135.0f);
455 EXPECT_FALSE(state.mapAndClipRect(&rect));
456 EXPECT_TRUE(rect.IsEmpty());
457 }
458
459 {
460 // Rect barely grazes clip bottom after scaling
461 DlRect rect = DlRect::MakeLTRB(100.0f, 124.0f, 110.0f, 135.0f);
462 EXPECT_TRUE(state.mapAndClipRect(&rect));
463 EXPECT_EQ(rect, DlRect::MakeLTRB(200.0f, 496.0f, 220.0f, 500.0f));
464 }
465}
466
468 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
469 DisplayListMatrixClipState state(rect);
470
471 auto test_rect = [&state](const DlRect& test_rect, bool expect) {
472 EXPECT_EQ(state.rect_covers_cull(test_rect), expect) << test_rect;
474 test_rect, state.matrix(), state.GetDeviceCullCoverage()),
475 expect)
476 << test_rect;
477 };
478
479 test_rect(rect, true);
480 test_rect(rect.Expand(0.1f, 0.0f, 0.0f, 0.0f), true);
481 test_rect(rect.Expand(0.0f, 0.1f, 0.0f, 0.0f), true);
482 test_rect(rect.Expand(0.0f, 0.0f, 0.1f, 0.0f), true);
483 test_rect(rect.Expand(0.0f, 0.0f, 0.0f, 0.1f), true);
484 test_rect(rect.Expand(-0.1f, 0.0f, 0.0f, 0.0f), false);
485 test_rect(rect.Expand(0.0f, -0.1f, 0.0f, 0.0f), false);
486 test_rect(rect.Expand(0.0f, 0.0f, -0.1f, 0.0f), false);
487 test_rect(rect.Expand(0.0f, 0.0f, 0.0f, -0.1f), false);
488}
489
490TEST(DisplayListMatrixClipState, RectCoverageAccuracy) {
491 // These particular values create bit errors if we use the path that
492 // tests for inclusion in local space, but work OK if we use a forward
493 // path that tests for inclusion in device space, due to the fact that
494 // the extra matrix inversion is just enough math to cause the transform
495 // to place the local space cull corners just outside the original rect.
496 // The test in device space only works under a simple scale, such as we
497 // use for DPR adjustments (and which are not always inversion friendly).
498
499 DlRect cull = DlRect::MakeLTRB(0.0f, 0.0f, 1080.0f, 2400.0f);
500 DlScalar DPR = 2.625;
501 DlRect rect = DlRect::MakeLTRB(0.0f, 0.0f, 1080.0f / DPR, 2400.0f / DPR);
502
503 DisplayListMatrixClipState state(cull);
504 state.scale(DPR, DPR);
505
506 auto test_rect = [&state](const DlRect& test_rect, bool expect) {
507 EXPECT_EQ(state.rect_covers_cull(test_rect), expect) << test_rect;
509 test_rect, state.matrix(), state.GetDeviceCullCoverage()),
510 expect)
511 << test_rect;
512 };
513
514 test_rect(rect, true);
515 test_rect(rect.Expand(0.1f, 0.0f, 0.0f, 0.0f), true);
516 test_rect(rect.Expand(0.0f, 0.1f, 0.0f, 0.0f), true);
517 test_rect(rect.Expand(0.0f, 0.0f, 0.1f, 0.0f), true);
518 test_rect(rect.Expand(0.0f, 0.0f, 0.0f, 0.1f), true);
519 test_rect(rect.Expand(-0.1f, 0.0f, 0.0f, 0.0f), false);
520 test_rect(rect.Expand(0.0f, -0.1f, 0.0f, 0.0f), false);
521 test_rect(rect.Expand(0.0f, 0.0f, -0.1f, 0.0f), false);
522 test_rect(rect.Expand(0.0f, 0.0f, 0.0f, -0.1f), false);
523}
524
525TEST(DisplayListMatrixClipState, RectCoverageUnderScale) {
526 DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
527 DisplayListMatrixClipState state(rect);
528 state.scale(2.0f, 2.0f);
529
530 auto test_rect = [&state](const DlRect& test_rect, bool expect) {
531 EXPECT_EQ(state.rect_covers_cull(test_rect), expect) << test_rect;
533 test_rect, state.matrix(), state.GetDeviceCullCoverage()),
534 expect)
535 << test_rect;
536 };
537
538 test_rect(DlRect::MakeLTRB(100, 100, 200, 200), false);
539 test_rect(DlRect::MakeLTRB(50, 50, 100, 100), true);
540 test_rect(DlRect::MakeLTRB(49, 50, 100, 100), true);
541 test_rect(DlRect::MakeLTRB(50, 49, 100, 100), true);
542 test_rect(DlRect::MakeLTRB(50, 50, 101, 100), true);
543 test_rect(DlRect::MakeLTRB(50, 50, 100, 101), true);
544 test_rect(DlRect::MakeLTRB(51, 50, 100, 100), false);
545 test_rect(DlRect::MakeLTRB(50, 51, 100, 100), false);
546 test_rect(DlRect::MakeLTRB(50, 50, 99, 100), false);
547 test_rect(DlRect::MakeLTRB(50, 50, 100, 99), false);
548}
549
550TEST(DisplayListMatrixClipState, RectCoverageUnderRotation) {
551 DlRect rect = DlRect::MakeLTRB(-1.0f, -1.0f, 1.0f, 1.0f);
552 DlRect cull = rect.Scale(impeller::kSqrt2 * 25);
553 DlRect test = rect.Scale(50.0f);
554 DlRect test_true = test.Expand(0.002f);
555 DlRect test_false = test.Expand(-0.002f);
556
557 for (int i = 0; i <= 360; i++) {
558 DisplayListMatrixClipState state(cull);
559 state.rotate(DlDegrees(i));
560 EXPECT_TRUE(state.rect_covers_cull(test_true))
561 << " testing " << test_true << std::endl
562 << " contains " << state.GetLocalCullCoverage() << std::endl
563 << " at " << i << " degrees";
565 test_true, DlMatrix::MakeRotationZ(DlDegrees(i)), cull))
566 << " testing " << test_true << std::endl
567 << " contains " << state.GetLocalCullCoverage() << std::endl
568 << " at " << i << " degrees";
569 if ((i % 90) == 45) {
570 // The cull rect is largest when viewed at multiples of 45
571 // degrees so we will fail to contain it at those angles
572 EXPECT_FALSE(state.rect_covers_cull(test_false))
573 << " testing " << test_false << std::endl
574 << " contains " << state.GetLocalCullCoverage() << std::endl
575 << " at " << i << " degrees";
577 test_false, DlMatrix::MakeRotationZ(DlDegrees(i)), cull))
578 << " testing " << test_false << std::endl
579 << " contains " << state.GetLocalCullCoverage() << std::endl
580 << " at " << i << " degrees";
581 } else {
582 // At other angles, the cull rect is not quite so big as to encroach
583 // upon the expanded test rectangle.
584 EXPECT_TRUE(state.rect_covers_cull(test_false))
585 << " testing " << test_false << std::endl
586 << " contains " << state.GetLocalCullCoverage() << std::endl
587 << " at " << i << " degrees";
589 test_false, DlMatrix::MakeRotationZ(DlDegrees(i)), cull))
590 << " testing " << test_false << std::endl
591 << " contains " << state.GetLocalCullCoverage() << std::endl
592 << " at " << i << " degrees";
593 }
594 }
595}
596
598 DlRect cull = DlRect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
599 DisplayListMatrixClipState state(cull);
600 // The cull rect corners will be at (50, 50) so the oval needs to have
601 // a radius large enough to cover that - sqrt(2*50*50) == sqrt(2) * 50
602 // We pad by an ever so slight 0.02f to account for round off error and
603 // then use larger expansion/contractions of 0.1f to cover/not-cover it.
604 DlRect test = cull.Scale(impeller::kSqrt2).Expand(0.02f);
605
606 auto test_oval = [&state](const DlRect& test_rect, bool expect) {
607 EXPECT_EQ(state.oval_covers_cull(test_rect), expect) << test_rect;
609 test_rect, state.matrix(), state.GetDeviceCullCoverage()),
610 expect)
611 << test_rect;
612 };
613
614 test_oval(test, true);
615 test_oval(test.Expand(0.1f, 0.0f, 0.0f, 0.0f), true);
616 test_oval(test.Expand(0.0f, 0.1f, 0.0f, 0.0f), true);
617 test_oval(test.Expand(0.0f, 0.0f, 0.1f, 0.0f), true);
618 test_oval(test.Expand(0.0f, 0.0f, 0.0f, 0.1f), true);
619 test_oval(test.Expand(-0.1f, 0.0f, 0.0f, 0.0f), false);
620 test_oval(test.Expand(0.0f, -0.1f, 0.0f, 0.0f), false);
621 test_oval(test.Expand(0.0f, 0.0f, -0.1f, 0.0f), false);
622 test_oval(test.Expand(0.0f, 0.0f, 0.0f, -0.1f), false);
623}
624
625TEST(DisplayListMatrixClipState, OvalCoverageUnderScale) {
626 DlRect cull = DlRect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
627 DisplayListMatrixClipState state(cull);
628 state.scale(2.0f, 2.0f);
629 // The cull rect corners will be at (50, 50) so the oval needs to have
630 // a radius large enough to cover that - sqrt(2*50*50) == sqrt(2) * 50
631 // We pad by an ever so slight 0.02f to account for round off error and
632 // then use larger expansion/contractions of 0.1f to cover/not-cover it.
633 // We combine that with an additional scale 0.5f since we are viewing
634 // the cull rect under a 2.0 scale.
635 DlRect test = cull.Scale(0.5f * impeller::kSqrt2).Expand(0.02f);
636
637 auto test_oval = [&state](const DlRect& test_rect, bool expect) {
638 EXPECT_EQ(state.oval_covers_cull(test_rect), expect) << test_rect;
640 test_rect, state.matrix(), state.GetDeviceCullCoverage()),
641 expect)
642 << test_rect;
643 };
644
645 test_oval(test, true);
646 test_oval(test.Expand(0.1f, 0.0f, 0.0f, 0.0f), true);
647 test_oval(test.Expand(0.0f, 0.1f, 0.0f, 0.0f), true);
648 test_oval(test.Expand(0.0f, 0.0f, 0.1f, 0.0f), true);
649 test_oval(test.Expand(0.0f, 0.0f, 0.0f, 0.1f), true);
650 test_oval(test.Expand(-0.1f, 0.0f, 0.0f, 0.0f), false);
651 test_oval(test.Expand(0.0f, -0.1f, 0.0f, 0.0f), false);
652 test_oval(test.Expand(0.0f, 0.0f, -0.1f, 0.0f), false);
653 test_oval(test.Expand(0.0f, 0.0f, 0.0f, -0.1f), false);
654}
655
656TEST(DisplayListMatrixClipState, OvalCoverageUnderRotation) {
657 DlRect unit = DlRect::MakeLTRB(-1.0f, -1.0f, 1.0f, 1.0f);
658 DlRect cull = unit.Scale(50.0f);
659 // See above, test bounds need to be sqrt(2) larger for the inscribed
660 // oval to contain the cull rect. These tests are simpler than the scaled
661 // rectangle coverage tests because this expanded test oval will
662 // precisely cover the cull rect at all angles.
663 DlRect test = cull.Scale(impeller::kSqrt2);
664 DlRect test_true = test.Expand(0.002f);
665 DlRect test_false = test.Expand(-0.002f);
666
667 for (int i = 0; i <= 360; i++) {
668 DisplayListMatrixClipState state(cull);
669 state.rotate(DlDegrees(i));
670 EXPECT_TRUE(state.oval_covers_cull(test_true))
671 << " testing " << test_true << std::endl
672 << " contains " << state.GetLocalCullCoverage() << std::endl
673 << " at " << i << " degrees";
674 EXPECT_FALSE(state.oval_covers_cull(test_false))
675 << " testing " << test_false << std::endl
676 << " contains " << state.GetLocalCullCoverage() << std::endl
677 << " at " << i << " degrees";
678
680 test_true, DlMatrix::MakeRotationZ(DlDegrees(i)), cull))
681 << " testing " << test_true << std::endl
682 << " contains " << state.GetLocalCullCoverage() << std::endl
683 << " at " << i << " degrees";
685 test_false, DlMatrix::MakeRotationZ(DlDegrees(i)), cull))
686 << " testing " << test_false << std::endl
687 << " contains " << state.GetLocalCullCoverage() << std::endl
688 << " at " << i << " degrees";
689 }
690}
691
693 DlRect cull = DlRect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
694 DisplayListMatrixClipState state(cull);
695 // test_bounds need to contain
696 DlRect test = cull.Expand(2.0f, 2.0f);
697
698 // RRect of cull with no corners covers
699 EXPECT_TRUE(
700 state.rrect_covers_cull(DlRoundRect::MakeRectXY(cull, 0.0f, 0.0f)));
702 DlRoundRect::MakeRectXY(cull, 0.0f, 0.0f), DlMatrix(), cull));
703
704 // RRect of cull with even the tiniest corners does not cover
705 EXPECT_FALSE(
706 state.rrect_covers_cull(DlRoundRect::MakeRectXY(cull, 0.01f, 0.01f)));
708 DlRoundRect::MakeRectXY(cull, 0.01f, 0.01f), DlMatrix(), cull));
709
710 // Expanded by 2.0 and then with a corner of 2.0 obviously still covers
711 EXPECT_TRUE(
712 state.rrect_covers_cull(DlRoundRect::MakeRectXY(test, 2.0f, 2.0f)));
714 DlRoundRect::MakeRectXY(test, 2.0f, 2.0f), DlMatrix(), cull));
715
716 // The corner point of the cull rect is at (c-2, c-2) relative to the
717 // corner of the rrect bounds so we compute its distance to the center
718 // of the circular part and compare it to the radius of the corner (c)
719 // to find the corner radius where it will start to leave the rounded
720 // rectangle:
721 //
722 // +----------- +
723 // | __---^^ |
724 // | +/------- + |
725 // | / \ | c
726 // | /| \ c-2 |
727 // |/ | \ | |
728 // || | * + +
729 //
730 // sqrt(2*(c-2)*(c-2)) > c
731 // 2*(c-2)*(c-2) > c*c
732 // 2*(cc - 4c + 4) > cc
733 // 2cc - 8c + 8 > cc
734 // cc - 8c + 8 > 0
735 // c > 8 +/- sqrt(64 - 32) / 2
736 // c > ~6.828
737 // corners set to 6.82 should still cover the cull rect
738 EXPECT_TRUE(
739 state.rrect_covers_cull(DlRoundRect::MakeRectXY(test, 6.82f, 6.82f)));
741 DlRoundRect::MakeRectXY(test, 6.82f, 6.82f), DlMatrix(), cull));
742
743 // but corners set to 6.83 should not cover the cull rect
744 EXPECT_FALSE(
745 state.rrect_covers_cull(DlRoundRect::MakeRectXY(test, 6.84f, 6.84f)));
747 DlRoundRect::MakeRectXY(test, 6.84f, 6.84f), DlMatrix(), cull));
748}
749
750} // namespace testing
751} // namespace flutter
void translate(DlScalar tx, DlScalar ty)
static bool TransformedRectCoversBounds(const DlRect &local_rect, const DlMatrix &matrix, const DlRect &cull_bounds)
Checks if the local rect, when transformed by the matrix, completely covers the indicated culling bou...
void transform2DAffine(DlScalar mxx, DlScalar mxy, DlScalar mxt, DlScalar myx, DlScalar myy, DlScalar myt)
void scale(DlScalar sx, DlScalar sy)
void clipRect(const DlRect &rect, DlClipOp op, bool is_aa)
bool rrect_covers_cull(const DlRoundRect &content) const
void clipRRect(const DlRoundRect &rrect, DlClipOp op, bool is_aa)
void transformFullPerspective(DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt, DlScalar myx, DlScalar myy, DlScalar myz, DlScalar myt, DlScalar mzx, DlScalar mzy, DlScalar mzz, DlScalar mzt, DlScalar mwx, DlScalar mwy, DlScalar mwz, DlScalar mwt)
void setTransform(const DlMatrix &matrix)
static bool TransformedRRectCoversBounds(const DlRoundRect &local_rrect, const DlMatrix &matrix, const DlRect &cull_bounds)
Checks if the local round rect, when transformed by the matrix, completely covers the indicated culli...
void transform(const DlMatrix &matrix)
void clipPath(const DlPath &path, DlClipOp op, bool is_aa)
bool oval_covers_cull(const DlRect &content_bounds) const
bool rect_covers_cull(const DlRect &content) const
void skew(DlScalar skx, DlScalar sky)
static bool TransformedOvalCoversBounds(const DlRect &local_oval_bounds, const DlMatrix &matrix, const DlRect &cull_bounds)
Checks if an oval defined by the local bounds, when transformed by the matrix, completely covers the ...
static DlPath MakeRect(const DlRect &rect)
Definition dl_path.cc:39
TEST(NativeAssetsManagerTest, NoAvailableAssets)
impeller::Scalar DlScalar
impeller::Matrix DlMatrix
impeller::Rect DlRect
impeller::Degrees DlDegrees
constexpr float kSqrt2
Definition constants.h:47
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
static constexpr Matrix MakeRow(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition matrix.h:83
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition matrix.h:127
constexpr Matrix Scale(const Vector3 &s) const
Definition matrix.h:275
static Matrix MakeRotationZ(Radians r)
Definition matrix.h:223
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition round_rect.h:31
static RoundRect MakeRect(const Rect &rect)
Definition round_rect.h:19
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition rect.h:297
constexpr bool Contains(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the half-open interior of this rectangle.
Definition rect.h:231
constexpr TRect Scale(Type scale) const
Definition rect.h:202
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition rect.h:618
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129