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