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 #include "include/core/SkMatrix.h"
21 #include "include/core/SkPath.h"
22 #include "include/core/SkPathTypes.h"
23 #include "include/core/SkRRect.h"
24 
25 namespace impeller {
26 namespace testing {
27 
28 using namespace flutter;
29 
30 TEST_P(AiksTest, RotateColorFilteredPath) {
31  DisplayListBuilder builder;
32  builder.Transform(SkMatrix::Translate(300, 300) * SkMatrix::RotateDeg(90));
33 
34  SkPath arrow_stem;
35  SkPath arrow_head;
36 
37  arrow_stem.moveTo({120, 190}).lineTo({120, 50});
38  arrow_head.moveTo({50, 120}).lineTo({120, 190}).lineTo({190, 120});
39 
40  auto filter =
41  DlBlendColorFilter::Make(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, paint);
52  builder.DrawPath(arrow_head, paint);
53 
54  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
55 }
56 
57 TEST_P(AiksTest, CanRenderStrokes) {
58  DisplayListBuilder builder;
59  DlPaint paint;
60  paint.setColor(DlColor::kRed());
61  paint.setStrokeWidth(20);
62  paint.setDrawStyle(DlDrawStyle::kStroke);
63 
64  builder.DrawPath(SkPath::Line({200, 100}, {800, 100}), paint);
65 
66  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
67 }
68 
69 TEST_P(AiksTest, CanRenderCurvedStrokes) {
70  DisplayListBuilder builder;
71  DlPaint paint;
72  paint.setColor(DlColor::kRed());
73  paint.setStrokeWidth(25);
74  paint.setDrawStyle(DlDrawStyle::kStroke);
75 
76  builder.DrawPath(SkPath::Circle(500, 500, 250), paint);
77 
78  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
79 }
80 
81 TEST_P(AiksTest, CanRenderThickCurvedStrokes) {
82  DisplayListBuilder builder;
83  DlPaint paint;
84  paint.setColor(DlColor::kRed());
85  paint.setStrokeWidth(100);
86  paint.setDrawStyle(DlDrawStyle::kStroke);
87 
88  builder.DrawPath(SkPath::Circle(100, 100, 50), paint);
89 
90  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
91 }
92 
93 TEST_P(AiksTest, CanRenderThinCurvedStrokes) {
94  DisplayListBuilder builder;
95  DlPaint paint;
96  paint.setColor(DlColor::kRed());
97  paint.setStrokeWidth(0.01);
98  paint.setDrawStyle(DlDrawStyle::kStroke);
99 
100  builder.DrawPath(SkPath::Circle(100, 100, 50), paint);
101 
102  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
103 }
104 
105 TEST_P(AiksTest, CanRenderStrokePathThatEndsAtSharpTurn) {
106  DisplayListBuilder builder;
107  DlPaint paint;
108  paint.setColor(DlColor::kRed());
109  paint.setStrokeWidth(200);
110  paint.setDrawStyle(DlDrawStyle::kStroke);
111 
112  SkPath path;
113  path.arcTo(SkRect::MakeXYWH(100, 100, 200, 200), 0, 90, false);
114 
115  builder.DrawPath(path, paint);
116 
117  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
118 }
119 
120 TEST_P(AiksTest, CanRenderStrokePathWithCubicLine) {
121  DisplayListBuilder builder;
122 
123  DlPaint paint;
124  paint.setColor(DlColor::kRed());
125  paint.setStrokeWidth(20);
126  paint.setDrawStyle(DlDrawStyle::kStroke);
127 
128  SkPath path;
129  path.moveTo(0, 200);
130  path.cubicTo(50, 400, 350, 0, 400, 200);
131 
132  builder.DrawPath(path, 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  SkPath path;
148  path.moveTo(250, 250);
149  path.quadTo(100, 100, 250, 250);
150 
151  builder.DrawPath(path, paint);
152 
153  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
154 }
155 
156 TEST_P(AiksTest, CanRenderDifferencePaths) {
157  DisplayListBuilder builder;
158 
159  DlPaint paint;
160  paint.setColor(DlColor::kRed());
161 
162  SkPoint radii[4] = {{50, 25}, {25, 50}, {50, 25}, {25, 50}};
163  SkPath path;
164  SkRRect rrect;
165  rrect.setRectRadii(SkRect::MakeXYWH(100, 100, 200, 200), radii);
166  path.addRRect(rrect);
167  path.addCircle(200, 200, 50);
168  path.setFillType(SkPathFillType::kEvenOdd);
169 
170  builder.DrawImage(
171  DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")), {10, 10},
172  {});
173  builder.DrawPath(path, paint);
174 
175  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
176 }
177 
178 // Regression test for https://github.com/flutter/flutter/issues/134816.
179 //
180 // It should be possible to draw 3 lines, and not have an implicit close path.
181 TEST_P(AiksTest, CanDrawAnOpenPath) {
182  DisplayListBuilder builder;
183 
184  // Starting at (50, 50), draw lines from:
185  // 1. (50, height)
186  // 2. (width, height)
187  // 3. (width, 50)
188  SkPath path;
189  path.moveTo(50, 50);
190  path.lineTo(50, 100);
191  path.lineTo(100, 100);
192  path.lineTo(100, 50);
193 
194  DlPaint paint;
195  paint.setColor(DlColor::kRed());
196  paint.setDrawStyle(DlDrawStyle::kStroke);
197  paint.setStrokeWidth(10);
198 
199  builder.DrawPath(path, paint);
200 
201  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
202 }
203 
204 TEST_P(AiksTest, CanDrawAnOpenPathThatIsntARect) {
205  DisplayListBuilder builder;
206 
207  // Draw a stroked path that is explicitly closed to verify
208  // It doesn't become a rectangle.
209  SkPath path;
210  // PathBuilder builder;
211  path.moveTo(50, 50);
212  path.lineTo(520, 120);
213  path.lineTo(300, 310);
214  path.lineTo(100, 50);
215  path.close();
216 
217  DlPaint paint;
218  paint.setColor(DlColor::kRed());
219  paint.setDrawStyle(DlDrawStyle::kStroke);
220  paint.setStrokeWidth(10);
221 
222  builder.DrawPath(path, paint);
223 
224  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
225 }
226 
227 TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
228  // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
229  auto callback = [&]() -> sk_sp<DisplayList> {
230  static Color color = Color::Black().WithAlpha(0.5);
231  static float scale = 3;
232  static bool add_circle_clip = true;
233 
234  if (AiksTest::ImGuiBegin("Controls", nullptr,
235  ImGuiWindowFlags_AlwaysAutoResize)) {
236  ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
237  ImGui::SliderFloat("Scale", &scale, 0, 6);
238  ImGui::Checkbox("Circle clip", &add_circle_clip);
239  ImGui::End();
240  }
241 
242  DisplayListBuilder builder;
243  builder.Scale(GetContentScale().x, GetContentScale().y);
244  DlPaint paint;
245 
246  paint.setColor(DlColor::kWhite());
247  builder.DrawPaint(paint);
248 
249  paint.setColor(
250  DlColor::ARGB(color.alpha, color.red, color.green, color.blue));
251  paint.setDrawStyle(DlDrawStyle::kStroke);
252  paint.setStrokeWidth(10);
253 
254  SkPath path;
255  path.moveTo({20, 20});
256  path.quadTo({60, 20}, {60, 60});
257  path.close();
258  path.moveTo({60, 20});
259  path.quadTo({60, 60}, {20, 60});
260 
261  builder.Scale(scale, scale);
262 
263  if (add_circle_clip) {
264  static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
265  Color::Red());
266  static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
267  Color::Red());
268  auto [handle_a, handle_b] =
269  DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
270 
271  SkMatrix screen_to_canvas = SkMatrix::I();
272  if (!builder.GetTransform().invert(&screen_to_canvas)) {
273  return nullptr;
274  }
275 
276  SkPoint point_a =
277  screen_to_canvas.mapPoint(SkPoint::Make(handle_a.x, handle_a.y));
278  SkPoint point_b =
279  screen_to_canvas.mapPoint(SkPoint::Make(handle_b.x, handle_b.y));
280 
281  SkPoint middle = point_a + point_b;
282  middle.scale(GetContentScale().x / 2);
283 
284  auto radius = SkPoint::Distance(point_a, middle);
285 
286  builder.ClipPath(SkPath::Circle(middle.x(), middle.y(), radius));
287  }
288 
289  for (auto join :
290  {DlStrokeJoin::kBevel, DlStrokeJoin::kRound, DlStrokeJoin::kMiter}) {
291  paint.setStrokeJoin(join);
292  for (auto cap :
293  {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
294  paint.setStrokeCap(cap);
295  builder.DrawPath(path, paint);
296  builder.Translate(80, 0);
297  }
298  builder.Translate(-240, 60);
299  }
300 
301  return builder.Build();
302  };
303 
304  ASSERT_TRUE(OpenPlaygroundHere(callback));
305 }
306 
307 TEST_P(AiksTest, DrawLinesRenderCorrectly) {
308  DisplayListBuilder builder;
309  builder.Scale(GetContentScale().x, GetContentScale().y);
310 
311  DlPaint paint;
312  paint.setColor(DlColor::kBlue());
313  paint.setStrokeWidth(10);
314 
315  auto draw = [&builder](DlPaint& paint) {
316  for (auto cap :
317  {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
318  paint.setStrokeCap(cap);
319  SkPoint origin = {100, 100};
320  builder.DrawLine({150, 100}, {250, 100}, paint);
321  for (int d = 15; d < 90; d += 15) {
323  Point origin = {100, 100};
324  Point p0 = {50, 0};
325  Point p1 = {150, 0};
326  auto a = origin + m * p0;
327  auto b = origin + m * p1;
328 
329  builder.DrawLine(SkPoint::Make(a.x, a.y), SkPoint::Make(b.x, b.y),
330  paint);
331  }
332  builder.DrawLine({100, 150}, {100, 250}, paint);
333  builder.DrawCircle({origin}, 35, paint);
334 
335  builder.DrawLine({250, 250}, {250, 250}, paint);
336 
337  builder.Translate(250, 0);
338  }
339  builder.Translate(-750, 250);
340  };
341 
342  std::vector<DlColor> colors = {
343  DlColor::ARGB(1, 0x1f / 255.0, 0.0, 0x5c / 255.0),
344  DlColor::ARGB(1, 0x5b / 255.0, 0.0, 0x60 / 255.0),
345  DlColor::ARGB(1, 0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0),
346  DlColor::ARGB(1, 0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0),
347  DlColor::ARGB(1, 0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0),
348  DlColor::ARGB(1, 0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0),
349  DlColor::ARGB(1, 0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0)};
350  std::vector<Scalar> stops = {
351  0.0,
352  (1.0 / 6.0) * 1,
353  (1.0 / 6.0) * 2,
354  (1.0 / 6.0) * 3,
355  (1.0 / 6.0) * 4,
356  (1.0 / 6.0) * 5,
357  1.0,
358  };
359 
360  auto texture = DlImageImpeller::Make(
361  CreateTextureForFixture("airplane.jpg",
362  /*enable_mipmapping=*/true));
363 
364  draw(paint);
365 
366  paint.setColorSource(DlColorSource::MakeRadial({100, 100}, 200, stops.size(),
367  colors.data(), stops.data(),
368  DlTileMode::kMirror));
369  draw(paint);
370 
371  SkMatrix matrix = SkMatrix::Translate(-150, 75);
372  paint.setColorSource(std::make_shared<DlImageColorSource>(
373  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
374  DlImageSampling::kMipmapLinear, &matrix));
375  draw(paint);
376 
377  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
378 }
379 
380 TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) {
381  DisplayListBuilder builder;
382  DlPaint paint;
383  paint.setColor(DlColor::kRed());
384  paint.setDrawStyle(DlDrawStyle::kStroke);
385  paint.setStrokeWidth(10);
386 
387  builder.Translate(100, 100);
388  builder.DrawPath(SkPath::Rect(SkRect::MakeSize(SkSize{100, 100})), {paint});
389 
390  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
391 }
392 
393 TEST_P(AiksTest, DrawRectStrokesWithBevelJoinRenderCorrectly) {
394  DisplayListBuilder builder;
395  DlPaint paint;
396  paint.setColor(DlColor::kRed());
397  paint.setDrawStyle(DlDrawStyle::kStroke);
398  paint.setStrokeWidth(10);
399  paint.setStrokeJoin(DlStrokeJoin::kBevel);
400 
401  builder.Translate(100, 100);
402  builder.DrawPath(SkPath::Rect(SkRect::MakeSize(SkSize{100, 100})), paint);
403 
404  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
405 }
406 
407 TEST_P(AiksTest, CanDrawMultiContourConvexPath) {
408  SkPath path;
409  for (auto i = 0; i < 10; i++) {
410  if (i % 2 == 0) {
411  path.addCircle(100 + 50 * i, 100 + 50 * i, 100);
412  path.close();
413  } else {
414  path.moveTo({100.f + 50.f * i - 100, 100.f + 50.f * i});
415  path.lineTo({100.f + 50.f * i, 100.f + 50.f * i - 100});
416  path.lineTo({100.f + 50.f * i - 100, 100.f + 50.f * i - 100});
417  path.close();
418  }
419  }
420 
421  DisplayListBuilder builder;
422  DlPaint paint;
423  paint.setColor(DlColor::kRed().withAlpha(102));
424  builder.DrawPath(path, paint);
425 
426  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
427 }
428 
429 TEST_P(AiksTest, ArcWithZeroSweepAndBlur) {
430  DisplayListBuilder builder;
431  builder.Scale(GetContentScale().x, GetContentScale().y);
432 
433  DlPaint paint;
434  paint.setColor(DlColor::kRed());
435 
436  std::vector<DlColor> colors = {DlColor::RGBA(1.0, 0.0, 0.0, 1.0),
437  DlColor::RGBA(0.0, 0.0, 0.0, 1.0)};
438  std::vector<Scalar> stops = {0.0, 1.0};
439 
440  paint.setColorSource(
441  DlColorSource::MakeSweep({100, 100}, 45, 135, stops.size(), colors.data(),
442  stops.data(), DlTileMode::kMirror));
443  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
444 
445  SkPath path;
446  path.addArc(SkRect::MakeXYWH(10, 10, 100, 100), 0, 0);
447  builder.DrawPath(path, paint);
448 
449  // Check that this empty picture can be created without crashing.
450  builder.Build();
451 }
452 
453 TEST_P(AiksTest, CanRenderClips) {
454  DisplayListBuilder builder;
455  DlPaint paint;
456  paint.setColor(DlColor::kFuchsia());
457 
458  builder.ClipPath(SkPath::Rect(SkRect::MakeXYWH(0, 0, 500, 500)));
459  builder.DrawPath(SkPath::Circle(500, 500, 250), paint);
460 
461  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
462 }
463 
464 TEST_P(AiksTest, CanRenderOverlappingMultiContourPath) {
465  DisplayListBuilder builder;
466 
467  DlPaint paint;
468  paint.setColor(DlColor::kRed());
469 
470  SkPoint radii[4] = {{50, 50}, {50, 50}, {50, 50}, {50, 50}};
471 
472  const Scalar kTriangleHeight = 100;
473  SkRRect rrect;
474  rrect.setRectRadii(
475  SkRect::MakeXYWH(-kTriangleHeight / 2.0f, -kTriangleHeight / 2.0f,
476  kTriangleHeight, kTriangleHeight),
477  radii //
478  );
479 
480  builder.Translate(200, 200);
481  // Form a path similar to the Material drop slider value indicator. Both
482  // shapes should render identically side-by-side.
483  {
484  SkPath path;
485  path.moveTo(0, kTriangleHeight);
486  path.lineTo(-kTriangleHeight / 2.0f, 0);
487  path.lineTo(kTriangleHeight / 2.0f, 0);
488  path.close();
489  path.addRRect(rrect);
490 
491  builder.DrawPath(path, paint);
492  }
493  builder.Translate(100, 0);
494 
495  {
496  SkPath path;
497  path.moveTo(0, kTriangleHeight);
498  path.lineTo(-kTriangleHeight / 2.0f, 0);
499  path.lineTo(0, -10);
500  path.lineTo(kTriangleHeight / 2.0f, 0);
501  path.close();
502  path.addRRect(rrect);
503 
504  builder.DrawPath(path, paint);
505  }
506 
507  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
508 }
509 
510 } // namespace testing
511 } // namespace impeller
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:271
aiks_unittests.h
impeller::Color
Definition: color.h:123
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:23
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
widgets.h
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:277
flutter
Definition: dl_golden_blur_unittests.cc:15
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:776
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:78
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:213
impeller::PlaygroundPoint
Definition: widgets.h:17
impeller::TPoint< Scalar >
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:265
scale
const Scalar scale
Definition: stroke_path_geometry.cc:301
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:30
impeller::Degrees
Definition: scalar.h:51
color
DlColor color
Definition: dl_golden_blur_unittests.cc:24
impeller
Definition: allocation.cc:12
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
dl_image_impeller.h