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