| Flutter Impeller |

aiks_dl_shadow_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
5 #include "flutter/impeller/display_list/aiks_unittests.h"
6
7 #include "flutter/display_list/dl_builder.h"
8 #include "flutter/display_list/dl_color.h"
9 #include "flutter/display_list/dl_paint.h"
10 #include "flutter/display_list/geometry/dl_path_builder.h"
11 #include "flutter/impeller/entity/geometry/shadow_path_geometry.h"
12 #include "flutter/testing/testing.h"
13
14 namespace impeller {
15 namespace testing {
16
17 using namespace flutter;
18
19 namespace {
20 /// @brief Reflect the segments of a path around a coordinate using the
21 /// PathReceiver interface.
22 class PathReflector : public PathReceiver {
23 public:
24 /// Reflect a path horizontally around the given x coordinate.
25 static PathReflector ReflectAroundX(Scalar x_coordinate) {
26 return PathReflector(-1.0f, x_coordinate * 2.0f, 1.0f, 0.0f);
27 }
28
29 /// Reflect a path vertically around the given y coordinate.
30 static PathReflector ReflectAroundY(Scalar y_coordinate) {
31 return PathReflector(1.0f, 0.0f, -1.0f, y_coordinate * 2.0f);
32 }
33
34 /// Reflect a path horizontally and vertically around the given coordinate.
35 static PathReflector ReflectAround(const Point& anchor) {
36 return PathReflector(-1.0f, anchor.x * 2.0f, -1.0f, anchor.y * 2.0f);
37 }
38
39 // |PathReceiver|
40 void MoveTo(const Point& p2, bool will_be_closed) override {
41 path_builder_.MoveTo(reflect(p2));
42 }
43
44 // |PathReceiver|
45 void LineTo(const Point& p2) override { path_builder_.LineTo(reflect(p2)); }
46
47 // |PathReceiver|
48 void QuadTo(const Point& cp, const Point& p2) override {
49 path_builder_.QuadraticCurveTo(reflect(cp), reflect(p2));
50 }
51
52 // |PathReceiver|
53 bool ConicTo(const Point& cp, const Point& p2, Scalar weight) override {
54 path_builder_.ConicCurveTo(reflect(cp), reflect(p2), weight);
55 return true;
56 }
57
58 // |PathReceiver|
59 void CubicTo(const Point& cp1, const Point& cp2, const Point& p2) override {
60 path_builder_.CubicCurveTo(reflect(cp1), reflect(cp2), reflect(p2));
61 }
62
63 // |PathReceiver|
64 void Close() override { path_builder_.Close(); }
65
66 DlPath TakePath() { return path_builder_.TakePath(); }
67
68 private:
69 PathReflector(Scalar scale_x,
70 Scalar translate_x,
71 Scalar scale_y,
72 Scalar translate_y)
73 : scale_x_(scale_x),
74 translate_x_(translate_x),
75 scale_y_(scale_y),
76 translate_y_(translate_y) {}
77
78 const Scalar scale_x_;
79 const Scalar translate_x_;
80 const Scalar scale_y_;
81 const Scalar translate_y_;
82
83 DlPoint reflect(const DlPoint& in_point) {
84 return DlPoint(in_point.x * scale_x_ + translate_x_,
85 in_point.y * scale_y_ + translate_y_);
86 }
87
88 DlPathBuilder path_builder_;
89 };
90
91 DlPath ReflectPath(const DlPath& path) {
92 PathReflector reflector =
93 PathReflector::ReflectAroundY(path.GetBounds().GetCenter().y);
94 path.Dispatch(reflector);
95 return reflector.TakePath();
96 }
97
98 void DrawShadowMesh(DisplayListBuilder& builder,
99 const DlPath& path,
100 Scalar elevation,
101 Scalar dpr) {
102 bool should_optimize = path.IsConvex();
103 Matrix matrix = builder.GetMatrix();
104
105 // From dl_dispatcher, making a MaskFilter.
106 Scalar light_radius = 800 / 600;
107 EXPECT_EQ(light_radius, 1.0f); // Value in dl_dispatcher is bad.
108 Scalar occluder_z = elevation * dpr;
109 Radius radius = Radius{light_radius * occluder_z / matrix.GetScale().y};
110 Sigma sigma = radius;
111
112 // From canvas.cc computing the device radius.
113 Scalar device_radius = sigma.sigma * 2.8 * matrix.GetMaxBasisLengthXY();
114
115 Tessellator tessellator;
116 std::shared_ptr<ShadowVertices> shadow_vertices =
117 ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path,
118 device_radius, matrix);
119 EXPECT_EQ(shadow_vertices != nullptr, should_optimize);
120 Point shadow_translate = Point(0, occluder_z) * matrix.Invert().GetScale().y;
121
122 DlPaint paint;
123 paint.setDrawStyle(DlDrawStyle::kStroke);
124 paint.setColor(DlColor::kDarkGrey());
125
126 if (shadow_vertices) {
127 builder.Save();
128 builder.Translate(shadow_translate.x, shadow_translate.y);
129 auto indices = shadow_vertices->GetIndices();
130 auto vertices = shadow_vertices->GetVertices();
131 DlPathBuilder mesh_builder;
132 for (size_t i = 0; i < shadow_vertices->GetIndexCount(); i += 3) {
133 mesh_builder.MoveTo(vertices[indices[i + 0]]);
134 mesh_builder.LineTo(vertices[indices[i + 1]]);
135 mesh_builder.LineTo(vertices[indices[i + 2]]);
136 mesh_builder.Close();
137 }
138 DlPath mesh_path = mesh_builder.TakePath();
139 builder.DrawPath(mesh_path, paint);
140 builder.Restore();
141 }
142
143 builder.Save();
144 builder.Translate(shadow_translate.x, shadow_translate.y);
145 paint.setColor(DlColor::kPurple());
146 builder.DrawPath(path, paint);
147 builder.Restore();
148 }
149
150 DlPath MakeComplexPath(const DlPath& path) {
151 DlPathBuilder path_builder;
152 path_builder.AddPath(path);
153 // A single line contour won't make any visible change to the shadow,
154 // but none of the shadow to mesh converters will touch a path that
155 // has multiple contours so this path should always default to the
156 // general shadow code based on a blur filter.
157 path_builder.LineTo(DlPoint(0, 0));
158 return path_builder.TakePath();
159 }
160
161 void DrawShadowAndCompareMeshes(DisplayListBuilder& builder,
162 const DlPath& path,
163 Scalar elevation,
164 Scalar dpr,
165 const DlPath* simple_path = nullptr) {
166 DlPath complex_path = MakeComplexPath(path);
167
168 builder.Save();
169
170 if (simple_path) {
171 builder.DrawShadow(*simple_path, DlColor::kBlue(), elevation, true, dpr);
172 }
173
174 builder.Translate(300, 0);
175 builder.DrawShadow(path, DlColor::kBlue(), elevation, true, dpr);
176
177 builder.Translate(300, 0);
178 builder.DrawShadow(complex_path, DlColor::kBlue(), elevation, true, dpr);
179
180 builder.Restore();
181 builder.Translate(0, 300);
182 builder.Save();
183
184 // Draw the mesh wireframe underneath the regular path output in the
185 // row above us.
186 builder.Translate(300, 0);
187 builder.DrawShadow(path, DlColor::kBlue(), elevation, true, dpr);
188 DrawShadowMesh(builder, path, elevation, dpr);
189
190 builder.Restore();
191 }
192
193 // Makes a Round Rect path using conics, but the weights on the corners is
194 // off by just a tiny amount so the path will not be recognized.
195 DlPath MakeAlmostRoundRectPath(const Rect& bounds,
196 const RoundingRadii& radii,
197 bool clockwise = true) {
198 DlScalar left = bounds.GetLeft();
199 DlScalar top = bounds.GetTop();
200 DlScalar right = bounds.GetRight();
201 DlScalar bottom = bounds.GetBottom();
202
203 // A weight of sqrt(2)/2 is how you really perform conic circular sections,
204 // but by tweaking it slightly the path will not be recognized as an oval
205 // and accelerated.
206 constexpr Scalar kWeight = kSqrt2Over2 - 0.0005f;
207
208 DlPathBuilder path_builder;
209 path_builder.MoveTo(DlPoint(right - radii.top_right.width, top));
210 path_builder.ConicCurveTo(DlPoint(right, top),
211 DlPoint(right, top + radii.top_right.height),
212 kWeight);
213 path_builder.LineTo(DlPoint(right, bottom - radii.bottom_right.height));
214 path_builder.ConicCurveTo(DlPoint(right, bottom),
215 DlPoint(right - radii.bottom_right.width, bottom),
216 kWeight);
217 path_builder.LineTo(DlPoint(left + radii.bottom_left.width, bottom));
218 path_builder.ConicCurveTo(DlPoint(left, bottom),
219 DlPoint(left, bottom - radii.bottom_left.height),
220 kWeight);
221 path_builder.LineTo(DlPoint(left, top + radii.top_left.height));
222 path_builder.ConicCurveTo(DlPoint(left, top),
223 DlPoint(left + radii.top_left.width, top), //
224 kWeight);
225 path_builder.Close();
226 DlPath path = path_builder.TakePath();
227 if (!clockwise) {
228 path = ReflectPath(path);
229 }
230 return path;
231 }
232 } // namespace
233
234 TEST_P(AiksTest, DrawShadowDoesNotOptimizeHourglass) {
235 DisplayListBuilder builder;
236 builder.Clear(DlColor::kWhite());
237 builder.Scale(GetContentScale().x, GetContentScale().y);
238 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
239 Scalar elevation = 30.0f;
240
241 DlPathBuilder path_builder;
242 path_builder.MoveTo(DlPoint(100, 100));
243 path_builder.LineTo(DlPoint(300, 300));
244 path_builder.LineTo(DlPoint(100, 300));
245 path_builder.LineTo(DlPoint(300, 100));
246 path_builder.Close();
247 DlPath path = path_builder.TakePath();
248
249 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
250
251 auto dl = builder.Build();
252 ASSERT_TRUE(OpenPlaygroundHere(dl));
253 }
254
255 TEST_P(AiksTest, DrawShadowDoesNotOptimizeInnerOuterSpiral) {
256 DisplayListBuilder builder;
257 builder.Clear(DlColor::kWhite());
258 builder.Scale(GetContentScale().x, GetContentScale().y);
259 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
260 Scalar elevation = 30.0f;
261 int step_count = 20;
262
263 DlPathBuilder path_builder;
264 path_builder.MoveTo(DlPoint(300, 200));
265 for (int i = 1; i < step_count * 2; i++) {
266 Scalar angle = (k2Pi * i) / step_count;
267 Scalar radius = 80.0f + std::abs(i - step_count);
268 path_builder.LineTo(DlPoint(200, 200) + DlPoint(std::cos(angle) * radius,
269 std::sin(angle) * radius));
270 }
271 path_builder.Close();
272 DlPath path = path_builder.TakePath();
273
274 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
275
276 auto dl = builder.Build();
277 ASSERT_TRUE(OpenPlaygroundHere(dl));
278 }
279
280 TEST_P(AiksTest, DrawShadowDoesNotOptimizeOuterInnerSpiral) {
281 DisplayListBuilder builder;
282 builder.Clear(DlColor::kWhite());
283 builder.Scale(GetContentScale().x, GetContentScale().y);
284 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
285 Scalar elevation = 30.0f;
286 int step_count = 20;
287
288 DlPathBuilder path_builder;
289 path_builder.MoveTo(DlPoint(280, 200));
290 for (int i = 1; i < step_count * 2; i++) {
291 Scalar angle = (k2Pi * i) / step_count;
292 Scalar radius = 100.0f - std::abs(i - step_count);
293 path_builder.LineTo(DlPoint(200, 200) + DlPoint(std::cos(angle) * radius,
294 std::sin(angle) * radius));
295 }
296 path_builder.Close();
297 DlPath path = path_builder.TakePath();
298
299 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
300
301 auto dl = builder.Build();
302 ASSERT_TRUE(OpenPlaygroundHere(dl));
303 }
304
305 TEST_P(AiksTest, DrawShadowDoesNotOptimizeMultipleContours) {
306 DisplayListBuilder builder;
307 builder.Clear(DlColor::kWhite());
308 builder.Scale(GetContentScale().x, GetContentScale().y);
309 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
310 Scalar elevation = 30.0f;
311
312 DlPathBuilder path_builder;
313 path_builder.MoveTo(DlPoint(150, 100));
314 path_builder.LineTo(DlPoint(200, 300));
315 path_builder.LineTo(DlPoint(100, 300));
316 path_builder.Close();
317 path_builder.MoveTo(DlPoint(250, 100));
318 path_builder.LineTo(DlPoint(300, 300));
319 path_builder.LineTo(DlPoint(200, 300));
320 path_builder.Close();
321 DlPath path = path_builder.TakePath();
322
323 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
324
325 auto dl = builder.Build();
326 ASSERT_TRUE(OpenPlaygroundHere(dl));
327 }
328
329 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseTriangle) {
330 DisplayListBuilder builder;
331 builder.Clear(DlColor::kWhite());
332 builder.Scale(GetContentScale().x, GetContentScale().y);
333 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
334 Scalar elevation = 30.0f;
335
336 DlPathBuilder path_builder;
337 path_builder.MoveTo(DlPoint(200, 100));
338 path_builder.LineTo(DlPoint(300, 300));
339 path_builder.LineTo(DlPoint(100, 300));
340 path_builder.Close();
341 DlPath path = path_builder.TakePath();
342
343 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
344
345 auto dl = builder.Build();
346 ASSERT_TRUE(OpenPlaygroundHere(dl));
347 }
348
349 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseTriangle) {
350 DisplayListBuilder builder;
351 builder.Clear(DlColor::kWhite());
352 builder.Scale(GetContentScale().x, GetContentScale().y);
353 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
354 Scalar elevation = 30.0f;
355
356 DlPathBuilder path_builder;
357 path_builder.MoveTo(DlPoint(200, 100));
358 path_builder.LineTo(DlPoint(100, 300));
359 path_builder.LineTo(DlPoint(300, 300));
360 path_builder.Close();
361 DlPath path = path_builder.TakePath();
362
363 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
364
365 auto dl = builder.Build();
366 ASSERT_TRUE(OpenPlaygroundHere(dl));
367 }
368
369 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseRect) {
370 DisplayListBuilder builder;
371 builder.Clear(DlColor::kWhite());
372 builder.Scale(GetContentScale().x, GetContentScale().y);
373 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
374 Scalar elevation = 30.0f;
375
376 DlPathBuilder path_builder;
377 path_builder.MoveTo(DlPoint(100, 100));
378 // Tweak one corner by a sub-pixel amount to prevent recognition as
379 // a rectangle, but still generating a rectangular shadow.
380 path_builder.LineTo(DlPoint(299.9, 100));
381 path_builder.LineTo(DlPoint(300, 300));
382 path_builder.LineTo(DlPoint(100, 300));
383 path_builder.Close();
384 DlPath path = path_builder.TakePath();
385
386 // Path must be convex, but unrecognizable as a simple shape.
387 ASSERT_TRUE(path.IsConvex());
388 ASSERT_FALSE(path.IsRect());
389 ASSERT_FALSE(path.IsOval());
390 ASSERT_FALSE(path.IsRoundRect());
391
392 const DlPath simple_path = DlPath::MakeRectLTRB(100, 100, 300, 300);
393 DrawShadowAndCompareMeshes(builder, path, elevation, dpr, &simple_path);
394
395 auto dl = builder.Build();
396 ASSERT_TRUE(OpenPlaygroundHere(dl));
397 }
398
399 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseRect) {
400 DisplayListBuilder builder;
401 builder.Clear(DlColor::kWhite());
402 builder.Scale(GetContentScale().x, GetContentScale().y);
403 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
404 Scalar elevation = 30.0f;
405
406 DlPathBuilder path_builder;
407 path_builder.MoveTo(DlPoint(100, 100));
408 path_builder.LineTo(DlPoint(100, 300));
409 path_builder.LineTo(DlPoint(300, 300));
410 // Tweak one corner by a sub-pixel amount to prevent recognition as
411 // a rectangle, but still generating a rectangular shadow.
412 path_builder.LineTo(DlPoint(299.9, 100));
413 path_builder.Close();
414 DlPath path = path_builder.TakePath();
415
416 // Path must be convex, but unrecognizable as a simple shape.
417 ASSERT_TRUE(path.IsConvex());
418 ASSERT_FALSE(path.IsRect());
419 ASSERT_FALSE(path.IsOval());
420 ASSERT_FALSE(path.IsRoundRect());
421
422 const DlPath simple_path = DlPath::MakeRectLTRB(100, 100, 300, 300);
423 DrawShadowAndCompareMeshes(builder, path, elevation, dpr, &simple_path);
424
425 auto dl = builder.Build();
426 ASSERT_TRUE(OpenPlaygroundHere(dl));
427 }
428
429 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseCircle) {
430 DisplayListBuilder builder;
431 builder.Clear(DlColor::kWhite());
432 builder.Scale(GetContentScale().x, GetContentScale().y);
433 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
434 Scalar elevation = 30.0f;
435
436 // A weight of sqrt(2) is how you really perform conic circular sections,
437 // but by tweaking it slightly the path will not be recognized as an oval
438 // and accelerated.
439 constexpr Scalar kWeight = kSqrt2Over2 - 0.0005f;
440
441 DlPathBuilder path_builder;
442 path_builder.MoveTo(DlPoint(200, 100));
443 path_builder.ConicCurveTo(DlPoint(300, 100), DlPoint(300, 200), kWeight);
444 path_builder.ConicCurveTo(DlPoint(300, 300), DlPoint(200, 300), kWeight);
445 path_builder.ConicCurveTo(DlPoint(100, 300), DlPoint(100, 200), kWeight);
446 path_builder.ConicCurveTo(DlPoint(100, 100), DlPoint(200, 100), kWeight);
447 path_builder.Close();
448 DlPath path = path_builder.TakePath();
449
450 // Path must be convex, but unrecognizable as a simple shape.
451 ASSERT_TRUE(path.IsConvex());
452 ASSERT_FALSE(path.IsRect());
453 ASSERT_FALSE(path.IsOval());
454 ASSERT_FALSE(path.IsRoundRect());
455
456 const DlPath simple_path = DlPath::MakeCircle(DlPoint(200, 200), 100);
457 DrawShadowAndCompareMeshes(builder, path, elevation, dpr, &simple_path);
458
459 auto dl = builder.Build();
460 ASSERT_TRUE(OpenPlaygroundHere(dl));
461 }
462
463 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseCircle) {
464 DisplayListBuilder builder;
465 builder.Clear(DlColor::kWhite());
466 builder.Scale(GetContentScale().x, GetContentScale().y);
467 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
468 Scalar elevation = 30.0f;
469
470 // A weight of sqrt(2)/2 is how you really perform conic circular sections,
471 // but by tweaking it slightly the path will not be recognized as an oval
472 // and accelerated.
473 constexpr Scalar kWeight = kSqrt2Over2 - 0.0005f;
474
475 DlPathBuilder path_builder;
476 path_builder.MoveTo(DlPoint(200, 100));
477 path_builder.ConicCurveTo(DlPoint(100, 100), DlPoint(100, 200), kWeight);
478 path_builder.ConicCurveTo(DlPoint(100, 300), DlPoint(200, 300), kWeight);
479 path_builder.ConicCurveTo(DlPoint(300, 300), DlPoint(300, 200), kWeight);
480 path_builder.ConicCurveTo(DlPoint(300, 100), DlPoint(200, 100), kWeight);
481 path_builder.Close();
482 DlPath path = path_builder.TakePath();
483
484 // Path must be convex, but unrecognizable as a simple shape.
485 ASSERT_TRUE(path.IsConvex());
486 ASSERT_FALSE(path.IsRect());
487 ASSERT_FALSE(path.IsOval());
488 ASSERT_FALSE(path.IsRoundRect());
489
490 const DlPath simple_path = DlPath::MakeCircle(DlPoint(200, 200), 100);
491 DrawShadowAndCompareMeshes(builder, path, elevation, dpr, &simple_path);
492
493 auto dl = builder.Build();
494 ASSERT_TRUE(OpenPlaygroundHere(dl));
495 }
496
497 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseOval) {
498 DisplayListBuilder builder;
499 builder.Clear(DlColor::kWhite());
500 builder.Scale(GetContentScale().x, GetContentScale().y);
501 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
502 Scalar elevation = 30.0f;
503
504 // A weight of sqrt(2) is how you really perform conic circular sections,
505 // but by tweaking it slightly the path will not be recognized as an oval
506 // and accelerated.
507 constexpr Scalar kWeight = kSqrt2Over2 - 0.0005f;
508
509 DlPathBuilder path_builder;
510 path_builder.MoveTo(DlPoint(200, 120));
511 path_builder.ConicCurveTo(DlPoint(300, 120), DlPoint(300, 200), kWeight);
512 path_builder.ConicCurveTo(DlPoint(300, 280), DlPoint(200, 280), kWeight);
513 path_builder.ConicCurveTo(DlPoint(100, 280), DlPoint(100, 200), kWeight);
514 path_builder.ConicCurveTo(DlPoint(100, 120), DlPoint(200, 120), kWeight);
515 path_builder.Close();
516 DlPath path = path_builder.TakePath();
517
518 // Path must be convex, but unrecognizable as a simple shape.
519 ASSERT_TRUE(path.IsConvex());
520 ASSERT_FALSE(path.IsRect());
521 ASSERT_FALSE(path.IsOval());
522 ASSERT_FALSE(path.IsRoundRect());
523
524 const DlPath simple_path = DlPath::MakeOvalLTRB(100, 120, 300, 280);
525 DrawShadowAndCompareMeshes(builder, path, elevation, dpr, &simple_path);
526
527 auto dl = builder.Build();
528 ASSERT_TRUE(OpenPlaygroundHere(dl));
529 }
530
531 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseOval) {
532 DisplayListBuilder builder;
533 builder.Clear(DlColor::kWhite());
534 builder.Scale(GetContentScale().x, GetContentScale().y);
535 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
536 Scalar elevation = 30.0f;
537
538 // A weight of sqrt(2)/2 is how you really perform conic circular sections,
539 // but by tweaking it slightly the path will not be recognized as an oval
540 // and accelerated.
541 constexpr Scalar kWeight = kSqrt2Over2 - 0.0005f;
542
543 DlPathBuilder path_builder;
544 path_builder.MoveTo(DlPoint(200, 120));
545 path_builder.ConicCurveTo(DlPoint(100, 120), DlPoint(100, 200), kWeight);
546 path_builder.ConicCurveTo(DlPoint(100, 280), DlPoint(200, 280), kWeight);
547 path_builder.ConicCurveTo(DlPoint(300, 280), DlPoint(300, 200), kWeight);
548 path_builder.ConicCurveTo(DlPoint(300, 120), DlPoint(200, 120), kWeight);
549 path_builder.Close();
550 DlPath path = path_builder.TakePath();
551
552 // Path must be convex, but unrecognizable as a simple shape.
553 ASSERT_TRUE(path.IsConvex());
554 ASSERT_FALSE(path.IsRect());
555 ASSERT_FALSE(path.IsOval());
556 ASSERT_FALSE(path.IsRoundRect());
557
558 const DlPath simple_path = DlPath::MakeOvalLTRB(100, 120, 300, 280);
559 DrawShadowAndCompareMeshes(builder, path, elevation, dpr, &simple_path);
560
561 auto dl = builder.Build();
562 ASSERT_TRUE(OpenPlaygroundHere(dl));
563 }
564
565 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseUniformRoundRect) {
566 DisplayListBuilder builder;
567 builder.Clear(DlColor::kWhite());
568 builder.Scale(GetContentScale().x, GetContentScale().y);
569 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
570 Scalar elevation = 30.0f;
571
572 DlPath path = MakeAlmostRoundRectPath(DlRect::MakeLTRB(100, 100, 300, 300),
573 DlRoundingRadii::MakeRadius(30), true);
574
575 // Path must be convex, but unrecognizable as a simple shape.
576 ASSERT_TRUE(path.IsConvex());
577 ASSERT_FALSE(path.IsRect());
578 ASSERT_FALSE(path.IsOval());
579 ASSERT_FALSE(path.IsRoundRect());
580
581 const RoundRect round_rect =
582 RoundRect::MakeRectRadius(Rect::MakeLTRB(100, 100, 300, 300), 30);
583 const DlPath simple_path = DlPath::MakeRoundRect(round_rect);
584 DrawShadowAndCompareMeshes(builder, path, elevation, dpr, &simple_path);
585
586 auto dl = builder.Build();
587 ASSERT_TRUE(OpenPlaygroundHere(dl));
588 }
589
590 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseUniformRoundRect) {
591 DisplayListBuilder builder;
592 builder.Clear(DlColor::kWhite());
593 builder.Scale(GetContentScale().x, GetContentScale().y);
594 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
595 Scalar elevation = 30.0f;
596
597 DlPath path = MakeAlmostRoundRectPath(DlRect::MakeLTRB(100, 100, 300, 300),
598 DlRoundingRadii::MakeRadius(30), false);
599
600 // Path must be convex, but unrecognizable as a simple shape.
601 ASSERT_TRUE(path.IsConvex());
602 ASSERT_FALSE(path.IsRect());
603 ASSERT_FALSE(path.IsOval());
604 ASSERT_FALSE(path.IsRoundRect());
605
606 const RoundRect round_rect =
607 RoundRect::MakeRectRadius(Rect::MakeLTRB(100, 100, 300, 300), 30);
608 const DlPath simple_path = DlPath::MakeRoundRect(round_rect);
609 DrawShadowAndCompareMeshes(builder, path, elevation, dpr, &simple_path);
610
611 auto dl = builder.Build();
612 ASSERT_TRUE(OpenPlaygroundHere(dl));
613 }
614
615 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseMultiRadiiRoundRect) {
616 DisplayListBuilder builder;
617 builder.Clear(DlColor::kWhite());
618 builder.Scale(GetContentScale().x, GetContentScale().y);
619 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
620 Scalar elevation = 30.0f;
621
622 DlRoundingRadii radii = DlRoundingRadii{
623 .top_left = {80, 60},
624 .top_right = {20, 25},
625 .bottom_left = {60, 80},
626 .bottom_right = {25, 20},
627 };
628 DlPath path = MakeAlmostRoundRectPath(DlRect::MakeLTRB(100, 100, 300, 300),
629 radii, true);
630
631 // Path must be convex, but unrecognizable as a simple shape.
632 ASSERT_TRUE(path.IsConvex());
633 ASSERT_FALSE(path.IsRect());
634 ASSERT_FALSE(path.IsOval());
635 ASSERT_FALSE(path.IsRoundRect());
636
637 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
638
639 auto dl = builder.Build();
640 ASSERT_TRUE(OpenPlaygroundHere(dl));
641 }
642
643 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseMultiRadiiRoundRect) {
644 DisplayListBuilder builder;
645 builder.Clear(DlColor::kWhite());
646 builder.Scale(GetContentScale().x, GetContentScale().y);
647 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
648 Scalar elevation = 30.0f;
649
650 DlRoundingRadii radii = DlRoundingRadii{
651 .top_left = {80, 60},
652 .top_right = {20, 25},
653 .bottom_left = {60, 80},
654 .bottom_right = {25, 20},
655 };
656 DlPath path = MakeAlmostRoundRectPath(DlRect::MakeLTRB(100, 100, 300, 300),
657 radii, false);
658
659 // Path must be convex, but unrecognizable as a simple shape.
660 ASSERT_TRUE(path.IsConvex());
661 ASSERT_FALSE(path.IsRect());
662 ASSERT_FALSE(path.IsOval());
663 ASSERT_FALSE(path.IsRoundRect());
664
665 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
666
667 auto dl = builder.Build();
668 ASSERT_TRUE(OpenPlaygroundHere(dl));
669 }
670
671 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseQuadratic) {
672 DisplayListBuilder builder;
673 builder.Clear(DlColor::kWhite());
674 builder.Scale(GetContentScale().x, GetContentScale().y);
675 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
676 Scalar elevation = 30.0f;
677
678 DlPathBuilder path_builder;
679 path_builder.MoveTo(DlPoint(200, 100));
680 path_builder.QuadraticCurveTo(DlPoint(300, 100), DlPoint(300, 200));
681 path_builder.QuadraticCurveTo(DlPoint(300, 300), DlPoint(200, 300));
682 path_builder.QuadraticCurveTo(DlPoint(100, 300), DlPoint(100, 200));
683 path_builder.QuadraticCurveTo(DlPoint(100, 100), DlPoint(200, 100));
684 path_builder.Close();
685 DlPath path = path_builder.TakePath();
686
687 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
688
689 auto dl = builder.Build();
690 ASSERT_TRUE(OpenPlaygroundHere(dl));
691 }
692
693 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseQuadratic) {
694 DisplayListBuilder builder;
695 builder.Clear(DlColor::kWhite());
696 builder.Scale(GetContentScale().x, GetContentScale().y);
697 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
698 Scalar elevation = 30.0f;
699
700 DlPathBuilder path_builder;
701 path_builder.MoveTo(DlPoint(200, 100));
702 path_builder.QuadraticCurveTo(DlPoint(100, 100), DlPoint(100, 200));
703 path_builder.QuadraticCurveTo(DlPoint(100, 300), DlPoint(200, 300));
704 path_builder.QuadraticCurveTo(DlPoint(300, 300), DlPoint(300, 200));
705 path_builder.QuadraticCurveTo(DlPoint(300, 100), DlPoint(200, 100));
706 path_builder.Close();
707 DlPath path = path_builder.TakePath();
708
709 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
710
711 auto dl = builder.Build();
712 ASSERT_TRUE(OpenPlaygroundHere(dl));
713 }
714
715 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseConic) {
716 DisplayListBuilder builder;
717 builder.Clear(DlColor::kWhite());
718 builder.Scale(GetContentScale().x, GetContentScale().y);
719 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
720 Scalar elevation = 30.0f;
721
722 DlPathBuilder path_builder;
723 path_builder.MoveTo(DlPoint(200, 100));
724 path_builder.ConicCurveTo(DlPoint(300, 100), DlPoint(300, 200), 0.4f);
725 path_builder.ConicCurveTo(DlPoint(300, 300), DlPoint(200, 300), 0.4f);
726 path_builder.ConicCurveTo(DlPoint(100, 300), DlPoint(100, 200), 0.4f);
727 path_builder.ConicCurveTo(DlPoint(100, 100), DlPoint(200, 100), 0.4f);
728 path_builder.Close();
729 DlPath path = path_builder.TakePath();
730
731 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
732
733 auto dl = builder.Build();
734 ASSERT_TRUE(OpenPlaygroundHere(dl));
735 }
736
737 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseConic) {
738 DisplayListBuilder builder;
739 builder.Clear(DlColor::kWhite());
740 builder.Scale(GetContentScale().x, GetContentScale().y);
741 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
742 Scalar elevation = 30.0f;
743
744 DlPathBuilder path_builder;
745 path_builder.MoveTo(DlPoint(200, 100));
746 path_builder.ConicCurveTo(DlPoint(100, 100), DlPoint(100, 200), 0.4f);
747 path_builder.ConicCurveTo(DlPoint(100, 300), DlPoint(200, 300), 0.4f);
748 path_builder.ConicCurveTo(DlPoint(300, 300), DlPoint(300, 200), 0.4f);
749 path_builder.ConicCurveTo(DlPoint(300, 100), DlPoint(200, 100), 0.4f);
750 path_builder.Close();
751 DlPath path = path_builder.TakePath();
752
753 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
754
755 auto dl = builder.Build();
756 ASSERT_TRUE(OpenPlaygroundHere(dl));
757 }
758
759 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseCubic) {
760 DisplayListBuilder builder;
761 builder.Clear(DlColor::kWhite());
762 builder.Scale(GetContentScale().x, GetContentScale().y);
763 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
764 Scalar elevation = 30.0f;
765
766 DlPathBuilder path_builder;
767 path_builder.MoveTo(DlPoint(200, 100));
768 path_builder.CubicCurveTo(DlPoint(280, 100), DlPoint(300, 120),
769 DlPoint(300, 200));
770 path_builder.CubicCurveTo(DlPoint(300, 280), DlPoint(280, 300),
771 DlPoint(200, 300));
772 path_builder.CubicCurveTo(DlPoint(120, 300), DlPoint(100, 280),
773 DlPoint(100, 200));
774 path_builder.CubicCurveTo(DlPoint(100, 120), DlPoint(120, 100),
775 DlPoint(200, 100));
776 path_builder.Close();
777 DlPath path = path_builder.TakePath();
778
779 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
780
781 auto dl = builder.Build();
782 ASSERT_TRUE(OpenPlaygroundHere(dl));
783 }
784
785 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseCubic) {
786 DisplayListBuilder builder;
787 builder.Clear(DlColor::kWhite());
788 builder.Scale(GetContentScale().x, GetContentScale().y);
789 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
790 Scalar elevation = 30.0f;
791
792 DlPathBuilder path_builder;
793 path_builder.MoveTo(DlPoint(200, 100));
794 path_builder.CubicCurveTo(DlPoint(120, 100), DlPoint(100, 120),
795 DlPoint(100, 200));
796 path_builder.CubicCurveTo(DlPoint(100, 280), DlPoint(120, 300),
797 DlPoint(200, 300));
798 path_builder.CubicCurveTo(DlPoint(280, 300), DlPoint(300, 280),
799 DlPoint(300, 200));
800 path_builder.CubicCurveTo(DlPoint(300, 120), DlPoint(280, 100),
801 DlPoint(200, 100));
802 path_builder.Close();
803 DlPath path = path_builder.TakePath();
804
805 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
806
807 auto dl = builder.Build();
808 ASSERT_TRUE(OpenPlaygroundHere(dl));
809 }
810
811 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseOctagon) {
812 DisplayListBuilder builder;
813 builder.Clear(DlColor::kWhite());
814 builder.Scale(GetContentScale().x, GetContentScale().y);
815 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
816 Scalar elevation = 30.0f;
817
818 DlPathBuilder path_builder;
819 path_builder.MoveTo(DlPoint(100, 125));
820 path_builder.LineTo(DlPoint(125, 100));
821 path_builder.LineTo(DlPoint(275, 100));
822 path_builder.LineTo(DlPoint(300, 125));
823 path_builder.LineTo(DlPoint(300, 275));
824 path_builder.LineTo(DlPoint(275, 300));
825 path_builder.LineTo(DlPoint(125, 300));
826 path_builder.LineTo(DlPoint(100, 275));
827 path_builder.Close();
828 DlPath path = path_builder.TakePath();
829
830 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
831
832 auto dl = builder.Build();
833 ASSERT_TRUE(OpenPlaygroundHere(dl));
834 }
835
836 TEST_P(AiksTest, DrawShadowCanOptimizeCounterClockwiseOctagon) {
837 DisplayListBuilder builder;
838 builder.Clear(DlColor::kWhite());
839 builder.Scale(GetContentScale().x, GetContentScale().y);
840 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
841 Scalar elevation = 30.0f;
842
843 DlPathBuilder path_builder;
844 path_builder.MoveTo(DlPoint(100, 125));
845 path_builder.LineTo(DlPoint(100, 275));
846 path_builder.LineTo(DlPoint(125, 300));
847 path_builder.LineTo(DlPoint(275, 300));
848 path_builder.LineTo(DlPoint(300, 275));
849 path_builder.LineTo(DlPoint(300, 125));
850 path_builder.LineTo(DlPoint(275, 100));
851 path_builder.LineTo(DlPoint(125, 100));
852 path_builder.Close();
853 DlPath path = path_builder.TakePath();
854
855 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
856
857 auto dl = builder.Build();
858 ASSERT_TRUE(OpenPlaygroundHere(dl));
859 }
860
861 TEST_P(AiksTest, DrawShadowCanOptimizeWithExtraneousMoveTos) {
862 DisplayListBuilder builder;
863 builder.Clear(DlColor::kWhite());
864 builder.Scale(GetContentScale().x, GetContentScale().y);
865 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
866 Scalar elevation = 30.0f;
867
868 DlPathBuilder path_builder;
869 path_builder.MoveTo(DlPoint(0, 0));
870 path_builder.MoveTo(DlPoint(1000, 1000));
871 path_builder.MoveTo(DlPoint(100, 50));
872 path_builder.MoveTo(DlPoint(200, 100));
873 path_builder.LineTo(DlPoint(300, 300));
874 path_builder.LineTo(DlPoint(100, 300));
875 path_builder.Close();
876 path_builder.MoveTo(DlPoint(1000, 1000));
877 path_builder.MoveTo(DlPoint(500, 300));
878 DlPath path = path_builder.TakePath();
879
880 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
881
882 auto dl = builder.Build();
883 ASSERT_TRUE(OpenPlaygroundHere(dl));
884 }
885
886 TEST_P(AiksTest, DrawShadowCanOptimizeClockwiseWithExtraColinearVertices) {
887 DisplayListBuilder builder;
888 builder.Clear(DlColor::kWhite());
889 builder.Scale(GetContentScale().x, GetContentScale().y);
890 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
891 Scalar elevation = 30.0f;
892
893 DlPathBuilder path_builder;
894 path_builder.MoveTo(DlPoint(200, 100));
895 path_builder.LineTo(DlPoint(250, 200));
896 path_builder.LineTo(DlPoint(300, 300));
897 path_builder.LineTo(DlPoint(200, 300));
898 path_builder.LineTo(DlPoint(100, 300));
899 path_builder.LineTo(DlPoint(150, 200));
900 path_builder.Close();
901 DlPath path = path_builder.TakePath();
902
903 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
904
905 auto dl = builder.Build();
906 ASSERT_TRUE(OpenPlaygroundHere(dl));
907 }
908
909 TEST_P(AiksTest,
910 DrawShadowCanOptimizeCounterClockwiseWithExtraColinearVertices) {
911 DisplayListBuilder builder;
912 builder.Clear(DlColor::kWhite());
913 builder.Scale(GetContentScale().x, GetContentScale().y);
914 Scalar dpr = std::max(GetContentScale().x, GetContentScale().y);
915 Scalar elevation = 30.0f;
916
917 DlPathBuilder path_builder;
918 path_builder.MoveTo(DlPoint(200, 100));
919 path_builder.LineTo(DlPoint(150, 200));
920 path_builder.LineTo(DlPoint(100, 300));
921 path_builder.LineTo(DlPoint(200, 300));
922 path_builder.LineTo(DlPoint(300, 300));
923 path_builder.LineTo(DlPoint(250, 200));
924 path_builder.Close();
925 DlPath path = path_builder.TakePath();
926
927 DrawShadowAndCompareMeshes(builder, path, elevation, dpr);
928
929 auto dl = builder.Build();
930 ASSERT_TRUE(OpenPlaygroundHere(dl));
931 }
932
933 } // namespace testing
934 } // namespace impeller
aiks_unittests.h
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::ShadowPathGeometry::MakeAmbientShadowVertices
static std::shared_ptr< ShadowVertices > MakeAmbientShadowVertices(Tessellator &tessellator, const PathSource &source, Scalar occluder_height, const Matrix &matrix)
Definition: shadow_path_geometry.cc:1457
x
int32_t x
Definition: dl_golden_unittests.cc:458
flutter
Definition: dl_golden_blur_unittests.cc:16
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:59
impeller
Definition: allocation.cc:12
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:29
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::kSqrt2Over2
constexpr float kSqrt2Over2
Definition: constants.h:51
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:788
impeller::Point
TPoint< Scalar > Point
Definition: point.h:425
impeller::DlPoint
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
impeller::DlPath
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
impeller::MoveTo
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
impeller::CubicTo
void CubicTo(PathBuilder *builder, Scalar x1, Scalar y1, Scalar x2, Scalar y2, Scalar x3, Scalar y3)
Definition: tessellator.cc:28
impeller::DlScalar
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:23
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
shadow_path_geometry.h
impeller::RoundRect
Definition: round_rect.h:16
impeller::RoundRect::MakeRectRadius
static RoundRect MakeRectRadius(const Rect &rect, Scalar radius)
Definition: round_rect.h:27
impeller::TPoint::y
Type y
Definition: point.h:32
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129