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