Flutter Impeller
aiks_dl_path_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 "display_list/dl_sampling_options.h"
6 #include "display_list/dl_tile_mode.h"
7 #include "display_list/effects/dl_color_source.h"
8 #include "display_list/effects/dl_mask_filter.h"
10 
11 #include "flutter/display_list/dl_blend_mode.h"
12 #include "flutter/display_list/dl_builder.h"
13 #include "flutter/display_list/dl_color.h"
14 #include "flutter/display_list/dl_paint.h"
15 #include "flutter/display_list/effects/dl_color_filter.h"
16 #include "flutter/testing/testing.h"
19 
20 namespace impeller {
21 namespace testing {
22 
23 using namespace flutter;
24 
25 TEST_P(AiksTest, RotateColorFilteredPath) {
26  DisplayListBuilder builder;
27  builder.Transform(DlMatrix::MakeTranslation(DlPoint(300, 300)) *
28  DlMatrix::MakeRotationZ(DlDegrees(90)));
29 
30  DlPathBuilder arrow_stem;
31  DlPathBuilder arrow_head;
32 
33  arrow_stem.MoveTo(DlPoint(120, 190)).LineTo(DlPoint(120, 50));
34  arrow_head.MoveTo(DlPoint(50, 120))
35  .LineTo(DlPoint(120, 190))
36  .LineTo(DlPoint(190, 120));
37 
38  auto filter =
39  DlColorFilter::MakeBlend(DlColor::kAliceBlue(), DlBlendMode::kSrcIn);
40 
41  DlPaint paint;
42  paint.setStrokeWidth(15.0);
43  paint.setStrokeCap(DlStrokeCap::kRound);
44  paint.setStrokeJoin(DlStrokeJoin::kRound);
45  paint.setDrawStyle(DlDrawStyle::kStroke);
46  paint.setColorFilter(filter);
47  paint.setColor(DlColor::kBlack());
48 
49  builder.DrawPath(DlPath(arrow_stem), paint);
50  builder.DrawPath(DlPath(arrow_head), paint);
51 
52  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
53 }
54 
55 TEST_P(AiksTest, CanRenderStrokes) {
56  DisplayListBuilder builder;
57  DlPaint paint;
58  paint.setColor(DlColor::kRed());
59  paint.setStrokeWidth(20);
60  paint.setDrawStyle(DlDrawStyle::kStroke);
61 
62  builder.DrawPath(DlPath::MakeLine(DlPoint(200, 100), DlPoint(800, 100)),
63  paint);
64 
65  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
66 }
67 
68 TEST_P(AiksTest, CanRenderCurvedStrokes) {
69  DisplayListBuilder builder;
70  DlPaint paint;
71  paint.setColor(DlColor::kRed());
72  paint.setStrokeWidth(25);
73  paint.setDrawStyle(DlDrawStyle::kStroke);
74 
75  builder.DrawPath(DlPath::MakeCircle(DlPoint(500, 500), 250), paint);
76 
77  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
78 }
79 
80 TEST_P(AiksTest, CanRenderThickCurvedStrokes) {
81  DisplayListBuilder builder;
82  DlPaint paint;
83  paint.setColor(DlColor::kRed());
84  paint.setStrokeWidth(100);
85  paint.setDrawStyle(DlDrawStyle::kStroke);
86 
87  builder.DrawPath(DlPath::MakeCircle(DlPoint(100, 100), 50), paint);
88 
89  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
90 }
91 
92 TEST_P(AiksTest, CanRenderThinCurvedStrokes) {
93  DisplayListBuilder builder;
94  DlPaint paint;
95  paint.setColor(DlColor::kRed());
96  paint.setStrokeWidth(0.01);
97  paint.setDrawStyle(DlDrawStyle::kStroke);
98 
99  builder.DrawPath(DlPath::MakeCircle(DlPoint(100, 100), 50), paint);
100 
101  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
102 }
103 
104 TEST_P(AiksTest, CanRenderStrokePathThatEndsAtSharpTurn) {
105  DisplayListBuilder builder;
106  DlPaint paint;
107  paint.setColor(DlColor::kRed());
108  paint.setStrokeWidth(200);
109  paint.setDrawStyle(DlDrawStyle::kStroke);
110 
111  DlPath path = DlPath::MakeArc(DlRect::MakeXYWH(100, 100, 200, 200), //
112  DlDegrees(0), DlDegrees(90), false);
113 
114  builder.DrawPath(path, paint);
115 
116  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
117 }
118 
119 TEST_P(AiksTest, CanRenderStrokePathWithCubicLine) {
120  DisplayListBuilder builder;
121 
122  DlPaint paint;
123  paint.setColor(DlColor::kRed());
124  paint.setStrokeWidth(20);
125  paint.setDrawStyle(DlDrawStyle::kStroke);
126 
127  DlPathBuilder path_builder;
128  path_builder.MoveTo(DlPoint(0, 200));
129  path_builder.CubicCurveTo(DlPoint(50, 400), DlPoint(350, 0),
130  DlPoint(400, 200));
131 
132  builder.DrawPath(DlPath(path_builder), paint);
133  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
134 }
135 
136 TEST_P(AiksTest, CanRenderQuadraticStrokeWithInstantTurn) {
137  DisplayListBuilder builder;
138 
139  DlPaint paint;
140  paint.setColor(DlColor::kRed());
141  paint.setStrokeWidth(50);
142  paint.setDrawStyle(DlDrawStyle::kStroke);
143  paint.setStrokeCap(DlStrokeCap::kRound);
144 
145  // Should draw a diagonal pill shape. If flat on either end, the stroke is
146  // rendering wrong.
147  DlPathBuilder path_builder;
148  path_builder.MoveTo(DlPoint(250, 250));
149  path_builder.QuadraticCurveTo(DlPoint(100, 100), DlPoint(250, 250));
150 
151  builder.DrawPath(DlPath(path_builder), paint);
152 
153  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
154 }
155 
156 TEST_P(AiksTest, CanRenderFilledConicPaths) {
157  DisplayListBuilder builder;
158  builder.Scale(GetContentScale().x, GetContentScale().y);
159 
160  DlPaint paint;
161  paint.setColor(DlColor::kRed());
162  paint.setDrawStyle(DlDrawStyle::kFill);
163 
164  DlPaint reference_paint;
165  reference_paint.setColor(DlColor::kGreen());
166  reference_paint.setDrawStyle(DlDrawStyle::kFill);
167 
168  DlPathBuilder path_builder;
169  DlPathBuilder reference_builder;
170 
171  // weight of 1.0 is just a quadratic bezier
172  path_builder.MoveTo(DlPoint(100, 100));
173  path_builder.ConicCurveTo(DlPoint(150, 150), DlPoint(200, 100), 1.0f);
174  reference_builder.MoveTo(DlPoint(300, 100));
175  reference_builder.QuadraticCurveTo(DlPoint(350, 150), DlPoint(400, 100));
176 
177  // weight of sqrt(2)/2 is a circular section
178  path_builder.MoveTo(DlPoint(100, 200));
179  path_builder.ConicCurveTo(DlPoint(150, 250), DlPoint(200, 200), kSqrt2Over2);
180  reference_builder.MoveTo(DlPoint(300, 200));
181  auto magic = DlPathBuilder::kArcApproximationMagic;
182  reference_builder.CubicCurveTo(DlPoint(300, 200) + DlPoint(50, 50) * magic,
183  DlPoint(400, 200) + DlPoint(-50, 50) * magic,
184  DlPoint(400, 200));
185 
186  // weight of .01 is nearly a straight line
187  path_builder.MoveTo(DlPoint(100, 300));
188  path_builder.ConicCurveTo(DlPoint(150, 350), DlPoint(200, 300), 0.01f);
189  reference_builder.MoveTo(DlPoint(300, 300));
190  reference_builder.LineTo(DlPoint(350, 300.5));
191  reference_builder.LineTo(DlPoint(400, 300));
192 
193  // weight of 100.0 is nearly a triangle
194  path_builder.MoveTo(DlPoint(100, 400));
195  path_builder.ConicCurveTo(DlPoint(150, 450), DlPoint(200, 400), 100.0f);
196  reference_builder.MoveTo(DlPoint(300, 400));
197  reference_builder.LineTo(DlPoint(350, 450));
198  reference_builder.LineTo(DlPoint(400, 400));
199 
200  builder.DrawPath(DlPath(path_builder), paint);
201  builder.DrawPath(DlPath(reference_builder), reference_paint);
202 
203  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
204 }
205 
206 TEST_P(AiksTest, CanRenderStrokedConicPaths) {
207  DisplayListBuilder builder;
208  builder.Scale(GetContentScale().x, GetContentScale().y);
209 
210  DlPaint paint;
211  paint.setColor(DlColor::kRed());
212  paint.setStrokeWidth(10);
213  paint.setDrawStyle(DlDrawStyle::kStroke);
214  paint.setStrokeCap(DlStrokeCap::kRound);
215  paint.setStrokeJoin(DlStrokeJoin::kRound);
216 
217  DlPaint reference_paint;
218  reference_paint.setColor(DlColor::kGreen());
219  reference_paint.setStrokeWidth(10);
220  reference_paint.setDrawStyle(DlDrawStyle::kStroke);
221  reference_paint.setStrokeCap(DlStrokeCap::kRound);
222  reference_paint.setStrokeJoin(DlStrokeJoin::kRound);
223 
224  DlPathBuilder path_builder;
225  DlPathBuilder reference_builder;
226 
227  // weight of 1.0 is just a quadratic bezier
228  path_builder.MoveTo(DlPoint(100, 100));
229  path_builder.ConicCurveTo(DlPoint(150, 150), DlPoint(200, 100), 1.0f);
230  reference_builder.MoveTo(DlPoint(300, 100));
231  reference_builder.QuadraticCurveTo(DlPoint(350, 150), DlPoint(400, 100));
232 
233  // weight of sqrt(2)/2 is a circular section
234  path_builder.MoveTo(DlPoint(100, 200));
235  path_builder.ConicCurveTo(DlPoint(150, 250), DlPoint(200, 200), kSqrt2Over2);
236  reference_builder.MoveTo(DlPoint(300, 200));
237  auto magic = DlPathBuilder::kArcApproximationMagic;
238  reference_builder.CubicCurveTo(DlPoint(300, 200) + DlPoint(50, 50) * magic,
239  DlPoint(400, 200) + DlPoint(-50, 50) * magic,
240  DlPoint(400, 200));
241 
242  // weight of .0 is a straight line
243  path_builder.MoveTo(DlPoint(100, 300));
244  path_builder.ConicCurveTo(DlPoint(150, 350), DlPoint(200, 300), 0.0f);
245  reference_builder.MoveTo(DlPoint(300, 300));
246  reference_builder.LineTo(DlPoint(400, 300));
247 
248  // weight of 100.0 is nearly a triangle
249  path_builder.MoveTo(DlPoint(100, 400));
250  path_builder.ConicCurveTo(DlPoint(150, 450), DlPoint(200, 400), 100.0f);
251  reference_builder.MoveTo(DlPoint(300, 400));
252  reference_builder.LineTo(DlPoint(350, 450));
253  reference_builder.LineTo(DlPoint(400, 400));
254 
255  builder.DrawPath(DlPath(path_builder), paint);
256  builder.DrawPath(DlPath(reference_builder), reference_paint);
257 
258  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
259 }
260 
261 TEST_P(AiksTest, HairlinePath) {
262  Scalar scale = 1.f;
263  Scalar rotation = 0.f;
264  Scalar offset = 0.f;
265  auto callback = [&]() -> sk_sp<DisplayList> {
266  if (AiksTest::ImGuiBegin("Controls", nullptr,
267  ImGuiWindowFlags_AlwaysAutoResize)) {
268  ImGui::SliderFloat("Scale", &scale, 0, 6);
269  ImGui::SliderFloat("Rotate", &rotation, 0, 90);
270  ImGui::SliderFloat("Offset", &offset, 0, 2);
271  ImGui::End();
272  }
273 
274  DisplayListBuilder builder;
275  builder.Scale(GetContentScale().x, GetContentScale().y);
276  builder.DrawPaint(DlPaint(DlColor(0xff111111)));
277 
278  DlPaint paint;
279  paint.setStrokeWidth(0.f);
280  paint.setColor(DlColor::kWhite());
281  paint.setStrokeCap(DlStrokeCap::kRound);
282  paint.setStrokeJoin(DlStrokeJoin::kRound);
283  paint.setDrawStyle(DlDrawStyle::kStroke);
284 
285  builder.Translate(512, 384);
286  builder.Scale(scale, scale);
287  builder.Rotate(rotation);
288  builder.Translate(-512, -384 + offset);
289 
290  for (int i = 0; i < 5; ++i) {
291  Scalar yoffset = i * 25.25f + 300.f;
292  DlPathBuilder path_builder;
293 
294  path_builder.MoveTo(DlPoint(100, yoffset));
295  path_builder.LineTo(DlPoint(924, yoffset));
296  builder.DrawPath(DlPath(path_builder), paint);
297  }
298 
299  return builder.Build();
300  };
301 
302  ASSERT_TRUE(OpenPlaygroundHere(callback));
303 }
304 
305 TEST_P(AiksTest, HairlineDrawLine) {
306  Scalar scale = 1.f;
307  Scalar rotation = 0.f;
308  Scalar offset = 0.f;
309  auto callback = [&]() -> sk_sp<DisplayList> {
310  if (AiksTest::ImGuiBegin("Controls", nullptr,
311  ImGuiWindowFlags_AlwaysAutoResize)) {
312  ImGui::SliderFloat("Scale", &scale, 0, 6);
313  ImGui::SliderFloat("Rotate", &rotation, 0, 90);
314  ImGui::SliderFloat("Offset", &offset, 0, 2);
315  ImGui::End();
316  }
317 
318  DisplayListBuilder builder;
319  builder.Scale(GetContentScale().x, GetContentScale().y);
320  builder.DrawPaint(DlPaint(DlColor(0xff111111)));
321 
322  DlPaint paint;
323  paint.setStrokeWidth(0.f);
324  paint.setColor(DlColor::kWhite());
325 
326  builder.Translate(512, 384);
327  builder.Scale(scale, scale);
328  builder.Rotate(rotation);
329  builder.Translate(-512, -384 + offset);
330 
331  for (int i = 0; i < 5; ++i) {
332  Scalar yoffset = i * 25.25f + 300.f;
333 
334  builder.DrawLine(DlPoint(100, yoffset), DlPoint(924, yoffset), paint);
335  }
336 
337  return builder.Build();
338  };
339 
340  ASSERT_TRUE(OpenPlaygroundHere(callback));
341 }
342 
343 TEST_P(AiksTest, CanRenderTightConicPath) {
344  DisplayListBuilder builder;
345  builder.Scale(GetContentScale().x, GetContentScale().y);
346 
347  DlPaint paint;
348  paint.setColor(DlColor::kRed());
349  paint.setDrawStyle(DlDrawStyle::kFill);
350 
351  DlPaint reference_paint;
352  reference_paint.setColor(DlColor::kGreen());
353  reference_paint.setDrawStyle(DlDrawStyle::kFill);
354 
355  DlPathBuilder path_builder;
356 
357  path_builder.MoveTo(DlPoint(100, 100));
358  path_builder.ConicCurveTo(DlPoint(150, 450), DlPoint(200, 100), 5.0f);
359 
360  DlPathBuilder reference_builder;
361  ConicPathComponent component(DlPoint(300, 100), //
362  DlPoint(350, 450), //
363  DlPoint(400, 100), //
364  5.0f);
365  reference_builder.MoveTo(component.p1);
366  constexpr int N = 100;
367  for (int i = 1; i < N; i++) {
368  reference_builder.LineTo(component.Solve(static_cast<Scalar>(i) / N));
369  }
370  reference_builder.LineTo(component.p2);
371 
372  DlPaint line_paint;
373  line_paint.setColor(DlColor::kYellow());
374  line_paint.setDrawStyle(DlDrawStyle::kStroke);
375  line_paint.setStrokeWidth(1.0f);
376 
377  // Draw some lines to provide a spacial reference for the curvature of
378  // the tips of the direct rendering and the manually tessellated versions.
379  builder.DrawLine(DlPoint(145, 100), DlPoint(145, 450), line_paint);
380  builder.DrawLine(DlPoint(155, 100), DlPoint(155, 450), line_paint);
381  builder.DrawLine(DlPoint(345, 100), DlPoint(345, 450), line_paint);
382  builder.DrawLine(DlPoint(355, 100), DlPoint(355, 450), line_paint);
383  builder.DrawLine(DlPoint(100, 392.5f), DlPoint(400, 392.5f), line_paint);
384 
385  // Draw the two paths (direct and manually tessellated) on top of the lines.
386  builder.DrawPath(DlPath(path_builder), paint);
387  builder.DrawPath(DlPath(reference_builder), reference_paint);
388 
389  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
390 }
391 
392 TEST_P(AiksTest, CanRenderDifferencePaths) {
393  DisplayListBuilder builder;
394 
395  DlPaint paint;
396  paint.setColor(DlColor::kRed());
397 
398  RoundingRadii radii = {
399  .top_left = {50, 25},
400  .top_right = {25, 50},
401  .bottom_left = {25, 50},
402  .bottom_right = {50, 25},
403  };
404  PathBuilder path_builder;
405  DlRoundRect rrect =
406  DlRoundRect::MakeRectRadii(DlRect::MakeXYWH(100, 100, 200, 200), radii);
407  // We use the factory method to convert the rrect and circle to a path so
408  // that they use the legacy conics for legacy golden output.
409  path_builder.AddPath(DlPath::MakeRoundRect(rrect).GetPath());
410  path_builder.AddPath(DlPath::MakeCircle(DlPoint(200, 200), 50).GetPath());
411  DlPath path(path_builder, DlPathFillType::kOdd);
412 
413  builder.DrawImage(
414  DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")),
415  DlPoint{10, 10}, {});
416  builder.DrawPath(path, paint);
417 
418  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
419 }
420 
421 // Regression test for https://github.com/flutter/flutter/issues/134816.
422 //
423 // It should be possible to draw 3 lines, and not have an implicit close path.
424 TEST_P(AiksTest, CanDrawAnOpenPath) {
425  DisplayListBuilder builder;
426 
427  // Starting at (50, 50), draw lines from:
428  // 1. (50, height)
429  // 2. (width, height)
430  // 3. (width, 50)
431  DlPathBuilder path_builder;
432  path_builder.MoveTo(DlPoint(50, 50));
433  path_builder.LineTo(DlPoint(50, 100));
434  path_builder.LineTo(DlPoint(100, 100));
435  path_builder.LineTo(DlPoint(100, 50));
436 
437  DlPaint paint;
438  paint.setColor(DlColor::kRed());
439  paint.setDrawStyle(DlDrawStyle::kStroke);
440  paint.setStrokeWidth(10);
441 
442  builder.DrawPath(DlPath(path_builder), paint);
443 
444  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
445 }
446 
447 TEST_P(AiksTest, CanDrawAnOpenPathThatIsntARect) {
448  DisplayListBuilder builder;
449 
450  // Draw a stroked path that is explicitly closed to verify
451  // It doesn't become a rectangle.
452  DlPathBuilder path_builder;
453  path_builder.MoveTo(DlPoint(50, 50));
454  path_builder.LineTo(DlPoint(520, 120));
455  path_builder.LineTo(DlPoint(300, 310));
456  path_builder.LineTo(DlPoint(100, 50));
457  path_builder.Close();
458 
459  DlPaint paint;
460  paint.setColor(DlColor::kRed());
461  paint.setDrawStyle(DlDrawStyle::kStroke);
462  paint.setStrokeWidth(10);
463 
464  builder.DrawPath(DlPath(path_builder), paint);
465 
466  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
467 }
468 
469 TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
470  // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
471  auto callback = [&]() -> sk_sp<DisplayList> {
472  static Color color = Color::Black().WithAlpha(0.5);
473  static float scale = 3;
474  static bool add_circle_clip = true;
475 
476  if (AiksTest::ImGuiBegin("Controls", nullptr,
477  ImGuiWindowFlags_AlwaysAutoResize)) {
478  ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
479  ImGui::SliderFloat("Scale", &scale, 0, 6);
480  ImGui::Checkbox("Circle clip", &add_circle_clip);
481  ImGui::End();
482  }
483 
484  DisplayListBuilder builder;
485  builder.Scale(GetContentScale().x, GetContentScale().y);
486  DlPaint paint;
487 
488  paint.setColor(DlColor::kWhite());
489  builder.DrawPaint(paint);
490 
491  paint.setColor(
492  DlColor::ARGB(color.alpha, color.red, color.green, color.blue));
493  paint.setDrawStyle(DlDrawStyle::kStroke);
494  paint.setStrokeWidth(10);
495 
496  DlPathBuilder path_builder;
497  path_builder.MoveTo(DlPoint(20, 20));
498  path_builder.QuadraticCurveTo(DlPoint(60, 20), DlPoint(60, 60));
499  path_builder.Close();
500  path_builder.MoveTo(DlPoint(60, 20));
501  path_builder.QuadraticCurveTo(DlPoint(60, 60), DlPoint(20, 60));
502  DlPath path(path_builder);
503 
504  builder.Scale(scale, scale);
505 
506  if (add_circle_clip) {
507  static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
508  Color::Red());
509  static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
510  Color::Red());
511  auto [handle_a, handle_b] =
512  DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
513 
514  Matrix screen_to_canvas = builder.GetMatrix();
515  if (!screen_to_canvas.IsInvertible()) {
516  return nullptr;
517  }
518  screen_to_canvas = screen_to_canvas.Invert();
519 
520  Point point_a = screen_to_canvas * handle_a;
521  Point point_b = screen_to_canvas * handle_b;
522 
523  Point middle = point_a + point_b;
524  middle *= GetContentScale().x / 2;
525 
526  auto radius = point_a.GetDistance(middle);
527 
528  builder.ClipPath(DlPath::MakeCircle(middle, radius));
529  }
530 
531  for (auto join :
532  {DlStrokeJoin::kBevel, DlStrokeJoin::kRound, DlStrokeJoin::kMiter}) {
533  paint.setStrokeJoin(join);
534  for (auto cap :
535  {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
536  paint.setStrokeCap(cap);
537  builder.DrawPath(path, paint);
538  builder.Translate(80, 0);
539  }
540  builder.Translate(-240, 60);
541  }
542 
543  return builder.Build();
544  };
545 
546  ASSERT_TRUE(OpenPlaygroundHere(callback));
547 }
548 
549 TEST_P(AiksTest, DrawLinesRenderCorrectly) {
550  DisplayListBuilder builder;
551  builder.Scale(GetContentScale().x, GetContentScale().y);
552 
553  DlPaint paint;
554  paint.setColor(DlColor::kBlue());
555  paint.setStrokeWidth(10);
556 
557  auto draw = [&builder](DlPaint& paint) {
558  for (auto cap :
559  {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
560  paint.setStrokeCap(cap);
561  DlPoint origin = {100, 100};
562  builder.DrawLine(DlPoint(150, 100), DlPoint(250, 100), paint);
563  for (int d = 15; d < 90; d += 15) {
565  Point origin = {100, 100};
566  Point p0 = {50, 0};
567  Point p1 = {150, 0};
568  auto a = origin + m * p0;
569  auto b = origin + m * p1;
570 
571  builder.DrawLine(a, b, paint);
572  }
573  builder.DrawLine(DlPoint(100, 150), DlPoint(100, 250), paint);
574  builder.DrawCircle(origin, 35, paint);
575 
576  builder.DrawLine(DlPoint(250, 250), DlPoint(250, 250), paint);
577 
578  builder.Translate(250, 0);
579  }
580  builder.Translate(-750, 250);
581  };
582 
583  std::vector<DlColor> colors = {
584  DlColor::ARGB(1, 0x1f / 255.0, 0.0, 0x5c / 255.0),
585  DlColor::ARGB(1, 0x5b / 255.0, 0.0, 0x60 / 255.0),
586  DlColor::ARGB(1, 0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0),
587  DlColor::ARGB(1, 0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0),
588  DlColor::ARGB(1, 0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0),
589  DlColor::ARGB(1, 0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0),
590  DlColor::ARGB(1, 0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0)};
591  std::vector<Scalar> stops = {
592  0.0,
593  (1.0 / 6.0) * 1,
594  (1.0 / 6.0) * 2,
595  (1.0 / 6.0) * 3,
596  (1.0 / 6.0) * 4,
597  (1.0 / 6.0) * 5,
598  1.0,
599  };
600 
601  auto texture = DlImageImpeller::Make(
602  CreateTextureForFixture("airplane.jpg",
603  /*enable_mipmapping=*/true));
604 
605  draw(paint);
606 
607  paint.setColorSource(DlColorSource::MakeRadial({100, 100}, 200, stops.size(),
608  colors.data(), stops.data(),
609  DlTileMode::kMirror));
610  draw(paint);
611 
612  DlMatrix matrix = DlMatrix::MakeTranslation({-150, 75});
613  paint.setColorSource(DlColorSource::MakeImage(
614  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
615  DlImageSampling::kMipmapLinear, &matrix));
616  draw(paint);
617 
618  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
619 }
620 
621 // The goal of this test is to show that scaling the lines doesn't also scale
622 // the antialiasing. The amount of blurring should be the same for both
623 // horizontal lines.
624 TEST_P(AiksTest, ScaleExperimentAntialiasLines) {
625  Scalar scale = 5.0;
626  Scalar line_width = 10.f;
627  auto callback = [&]() -> sk_sp<DisplayList> {
628  if (AiksTest::ImGuiBegin("Controls", nullptr,
629  ImGuiWindowFlags_AlwaysAutoResize)) {
630  ImGui::SliderFloat("Scale", &scale, 0.001, 5);
631  ImGui::SliderFloat("Width", &line_width, 1, 20);
632 
633  ImGui::End();
634  }
635  DisplayListBuilder builder;
636  builder.Scale(GetContentScale().x, GetContentScale().y);
637 
638  builder.DrawPaint(DlPaint(DlColor(0xff111111)));
639 
640  {
641  DlPaint paint;
642  paint.setColor(DlColor::kGreenYellow());
643  paint.setStrokeWidth(line_width);
644 
645  builder.DrawLine(DlPoint(100, 100), DlPoint(350, 100), paint);
646  builder.DrawLine(DlPoint(100, 100), DlPoint(350, 150), paint);
647 
648  builder.Save();
649  builder.Translate(100, 300);
650  builder.Scale(scale, scale);
651  builder.Translate(-100, -300);
652  builder.DrawLine(DlPoint(100, 300), DlPoint(350, 300), paint);
653  builder.DrawLine(DlPoint(100, 300), DlPoint(350, 450), paint);
654  builder.Restore();
655  }
656 
657  {
658  DlPaint paint;
659  paint.setColor(DlColor::kGreenYellow());
660  paint.setStrokeWidth(2.0);
661 
662  builder.Save();
663  builder.Translate(100, 500);
664  builder.Scale(0.2, 0.2);
665  builder.Translate(-100, -500);
666  builder.DrawLine(DlPoint(100, 500), DlPoint(350, 500), paint);
667  builder.DrawLine(DlPoint(100, 500), DlPoint(350, 650), paint);
668  builder.Restore();
669  }
670 
671  return builder.Build();
672  };
673  ASSERT_TRUE(OpenPlaygroundHere(callback));
674 }
675 
676 TEST_P(AiksTest, HexagonExperimentAntialiasLines) {
677  float scale = 5.0f;
678  float line_width = 10.f;
679  float rotation = 0.f;
680 
681  auto callback = [&]() -> sk_sp<DisplayList> {
682  if (AiksTest::ImGuiBegin("Controls", nullptr,
683  ImGuiWindowFlags_AlwaysAutoResize)) {
684  // Use ImGui::SliderFloat for consistency
685  ImGui::SliderFloat("Scale", &scale, 0.001f, 5.0f);
686  ImGui::SliderFloat("Width", &line_width, 1.0f, 20.0f);
687  ImGui::SliderFloat("Rotation", &rotation, 0.0f, 180.0f);
688 
689  ImGui::End();
690  }
691  DisplayListBuilder builder;
692  builder.Scale(static_cast<float>(GetContentScale().x),
693  static_cast<float>(GetContentScale().y));
694 
695  builder.DrawPaint(DlPaint(DlColor(0xff111111))); // Background
696 
697  {
698  DlPaint hex_paint;
699  hex_paint.setColor(
700  DlColor::kGreen()); // Changed color to Red for visibility
701  hex_paint.setStrokeWidth(line_width); // Use the interactive width
702 
703  float cx = 512.0f; // Center X
704  float cy = 384.0f; // Center Y
705  float r = 80.0f; // Radius (distance from center to vertex)
706 
707  float r_sin60 = r * std::sqrt(3.0f) / 2.0f;
708  float r_cos60 = r / 2.0f;
709 
710  DlPoint v0 = DlPoint(cx + r, cy); // Right vertex
711  DlPoint v1 = DlPoint(cx + r_cos60, cy - r_sin60); // Top-right vertex
712  DlPoint v2 = DlPoint(
713  cx - r_cos60,
714  cy - r_sin60); // Top-left vertex (v1-v2 is top horizontal side)
715  DlPoint v3 = DlPoint(cx - r, cy); // Left vertex
716  DlPoint v4 = DlPoint(cx - r_cos60, cy + r_sin60); // Bottom-left vertex
717  DlPoint v5 =
718  DlPoint(cx + r_cos60, cy + r_sin60); // Bottom-right vertex (v4-v5 is
719  // bottom horizontal side)
720 
721  builder.Translate(cx, cy);
722  builder.Scale(scale, scale);
723  builder.Rotate(rotation);
724  builder.Translate(-cx, -cy);
725 
726  builder.DrawLine(v0, v1, hex_paint);
727  builder.DrawLine(v1, v2, hex_paint); // Top side
728  builder.DrawLine(v2, v3, hex_paint);
729  builder.DrawLine(v3, v4, hex_paint);
730  builder.DrawLine(v4, v5, hex_paint); // Bottom side
731  builder.DrawLine(v5, v0, hex_paint); // Close the hexagon
732  }
733 
734  return builder.Build();
735  };
736  ASSERT_TRUE(OpenPlaygroundHere(callback));
737 }
738 
739 TEST_P(AiksTest, SimpleExperimentAntialiasLines) {
740  DisplayListBuilder builder;
741  builder.Scale(GetContentScale().x, GetContentScale().y);
742 
743  builder.DrawPaint(DlPaint(DlColor(0xff111111)));
744 
745  DlPaint paint;
746  paint.setColor(DlColor::kGreenYellow());
747  paint.setStrokeWidth(10);
748 
749  auto draw = [&builder](DlPaint& paint) {
750  for (auto cap :
751  {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
752  paint.setStrokeCap(cap);
753  DlPoint origin = {100, 100};
754  builder.DrawLine(DlPoint(150, 100), DlPoint(250, 100), paint);
755  for (int d = 15; d < 90; d += 15) {
757  Point origin = {100, 100};
758  Point p0 = {50, 0};
759  Point p1 = {150, 0};
760  auto a = origin + m * p0;
761  auto b = origin + m * p1;
762 
763  builder.DrawLine(a, b, paint);
764  }
765  builder.DrawLine(DlPoint(100, 150), DlPoint(100, 250), paint);
766  builder.DrawCircle(origin, 35, paint);
767 
768  builder.DrawLine(DlPoint(250, 250), DlPoint(250, 250), paint);
769 
770  builder.Translate(250, 0);
771  }
772  builder.Translate(-750, 250);
773  };
774 
775  draw(paint);
776 
777  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
778 }
779 
780 TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) {
781  DisplayListBuilder builder;
782  DlPaint paint;
783  paint.setColor(DlColor::kRed());
784  paint.setDrawStyle(DlDrawStyle::kStroke);
785  paint.setStrokeWidth(10);
786 
787  builder.Translate(100, 100);
788  builder.DrawPath(DlPath::MakeRect(DlRect::MakeSize(DlSize(100, 100))), paint);
789 
790  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
791 }
792 
793 TEST_P(AiksTest, DrawRectStrokesWithBevelJoinRenderCorrectly) {
794  DisplayListBuilder builder;
795  DlPaint paint;
796  paint.setColor(DlColor::kRed());
797  paint.setDrawStyle(DlDrawStyle::kStroke);
798  paint.setStrokeWidth(10);
799  paint.setStrokeJoin(DlStrokeJoin::kBevel);
800 
801  builder.Translate(100, 100);
802  builder.DrawPath(DlPath::MakeRect(DlRect::MakeSize(DlSize(100, 100))), paint);
803 
804  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
805 }
806 
807 TEST_P(AiksTest, CanDrawMultiContourConvexPath) {
808  DlPathBuilder path_builder;
809  for (auto i = 0; i < 10; i++) {
810  if (i % 2 == 0) {
811  // We use the factory method to convert the circle to a path so that it
812  // uses the legacy conics for legacy golden output.
813  DlPath circle =
814  DlPath::MakeCircle(DlPoint(100 + 50 * i, 100 + 50 * i), 100);
815  path_builder.AddPath(circle.GetPath());
816  path_builder.Close();
817  } else {
818  path_builder.MoveTo(DlPoint(100.f + 50.f * i - 100, 100.f + 50.f * i));
819  path_builder.LineTo(DlPoint(100.f + 50.f * i, 100.f + 50.f * i - 100));
820  path_builder.LineTo(DlPoint(100.f + 50.f * i - 100, //
821  100.f + 50.f * i - 100));
822  path_builder.Close();
823  }
824  }
825  DlPath path(path_builder);
826 
827  DisplayListBuilder builder;
828  DlPaint paint;
829  paint.setColor(DlColor::kRed().withAlpha(102));
830  builder.DrawPath(path, paint);
831 
832  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
833 }
834 
835 TEST_P(AiksTest, ArcWithZeroSweepAndBlur) {
836  DisplayListBuilder builder;
837  builder.Scale(GetContentScale().x, GetContentScale().y);
838 
839  DlPaint paint;
840  paint.setColor(DlColor::kRed());
841 
842  std::vector<DlColor> colors = {DlColor::RGBA(1.0, 0.0, 0.0, 1.0),
843  DlColor::RGBA(0.0, 0.0, 0.0, 1.0)};
844  std::vector<Scalar> stops = {0.0, 1.0};
845 
846  paint.setColorSource(
847  DlColorSource::MakeSweep({100, 100}, 45, 135, stops.size(), colors.data(),
848  stops.data(), DlTileMode::kMirror));
849  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
850 
851  DlPathBuilder path_builder;
852  path_builder.AddArc(DlRect::MakeXYWH(10, 10, 100, 100), //
853  DlDegrees(0), DlDegrees(0));
854  builder.DrawPath(DlPath(path_builder), paint);
855 
856  // Check that this empty picture can be created without crashing.
857  builder.Build();
858 }
859 
860 TEST_P(AiksTest, CanRenderClips) {
861  DisplayListBuilder builder;
862  DlPaint paint;
863  paint.setColor(DlColor::kFuchsia());
864 
865  builder.ClipPath(DlPath::MakeRect(DlRect::MakeXYWH(0, 0, 500, 500)));
866  builder.DrawPath(DlPath::MakeCircle(DlPoint(500, 500), 250), paint);
867 
868  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
869 }
870 
871 TEST_P(AiksTest, FatStrokeArc) {
872  DlScalar stroke_width = 300;
873  DlScalar aspect = 1.0;
874  DlScalar start_angle = 0;
875  DlScalar end_angle = 90;
876  auto callback = [&]() -> sk_sp<DisplayList> {
877  if (AiksTest::ImGuiBegin("Controls", nullptr,
878  ImGuiWindowFlags_AlwaysAutoResize)) {
879  ImGui::SliderFloat("Stroke Width", &stroke_width, 1, 300);
880  ImGui::SliderFloat("Aspect", &aspect, 0.5, 2.0);
881  ImGui::SliderFloat("Start Angle", &start_angle, 0, 360);
882  ImGui::SliderFloat("End Angle", &end_angle, 0, 360);
883  ImGui::End();
884  }
885 
886  DisplayListBuilder builder;
887  DlPaint grey_paint;
888  grey_paint.setColor(DlColor(0xff111111));
889  builder.DrawPaint(grey_paint);
890 
891  DlPaint white_paint;
892  white_paint.setColor(DlColor::kWhite());
893  white_paint.setStrokeWidth(stroke_width);
894  white_paint.setDrawStyle(DlDrawStyle::kStroke);
895  DlPaint red_paint;
896  red_paint.setColor(DlColor::kRed());
897 
898  Rect rect = Rect::MakeXYWH(100, 100, 100, aspect * 100);
899  builder.DrawRect(rect, red_paint);
900  builder.DrawArc(rect, start_angle, end_angle,
901  /*useCenter=*/false, white_paint);
902  DlScalar frontier = rect.GetRight() + stroke_width / 2.0;
903  builder.DrawLine(Point(frontier, 0), Point(frontier, 150), red_paint);
904 
905  return builder.Build();
906  };
907  ASSERT_TRUE(OpenPlaygroundHere(callback));
908 }
909 
910 TEST_P(AiksTest, CanRenderOverlappingMultiContourPath) {
911  DisplayListBuilder builder;
912 
913  DlPaint paint;
914  paint.setColor(DlColor::kRed());
915 
916  RoundingRadii radii = {
917  .top_left = DlSize(50, 50),
918  .top_right = DlSize(50, 50),
919  .bottom_left = DlSize(50, 50),
920  .bottom_right = DlSize(50, 50),
921  };
922 
923  const Scalar kTriangleHeight = 100;
924  DlRoundRect rrect = DlRoundRect::MakeRectRadii(
925  DlRect::MakeXYWH(-kTriangleHeight / 2.0f, -kTriangleHeight / 2.0f,
926  kTriangleHeight, kTriangleHeight),
927  radii //
928  );
929  // We use the factory method to convert the rrect to a path so that it
930  // uses the legacy conics for legacy golden output.
931  DlPath rrect_path = DlPath::MakeRoundRect(rrect);
932 
933  builder.Translate(200, 200);
934  // Form a path similar to the Material drop slider value indicator. Both
935  // shapes should render identically side-by-side.
936  {
937  DlPathBuilder path_builder;
938  path_builder.MoveTo(DlPoint(0, kTriangleHeight));
939  path_builder.LineTo(DlPoint(-kTriangleHeight / 2.0f, 0));
940  path_builder.LineTo(DlPoint(kTriangleHeight / 2.0f, 0));
941  path_builder.Close();
942  path_builder.AddPath(rrect_path.GetPath());
943 
944  builder.DrawPath(DlPath(path_builder), paint);
945  }
946  builder.Translate(100, 0);
947 
948  {
949  DlPathBuilder path_builder;
950  path_builder.MoveTo(DlPoint(0, kTriangleHeight));
951  path_builder.LineTo(DlPoint(-kTriangleHeight / 2.0f, 0));
952  path_builder.LineTo(DlPoint(0, -10));
953  path_builder.LineTo(DlPoint(kTriangleHeight / 2.0f, 0));
954  path_builder.Close();
955  path_builder.AddPath(rrect_path.GetPath());
956 
957  builder.DrawPath(DlPath(path_builder), paint);
958  }
959 
960  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
961 }
962 
963 TEST_P(AiksTest, TwoContourPathWithSinglePointContour) {
964  DisplayListBuilder builder;
965 
966  DlPaint paint;
967  paint.setColor(DlColor::kRed());
968  paint.setDrawStyle(DlDrawStyle::kStroke);
969  paint.setStrokeWidth(15.0);
970  paint.setStrokeCap(DlStrokeCap::kRound);
971 
972  DlPathBuilder path_builder;
973  path_builder.MoveTo(DlPoint(100, 100));
974  path_builder.LineTo(DlPoint(150, 150));
975  path_builder.MoveTo(DlPoint(200, 200));
976  path_builder.LineTo(DlPoint(200, 200));
977 
978  builder.DrawPath(DlPath(path_builder), paint);
979 
980  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
981 }
982 
983 } // namespace testing
984 } // namespace impeller
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
PathBuilder & AddPath(const Path &path)
int32_t x
TEST_P(AiksTest, DrawAtlasNoColor)
float Scalar
Definition: scalar.h:18
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
constexpr float kSqrt2Over2
Definition: constants.h:51
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:27
TPoint< Scalar > Point
Definition: point.h:327
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:23
const Scalar stroke_width
const Scalar scale
SeparatedVector2 offset
Scalar blue
Definition: color.h:138
Scalar alpha
Definition: color.h:143
static constexpr Color Black()
Definition: color.h:266
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:278
Scalar red
Definition: color.h:128
static constexpr Color Red()
Definition: color.h:272
Scalar green
Definition: color.h:133
Point Solve(Scalar time) const
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
bool IsInvertible() const
Definition: matrix.h:321
Matrix Invert() const
Definition: matrix.cc:97
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:223
constexpr Type GetDistance(const TPoint &p) const
Definition: point.h:200
constexpr auto GetRight() const
Definition: rect.h:359
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136