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