Flutter Impeller
aiks_dl_basic_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/display_list.h"
6 #include "display_list/dl_sampling_options.h"
7 #include "display_list/dl_tile_mode.h"
8 #include "display_list/effects/dl_color_filter.h"
9 #include "display_list/effects/dl_color_source.h"
10 #include "display_list/effects/dl_image_filter.h"
11 #include "display_list/effects/dl_mask_filter.h"
13 
14 #include "flutter/display_list/dl_blend_mode.h"
15 #include "flutter/display_list/dl_builder.h"
16 #include "flutter/display_list/dl_color.h"
17 #include "flutter/display_list/dl_paint.h"
18 #include "flutter/display_list/geometry/dl_path_builder.h"
21 #include "flutter/testing/display_list_testing.h"
22 #include "flutter/testing/testing.h"
23 #include "imgui.h"
25 
26 namespace impeller {
27 namespace testing {
28 
29 using namespace flutter;
30 
31 TEST_P(AiksTest, CanRenderColoredRect) {
32  DisplayListBuilder builder;
33  DlPaint paint;
34  paint.setColor(DlColor::kBlue());
35  builder.DrawPath(DlPath::MakeRectXYWH(100.0f, 100.0f, 100.0f, 100.0f), paint);
36  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
37 }
38 
39 namespace {
40 using DrawRectProc =
41  std::function<void(DisplayListBuilder&, const DlRect&, const DlPaint&)>;
42 
43 sk_sp<DisplayList> MakeWideStrokedRects(Point scale,
44  const DrawRectProc& draw_rect) {
45  DisplayListBuilder builder;
46  builder.Scale(scale.x, scale.y);
47  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
48 
49  DlPaint paint;
50  paint.setColor(DlColor::kBlue().withAlphaF(0.5));
51  paint.setDrawStyle(DlDrawStyle::kStroke);
52  paint.setStrokeWidth(30.0f);
53 
54  // Each of these 3 sets of rects includes (with different join types):
55  // - One rectangle with a gap in the middle
56  // - One rectangle with no gap because it is too narrow
57  // - One rectangle with no gap because it is too short
58  paint.setStrokeJoin(DlStrokeJoin::kBevel);
59  draw_rect(builder, DlRect::MakeXYWH(100.0f, 100.0f, 100.0f, 100.0f), paint);
60  draw_rect(builder, DlRect::MakeXYWH(250.0f, 100.0f, 10.0f, 100.0f), paint);
61  draw_rect(builder, DlRect::MakeXYWH(100.0f, 250.0f, 100.0f, 10.0f), paint);
62 
63  paint.setStrokeJoin(DlStrokeJoin::kRound);
64  draw_rect(builder, DlRect::MakeXYWH(350.0f, 100.0f, 100.0f, 100.0f), paint);
65  draw_rect(builder, DlRect::MakeXYWH(500.0f, 100.0f, 10.0f, 100.0f), paint);
66  draw_rect(builder, DlRect::MakeXYWH(350.0f, 250.0f, 100.0f, 10.0f), paint);
67 
68  paint.setStrokeJoin(DlStrokeJoin::kMiter);
69  draw_rect(builder, DlRect::MakeXYWH(600.0f, 100.0f, 100.0f, 100.0f), paint);
70  draw_rect(builder, DlRect::MakeXYWH(750.0f, 100.0f, 10.0f, 100.0f), paint);
71  draw_rect(builder, DlRect::MakeXYWH(600.0f, 250.0f, 100.0f, 10.0f), paint);
72 
73  // And now draw 3 rectangles with a stroke width so large that that it
74  // overlaps in the middle in both directions (horizontal/vertical).
75  paint.setStrokeWidth(110.0f);
76 
77  paint.setStrokeJoin(DlStrokeJoin::kBevel);
78  draw_rect(builder, DlRect::MakeXYWH(100.0f, 400.0f, 100.0f, 100.0f), paint);
79 
80  paint.setStrokeJoin(DlStrokeJoin::kRound);
81  draw_rect(builder, DlRect::MakeXYWH(350.0f, 400.0f, 100.0f, 100.0f), paint);
82 
83  paint.setStrokeJoin(DlStrokeJoin::kMiter);
84  draw_rect(builder, DlRect::MakeXYWH(600.0f, 400.0f, 100.0f, 100.0f), paint);
85 
86  return builder.Build();
87 }
88 } // namespace
89 
90 TEST_P(AiksTest, CanRenderWideStrokedRectWithoutOverlap) {
91  ASSERT_TRUE(OpenPlaygroundHere(MakeWideStrokedRects(
92  GetContentScale(), [](DisplayListBuilder& builder, const DlRect& rect,
93  const DlPaint& paint) {
94  // Draw the rect directly
95  builder.DrawRect(rect, paint);
96  })));
97 }
98 
99 TEST_P(AiksTest, CanRenderWideStrokedRectPathWithoutOverlap) {
100  ASSERT_TRUE(OpenPlaygroundHere(MakeWideStrokedRects(
101  GetContentScale(), [](DisplayListBuilder& builder, const DlRect& rect,
102  const DlPaint& paint) {
103  // Draw the rect as a Path
104  builder.DrawPath(DlPath::MakeRect(rect), paint);
105  })));
106 }
107 
108 TEST_P(AiksTest, CanRenderImage) {
109  DisplayListBuilder builder;
110  DlPaint paint;
111  paint.setColor(DlColor::kRed());
112  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
113  builder.DrawImage(image, DlPoint(100.0, 100.0),
114  DlImageSampling::kNearestNeighbor, &paint);
115  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
116 }
117 
118 TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) {
119  DisplayListBuilder builder;
120  DlPaint paint;
121  paint.setColor(DlColor::kRed());
122  paint.setColorFilter(
123  DlColorFilter::MakeBlend(DlColor::kYellow(), DlBlendMode::kSrcOver));
124  paint.setInvertColors(true);
125  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
126 
127  builder.DrawImage(image, DlPoint(100.0, 100.0),
128  DlImageSampling::kNearestNeighbor, &paint);
129  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
130 }
131 
132 TEST_P(AiksTest, CanRenderColorFilterWithInvertColors) {
133  DisplayListBuilder builder;
134  DlPaint paint;
135  paint.setColor(DlColor::kRed());
136  paint.setColorFilter(
137  DlColorFilter::MakeBlend(DlColor::kYellow(), DlBlendMode::kSrcOver));
138  paint.setInvertColors(true);
139 
140  builder.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), paint);
141  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
142 }
143 
144 TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) {
145  DisplayListBuilder builder;
146  DlPaint paint;
147  paint.setColor(DlColor::kRed());
148  paint.setColorFilter(
149  DlColorFilter::MakeBlend(DlColor::kYellow(), DlBlendMode::kSrcOver));
150  paint.setInvertColors(true);
151 
152  builder.DrawPaint(paint);
153  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
154 }
155 
156 namespace {
157 bool GenerateMipmap(const std::shared_ptr<Context>& context,
158  std::shared_ptr<Texture> texture,
159  std::string_view label) {
160  auto buffer = context->CreateCommandBuffer();
161  if (!buffer) {
162  return false;
163  }
164  auto pass = buffer->CreateBlitPass();
165  if (!pass) {
166  return false;
167  }
168  pass->GenerateMipmap(std::move(texture), label);
169 
170  pass->EncodeCommands();
171  return context->GetCommandQueue()->Submit({buffer}).ok();
172 }
173 
174 void CanRenderTiledTexture(AiksTest* aiks_test,
175  DlTileMode tile_mode,
176  Matrix local_matrix = {}) {
177  auto context = aiks_test->GetContext();
178  ASSERT_TRUE(context);
179  auto texture = aiks_test->CreateTextureForFixture("table_mountain_nx.png",
180  /*enable_mipmapping=*/true);
181  GenerateMipmap(context, texture, "table_mountain_nx");
182  auto image = DlImageImpeller::Make(texture);
183  auto color_source = DlColorSource::MakeImage(
184  image, tile_mode, tile_mode, DlImageSampling::kNearestNeighbor,
185  &local_matrix);
186 
187  DisplayListBuilder builder;
188  DlPaint paint;
189  paint.setColor(DlColor::kWhite());
190  paint.setColorSource(color_source);
191 
192  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
193  builder.Translate(100.0f, 100.0f);
194  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
195 
196  // Should not change the image.
197  constexpr auto stroke_width = 64;
198  paint.setDrawStyle(DlDrawStyle::kStroke);
199  paint.setStrokeWidth(stroke_width);
200  if (tile_mode == DlTileMode::kDecal) {
201  builder.DrawRect(DlRect::MakeXYWH(stroke_width, stroke_width, 600, 600),
202  paint);
203  } else {
204  builder.DrawRect(DlRect::MakeXYWH(0, 0, 600, 600), paint);
205  }
206 
207  {
208  // Should not change the image.
209  DlPathBuilder path_builder;
210  path_builder.AddCircle(DlPoint(150, 150), 150);
211  path_builder.AddRoundRect(
212  RoundRect::MakeRectXY(DlRect::MakeLTRB(300, 300, 600, 600), 10, 10));
213  DlPath path = path_builder.TakePath();
214 
215  // Make sure path cannot be simplified...
216  EXPECT_FALSE(path.IsRect(nullptr));
217  EXPECT_FALSE(path.IsOval(nullptr));
218  EXPECT_FALSE(path.IsRoundRect(nullptr));
219 
220  // Make sure path will not trigger the optimal convex code
221  EXPECT_FALSE(path.IsConvex());
222 
223  paint.setDrawStyle(DlDrawStyle::kFill);
224  builder.DrawPath(path, paint);
225  }
226 
227  {
228  // Should not change the image. Tests the Convex short-cut code.
229 
230  // To avoid simplification, construct an explicit circle using conics.
231  constexpr float kConicWeight = 0.707106781f; // sqrt(2)/2
232  const DlPath path = DlPathBuilder()
233  .MoveTo({150, 300})
234  .ConicCurveTo({300, 300}, {300, 450}, kConicWeight)
235  .ConicCurveTo({300, 600}, {150, 600}, kConicWeight)
236  .ConicCurveTo({0, 600}, {0, 450}, kConicWeight)
237  .ConicCurveTo({0, 300}, {150, 300}, kConicWeight)
238  .Close()
239  .TakePath();
240 
241  // Make sure path cannot be simplified...
242  EXPECT_FALSE(path.IsRect(nullptr));
243  EXPECT_FALSE(path.IsOval(nullptr));
244  EXPECT_FALSE(path.IsRoundRect(nullptr));
245 
246  // But check that we will trigger the optimal convex code
247  EXPECT_TRUE(path.IsConvex());
248 
249  paint.setDrawStyle(DlDrawStyle::kFill);
250  builder.DrawPath(path, paint);
251  }
252 
253  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
254 }
255 } // namespace
256 
257 TEST_P(AiksTest, CanRenderTiledTextureClamp) {
258  CanRenderTiledTexture(this, DlTileMode::kClamp);
259 }
260 
261 TEST_P(AiksTest, CanRenderTiledTextureRepeat) {
262  CanRenderTiledTexture(this, DlTileMode::kRepeat);
263 }
264 
265 TEST_P(AiksTest, CanRenderTiledTextureMirror) {
266  CanRenderTiledTexture(this, DlTileMode::kMirror);
267 }
268 
269 TEST_P(AiksTest, CanRenderTiledTextureDecal) {
270  CanRenderTiledTexture(this, DlTileMode::kDecal);
271 }
272 
273 TEST_P(AiksTest, CanRenderTiledTextureClampWithTranslate) {
274  CanRenderTiledTexture(this, DlTileMode::kClamp,
275  Matrix::MakeTranslation({172.f, 172.f, 0.f}));
276 }
277 
278 TEST_P(AiksTest, CanRenderImageRect) {
279  DisplayListBuilder builder;
280  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
281 
282  DlISize image_half_size =
283  DlISize(image->GetSize().width * 0.5f, image->GetSize().height * 0.5f);
284 
285  // Render the bottom right quarter of the source image in a stretched rect.
286  auto source_rect = DlRect::MakeSize(image_half_size);
287  source_rect =
288  source_rect.Shift(image_half_size.width, image_half_size.height);
289 
290  builder.DrawImageRect(image, source_rect,
291  DlRect::MakeXYWH(100, 100, 600, 600),
292  DlImageSampling::kNearestNeighbor);
293  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
294 }
295 
296 TEST_P(AiksTest, DrawImageRectSrcOutsideBounds) {
297  DisplayListBuilder builder;
298  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
299 
300  // Use a source rect that is partially outside the bounds of the image.
301  auto source_rect = DlRect::MakeXYWH(
302  image->GetSize().width * 0.25f, image->GetSize().height * 0.4f,
303  image->GetSize().width, image->GetSize().height);
304 
305  auto dest_rect = DlRect::MakeXYWH(100, 100, 600, 600);
306 
307  DlPaint paint;
308  paint.setColor(DlColor::kMidGrey());
309  builder.DrawRect(dest_rect, paint);
310 
311  builder.DrawImageRect(image, source_rect, dest_rect,
312  DlImageSampling::kNearestNeighbor);
313  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
314 }
315 
316 TEST_P(AiksTest, CanRenderSimpleClips) {
317  DisplayListBuilder builder;
318  builder.Scale(GetContentScale().x, GetContentScale().y);
319  DlPaint paint;
320 
321  paint.setColor(DlColor::kWhite());
322  builder.DrawPaint(paint);
323 
324  auto draw = [&builder](const DlPaint& paint, Scalar x, Scalar y) {
325  builder.Save();
326  builder.Translate(x, y);
327  {
328  builder.Save();
329  builder.ClipRect(DlRect::MakeLTRB(50, 50, 150, 150));
330  builder.DrawPaint(paint);
331  builder.Restore();
332  }
333  {
334  builder.Save();
335  builder.ClipOval(DlRect::MakeLTRB(200, 50, 300, 150));
336  builder.DrawPaint(paint);
337  builder.Restore();
338  }
339  {
340  builder.Save();
341  builder.ClipRoundRect(
342  DlRoundRect::MakeRectXY(DlRect::MakeLTRB(50, 200, 150, 300), 20, 20));
343  builder.DrawPaint(paint);
344  builder.Restore();
345  }
346  {
347  builder.Save();
348  builder.ClipRoundRect(DlRoundRect::MakeRectXY(
349  DlRect::MakeLTRB(200, 230, 300, 270), 20, 20));
350  builder.DrawPaint(paint);
351  builder.Restore();
352  }
353  {
354  builder.Save();
355  builder.ClipRoundRect(DlRoundRect::MakeRectXY(
356  DlRect::MakeLTRB(230, 200, 270, 300), 20, 20));
357  builder.DrawPaint(paint);
358  builder.Restore();
359  }
360  builder.Restore();
361  };
362 
363  paint.setColor(DlColor::kBlue());
364  draw(paint, 0, 0);
365 
366  DlColor gradient_colors[7] = {
367  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
368  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
369  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
370  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
371  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
372  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
373  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
374  };
375  Scalar stops[7] = {
376  0.0,
377  (1.0 / 6.0) * 1,
378  (1.0 / 6.0) * 2,
379  (1.0 / 6.0) * 3,
380  (1.0 / 6.0) * 4,
381  (1.0 / 6.0) * 5,
382  1.0,
383  };
384  auto texture = CreateTextureForFixture("airplane.jpg",
385  /*enable_mipmapping=*/true);
386  auto image = DlImageImpeller::Make(texture);
387 
388  paint.setColorSource(DlColorSource::MakeRadial(
389  DlPoint(500, 600), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
390  draw(paint, 0, 300);
391 
392  paint.setColorSource(
393  DlColorSource::MakeImage(image, DlTileMode::kRepeat, DlTileMode::kRepeat,
394  DlImageSampling::kNearestNeighbor));
395  draw(paint, 300, 0);
396 
397  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
398 }
399 
400 TEST_P(AiksTest, CanSaveLayerStandalone) {
401  DisplayListBuilder builder;
402 
403  DlPaint red;
404  red.setColor(DlColor::kRed());
405 
406  DlPaint alpha;
407  alpha.setColor(DlColor::kRed().modulateOpacity(0.5));
408 
409  builder.SaveLayer(std::nullopt, &alpha);
410 
411  builder.DrawCircle(DlPoint(125, 125), 125, red);
412 
413  builder.Restore();
414 
415  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
416 }
417 
418 TEST_P(AiksTest, CanRenderDifferentShapesWithSameColorSource) {
419  DisplayListBuilder builder;
420  DlPaint paint;
421 
422  DlColor colors[2] = {
423  DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
424  DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0),
425  };
426  DlScalar stops[2] = {
427  0.0,
428  1.0,
429  };
430 
431  paint.setColorSource(DlColorSource::MakeLinear(
432  /*start_point=*/DlPoint(0, 0), //
433  /*end_point=*/DlPoint(100, 100), //
434  /*stop_count=*/2, //
435  /*colors=*/colors, //
436  /*stops=*/stops, //
437  /*tile_mode=*/DlTileMode::kRepeat //
438  ));
439 
440  builder.Save();
441  builder.Translate(100, 100);
442  builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), paint);
443  builder.Restore();
444 
445  builder.Save();
446  builder.Translate(100, 400);
447  builder.DrawCircle(DlPoint(100, 100), 100, paint);
448  builder.Restore();
449  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
450 }
451 
452 TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) {
453  DisplayListBuilder builder;
454  DlPaint paint;
455  paint.setColor(DlColor::kRed());
456 
457  RoundingRadii radii = {
458  .top_left = DlSize(50, 25),
459  .top_right = DlSize(25, 50),
460  .bottom_left = DlSize(25, 50),
461  .bottom_right = DlSize(50, 25),
462  };
463  DlRoundRect rrect =
464  DlRoundRect::MakeRectRadii(DlRect::MakeXYWH(100, 100, 500, 500), radii);
465 
466  builder.DrawRoundRect(rrect, paint);
467 
468  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
469 }
470 
471 TEST_P(AiksTest, CanDrawPaint) {
472  auto medium_turquoise =
473  DlColor::RGBA(72.0f / 255.0f, 209.0f / 255.0f, 204.0f / 255.0f, 1.0f);
474 
475  DisplayListBuilder builder;
476  builder.Scale(0.2, 0.2);
477  builder.DrawPaint(DlPaint().setColor(medium_turquoise));
478  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
479 }
480 
481 TEST_P(AiksTest, CanDrawPaintMultipleTimes) {
482  auto medium_turquoise =
483  DlColor::RGBA(72.0f / 255.0f, 209.0f / 255.0f, 204.0f / 255.0f, 1.0f);
484  auto orange_red =
485  DlColor::RGBA(255.0f / 255.0f, 69.0f / 255.0f, 0.0f / 255.0f, 1.0f);
486 
487  DisplayListBuilder builder;
488  builder.Scale(0.2, 0.2);
489  builder.DrawPaint(DlPaint().setColor(medium_turquoise));
490  builder.DrawPaint(DlPaint().setColor(orange_red.modulateOpacity(0.5f)));
491  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
492 }
493 
494 TEST_P(AiksTest, StrokedRectsRenderCorrectly) {
495  DisplayListBuilder builder;
496  builder.Scale(GetContentScale().x, GetContentScale().y);
497 
498  DlPaint paint;
499  paint.setColor(DlColor::kPurple());
500  paint.setDrawStyle(DlDrawStyle::kStroke);
501  paint.setStrokeWidth(20.0f);
502 
503  DlPaint thin_paint = paint;
504  thin_paint.setColor(DlColor::kYellow());
505  thin_paint.setStrokeWidth(0.0f);
506 
507  DlRect rect = DlRect::MakeLTRB(10, 10, 90, 90);
508  DlRect thin_tall_rect = DlRect::MakeLTRB(120, 10, 120, 90);
509  DlRect thin_wide_rect = DlRect::MakeLTRB(10, 120, 90, 120);
510  DlRect empty_rect = DlRect::MakeLTRB(120, 120, 120, 120);
511 
512  // We draw the following sets of rectangles:
513  //
514  // A E X
515  // X
516  // B F X
517  // X
518  // C D G H X
519  //
520  // Purple A,B,C,D are all drawn with stroke width 20 (non-overflowing).
521  // Each of those sets has 4 rectangles of dimension 80x80, 80x0, 0x80,
522  // and 0,0 to demonstrate the basic behavior and also the behavior of
523  // empty dimensions.
524  //
525  // Blue E,F,G,H are the same 80x80 rectangles, but with an overflowing
526  // stroke width of 120 to show the behavior with degenerately large
527  // stroke widths.
528  //
529  // A,E are drawn with Bevel joins.
530  // B,F are drawn with Round joins.
531  // C,G are drawn with Miter joins and a large enough miter limit.
532  // D,H are drawn with Miter joins and a too small miter limit (== Bevel).
533  //
534  // All orange X rectangles are drawn with round joins and increasing stroke
535  // widths to demonstrate fidelity of the rounding code at various arc sizes.
536  // These X rectangles also help test that the variable sizing estimates in
537  // the round join code are accurate.
538 
539  // rects (A)
540  paint.setStrokeJoin(DlStrokeJoin::kBevel);
541  builder.DrawRect(rect.Shift({100, 100}), paint);
542  builder.DrawRect(rect.Shift({100, 100}), thin_paint);
543  builder.DrawRect(thin_tall_rect.Shift({100, 100}), paint);
544  builder.DrawRect(thin_tall_rect.Shift({100, 100}), thin_paint);
545  builder.DrawRect(thin_wide_rect.Shift({100, 100}), paint);
546  builder.DrawRect(thin_wide_rect.Shift({100, 100}), thin_paint);
547  builder.DrawRect(empty_rect.Shift({100, 100}), paint);
548  builder.DrawRect(empty_rect.Shift({100, 100}), thin_paint);
549 
550  // rects (B)
551  paint.setStrokeJoin(DlStrokeJoin::kRound);
552  builder.DrawRect(rect.Shift({100, 300}), paint);
553  builder.DrawRect(rect.Shift({100, 300}), thin_paint);
554  builder.DrawRect(thin_tall_rect.Shift({100, 300}), paint);
555  builder.DrawRect(thin_tall_rect.Shift({100, 300}), thin_paint);
556  builder.DrawRect(thin_wide_rect.Shift({100, 300}), paint);
557  builder.DrawRect(thin_wide_rect.Shift({100, 300}), thin_paint);
558  builder.DrawRect(empty_rect.Shift({100, 300}), paint);
559  builder.DrawRect(empty_rect.Shift({100, 300}), thin_paint);
560 
561  // rects (C)
562  paint.setStrokeJoin(DlStrokeJoin::kMiter);
563  paint.setStrokeMiter(kSqrt2 + flutter::kEhCloseEnough);
564  builder.DrawRect(rect.Shift({100, 500}), paint);
565  builder.DrawRect(rect.Shift({100, 500}), thin_paint);
566  builder.DrawRect(thin_tall_rect.Shift({100, 500}), paint);
567  builder.DrawRect(thin_tall_rect.Shift({100, 500}), thin_paint);
568  builder.DrawRect(thin_wide_rect.Shift({100, 500}), paint);
569  builder.DrawRect(thin_wide_rect.Shift({100, 500}), thin_paint);
570  builder.DrawRect(empty_rect.Shift({100, 500}), paint);
571  builder.DrawRect(empty_rect.Shift({100, 500}), thin_paint);
572 
573  // rects (D)
574  paint.setStrokeJoin(DlStrokeJoin::kMiter);
575  paint.setStrokeMiter(kSqrt2 - flutter::kEhCloseEnough);
576  builder.DrawRect(rect.Shift({300, 500}), paint);
577  builder.DrawRect(rect.Shift({300, 500}), thin_paint);
578  builder.DrawRect(thin_tall_rect.Shift({300, 500}), paint);
579  builder.DrawRect(thin_tall_rect.Shift({300, 500}), thin_paint);
580  builder.DrawRect(thin_wide_rect.Shift({300, 500}), paint);
581  builder.DrawRect(thin_wide_rect.Shift({300, 500}), thin_paint);
582  builder.DrawRect(empty_rect.Shift({300, 500}), paint);
583  builder.DrawRect(empty_rect.Shift({300, 500}), thin_paint);
584 
585  paint.setStrokeWidth(120.0f);
586  paint.setColor(DlColor::kBlue());
587  rect = rect.Expand(-20);
588 
589  // rect (E)
590  paint.setStrokeJoin(DlStrokeJoin::kBevel);
591  builder.DrawRect(rect.Shift({500, 100}), paint);
592  builder.DrawRect(rect.Shift({500, 100}), thin_paint);
593 
594  // rect (F)
595  paint.setStrokeJoin(DlStrokeJoin::kRound);
596  builder.DrawRect(rect.Shift({500, 300}), paint);
597  builder.DrawRect(rect.Shift({500, 300}), thin_paint);
598 
599  // rect (G)
600  paint.setStrokeJoin(DlStrokeJoin::kMiter);
601  paint.setStrokeMiter(kSqrt2 + flutter::kEhCloseEnough);
602  builder.DrawRect(rect.Shift({500, 500}), paint);
603  builder.DrawRect(rect.Shift({500, 500}), thin_paint);
604 
605  // rect (H)
606  paint.setStrokeJoin(DlStrokeJoin::kMiter);
607  paint.setStrokeMiter(kSqrt2 - flutter::kEhCloseEnough);
608  builder.DrawRect(rect.Shift({700, 500}), paint);
609  builder.DrawRect(rect.Shift({700, 500}), thin_paint);
610 
611  DlPaint round_mock_paint;
612  round_mock_paint.setColor(DlColor::kGreen());
613  round_mock_paint.setDrawStyle(DlDrawStyle::kFill);
614 
615  // array of rects (X)
616  Scalar x = 900;
617  Scalar y = 50;
618  for (int i = 0; i < 15; i++) {
619  paint.setStrokeWidth(i);
620  paint.setColor(DlColor::kOrange());
621  paint.setStrokeJoin(DlStrokeJoin::kRound);
622  builder.DrawRect(DlRect::MakeXYWH(x, y, 30, 30), paint);
623  y += 32 + i;
624  }
625 
626  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
627 }
628 
629 TEST_P(AiksTest, FilledCirclesRenderCorrectly) {
630  DisplayListBuilder builder;
631  builder.Scale(GetContentScale().x, GetContentScale().y);
632  DlPaint paint;
633  const int color_count = 3;
634  DlColor colors[color_count] = {
635  DlColor::kBlue(),
636  DlColor::kGreen(),
637  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
638  };
639 
640  paint.setColor(DlColor::kWhite());
641  builder.DrawPaint(paint);
642 
643  int c_index = 0;
644  int radius = 600;
645  while (radius > 0) {
646  paint.setColor(colors[(c_index++) % color_count]);
647  builder.DrawCircle(DlPoint(10, 10), radius, paint);
648  if (radius > 30) {
649  radius -= 10;
650  } else {
651  radius -= 2;
652  }
653  }
654 
655  DlColor gradient_colors[7] = {
656  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
657  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
658  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
659  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
660  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
661  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
662  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
663  };
664  DlScalar stops[7] = {
665  0.0,
666  (1.0 / 6.0) * 1,
667  (1.0 / 6.0) * 2,
668  (1.0 / 6.0) * 3,
669  (1.0 / 6.0) * 4,
670  (1.0 / 6.0) * 5,
671  1.0,
672  };
673  auto texture = CreateTextureForFixture("airplane.jpg",
674  /*enable_mipmapping=*/true);
675  auto image = DlImageImpeller::Make(texture);
676 
677  paint.setColorSource(DlColorSource::MakeRadial(
678  DlPoint(500, 600), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
679  builder.DrawCircle(DlPoint(500, 600), 100, paint);
680 
681  DlMatrix local_matrix = DlMatrix::MakeTranslation({700, 200});
682  paint.setColorSource(DlColorSource::MakeImage(
683  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
684  DlImageSampling::kNearestNeighbor, &local_matrix));
685  builder.DrawCircle(DlPoint(800, 300), 100, paint);
686 
687  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
688 }
689 
690 TEST_P(AiksTest, DrawThinStrokedCircle) {
691  auto callback = [&]() {
692  static float stroked_radius = 100.0;
693  static float stroke_width = 0.0;
694  static float stroke_width_fine = 2.0;
695  static float stroked_alpha = 255.0;
696  static float stroked_scale[2] = {1.0, 1.0};
697 
698  if (AiksTest::ImGuiBegin("Controls", nullptr,
699  ImGuiWindowFlags_AlwaysAutoResize)) {
700  ImGui::SliderFloat("Stroked Radius", &stroked_radius, 0, 500);
701  ImGui::SliderFloat("Stroked Width", &stroke_width, 0, 500);
702  ImGui::SliderFloat("Stroked Width Fine", &stroke_width_fine, 0, 5);
703  ImGui::SliderFloat("Stroked Alpha", &stroked_alpha, 0, 10.0);
704  ImGui::SliderFloat2("Stroked Scale", stroked_scale, 0, 10.0);
705  ImGui::End();
706  }
707 
708  flutter::DisplayListBuilder builder;
709 
710  DlPaint background_paint;
711  background_paint.setColor(DlColor(1, 0.1, 0.1, 0.1, DlColorSpace::kSRGB));
712  builder.DrawPaint(background_paint);
713 
714  flutter::DlPaint paint;
715 
716  paint.setColor(flutter::DlColor::kRed().withAlpha(stroked_alpha));
717  paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
718  paint.setStrokeWidth(stroke_width + stroke_width_fine);
719  builder.Save();
720  builder.Translate(250, 250);
721  builder.Scale(stroked_scale[0], stroked_scale[1]);
722  builder.Translate(-250, -250);
723  builder.DrawCircle(DlPoint(250, 250), stroked_radius, paint);
724  builder.Restore();
725  return builder.Build();
726  };
727 
728  ASSERT_TRUE(OpenPlaygroundHere(callback));
729 }
730 
731 TEST_P(AiksTest, StrokedCirclesRenderCorrectly) {
732  DisplayListBuilder builder;
733  builder.Scale(GetContentScale().x, GetContentScale().y);
734  DlPaint paint;
735  const int color_count = 3;
736  DlColor colors[color_count] = {
737  DlColor::kBlue(),
738  DlColor::kGreen(),
739  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
740  };
741 
742  paint.setColor(DlColor::kWhite());
743  builder.DrawPaint(paint);
744 
745  int c_index = 0;
746 
747  auto draw = [&paint, &colors, &c_index](DlCanvas& canvas, DlPoint center,
748  Scalar r, Scalar dr, int n) {
749  for (int i = 0; i < n; i++) {
750  paint.setColor(colors[(c_index++) % color_count]);
751  canvas.DrawCircle(center, r, paint);
752  r += dr;
753  }
754  };
755 
756  paint.setDrawStyle(DlDrawStyle::kStroke);
757  paint.setStrokeWidth(1);
758  draw(builder, DlPoint(10, 10), 2, 2, 14); // r = [2, 28], covers [1,29]
759  paint.setStrokeWidth(5);
760  draw(builder, DlPoint(10, 10), 35, 10, 56); // r = [35, 585], covers [30,590]
761 
762  DlColor gradient_colors[7] = {
763  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
764  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
765  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
766  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
767  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
768  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
769  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
770  };
771  DlScalar stops[7] = {
772  0.0,
773  (1.0 / 6.0) * 1,
774  (1.0 / 6.0) * 2,
775  (1.0 / 6.0) * 3,
776  (1.0 / 6.0) * 4,
777  (1.0 / 6.0) * 5,
778  1.0,
779  };
780  auto texture = CreateTextureForFixture("airplane.jpg",
781  /*enable_mipmapping=*/true);
782  auto image = DlImageImpeller::Make(texture);
783 
784  paint.setColorSource(DlColorSource::MakeRadial(
785  DlPoint(500, 600), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
786  draw(builder, DlPoint(500, 600), 5, 10, 10);
787 
788  DlMatrix local_matrix = DlMatrix::MakeTranslation({700, 200});
789  paint.setColorSource(DlColorSource::MakeImage(
790  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
791  DlImageSampling::kNearestNeighbor, &local_matrix));
792  draw(builder, DlPoint(800, 300), 5, 10, 10);
793 
794  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
795 }
796 
797 TEST_P(AiksTest, FilledEllipsesRenderCorrectly) {
798  DisplayListBuilder builder;
799  builder.Scale(GetContentScale().x, GetContentScale().y);
800  DlPaint paint;
801  const int color_count = 3;
802  DlColor colors[color_count] = {
803  DlColor::kBlue(),
804  DlColor::kGreen(),
805  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
806  };
807 
808  paint.setColor(DlColor::kWhite());
809  builder.DrawPaint(paint);
810 
811  int c_index = 0;
812  int long_radius = 600;
813  int short_radius = 600;
814  while (long_radius > 0 && short_radius > 0) {
815  paint.setColor(colors[(c_index++) % color_count]);
816  builder.DrawOval(DlRect::MakeXYWH(10 - long_radius, 10 - short_radius,
817  long_radius * 2, short_radius * 2),
818  paint);
819  builder.DrawOval(DlRect::MakeXYWH(1000 - short_radius, 750 - long_radius,
820  short_radius * 2, long_radius * 2),
821  paint);
822  if (short_radius > 30) {
823  short_radius -= 10;
824  long_radius -= 5;
825  } else {
826  short_radius -= 2;
827  long_radius -= 1;
828  }
829  }
830 
831  DlColor gradient_colors[7] = {
832  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
833  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
834  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
835  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
836  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
837  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
838  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
839  };
840  DlScalar stops[7] = {
841  0.0,
842  (1.0 / 6.0) * 1,
843  (1.0 / 6.0) * 2,
844  (1.0 / 6.0) * 3,
845  (1.0 / 6.0) * 4,
846  (1.0 / 6.0) * 5,
847  1.0,
848  };
849  auto texture = CreateTextureForFixture("airplane.jpg",
850  /*enable_mipmapping=*/true);
851  auto image = DlImageImpeller::Make(texture);
852 
853  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
854 
855  paint.setColorSource(DlColorSource::MakeRadial(
856  DlPoint(300, 650), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
857  builder.DrawOval(DlRect::MakeXYWH(200, 625, 200, 50), paint);
858  builder.DrawOval(DlRect::MakeXYWH(275, 550, 50, 200), paint);
859 
860  DlMatrix local_matrix = DlMatrix::MakeTranslation({610, 15});
861  paint.setColorSource(DlColorSource::MakeImage(
862  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
863  DlImageSampling::kNearestNeighbor, &local_matrix));
864  builder.DrawOval(DlRect::MakeXYWH(610, 90, 200, 50), paint);
865  builder.DrawOval(DlRect::MakeXYWH(685, 15, 50, 200), paint);
866 
867  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
868 }
869 
870 namespace {
871 struct ArcFarmOptions {
872  bool use_center = false;
873  bool full_circles = false;
874  bool sweeps_over_360 = false;
876 };
877 
878 void RenderArcFarm(DisplayListBuilder& builder,
879  const DlPaint& paint,
880  const ArcFarmOptions& opts) {
881  builder.Save();
882  builder.Translate(50, 50);
883  const Rect arc_bounds = Rect::MakeLTRB(0, 0, 42, 42 * opts.vertical_scale);
884  const int sweep_limit = opts.sweeps_over_360 ? 420 : 360;
885  for (int start = 0; start <= 360; start += 30) {
886  builder.Save();
887  for (int sweep = 30; sweep <= sweep_limit; sweep += 30) {
888  builder.DrawArc(arc_bounds, start, opts.full_circles ? 360 : sweep,
889  opts.use_center, paint);
890  builder.Translate(50, 0);
891  }
892  builder.Restore();
893  builder.Translate(0, 50);
894  }
895  builder.Restore();
896 }
897 
898 void RenderArcFarmForOverlappingCapsTest(DisplayListBuilder& builder,
899  const DlPaint& paint) {
900  builder.Save();
901  builder.Translate(40, 30);
902  const Rect arc_bounds = Rect::MakeLTRB(0, 0, 40, 40);
903  for (int stroke_width = 10; stroke_width <= 40; stroke_width += 3) {
904  DlPaint modified_paint = DlPaint(paint);
905  modified_paint.setStrokeWidth(stroke_width);
906  builder.Save();
907  for (int sweep = 160; sweep <= 360; sweep += 20) {
908  builder.DrawArc(arc_bounds, 0, sweep, false, modified_paint);
909  builder.Translate(84, 0);
910  }
911  builder.Restore();
912  builder.Translate(0, 44 + stroke_width);
913  }
914  builder.Restore();
915 }
916 } // namespace
917 
918 TEST_P(AiksTest, FilledArcsRenderCorrectly) {
919  DisplayListBuilder builder;
920  builder.Scale(GetContentScale().x, GetContentScale().y);
921  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
922 
923  DlPaint paint;
924  paint.setColor(DlColor::kBlue());
925 
926  RenderArcFarm(builder, paint,
927  {
928  .use_center = false,
929  .full_circles = false,
930  });
931 
932  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
933 }
934 
935 TEST_P(AiksTest, TranslucentFilledArcsRenderCorrectly) {
936  DisplayListBuilder builder;
937  builder.Scale(GetContentScale().x, GetContentScale().y);
938  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
939 
940  DlPaint paint;
941  paint.setColor(DlColor::kBlue().modulateOpacity(0.5));
942 
943  RenderArcFarm(builder, paint,
944  {
945  .use_center = false,
946  .full_circles = false,
947  });
948 
949  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
950 }
951 
952 TEST_P(AiksTest, FilledArcsRenderCorrectlyWithCenter) {
953  DisplayListBuilder builder;
954  builder.Scale(GetContentScale().x, GetContentScale().y);
955  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
956 
957  DlPaint paint;
958  paint.setColor(DlColor::kBlue());
959 
960  RenderArcFarm(builder, paint,
961  {
962  .use_center = true,
963  .full_circles = false,
964  });
965 
966  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
967 }
968 
969 TEST_P(AiksTest, NonSquareFilledArcsRenderCorrectly) {
970  DisplayListBuilder builder;
971  builder.Scale(GetContentScale().x, GetContentScale().y);
972  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
973 
974  DlPaint paint;
975  paint.setColor(DlColor::kBlue());
976 
977  RenderArcFarm(builder, paint,
978  {
979  .use_center = false,
980  .full_circles = false,
981  .vertical_scale = 0.8f,
982  });
983 
984  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
985 }
986 
987 TEST_P(AiksTest, NonSquareFilledArcsRenderCorrectlyWithCenter) {
988  DisplayListBuilder builder;
989  builder.Scale(GetContentScale().x, GetContentScale().y);
990  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
991 
992  DlPaint paint;
993  paint.setColor(DlColor::kBlue());
994 
995  RenderArcFarm(builder, paint,
996  {
997  .use_center = true,
998  .full_circles = false,
999  .vertical_scale = 0.8f,
1000  });
1001 
1002  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1003 }
1004 
1005 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithButtEnds) {
1006  DisplayListBuilder builder;
1007  builder.Scale(GetContentScale().x, GetContentScale().y);
1008  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1009 
1010  DlPaint paint;
1011  paint.setDrawStyle(DlDrawStyle::kStroke);
1012  paint.setStrokeWidth(6.0f);
1013  paint.setStrokeCap(DlStrokeCap::kButt);
1014  paint.setColor(DlColor::kBlue());
1015 
1016  RenderArcFarm(builder, paint,
1017  {
1018  .use_center = false,
1019  .full_circles = false,
1020  });
1021 
1022  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1023 }
1024 
1025 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithSquareEnds) {
1026  DisplayListBuilder builder;
1027  builder.Scale(GetContentScale().x, GetContentScale().y);
1028  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1029 
1030  DlPaint paint;
1031  paint.setDrawStyle(DlDrawStyle::kStroke);
1032  paint.setStrokeWidth(6.0f);
1033  paint.setStrokeCap(DlStrokeCap::kSquare);
1034  paint.setColor(DlColor::kBlue());
1035 
1036  RenderArcFarm(builder, paint,
1037  {
1038  .use_center = false,
1039  .full_circles = false,
1040  });
1041 
1042  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1043 }
1044 
1045 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithTranslucencyAndSquareEnds) {
1046  DisplayListBuilder builder;
1047  builder.Scale(GetContentScale().x, GetContentScale().y);
1048  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1049 
1050  DlPaint paint;
1051  paint.setDrawStyle(DlDrawStyle::kStroke);
1052  paint.setStrokeCap(DlStrokeCap::kSquare);
1053  paint.setColor(DlColor::kBlue().modulateOpacity(0.5));
1054 
1055  RenderArcFarmForOverlappingCapsTest(builder, paint);
1056 
1057  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1058 }
1059 
1060 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithRoundEnds) {
1061  DisplayListBuilder builder;
1062  builder.Scale(GetContentScale().x, GetContentScale().y);
1063  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1064 
1065  DlPaint paint;
1066  paint.setDrawStyle(DlDrawStyle::kStroke);
1067  paint.setStrokeWidth(6.0f);
1068  paint.setStrokeCap(DlStrokeCap::kRound);
1069  paint.setColor(DlColor::kBlue());
1070 
1071  RenderArcFarm(builder, paint,
1072  {
1073  .use_center = false,
1074  .full_circles = false,
1075  });
1076 
1077  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1078 }
1079 
1080 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithTranslucencyAndRoundEnds) {
1081  DisplayListBuilder builder;
1082  builder.Scale(GetContentScale().x, GetContentScale().y);
1083  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1084 
1085  DlPaint paint;
1086  paint.setDrawStyle(DlDrawStyle::kStroke);
1087  paint.setStrokeCap(DlStrokeCap::kRound);
1088  paint.setColor(DlColor::kBlue().modulateOpacity(0.5));
1089 
1090  RenderArcFarmForOverlappingCapsTest(builder, paint);
1091 
1092  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1093 }
1094 
1095 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithBevelJoinsAndCenter) {
1096  DisplayListBuilder builder;
1097  builder.Scale(GetContentScale().x, GetContentScale().y);
1098  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1099 
1100  DlPaint paint;
1101  paint.setDrawStyle(DlDrawStyle::kStroke);
1102  paint.setStrokeWidth(6.0f);
1103  paint.setStrokeJoin(DlStrokeJoin::kBevel);
1104  paint.setColor(DlColor::kBlue());
1105 
1106  RenderArcFarm(builder, paint,
1107  {
1108  .use_center = true,
1109  .full_circles = false,
1110  .sweeps_over_360 = true,
1111  });
1112 
1113  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1114 }
1115 
1116 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithMiterJoinsAndCenter) {
1117  DisplayListBuilder builder;
1118  builder.Scale(GetContentScale().x, GetContentScale().y);
1119  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1120 
1121  DlPaint paint;
1122  paint.setDrawStyle(DlDrawStyle::kStroke);
1123  paint.setStrokeWidth(6.0f);
1124  paint.setStrokeJoin(DlStrokeJoin::kMiter);
1125  // Default miter of 4.0 does a miter on all of the centers, but
1126  // using 3.0 will show some bevels on the widest interior angles...
1127  paint.setStrokeMiter(3.0f);
1128  paint.setColor(DlColor::kBlue());
1129 
1130  RenderArcFarm(builder, paint,
1131  {
1132  .use_center = true,
1133  .full_circles = false,
1134  .sweeps_over_360 = true,
1135  });
1136 
1137  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1138 }
1139 
1140 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithRoundJoinsAndCenter) {
1141  DisplayListBuilder builder;
1142  builder.Scale(GetContentScale().x, GetContentScale().y);
1143  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1144 
1145  DlPaint paint;
1146  paint.setDrawStyle(DlDrawStyle::kStroke);
1147  paint.setStrokeWidth(6.0f);
1148  paint.setStrokeJoin(DlStrokeJoin::kRound);
1149  paint.setColor(DlColor::kBlue());
1150 
1151  RenderArcFarm(builder, paint,
1152  {
1153  .use_center = true,
1154  .full_circles = false,
1155  .sweeps_over_360 = true,
1156  });
1157 
1158  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1159 }
1160 
1161 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithSquareAndButtEnds) {
1162  DisplayListBuilder builder;
1163  builder.Scale(GetContentScale().x, GetContentScale().y);
1164  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1165 
1166  DlPaint paint;
1167  paint.setDrawStyle(DlDrawStyle::kStroke);
1168  paint.setStrokeWidth(8.0f);
1169  paint.setStrokeCap(DlStrokeCap::kSquare);
1170  paint.setColor(DlColor::kRed());
1171 
1172  RenderArcFarm(builder, paint,
1173  {
1174  .use_center = false,
1175  .full_circles = false,
1176  });
1177 
1178  paint.setStrokeCap(DlStrokeCap::kButt);
1179  paint.setColor(DlColor::kBlue());
1180 
1181  RenderArcFarm(builder, paint,
1182  {
1183  .use_center = false,
1184  .full_circles = false,
1185  });
1186 
1187  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1188 }
1189 
1190 TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithSquareAndButtAndRoundEnds) {
1191  DisplayListBuilder builder;
1192  builder.Scale(GetContentScale().x, GetContentScale().y);
1193  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1194 
1195  DlPaint paint;
1196  paint.setDrawStyle(DlDrawStyle::kStroke);
1197  paint.setStrokeWidth(8.0f);
1198  paint.setStrokeCap(DlStrokeCap::kSquare);
1199  paint.setColor(DlColor::kRed());
1200 
1201  RenderArcFarm(builder, paint,
1202  {
1203  .use_center = false,
1204  .full_circles = false,
1205  });
1206 
1207  paint.setStrokeCap(DlStrokeCap::kRound);
1208  paint.setColor(DlColor::kGreen());
1209 
1210  RenderArcFarm(builder, paint,
1211  {
1212  .use_center = false,
1213  .full_circles = false,
1214  });
1215 
1216  paint.setStrokeCap(DlStrokeCap::kButt);
1217  paint.setColor(DlColor::kBlue());
1218 
1219  RenderArcFarm(builder, paint,
1220  {
1221  .use_center = false,
1222  .full_circles = false,
1223  });
1224 
1225  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1226 }
1227 
1228 TEST_P(AiksTest, StrokedArcsCoverFullArcWithButtEnds) {
1229  // This test compares the rendering of a full circle arc against a partial
1230  // arc by drawing a one over the other in high contrast. If the partial
1231  // arc misses any pixels that were drawn by the full arc, there will be
1232  // some "pixel dirt" around the missing "erased" parts of the arcs. This
1233  // case arises while rendering a CircularProgressIndicator with a background
1234  // color where we want the rendering of the background full arc to hit the
1235  // same pixels around the edges as the partial arc that covers it.
1236  //
1237  // In this case we draw a full blue circle and then draw a partial arc
1238  // over it in the background color (white).
1239 
1240  DisplayListBuilder builder;
1241  builder.Scale(GetContentScale().x, GetContentScale().y);
1242  builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);
1243 
1244  DlPaint paint;
1245  paint.setDrawStyle(DlDrawStyle::kStroke);
1246  paint.setStrokeWidth(6.0f);
1247  paint.setStrokeCap(DlStrokeCap::kButt);
1248  paint.setColor(DlColor::kBlue());
1249 
1250  // First draw full circles in blue to establish the pixels to be erased
1251  RenderArcFarm(builder, paint,
1252  {
1253  .use_center = false,
1254  .full_circles = true,
1255  });
1256 
1257  paint.setColor(DlColor::kWhite());
1258 
1259  // Then draw partial arcs in white over the circles to "erase" them
1260  RenderArcFarm(builder, paint,
1261  {
1262  .use_center = false,
1263  .full_circles = false,
1264  });
1265 
1266  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1267 }
1268 
1269 TEST_P(AiksTest, FilledRoundRectsRenderCorrectly) {
1270  DisplayListBuilder builder;
1271  builder.Scale(GetContentScale().x, GetContentScale().y);
1272  DlPaint paint;
1273  const int color_count = 3;
1274  DlColor colors[color_count] = {
1275  DlColor::kBlue(),
1276  DlColor::kGreen(),
1277  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
1278  };
1279 
1280  paint.setColor(DlColor::kWhite());
1281  builder.DrawPaint(paint);
1282 
1283  int c_index = 0;
1284  for (int i = 0; i < 4; i++) {
1285  for (int j = 0; j < 4; j++) {
1286  paint.setColor(colors[(c_index++) % color_count]);
1287  builder.DrawRoundRect(
1288  DlRoundRect::MakeRectXY(
1289  DlRect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80), //
1290  i * 5 + 10, j * 5 + 10),
1291  paint);
1292  }
1293  }
1294  paint.setColor(colors[(c_index++) % color_count]);
1295  builder.DrawRoundRect(
1296  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(10, 420, 380, 80), 40, 40),
1297  paint);
1298  paint.setColor(colors[(c_index++) % color_count]);
1299  builder.DrawRoundRect(
1300  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(410, 20, 80, 380), 40, 40),
1301  paint);
1302 
1303  DlColor gradient_colors[7] = {
1304  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
1305  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
1306  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
1307  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
1308  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
1309  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
1310  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
1311  };
1312  DlScalar stops[7] = {
1313  0.0,
1314  (1.0 / 6.0) * 1,
1315  (1.0 / 6.0) * 2,
1316  (1.0 / 6.0) * 3,
1317  (1.0 / 6.0) * 4,
1318  (1.0 / 6.0) * 5,
1319  1.0,
1320  };
1321  auto texture = CreateTextureForFixture("airplane.jpg",
1322  /*enable_mipmapping=*/true);
1323  auto image = DlImageImpeller::Make(texture);
1324 
1325  paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1326  paint.setColorSource(DlColorSource::MakeRadial(
1327  DlPoint(550, 550), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
1328  for (int i = 1; i <= 10; i++) {
1329  int j = 11 - i;
1330  builder.DrawRoundRect(
1331  DlRoundRect::MakeRectXY(DlRect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1332  550 + i * 20, 550 + j * 20),
1333  i * 10, j * 10),
1334  paint);
1335  }
1336 
1337  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1338  paint.setColorSource(DlColorSource::MakeRadial(
1339  DlPoint(200, 650), 75, 7, gradient_colors, stops, DlTileMode::kMirror));
1340  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1341  builder.DrawRoundRect(
1342  DlRoundRect::MakeRectXY(DlRect::MakeLTRB(100, 610, 300, 690), 40, 40),
1343  paint);
1344  builder.DrawRoundRect(
1345  DlRoundRect::MakeRectXY(DlRect::MakeLTRB(160, 550, 240, 750), 40, 40),
1346  paint);
1347 
1348  paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1349  DlMatrix local_matrix = DlMatrix::MakeTranslation({520, 20});
1350  paint.setColorSource(DlColorSource::MakeImage(
1351  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
1352  DlImageSampling::kNearestNeighbor, &local_matrix));
1353  for (int i = 1; i <= 10; i++) {
1354  int j = 11 - i;
1355  builder.DrawRoundRect(
1356  DlRoundRect::MakeRectXY(DlRect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1357  720 + i * 20, 220 + j * 20),
1358  i * 10, j * 10),
1359  paint);
1360  }
1361 
1362  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1363  local_matrix = DlMatrix::MakeTranslation({800, 300});
1364  paint.setColorSource(DlColorSource::MakeImage(
1365  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
1366  DlImageSampling::kNearestNeighbor, &local_matrix));
1367  builder.DrawRoundRect(
1368  DlRoundRect::MakeRectXY(DlRect::MakeLTRB(800, 410, 1000, 490), 40, 40),
1369  paint);
1370  builder.DrawRoundRect(
1371  DlRoundRect::MakeRectXY(DlRect::MakeLTRB(860, 350, 940, 550), 40, 40),
1372  paint);
1373 
1374  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1375 }
1376 
1377 TEST_P(AiksTest, SolidColorCirclesOvalsRRectsMaskBlurCorrectly) {
1378  DisplayListBuilder builder;
1379  builder.Scale(GetContentScale().x, GetContentScale().y);
1380  DlPaint paint;
1381  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 1.0f));
1382 
1383  builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
1384 
1385  paint.setColor(
1386  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f));
1387  Scalar y = 100.0f;
1388  for (int i = 0; i < 5; i++) {
1389  Scalar x = (i + 1) * 100;
1390  Scalar radius = x / 10.0f;
1391  builder.DrawRect(DlRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1392  radius, 60.0f - radius),
1393  paint);
1394  }
1395 
1396  paint.setColor(DlColor::kBlue());
1397  y += 100.0f;
1398  for (int i = 0; i < 5; i++) {
1399  Scalar x = (i + 1) * 100;
1400  Scalar radius = x / 10.0f;
1401  builder.DrawCircle(DlPoint(x + 25, y + 25), radius, paint);
1402  }
1403 
1404  paint.setColor(DlColor::kGreen());
1405  y += 100.0f;
1406  for (int i = 0; i < 5; i++) {
1407  Scalar x = (i + 1) * 100;
1408  Scalar radius = x / 10.0f;
1409  builder.DrawOval(DlRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1410  radius, 60.0f - radius),
1411  paint);
1412  }
1413 
1414  paint.setColor(
1415  DlColor::RGBA(128.0f / 255.0f, 0.0f / 255.0f, 128.0f / 255.0f, 1.0f));
1416  y += 100.0f;
1417  for (int i = 0; i < 5; i++) {
1418  Scalar x = (i + 1) * 100;
1419  Scalar radius = x / 20.0f;
1420  builder.DrawRoundRect(
1421  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(x, y, 60.0f, 60.0f), //
1422  radius, radius),
1423  paint);
1424  }
1425 
1426  paint.setColor(
1427  DlColor::RGBA(255.0f / 255.0f, 165.0f / 255.0f, 0.0f / 255.0f, 1.0f));
1428  y += 100.0f;
1429  for (int i = 0; i < 5; i++) {
1430  Scalar x = (i + 1) * 100;
1431  Scalar radius = x / 20.0f;
1432  builder.DrawRoundRect(
1433  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(x, y, 60.0f, 60.0f), //
1434  radius, 5.0f),
1435  paint);
1436  }
1437 
1438  auto dl = builder.Build();
1439  ASSERT_TRUE(OpenPlaygroundHere(dl));
1440 }
1441 
1442 TEST_P(AiksTest, CanRenderClippedBackdropFilter) {
1443  DisplayListBuilder builder;
1444 
1445  builder.Scale(GetContentScale().x, GetContentScale().y);
1446 
1447  // Draw something interesting in the background.
1448  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
1449  DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0)};
1450  std::vector<Scalar> stops = {
1451  0.0,
1452  1.0,
1453  };
1454  DlPaint paint;
1455  paint.setColorSource(DlColorSource::MakeLinear(
1456  /*start_point=*/DlPoint(0, 0), //
1457  /*end_point=*/DlPoint(100, 100), //
1458  /*stop_count=*/2, //
1459  /*colors=*/colors.data(), //
1460  /*stops=*/stops.data(), //
1461  /*tile_mode=*/DlTileMode::kRepeat //
1462  ));
1463 
1464  builder.DrawPaint(paint);
1465 
1466  DlRect clip_rect = DlRect::MakeLTRB(50, 50, 400, 300);
1467  DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_rect, 100, 100);
1468 
1469  // Draw a clipped SaveLayer, where the clip coverage and SaveLayer size are
1470  // the same.
1471  builder.ClipRoundRect(clip_rrect, DlClipOp::kIntersect);
1472 
1473  DlPaint save_paint;
1474  auto backdrop_filter = DlImageFilter::MakeColorFilter(
1475  DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kExclusion));
1476  builder.SaveLayer(clip_rect, &save_paint, backdrop_filter.get());
1477 
1478  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1479 }
1480 
1481 TEST_P(AiksTest, CanDrawPerspectiveTransformWithClips) {
1482  // Avoiding `GetSecondsElapsed()` to reduce risk of golden flakiness.
1483  int time = 0;
1484  auto callback = [&]() -> sk_sp<DisplayList> {
1485  DisplayListBuilder builder;
1486 
1487  builder.Save();
1488  {
1489  builder.Translate(300, 300);
1490 
1491  // 1. Draw/restore a clip before drawing the image, which will get drawn
1492  // to the depth buffer behind the image.
1493  builder.Save();
1494  {
1495  DlPaint paint;
1496  paint.setColor(DlColor::kGreen());
1497  builder.DrawPaint(paint);
1498  builder.ClipRect(DlRect::MakeLTRB(-180, -180, 180, 180),
1499  DlClipOp::kDifference);
1500 
1501  paint.setColor(DlColor::kBlack());
1502  builder.DrawPaint(paint);
1503  }
1504  builder.Restore(); // Restore rectangle difference clip.
1505 
1506  builder.Save();
1507  {
1508  // 2. Draw an oval clip that applies to the image, which will get drawn
1509  // in front of the image on the depth buffer.
1510  builder.ClipOval(DlRect::MakeLTRB(-200, -200, 200, 200));
1511 
1512  Matrix result =
1513  Matrix(1.0, 0.0, 0.0, 0.0, //
1514  0.0, 1.0, 0.0, 0.0, //
1515  0.0, 0.0, 1.0, 0.003, //
1516  0.0, 0.0, 0.0, 1.0) *
1517  Matrix::MakeRotationY({Radians{-1.0f + (time++ / 60.0f)}});
1518 
1519  // 3. Draw the rotating image with a perspective transform.
1520  builder.Transform(result);
1521 
1522  auto image =
1523  DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
1524  auto position =
1525  -DlPoint(image->GetSize().width, image->GetSize().height) * 0.5;
1526  builder.DrawImage(image, position, {});
1527  }
1528  builder.Restore(); // Restore oval intersect clip.
1529 
1530  // 4. Draw a semi-translucent blue circle atop all previous draws.
1531  DlPaint paint;
1532  paint.setColor(DlColor::kBlue().modulateOpacity(0.4));
1533  builder.DrawCircle(DlPoint(), 230, paint);
1534  }
1535  builder.Restore(); // Restore translation.
1536 
1537  return builder.Build();
1538  };
1539  ASSERT_TRUE(OpenPlaygroundHere(callback));
1540 }
1541 
1542 TEST_P(AiksTest, ImageColorSourceEffectTransform) {
1543  // Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885
1544 
1545  DisplayListBuilder builder;
1546  auto texture = DlImageImpeller::Make(CreateTextureForFixture("monkey.png"));
1547 
1548  DlPaint paint;
1549  paint.setColor(DlColor::kWhite());
1550  builder.DrawPaint(paint);
1551 
1552  // Translation
1553  {
1554  DlMatrix matrix = DlMatrix::MakeTranslation({50, 50});
1555  DlPaint paint;
1556  paint.setColorSource(DlColorSource::MakeImage(
1557  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1558  DlImageSampling::kNearestNeighbor, &matrix));
1559 
1560  builder.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100), paint);
1561  }
1562 
1563  // Rotation/skew
1564  {
1565  builder.Save();
1566  builder.Rotate(45);
1567  DlPaint paint;
1568 
1569  Matrix matrix(1, -1, 0, 0, //
1570  1, 1, 0, 0, //
1571  0, 0, 1, 0, //
1572  0, 0, 0, 1);
1573  paint.setColorSource(DlColorSource::MakeImage(
1574  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1575  DlImageSampling::kNearestNeighbor, &matrix));
1576  builder.DrawRect(DlRect::MakeLTRB(100, 0, 200, 100), paint);
1577  builder.Restore();
1578  }
1579 
1580  // Scale
1581  {
1582  builder.Save();
1583  builder.Translate(100, 0);
1584  builder.Scale(100, 100);
1585  DlPaint paint;
1586 
1587  DlMatrix matrix = DlMatrix::MakeScale({0.005, 0.005, 1});
1588  paint.setColorSource(DlColorSource::MakeImage(
1589  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1590  DlImageSampling::kNearestNeighbor, &matrix));
1591 
1592  builder.DrawRect(DlRect::MakeLTRB(0, 0, 1, 1), paint);
1593  builder.Restore();
1594  }
1595 
1596  // Perspective
1597  {
1598  builder.Save();
1599  builder.Translate(150, 150);
1600  DlPaint paint;
1601 
1602  DlMatrix matrix =
1603  DlMatrix::MakePerspective(Radians{0.5}, ISize{200, 200}, 0.05, 1);
1604  paint.setColorSource(DlColorSource::MakeImage(
1605  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1606  DlImageSampling::kNearestNeighbor, &matrix));
1607 
1608  builder.DrawRect(DlRect::MakeLTRB(0, 0, 200, 200), paint);
1609  builder.Restore();
1610  }
1611 
1612  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1613 }
1614 
1615 TEST_P(AiksTest, SubpassWithClearColorOptimization) {
1616  DisplayListBuilder builder;
1617 
1618  // Use a non-srcOver blend mode to ensure that we don't detect this as an
1619  // opacity peephole optimization.
1620  DlPaint paint;
1621  paint.setColor(DlColor::kBlue().modulateOpacity(0.5));
1622  paint.setBlendMode(DlBlendMode::kSrc);
1623 
1624  DlRect bounds = DlRect::MakeLTRB(0, 0, 200, 200);
1625  builder.SaveLayer(bounds, &paint);
1626 
1627  paint.setColor(DlColor::kTransparent());
1628  paint.setBlendMode(DlBlendMode::kSrc);
1629  builder.DrawPaint(paint);
1630  builder.Restore();
1631 
1632  paint.setColor(DlColor::kBlue());
1633  paint.setBlendMode(DlBlendMode::kDstOver);
1634  builder.SaveLayer(std::nullopt, &paint);
1635  builder.Restore();
1636 
1637  // This playground should appear blank on CI since we are only drawing
1638  // transparent black. If the clear color optimization is broken, the texture
1639  // will be filled with NaNs and may produce a magenta texture on macOS or iOS.
1640  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1641 }
1642 
1643 // Render a white circle at the top left corner of the screen.
1644 TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) {
1645  DisplayListBuilder builder;
1646  builder.Scale(GetContentScale().x, GetContentScale().y);
1647  builder.Translate(100, 100);
1648  // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
1649  // +300 translation applied by a SaveLayer image filter.
1650  DlPaint paint;
1651  DlMatrix translate = DlMatrix::MakeTranslation({300, 0});
1652  paint.setImageFilter(
1653  DlImageFilter::MakeMatrix(translate, DlImageSampling::kLinear));
1654  builder.SaveLayer(std::nullopt, &paint);
1655 
1656  DlPaint circle_paint;
1657  circle_paint.setColor(DlColor::kGreen());
1658  builder.DrawCircle(DlPoint(-300, 0), 100, circle_paint);
1659  builder.Restore();
1660 
1661  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1662 }
1663 
1664 // Render a white circle at the top left corner of the screen.
1666  MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) {
1667  DisplayListBuilder builder;
1668  builder.Scale(GetContentScale().x, GetContentScale().y);
1669  builder.Translate(100, 100);
1670  // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
1671  // +300 translation applied by a SaveLayer image filter.
1672 
1673  DlPaint paint;
1674  paint.setImageFilter(DlImageFilter::MakeMatrix(
1675  DlMatrix::MakeTranslation({300, 0}) * DlMatrix::MakeScale({2, 2, 1}),
1676  DlImageSampling::kNearestNeighbor));
1677  builder.SaveLayer(std::nullopt, &paint);
1678 
1679  DlPaint circle_paint;
1680  circle_paint.setColor(DlColor::kGreen());
1681  builder.DrawCircle(DlPoint(-150, 0), 50, circle_paint);
1682  builder.Restore();
1683 
1684  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1685 }
1686 
1687 // This should be solid red, if you see a little red box this is broken.
1688 TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) {
1689  SetWindowSize({400, 400});
1690  DisplayListBuilder builder;
1691 
1692  builder.Scale(GetContentScale().x, GetContentScale().y);
1693 
1694  DlPaint paint;
1695  paint.setColor(DlColor::kRed());
1696  builder.DrawRect(DlRect::MakeLTRB(200, 200, 300, 300), paint);
1697 
1698  paint.setImageFilter(DlImageFilter::MakeMatrix(DlMatrix::MakeScale({2, 2, 1}),
1699  DlImageSampling::kLinear));
1700  builder.SaveLayer(std::nullopt, &paint);
1701  // Draw a rectangle that would fully cover the parent pass size, but not
1702  // the subpass that it is rendered in.
1703  paint.setColor(DlColor::kGreen());
1704  builder.DrawRect(DlRect::MakeLTRB(0, 0, 400, 400), paint);
1705  // Draw a bigger rectangle to force the subpass to be bigger.
1706 
1707  paint.setColor(DlColor::kRed());
1708  builder.DrawRect(DlRect::MakeLTRB(0, 0, 800, 800), paint);
1709  builder.Restore();
1710 
1711  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1712 }
1713 
1714 TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) {
1715  DisplayListBuilder builder;
1716  builder.Scale(GetContentScale().x, GetContentScale().y);
1717 
1718  DlPaint paint;
1719  paint.setColor(DlColor::kRed());
1720  builder.DrawPaint(paint);
1721  builder.ClipRect(DlRect::MakeXYWH(100, 100, 200, 200));
1722  paint.setColor(DlColor::kBlue());
1723  builder.SaveLayer(std::nullopt, &paint);
1724  builder.Restore();
1725 
1726  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1727 }
1728 
1729 TEST_P(AiksTest, EmptySaveLayerRendersWithClear) {
1730  DisplayListBuilder builder;
1731  builder.Scale(GetContentScale().x, GetContentScale().y);
1732  auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
1733  builder.DrawImage(image, DlPoint(10, 10), {});
1734  builder.ClipRect(DlRect::MakeXYWH(100, 100, 200, 200));
1735 
1736  DlPaint paint;
1737  paint.setBlendMode(DlBlendMode::kClear);
1738  builder.SaveLayer(std::nullopt, &paint);
1739  builder.Restore();
1740 
1741  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1742 }
1743 
1745  CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) {
1746  DisplayListBuilder builder;
1747 
1748  DlPaint red;
1749  red.setColor(DlColor::kRed());
1750 
1751  DlPaint green;
1752  green.setColor(DlColor::kGreen());
1753 
1754  DlPaint blue;
1755  blue.setColor(DlColor::kBlue());
1756 
1757  DlPaint save;
1758  save.setColor(DlColor::kBlack().modulateOpacity(0.5));
1759 
1760  DlRect huge_bounds = DlRect::MakeXYWH(0, 0, 100000, 100000);
1761  builder.SaveLayer(huge_bounds, &save);
1762 
1763  builder.DrawRect(DlRect::MakeXYWH(0, 0, 100, 100), red);
1764  builder.DrawRect(DlRect::MakeXYWH(10, 10, 100, 100), green);
1765  builder.DrawRect(DlRect::MakeXYWH(20, 20, 100, 100), blue);
1766 
1767  builder.Restore();
1768 
1769  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1770 }
1771 
1772 // This makes sure the WideGamut named tests use 16bit float pixel format.
1773 TEST_P(AiksTest, FormatWideGamut) {
1774  EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
1776 }
1777 
1778 TEST_P(AiksTest, FormatSRGB) {
1779  PixelFormat pixel_format =
1780  GetContext()->GetCapabilities()->GetDefaultColorFormat();
1781  EXPECT_TRUE(pixel_format == PixelFormat::kR8G8B8A8UNormInt ||
1782  pixel_format == PixelFormat::kB8G8R8A8UNormInt)
1783  << "pixel format: " << PixelFormatToString(pixel_format);
1784 }
1785 
1786 TEST_P(AiksTest, CoordinateConversionsAreCorrect) {
1787  DisplayListBuilder builder;
1788 
1789  // Render a texture directly.
1790  {
1791  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
1792 
1793  builder.Save();
1794  builder.Translate(100, 200);
1795  builder.Scale(0.5, 0.5);
1796  builder.DrawImage(image, DlPoint(100.0, 100.0),
1797  DlImageSampling::kNearestNeighbor);
1798  builder.Restore();
1799  }
1800 
1801  // Render an offscreen rendered texture.
1802  {
1803  DlPaint alpha;
1804  alpha.setColor(DlColor::kRed().modulateOpacity(0.5));
1805 
1806  builder.SaveLayer(std::nullopt, &alpha);
1807 
1808  DlPaint paint;
1809  paint.setColor(DlColor::kRed());
1810  builder.DrawRect(DlRect::MakeXYWH(000, 000, 100, 100), paint);
1811  paint.setColor(DlColor::kGreen());
1812  builder.DrawRect(DlRect::MakeXYWH(020, 020, 100, 100), paint);
1813  paint.setColor(DlColor::kBlue());
1814  builder.DrawRect(DlRect::MakeXYWH(040, 040, 100, 100), paint);
1815 
1816  builder.Restore();
1817  }
1818 
1819  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1820 }
1821 
1822 TEST_P(AiksTest, CanPerformFullScreenMSAA) {
1823  DisplayListBuilder builder;
1824 
1825  DlPaint paint;
1826  paint.setColor(DlColor::kRed());
1827  builder.DrawCircle(DlPoint(250, 250), 125, paint);
1828 
1829  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1830 }
1831 
1832 TEST_P(AiksTest, CanPerformSkew) {
1833  DisplayListBuilder builder;
1834 
1835  DlPaint red;
1836  red.setColor(DlColor::kRed());
1837  builder.Skew(2, 5);
1838  builder.DrawRect(DlRect::MakeXYWH(0, 0, 100, 100), red);
1839 
1840  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1841 }
1842 
1843 TEST_P(AiksTest, CanPerformSaveLayerWithBounds) {
1844  DisplayListBuilder builder;
1845 
1846  DlPaint save;
1847  save.setColor(DlColor::kBlack());
1848 
1849  DlRect save_bounds = DlRect::MakeXYWH(0, 0, 50, 50);
1850  builder.SaveLayer(save_bounds, &save);
1851 
1852  DlPaint paint;
1853  paint.setColor(DlColor::kRed());
1854  builder.DrawRect(DlRect::MakeXYWH(0, 0, 100, 100), paint);
1855  paint.setColor(DlColor::kGreen());
1856  builder.DrawRect(DlRect::MakeXYWH(10, 10, 100, 100), paint);
1857  paint.setColor(DlColor::kBlue());
1858  builder.DrawRect(DlRect::MakeXYWH(20, 20, 100, 100), paint);
1859 
1860  builder.Restore();
1861 
1862  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1863 }
1864 
1865 TEST_P(AiksTest, FilledRoundRectPathsRenderCorrectly) {
1866  DisplayListBuilder builder;
1867  builder.Scale(GetContentScale().x, GetContentScale().y);
1868 
1869  DlPaint paint;
1870  const int color_count = 3;
1871  DlColor colors[color_count] = {
1872  DlColor::kBlue(),
1873  DlColor::kGreen(),
1874  DlColor::ARGB(1.0, 220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f),
1875  };
1876 
1877  paint.setColor(DlColor::kWhite());
1878  builder.DrawPaint(paint);
1879 
1880  auto draw_rrect_as_path = [&builder](const DlRect& rect, Scalar x, Scalar y,
1881  const DlPaint& paint) {
1882  builder.DrawPath(DlPath::MakeRoundRectXY(rect, x, y), paint);
1883  };
1884 
1885  int c_index = 0;
1886  for (int i = 0; i < 4; i++) {
1887  for (int j = 0; j < 4; j++) {
1888  paint.setColor(colors[(c_index++) % color_count]);
1889  draw_rrect_as_path(DlRect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1890  i * 5 + 10, j * 5 + 10, paint);
1891  }
1892  }
1893  paint.setColor(colors[(c_index++) % color_count]);
1894  draw_rrect_as_path(DlRect::MakeXYWH(10, 420, 380, 80), 40, 40, paint);
1895  paint.setColor(colors[(c_index++) % color_count]);
1896  draw_rrect_as_path(DlRect::MakeXYWH(410, 20, 80, 380), 40, 40, paint);
1897 
1898  std::vector<DlColor> gradient_colors = {
1899  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
1900  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
1901  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
1902  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
1903  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
1904  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
1905  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0)};
1906  std::vector<Scalar> stops = {
1907  0.0,
1908  (1.0 / 6.0) * 1,
1909  (1.0 / 6.0) * 2,
1910  (1.0 / 6.0) * 3,
1911  (1.0 / 6.0) * 4,
1912  (1.0 / 6.0) * 5,
1913  1.0,
1914  };
1915  auto texture = DlImageImpeller::Make(
1916  CreateTextureForFixture("airplane.jpg",
1917  /*enable_mipmapping=*/true));
1918 
1919  paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1920  paint.setColorSource(DlColorSource::MakeRadial(
1921  /*center=*/DlPoint(550, 550),
1922  /*radius=*/75,
1923  /*stop_count=*/gradient_colors.size(),
1924  /*colors=*/gradient_colors.data(),
1925  /*stops=*/stops.data(),
1926  /*tile_mode=*/DlTileMode::kMirror));
1927  for (int i = 1; i <= 10; i++) {
1928  int j = 11 - i;
1929  draw_rrect_as_path(DlRect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1930  550 + i * 20, 550 + j * 20),
1931  i * 10, j * 10, paint);
1932  }
1933  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1934  paint.setColorSource(DlColorSource::MakeRadial(
1935  /*center=*/DlPoint(200, 650),
1936  /*radius=*/75,
1937  /*stop_count=*/gradient_colors.size(),
1938  /*colors=*/gradient_colors.data(),
1939  /*stops=*/stops.data(),
1940  /*tile_mode=*/DlTileMode::kMirror));
1941  draw_rrect_as_path(DlRect::MakeLTRB(100, 610, 300, 690), 40, 40, paint);
1942  draw_rrect_as_path(DlRect::MakeLTRB(160, 550, 240, 750), 40, 40, paint);
1943 
1944  auto matrix = DlMatrix::MakeTranslation({520, 20});
1945  paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1946  paint.setColorSource(DlColorSource::MakeImage(
1947  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1948  DlImageSampling::kMipmapLinear, &matrix));
1949  for (int i = 1; i <= 10; i++) {
1950  int j = 11 - i;
1951  draw_rrect_as_path(DlRect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1952  720 + i * 20, 220 + j * 20),
1953  i * 10, j * 10, paint);
1954  }
1955  matrix = DlMatrix::MakeTranslation({800, 300});
1956  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1957  paint.setColorSource(DlColorSource::MakeImage(
1958  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1959  DlImageSampling::kMipmapLinear, &matrix));
1960 
1961  draw_rrect_as_path(DlRect::MakeLTRB(800, 410, 1000, 490), 40, 40, paint);
1962  draw_rrect_as_path(DlRect::MakeLTRB(860, 350, 940, 550), 40, 40, paint);
1963 
1964  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1965 }
1966 
1967 TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
1968  auto callback = [&]() -> sk_sp<DisplayList> {
1969  DisplayListBuilder builder;
1970  builder.Scale(GetContentScale().x, GetContentScale().y);
1971 
1972  DlPaint alpha;
1973  alpha.setColor(DlColor::kRed().modulateOpacity(0.5));
1974 
1975  auto current = Point{25, 25};
1976  const auto offset = Point{25, 25};
1977  const auto size = Size(100, 100);
1978 
1979  static PlaygroundPoint point_a(Point(40, 40), 10, Color::White());
1980  static PlaygroundPoint point_b(Point(160, 160), 10, Color::White());
1981  auto [b0, b1] = DrawPlaygroundLine(point_a, point_b);
1982  DlRect bounds = DlRect::MakeLTRB(b0.x, b0.y, b1.x, b1.y);
1983 
1984  DlPaint stroke_paint;
1985  stroke_paint.setColor(DlColor::kYellow());
1986  stroke_paint.setStrokeWidth(5);
1987  stroke_paint.setDrawStyle(DlDrawStyle::kStroke);
1988  builder.DrawRect(bounds, stroke_paint);
1989 
1990  builder.SaveLayer(bounds, &alpha);
1991 
1992  DlPaint paint;
1993  paint.setColor(DlColor::kRed());
1994  builder.DrawRect(
1995  DlRect::MakeXYWH(current.x, current.y, size.width, size.height), paint);
1996 
1997  paint.setColor(DlColor::kGreen());
1998  current += offset;
1999  builder.DrawRect(
2000  DlRect::MakeXYWH(current.x, current.y, size.width, size.height), paint);
2001 
2002  paint.setColor(DlColor::kBlue());
2003  current += offset;
2004  builder.DrawRect(
2005  DlRect::MakeXYWH(current.x, current.y, size.width, size.height), paint);
2006 
2007  builder.Restore();
2008 
2009  return builder.Build();
2010  };
2011 
2012  ASSERT_TRUE(OpenPlaygroundHere(callback));
2013 }
2014 
2015 TEST_P(AiksTest, SaveLayerDrawsBehindSubsequentEntities) {
2016  // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636
2017  DisplayListBuilder builder;
2018  DlPaint paint;
2019 
2020  paint.setColor(DlColor::kBlack());
2021  DlRect rect = DlRect::MakeXYWH(25, 25, 25, 25);
2022  builder.DrawRect(rect, paint);
2023 
2024  builder.Translate(10, 10);
2025 
2026  DlPaint save_paint;
2027  builder.SaveLayer(std::nullopt, &save_paint);
2028 
2029  paint.setColor(DlColor::kGreen());
2030  builder.DrawRect(rect, paint);
2031 
2032  builder.Restore();
2033 
2034  builder.Translate(10, 10);
2035  paint.setColor(DlColor::kRed());
2036  builder.DrawRect(rect, paint);
2037 
2038  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2039 }
2040 
2041 TEST_P(AiksTest, SiblingSaveLayerBoundsAreRespected) {
2042  DisplayListBuilder builder;
2043  DlPaint paint;
2044  DlRect rect = DlRect::MakeXYWH(0, 0, 1000, 1000);
2045 
2046  // Black, green, and red squares offset by [10, 10].
2047  {
2048  DlPaint save_paint;
2049  DlRect bounds = DlRect::MakeXYWH(25, 25, 25, 25);
2050  builder.SaveLayer(bounds, &save_paint);
2051  paint.setColor(DlColor::kBlack());
2052  builder.DrawRect(rect, paint);
2053  builder.Restore();
2054  }
2055 
2056  {
2057  DlPaint save_paint;
2058  DlRect bounds = DlRect::MakeXYWH(35, 35, 25, 25);
2059  builder.SaveLayer(bounds, &save_paint);
2060  paint.setColor(DlColor::kGreen());
2061  builder.DrawRect(rect, paint);
2062  builder.Restore();
2063  }
2064 
2065  {
2066  DlPaint save_paint;
2067  DlRect bounds = DlRect::MakeXYWH(45, 45, 25, 25);
2068  builder.SaveLayer(bounds, &save_paint);
2069  paint.setColor(DlColor::kRed());
2070  builder.DrawRect(rect, paint);
2071  builder.Restore();
2072  }
2073 
2074  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2075 }
2076 
2077 TEST_P(AiksTest, CanRenderClippedLayers) {
2078  DisplayListBuilder builder;
2079 
2080  DlPaint paint;
2081  paint.setColor(DlColor::kWhite());
2082  builder.DrawPaint(paint);
2083 
2084  // Draw a green circle on the screen.
2085  {
2086  // Increase the clip depth for the savelayer to contend with.
2087  DlPath path = DlPath::MakeCircle(DlPoint(100, 100), 50);
2088  builder.ClipPath(path);
2089 
2090  DlRect bounds = DlRect::MakeXYWH(50, 50, 100, 100);
2091  DlPaint save_paint;
2092  builder.SaveLayer(bounds, &save_paint);
2093 
2094  // Fill the layer with white.
2095  paint.setColor(DlColor::kWhite());
2096  builder.DrawRect(DlRect::MakeSize(DlSize(400, 400)), paint);
2097  // Fill the layer with green, but do so with a color blend that can't be
2098  // collapsed into the parent pass.
2099  paint.setColor(DlColor::kGreen());
2100  paint.setBlendMode(DlBlendMode::kHardLight);
2101  builder.DrawRect(DlRect::MakeSize(DlSize(400, 400)), paint);
2102  }
2103 
2104  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2105 }
2106 
2107 TEST_P(AiksTest, SaveLayerFiltersScaleWithTransform) {
2108  DisplayListBuilder builder;
2109 
2110  builder.Scale(GetContentScale().x, GetContentScale().y);
2111  builder.Translate(100, 100);
2112 
2113  auto texture = DlImageImpeller::Make(CreateTextureForFixture("boston.jpg"));
2114  auto draw_image_layer = [&builder, &texture](const DlPaint& paint) {
2115  builder.SaveLayer(std::nullopt, &paint);
2116  builder.DrawImage(texture, DlPoint(), DlImageSampling::kLinear);
2117  builder.Restore();
2118  };
2119 
2120  DlPaint effect_paint;
2121  effect_paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 6));
2122  draw_image_layer(effect_paint);
2123 
2124  builder.Translate(300, 300);
2125  builder.Scale(3, 3);
2126  draw_image_layer(effect_paint);
2127 
2128  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2129 }
2130 
2131 TEST_P(AiksTest, FastEllipticalRRectMaskBlursRenderCorrectly) {
2132  DisplayListBuilder builder;
2133 
2134  builder.Scale(GetContentScale().x, GetContentScale().y);
2135  DlPaint paint;
2136  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 1));
2137 
2138  DlPaint save_paint;
2139  save_paint.setColor(DlColor::kWhite());
2140  builder.DrawPaint(save_paint);
2141 
2142  paint.setColor(DlColor::kBlue());
2143  for (int i = 0; i < 5; i++) {
2144  Scalar y = i * 125;
2145  Scalar y_radius = i * 15;
2146  for (int j = 0; j < 5; j++) {
2147  Scalar x = j * 125;
2148  Scalar x_radius = j * 15;
2149  builder.DrawRoundRect(
2150  DlRoundRect::MakeRectXY(
2151  DlRect::MakeXYWH(x + 50, y + 50, 100.0f, 100.0f), //
2152  x_radius, y_radius),
2153  paint);
2154  }
2155  }
2156 
2157  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2158 }
2159 
2160 TEST_P(AiksTest, PipelineBlendSingleParameter) {
2161  DisplayListBuilder builder;
2162 
2163  // Should render a green square in the middle of a blue circle.
2164  DlPaint paint;
2165  builder.SaveLayer(std::nullopt, &paint);
2166  {
2167  builder.Translate(100, 100);
2168  paint.setColor(DlColor::kBlue());
2169  builder.DrawCircle(DlPoint(200, 200), 200, paint);
2170  builder.ClipRect(DlRect::MakeXYWH(100, 100, 200, 200));
2171 
2172  paint.setColor(DlColor::kGreen());
2173  paint.setBlendMode(DlBlendMode::kSrcOver);
2174  paint.setImageFilter(DlImageFilter::MakeColorFilter(
2175  DlColorFilter::MakeBlend(DlColor::kWhite(), DlBlendMode::kDst)));
2176  builder.DrawCircle(DlPoint(200, 200), 200, paint);
2177  builder.Restore();
2178  }
2179 
2180  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2181 }
2182 
2183 // Creates an image matrix filter that scales large content such that it would
2184 // exceed the max texture size. See
2185 // https://github.com/flutter/flutter/issues/128912
2186 TEST_P(AiksTest, MassiveScalingMatrixImageFilter) {
2187  if (GetBackend() == PlaygroundBackend::kVulkan) {
2188  GTEST_SKIP() << "Swiftshader is running out of memory on this example.";
2189  }
2190  DisplayListBuilder builder(DlRect::MakeSize(DlSize(1000, 1000)));
2191 
2192  auto filter = DlImageFilter::MakeMatrix(
2193  DlMatrix::MakeScale({0.001, 0.001, 1}), DlImageSampling::kLinear);
2194 
2195  DlPaint paint;
2196  paint.setImageFilter(filter);
2197  builder.SaveLayer(std::nullopt, &paint);
2198  {
2199  DlPaint paint;
2200  paint.setColor(DlColor::kRed());
2201  builder.DrawRect(DlRect::MakeLTRB(0, 0, 100000, 100000), paint);
2202  }
2203  builder.Restore();
2204 
2205  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2206 }
2207 
2208 TEST_P(AiksTest, NoDimplesInRRectPath) {
2209  Scalar width = 200.f;
2210  Scalar height = 60.f;
2211  Scalar corner = 1.f;
2212  auto callback = [&]() -> sk_sp<DisplayList> {
2213  if (AiksTest::ImGuiBegin("Controls", nullptr,
2214  ImGuiWindowFlags_AlwaysAutoResize)) {
2215  ImGui::SliderFloat("width", &width, 0, 200);
2216  ImGui::SliderFloat("height", &height, 0, 200);
2217  ImGui::SliderFloat("corner", &corner, 0, 1);
2218  ImGui::End();
2219  }
2220 
2221  DisplayListBuilder builder;
2222  builder.Scale(GetContentScale().x, GetContentScale().y);
2223 
2224  DlPaint background_paint;
2225  background_paint.setColor(DlColor(1, 0.1, 0.1, 0.1, DlColorSpace::kSRGB));
2226  builder.DrawPaint(background_paint);
2227 
2228  std::vector<DlColor> colors = {DlColor::kRed(), DlColor::kBlue()};
2229  std::vector<Scalar> stops = {0.0, 1.0};
2230 
2231  DlPaint paint;
2232  auto gradient = DlColorSource::MakeLinear(DlPoint(0, 0), DlPoint(200, 200),
2233  2, colors.data(), stops.data(),
2234  DlTileMode::kClamp);
2235  paint.setColorSource(gradient);
2236  paint.setColor(DlColor::kWhite());
2237  paint.setDrawStyle(DlDrawStyle::kStroke);
2238  paint.setStrokeWidth(20);
2239 
2240  builder.Save();
2241  builder.Translate(100, 100);
2242 
2243  Scalar corner_x = ((1 - corner) * 50) + 50;
2244  Scalar corner_y = corner * 50 + 50;
2245  DlRoundRect rrect = DlRoundRect::MakeRectXY(
2246  DlRect::MakeXYWH(0, 0, width, height), corner_x, corner_y);
2247  builder.DrawRoundRect(rrect, paint);
2248  builder.Restore();
2249  return builder.Build();
2250  };
2251  ASSERT_TRUE(OpenPlaygroundHere(callback));
2252 }
2253 
2254 TEST_P(AiksTest, BackdropFilterOverUnclosedClip) {
2255  DisplayListBuilder builder;
2256 
2257  builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
2258  builder.Save();
2259  {
2260  builder.ClipRect(DlRect::MakeLTRB(100, 100, 800, 800));
2261 
2262  builder.Save();
2263  {
2264  builder.ClipRect(DlRect::MakeLTRB(600, 600, 800, 800));
2265  builder.DrawPaint(DlPaint().setColor(DlColor::kRed()));
2266  builder.DrawPaint(DlPaint().setColor(DlColor::kBlue().withAlphaF(0.5)));
2267  builder.ClipRect(DlRect::MakeLTRB(700, 700, 750, 800));
2268  builder.DrawPaint(DlPaint().setColor(DlColor::kRed().withAlphaF(0.5)));
2269  }
2270  builder.Restore();
2271 
2272  auto image_filter = DlImageFilter::MakeBlur(10, 10, DlTileMode::kDecal);
2273  builder.SaveLayer(std::nullopt, nullptr, image_filter.get());
2274  }
2275  builder.Restore();
2276  builder.DrawCircle(DlPoint(100, 100), 100,
2277  DlPaint().setColor(DlColor::kAqua()));
2278 
2279  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
2280 }
2281 
2282 } // namespace testing
2283 } // namespace impeller
bool full_circles
Scalar vertical_scale
bool sweeps_over_360
bool use_center
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)
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
float Scalar
Definition: scalar.h:19
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:51
constexpr float kEhCloseEnough
Definition: constants.h:57
flutter::DlRoundRect DlRoundRect
Definition: dl_dispatcher.h:27
TRect< Scalar > Rect
Definition: rect.h:788
TPoint< Scalar > Point
Definition: point.h:425
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
TSize< Scalar > Size
Definition: size.h:159
constexpr float kSqrt2
Definition: constants.h:47
constexpr const char * PixelFormatToString(PixelFormat format)
Definition: formats.h:141
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:23
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
static constexpr Color White()
Definition: color.h:264
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:208
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:623
static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition: round_rect.h:31
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
const size_t start