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 
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"
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 =
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 
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
static std::shared_ptr< ShadowVertices > MakeAmbientShadowVertices(Tessellator &tessellator, const PathSource &source, Scalar occluder_height, const Matrix &matrix)
int32_t x
TEST_P(AiksTest, DrawAtlasNoColor)
constexpr float k2Pi
Definition: constants.h:29
float Scalar
Definition: scalar.h:19
constexpr float kSqrt2Over2
Definition: constants.h:51
TRect< Scalar > Rect
Definition: rect.h:788
TPoint< Scalar > Point
Definition: point.h:425
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
void CubicTo(PathBuilder *builder, Scalar x1, Scalar y1, Scalar x2, Scalar y2, Scalar x3, Scalar y3)
Definition: tessellator.cc:28
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:23
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
static RoundRect MakeRectRadius(const Rect &rect, Scalar radius)
Definition: round_rect.h:27
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129