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