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