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"
20 #include "flutter/testing/display_list_testing.h"
21 #include "flutter/testing/testing.h"
23 #include "include/core/SkMatrix.h"
24 
25 namespace impeller {
26 namespace testing {
27 
28 namespace {
29 SkM44 FromImpellerMatrix(const Matrix& matrix) {
30  return SkM44::ColMajor(matrix.m);
31 }
32 } // namespace
33 
34 using namespace flutter;
35 
36 TEST_P(AiksTest, CanRenderColoredRect) {
37  DisplayListBuilder builder;
38  DlPaint paint;
39  paint.setColor(DlColor::kBlue());
40  SkPath path = SkPath();
41  path.addRect(SkRect::MakeXYWH(100.0, 100.0, 100.0, 100.0));
42  builder.DrawPath(path, paint);
43  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
44 }
45 
46 TEST_P(AiksTest, CanRenderImage) {
47  DisplayListBuilder builder;
48  DlPaint paint;
49  paint.setColor(DlColor::kRed());
50  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
51  builder.DrawImage(image, SkPoint::Make(100.0, 100.0),
52  DlImageSampling::kNearestNeighbor, &paint);
53  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
54 }
55 
56 TEST_P(AiksTest, CanRenderInvertedImageWithColorFilter) {
57  DisplayListBuilder builder;
58  DlPaint paint;
59  paint.setColor(DlColor::kRed());
60  paint.setColorFilter(
61  DlBlendColorFilter::Make(DlColor::kYellow(), DlBlendMode::kSrcOver));
62  paint.setInvertColors(true);
63  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
64 
65  builder.DrawImage(image, SkPoint::Make(100.0, 100.0),
66  DlImageSampling::kNearestNeighbor, &paint);
67  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
68 }
69 
70 TEST_P(AiksTest, CanRenderColorFilterWithInvertColors) {
71  DisplayListBuilder builder;
72  DlPaint paint;
73  paint.setColor(DlColor::kRed());
74  paint.setColorFilter(
75  DlBlendColorFilter::Make(DlColor::kYellow(), DlBlendMode::kSrcOver));
76  paint.setInvertColors(true);
77 
78  builder.DrawRect(SkRect::MakeLTRB(0, 0, 100, 100), paint);
79  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
80 }
81 
82 TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) {
83  DisplayListBuilder builder;
84  DlPaint paint;
85  paint.setColor(DlColor::kRed());
86  paint.setColorFilter(
87  DlBlendColorFilter::Make(DlColor::kYellow(), DlBlendMode::kSrcOver));
88  paint.setInvertColors(true);
89 
90  builder.DrawPaint(paint);
91  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
92 }
93 
94 namespace {
95 bool GenerateMipmap(const std::shared_ptr<Context>& context,
96  std::shared_ptr<Texture> texture,
97  std::string label) {
98  auto buffer = context->CreateCommandBuffer();
99  if (!buffer) {
100  return false;
101  }
102  auto pass = buffer->CreateBlitPass();
103  if (!pass) {
104  return false;
105  }
106  pass->GenerateMipmap(std::move(texture), std::move(label));
107 
108  pass->EncodeCommands(context->GetResourceAllocator());
109  return context->GetCommandQueue()->Submit({buffer}).ok();
110 }
111 
112 void CanRenderTiledTexture(AiksTest* aiks_test,
113  DlTileMode tile_mode,
114  Matrix local_matrix = {}) {
115  auto context = aiks_test->GetContext();
116  ASSERT_TRUE(context);
117  auto texture = aiks_test->CreateTextureForFixture("table_mountain_nx.png",
118  /*enable_mipmapping=*/true);
119  GenerateMipmap(context, texture, "table_mountain_nx");
120  auto image = DlImageImpeller::Make(texture);
121  SkMatrix sk_local_matrix = ToSkMatrix(local_matrix);
122  DlImageColorSource color_source(image, tile_mode, tile_mode,
123  DlImageSampling::kNearestNeighbor,
124  &sk_local_matrix);
125 
126  DisplayListBuilder builder;
127  DlPaint paint;
128  paint.setColor(DlColor::kWhite());
129  paint.setColorSource(&color_source);
130 
131  builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y);
132  builder.Translate(100.0f, 100.0f);
133  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
134 
135  // Should not change the image.
136  constexpr auto stroke_width = 64;
137  paint.setDrawStyle(DlDrawStyle::kStroke);
138  paint.setStrokeWidth(stroke_width);
139  if (tile_mode == DlTileMode::kDecal) {
140  builder.DrawRect(SkRect::MakeXYWH(stroke_width, stroke_width, 600, 600),
141  paint);
142  } else {
143  builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
144  }
145 
146  {
147  // Should not change the image.
148  SkPath path;
149  path.addCircle(150, 150, 150);
150  path.addRoundRect(SkRect::MakeLTRB(300, 300, 600, 600), 10, 10);
151 
152  // Make sure path cannot be simplified...
153  EXPECT_FALSE(path.isRect(nullptr));
154  EXPECT_FALSE(path.isOval(nullptr));
155  EXPECT_FALSE(path.isRRect(nullptr));
156 
157  // Make sure path will not trigger the optimal convex code
158  EXPECT_FALSE(path.isConvex());
159 
160  paint.setDrawStyle(DlDrawStyle::kFill);
161  builder.DrawPath(path, paint);
162  }
163 
164  {
165  // Should not change the image. Tests the Convex short-cut code.
166  SkPath circle;
167  circle.addCircle(150, 450, 150);
168 
169  // Unfortunately, the circle path can be simplified...
170  EXPECT_TRUE(circle.isOval(nullptr));
171  // At least it's convex, though...
172  EXPECT_TRUE(circle.isConvex());
173 
174  // Let's make a copy that doesn't remember that it's just a circle...
175  SkPath path;
176  // This moveTo confuses addPath into appending rather than replacing,
177  // which prevents it from noticing that it's just a circle...
178  path.moveTo(10, 10);
179  path.addPath(circle);
180 
181  // Make sure path cannot be simplified...
182  EXPECT_FALSE(path.isRect(nullptr));
183  EXPECT_FALSE(path.isOval(nullptr));
184  EXPECT_FALSE(path.isRRect(nullptr));
185 
186  // But check that we will trigger the optimal convex code
187  EXPECT_TRUE(path.isConvex());
188 
189  paint.setDrawStyle(DlDrawStyle::kFill);
190  builder.DrawPath(path, paint);
191  }
192 
193  ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build()));
194 }
195 } // namespace
196 
197 TEST_P(AiksTest, CanRenderTiledTextureClamp) {
198  CanRenderTiledTexture(this, DlTileMode::kClamp);
199 }
200 
201 TEST_P(AiksTest, CanRenderTiledTextureRepeat) {
202  CanRenderTiledTexture(this, DlTileMode::kRepeat);
203 }
204 
205 TEST_P(AiksTest, CanRenderTiledTextureMirror) {
206  CanRenderTiledTexture(this, DlTileMode::kMirror);
207 }
208 
209 TEST_P(AiksTest, CanRenderTiledTextureDecal) {
210  CanRenderTiledTexture(this, DlTileMode::kDecal);
211 }
212 
213 TEST_P(AiksTest, CanRenderTiledTextureClampWithTranslate) {
214  CanRenderTiledTexture(this, DlTileMode::kClamp,
215  Matrix::MakeTranslation({172.f, 172.f, 0.f}));
216 }
217 
218 TEST_P(AiksTest, CanRenderImageRect) {
219  DisplayListBuilder builder;
220  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
221 
222  SkSize image_half_size = SkSize::Make(image->dimensions().fWidth * 0.5f,
223  image->dimensions().fHeight * 0.5f);
224 
225  // Render the bottom right quarter of the source image in a stretched rect.
226  auto source_rect = SkRect::MakeSize(image_half_size);
227  source_rect =
228  source_rect.makeOffset(image_half_size.fWidth, image_half_size.fHeight);
229 
230  builder.DrawImageRect(image, source_rect,
231  SkRect::MakeXYWH(100, 100, 600, 600),
232  DlImageSampling::kNearestNeighbor);
233  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
234 }
235 
236 TEST_P(AiksTest, CanRenderSimpleClips) {
237  DisplayListBuilder builder;
238  builder.Scale(GetContentScale().x, GetContentScale().y);
239  DlPaint paint;
240 
241  paint.setColor(DlColor::kWhite());
242  builder.DrawPaint(paint);
243 
244  auto draw = [&builder](const DlPaint& paint, Scalar x, Scalar y) {
245  builder.Save();
246  builder.Translate(x, y);
247  {
248  builder.Save();
249  builder.ClipRect(SkRect::MakeLTRB(50, 50, 150, 150));
250  builder.DrawPaint(paint);
251  builder.Restore();
252  }
253  {
254  builder.Save();
255  builder.ClipOval(SkRect::MakeLTRB(200, 50, 300, 150));
256  builder.DrawPaint(paint);
257  builder.Restore();
258  }
259  {
260  builder.Save();
261  builder.ClipRRect(
262  SkRRect::MakeRectXY(SkRect::MakeLTRB(50, 200, 150, 300), 20, 20));
263  builder.DrawPaint(paint);
264  builder.Restore();
265  }
266  {
267  builder.Save();
268  builder.ClipRRect(
269  SkRRect::MakeRectXY(SkRect::MakeLTRB(200, 230, 300, 270), 20, 20));
270  builder.DrawPaint(paint);
271  builder.Restore();
272  }
273  {
274  builder.Save();
275  builder.ClipRRect(
276  SkRRect::MakeRectXY(SkRect::MakeLTRB(230, 200, 270, 300), 20, 20));
277  builder.DrawPaint(paint);
278  builder.Restore();
279  }
280  builder.Restore();
281  };
282 
283  paint.setColor(DlColor::kBlue());
284  draw(paint, 0, 0);
285 
286  DlColor gradient_colors[7] = {
287  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
288  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
289  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
290  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
291  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
292  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
293  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
294  };
295  Scalar stops[7] = {
296  0.0,
297  (1.0 / 6.0) * 1,
298  (1.0 / 6.0) * 2,
299  (1.0 / 6.0) * 3,
300  (1.0 / 6.0) * 4,
301  (1.0 / 6.0) * 5,
302  1.0,
303  };
304  auto texture = CreateTextureForFixture("airplane.jpg",
305  /*enable_mipmapping=*/true);
306  auto image = DlImageImpeller::Make(texture);
307 
308  paint.setColorSource(DlColorSource::MakeRadial(
309  {500, 600}, 75, 7, gradient_colors, stops, DlTileMode::kMirror));
310  draw(paint, 0, 300);
311 
312  DlImageColorSource image_source(image, DlTileMode::kRepeat,
313  DlTileMode::kRepeat,
314  DlImageSampling::kNearestNeighbor);
315  paint.setColorSource(&image_source);
316  draw(paint, 300, 0);
317 
318  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
319 }
320 
321 TEST_P(AiksTest, CanSaveLayerStandalone) {
322  DisplayListBuilder builder;
323 
324  DlPaint red;
325  red.setColor(DlColor::kRed());
326 
327  DlPaint alpha;
328  alpha.setColor(DlColor::kRed().modulateOpacity(0.5));
329 
330  builder.SaveLayer(nullptr, &alpha);
331 
332  builder.DrawCircle({125, 125}, 125, red);
333 
334  builder.Restore();
335 
336  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
337 }
338 
339 TEST_P(AiksTest, CanRenderDifferentShapesWithSameColorSource) {
340  DisplayListBuilder builder;
341  DlPaint paint;
342 
343  DlColor colors[2] = {
344  DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
345  DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0),
346  };
347  DlScalar stops[2] = {
348  0.0,
349  1.0,
350  };
351 
352  paint.setColorSource(DlColorSource::MakeLinear(
353  /*start_point=*/{0, 0}, //
354  /*end_point=*/{100, 100}, //
355  /*stop_count=*/2, //
356  /*colors=*/colors, //
357  /*stops=*/stops, //
358  /*tile_mode=*/DlTileMode::kRepeat //
359  ));
360 
361  builder.Save();
362  builder.Translate(100, 100);
363  builder.DrawRect(SkRect::MakeXYWH(0, 0, 200, 200), paint);
364  builder.Restore();
365 
366  builder.Save();
367  builder.Translate(100, 400);
368  builder.DrawCircle({100, 100}, 100, paint);
369  builder.Restore();
370  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
371 }
372 
373 TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) {
374  DisplayListBuilder builder;
375  DlPaint paint;
376  paint.setColor(DlColor::kRed());
377 
378  SkRRect rrect;
379  SkVector radii[4] = {
380  SkVector{50, 25},
381  SkVector{25, 50},
382  SkVector{50, 25},
383  SkVector{25, 50},
384  };
385  rrect.setRectRadii(SkRect::MakeXYWH(100, 100, 500, 500), radii);
386 
387  builder.DrawRRect(rrect, paint);
388 
389  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
390 }
391 
392 TEST_P(AiksTest, CanDrawPaint) {
393  auto medium_turquoise =
394  DlColor::RGBA(72.0f / 255.0f, 209.0f / 255.0f, 204.0f / 255.0f, 1.0f);
395 
396  DisplayListBuilder builder;
397  builder.Scale(0.2, 0.2);
398  builder.DrawPaint(DlPaint().setColor(medium_turquoise));
399  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
400 }
401 
402 TEST_P(AiksTest, CanDrawPaintMultipleTimes) {
403  auto medium_turquoise =
404  DlColor::RGBA(72.0f / 255.0f, 209.0f / 255.0f, 204.0f / 255.0f, 1.0f);
405  auto orange_red =
406  DlColor::RGBA(255.0f / 255.0f, 69.0f / 255.0f, 0.0f / 255.0f, 1.0f);
407 
408  DisplayListBuilder builder;
409  builder.Scale(0.2, 0.2);
410  builder.DrawPaint(DlPaint().setColor(medium_turquoise));
411  builder.DrawPaint(DlPaint().setColor(orange_red.modulateOpacity(0.5f)));
412  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
413 }
414 
415 TEST_P(AiksTest, FilledCirclesRenderCorrectly) {
416  DisplayListBuilder builder;
417  builder.Scale(GetContentScale().x, GetContentScale().y);
418  DlPaint paint;
419  const int color_count = 3;
420  DlColor colors[color_count] = {
421  DlColor::kBlue(),
422  DlColor::kGreen(),
423  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
424  };
425 
426  paint.setColor(DlColor::kWhite());
427  builder.DrawPaint(paint);
428 
429  int c_index = 0;
430  int radius = 600;
431  while (radius > 0) {
432  paint.setColor(colors[(c_index++) % color_count]);
433  builder.DrawCircle({10, 10}, radius, paint);
434  if (radius > 30) {
435  radius -= 10;
436  } else {
437  radius -= 2;
438  }
439  }
440 
441  DlColor gradient_colors[7] = {
442  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
443  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
444  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
445  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
446  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
447  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
448  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
449  };
450  DlScalar stops[7] = {
451  0.0,
452  (1.0 / 6.0) * 1,
453  (1.0 / 6.0) * 2,
454  (1.0 / 6.0) * 3,
455  (1.0 / 6.0) * 4,
456  (1.0 / 6.0) * 5,
457  1.0,
458  };
459  auto texture = CreateTextureForFixture("airplane.jpg",
460  /*enable_mipmapping=*/true);
461  auto image = DlImageImpeller::Make(texture);
462 
463  paint.setColorSource(DlColorSource::MakeRadial(
464  {500, 600}, 75, 7, gradient_colors, stops, DlTileMode::kMirror));
465  builder.DrawCircle({500, 600}, 100, paint);
466 
467  SkMatrix local_matrix = SkMatrix::Translate(700, 200);
468  DlImageColorSource image_source(
469  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
470  DlImageSampling::kNearestNeighbor, &local_matrix);
471  paint.setColorSource(&image_source);
472  builder.DrawCircle({800, 300}, 100, paint);
473 
474  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
475 }
476 
477 TEST_P(AiksTest, StrokedCirclesRenderCorrectly) {
478  DisplayListBuilder builder;
479  builder.Scale(GetContentScale().x, GetContentScale().y);
480  DlPaint paint;
481  const int color_count = 3;
482  DlColor colors[color_count] = {
483  DlColor::kBlue(),
484  DlColor::kGreen(),
485  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
486  };
487 
488  paint.setColor(DlColor::kWhite());
489  builder.DrawPaint(paint);
490 
491  int c_index = 0;
492 
493  auto draw = [&paint, &colors, &c_index](DlCanvas& canvas, SkPoint center,
494  Scalar r, Scalar dr, int n) {
495  for (int i = 0; i < n; i++) {
496  paint.setColor(colors[(c_index++) % color_count]);
497  canvas.DrawCircle(center, r, paint);
498  r += dr;
499  }
500  };
501 
502  paint.setDrawStyle(DlDrawStyle::kStroke);
503  paint.setStrokeWidth(1);
504  draw(builder, {10, 10}, 2, 2, 14); // r = [2, 28], covers [1,29]
505  paint.setStrokeWidth(5);
506  draw(builder, {10, 10}, 35, 10, 56); // r = [35, 585], covers [30,590]
507 
508  DlColor gradient_colors[7] = {
509  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
510  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
511  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
512  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
513  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
514  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
515  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
516  };
517  DlScalar stops[7] = {
518  0.0,
519  (1.0 / 6.0) * 1,
520  (1.0 / 6.0) * 2,
521  (1.0 / 6.0) * 3,
522  (1.0 / 6.0) * 4,
523  (1.0 / 6.0) * 5,
524  1.0,
525  };
526  auto texture = CreateTextureForFixture("airplane.jpg",
527  /*enable_mipmapping=*/true);
528  auto image = DlImageImpeller::Make(texture);
529 
530  paint.setColorSource(DlColorSource::MakeRadial(
531  {500, 600}, 75, 7, gradient_colors, stops, DlTileMode::kMirror));
532  draw(builder, {500, 600}, 5, 10, 10);
533 
534  SkMatrix local_matrix = SkMatrix::Translate(700, 200);
535  DlImageColorSource image_source(
536  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
537  DlImageSampling::kNearestNeighbor, &local_matrix);
538  paint.setColorSource(&image_source);
539  draw(builder, {800, 300}, 5, 10, 10);
540 
541  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
542 }
543 
544 TEST_P(AiksTest, FilledEllipsesRenderCorrectly) {
545  DisplayListBuilder builder;
546  builder.Scale(GetContentScale().x, GetContentScale().y);
547  DlPaint paint;
548  const int color_count = 3;
549  DlColor colors[color_count] = {
550  DlColor::kBlue(),
551  DlColor::kGreen(),
552  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
553  };
554 
555  paint.setColor(DlColor::kWhite());
556  builder.DrawPaint(paint);
557 
558  int c_index = 0;
559  int long_radius = 600;
560  int short_radius = 600;
561  while (long_radius > 0 && short_radius > 0) {
562  paint.setColor(colors[(c_index++) % color_count]);
563  builder.DrawOval(SkRect::MakeXYWH(10 - long_radius, 10 - short_radius,
564  long_radius * 2, short_radius * 2),
565  paint);
566  builder.DrawOval(SkRect::MakeXYWH(1000 - short_radius, 750 - long_radius,
567  short_radius * 2, long_radius * 2),
568  paint);
569  if (short_radius > 30) {
570  short_radius -= 10;
571  long_radius -= 5;
572  } else {
573  short_radius -= 2;
574  long_radius -= 1;
575  }
576  }
577 
578  DlColor gradient_colors[7] = {
579  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
580  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
581  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
582  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
583  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
584  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
585  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
586  };
587  DlScalar stops[7] = {
588  0.0,
589  (1.0 / 6.0) * 1,
590  (1.0 / 6.0) * 2,
591  (1.0 / 6.0) * 3,
592  (1.0 / 6.0) * 4,
593  (1.0 / 6.0) * 5,
594  1.0,
595  };
596  auto texture = CreateTextureForFixture("airplane.jpg",
597  /*enable_mipmapping=*/true);
598  auto image = DlImageImpeller::Make(texture);
599 
600  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
601 
602  paint.setColorSource(DlColorSource::MakeRadial(
603  {300, 650}, 75, 7, gradient_colors, stops, DlTileMode::kMirror));
604  builder.DrawOval(SkRect::MakeXYWH(200, 625, 200, 50), paint);
605  builder.DrawOval(SkRect::MakeXYWH(275, 550, 50, 200), paint);
606 
607  SkMatrix local_matrix = SkMatrix::Translate(610, 15);
608  DlImageColorSource image_source(
609  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
610  DlImageSampling::kNearestNeighbor, &local_matrix);
611  paint.setColorSource(&image_source);
612  builder.DrawOval(SkRect::MakeXYWH(610, 90, 200, 50), paint);
613  builder.DrawOval(SkRect::MakeXYWH(685, 15, 50, 200), paint);
614 
615  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
616 }
617 
618 TEST_P(AiksTest, FilledRoundRectsRenderCorrectly) {
619  DisplayListBuilder builder;
620  builder.Scale(GetContentScale().x, GetContentScale().y);
621  DlPaint paint;
622  const int color_count = 3;
623  DlColor colors[color_count] = {
624  DlColor::kBlue(),
625  DlColor::kGreen(),
626  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f),
627  };
628 
629  paint.setColor(DlColor::kWhite());
630  builder.DrawPaint(paint);
631 
632  int c_index = 0;
633  for (int i = 0; i < 4; i++) {
634  for (int j = 0; j < 4; j++) {
635  paint.setColor(colors[(c_index++) % color_count]);
636  builder.DrawRRect(
637  SkRRect::MakeRectXY(
638  SkRect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80), //
639  i * 5 + 10, j * 5 + 10),
640  paint);
641  }
642  }
643  paint.setColor(colors[(c_index++) % color_count]);
644  builder.DrawRRect(
645  SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 420, 380, 80), 40, 40), paint);
646  paint.setColor(colors[(c_index++) % color_count]);
647  builder.DrawRRect(
648  SkRRect::MakeRectXY(SkRect::MakeXYWH(410, 20, 80, 380), 40, 40), paint);
649 
650  DlColor gradient_colors[7] = {
651  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
652  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
653  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
654  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
655  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
656  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
657  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0),
658  };
659  DlScalar stops[7] = {
660  0.0,
661  (1.0 / 6.0) * 1,
662  (1.0 / 6.0) * 2,
663  (1.0 / 6.0) * 3,
664  (1.0 / 6.0) * 4,
665  (1.0 / 6.0) * 5,
666  1.0,
667  };
668  auto texture = CreateTextureForFixture("airplane.jpg",
669  /*enable_mipmapping=*/true);
670  auto image = DlImageImpeller::Make(texture);
671 
672  paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
673  paint.setColorSource(DlColorSource::MakeRadial(
674  {550, 550}, 75, 7, gradient_colors, stops, DlTileMode::kMirror));
675  for (int i = 1; i <= 10; i++) {
676  int j = 11 - i;
677  builder.DrawRRect(
678  SkRRect::MakeRectXY(SkRect::MakeLTRB(550 - i * 20, 550 - j * 20, //
679  550 + i * 20, 550 + j * 20),
680  i * 10, j * 10),
681  paint);
682  }
683 
684  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
685  paint.setColorSource(DlColorSource::MakeRadial(
686  {200, 650}, 75, 7, gradient_colors, stops, DlTileMode::kMirror));
687  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
688  builder.DrawRRect(
689  SkRRect::MakeRectXY(SkRect::MakeLTRB(100, 610, 300, 690), 40, 40), paint);
690  builder.DrawRRect(
691  SkRRect::MakeRectXY(SkRect::MakeLTRB(160, 550, 240, 750), 40, 40), paint);
692 
693  paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
694  SkMatrix local_matrix = SkMatrix::Translate(520, 20);
695  DlImageColorSource image_source(
696  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
697  DlImageSampling::kNearestNeighbor, &local_matrix);
698  paint.setColorSource(&image_source);
699  for (int i = 1; i <= 10; i++) {
700  int j = 11 - i;
701  builder.DrawRRect(
702  SkRRect::MakeRectXY(SkRect::MakeLTRB(720 - i * 20, 220 - j * 20, //
703  720 + i * 20, 220 + j * 20),
704  i * 10, j * 10),
705  paint);
706  }
707 
708  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
709  local_matrix = SkMatrix::Translate(800, 300);
710  DlImageColorSource image_source2(
711  image, DlTileMode::kRepeat, DlTileMode::kRepeat,
712  DlImageSampling::kNearestNeighbor, &local_matrix);
713  paint.setColorSource(&image_source2);
714  builder.DrawRRect(
715  SkRRect::MakeRectXY(SkRect::MakeLTRB(800, 410, 1000, 490), 40, 40),
716  paint);
717  builder.DrawRRect(
718  SkRRect::MakeRectXY(SkRect::MakeLTRB(860, 350, 940, 550), 40, 40), paint);
719 
720  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
721 }
722 
723 TEST_P(AiksTest, SolidColorCirclesOvalsRRectsMaskBlurCorrectly) {
724  DisplayListBuilder builder;
725  builder.Scale(GetContentScale().x, GetContentScale().y);
726  DlPaint paint;
727  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 1.0f));
728 
729  builder.DrawPaint(DlPaint().setColor(DlColor::kWhite()));
730 
731  paint.setColor(
732  DlColor::RGBA(220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, 1.0f));
733  Scalar y = 100.0f;
734  for (int i = 0; i < 5; i++) {
735  Scalar x = (i + 1) * 100;
736  Scalar radius = x / 10.0f;
737  builder.DrawRect(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
738  radius, 60.0f - radius),
739  paint);
740  }
741 
742  paint.setColor(DlColor::kBlue());
743  y += 100.0f;
744  for (int i = 0; i < 5; i++) {
745  Scalar x = (i + 1) * 100;
746  Scalar radius = x / 10.0f;
747  builder.DrawCircle({x + 25, y + 25}, radius, paint);
748  }
749 
750  paint.setColor(DlColor::kGreen());
751  y += 100.0f;
752  for (int i = 0; i < 5; i++) {
753  Scalar x = (i + 1) * 100;
754  Scalar radius = x / 10.0f;
755  builder.DrawOval(SkRect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
756  radius, 60.0f - radius),
757  paint);
758  }
759 
760  paint.setColor(
761  DlColor::RGBA(128.0f / 255.0f, 0.0f / 255.0f, 128.0f / 255.0f, 1.0f));
762  y += 100.0f;
763  for (int i = 0; i < 5; i++) {
764  Scalar x = (i + 1) * 100;
765  Scalar radius = x / 20.0f;
766  builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f),
767  radius, radius),
768  paint);
769  }
770 
771  paint.setColor(
772  DlColor::RGBA(255.0f / 255.0f, 165.0f / 255.0f, 0.0f / 255.0f, 1.0f));
773  y += 100.0f;
774  for (int i = 0; i < 5; i++) {
775  Scalar x = (i + 1) * 100;
776  Scalar radius = x / 20.0f;
777  builder.DrawRRect(
778  SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, 60.0f, 60.0f), radius, 5.0f),
779  paint);
780  }
781 
782  auto dl = builder.Build();
783  ASSERT_TRUE(OpenPlaygroundHere(dl));
784 }
785 
786 TEST_P(AiksTest, CanRenderClippedBackdropFilter) {
787  DisplayListBuilder builder;
788 
789  builder.Scale(GetContentScale().x, GetContentScale().y);
790 
791  // Draw something interesting in the background.
792  std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
793  DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0)};
794  std::vector<Scalar> stops = {
795  0.0,
796  1.0,
797  };
798  DlPaint paint;
799  paint.setColorSource(DlColorSource::MakeLinear(
800  /*start_point=*/{0, 0}, //
801  /*end_point=*/{100, 100}, //
802  /*stop_count=*/2, //
803  /*colors=*/colors.data(), //
804  /*stops=*/stops.data(), //
805  /*tile_mode=*/DlTileMode::kRepeat //
806  ));
807 
808  builder.DrawPaint(paint);
809 
810  SkRect clip_rect = SkRect::MakeLTRB(50, 50, 400, 300);
811  SkRRect clip_rrect = SkRRect::MakeRectXY(clip_rect, 100, 100);
812 
813  // Draw a clipped SaveLayer, where the clip coverage and SaveLayer size are
814  // the same.
815  builder.ClipRRect(clip_rrect, DlCanvas::ClipOp::kIntersect);
816 
817  DlPaint save_paint;
818  auto backdrop_filter = std::make_shared<DlColorFilterImageFilter>(
819  DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kExclusion));
820  builder.SaveLayer(&clip_rect, &save_paint, backdrop_filter.get());
821 
822  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
823 }
824 
825 TEST_P(AiksTest, CanDrawPerspectiveTransformWithClips) {
826  // Avoiding `GetSecondsElapsed()` to reduce risk of golden flakiness.
827  int time = 0;
828  auto callback = [&]() -> sk_sp<DisplayList> {
829  DisplayListBuilder builder;
830 
831  builder.Save();
832  {
833  builder.Translate(300, 300);
834 
835  // 1. Draw/restore a clip before drawing the image, which will get drawn
836  // to the depth buffer behind the image.
837  builder.Save();
838  {
839  DlPaint paint;
840  paint.setColor(DlColor::kGreen());
841  builder.DrawPaint(paint);
842  builder.ClipRect(SkRect::MakeLTRB(-180, -180, 180, 180),
843  DlCanvas::ClipOp::kDifference);
844 
845  paint.setColor(DlColor::kBlack());
846  builder.DrawPaint(paint);
847  }
848  builder.Restore(); // Restore rectangle difference clip.
849 
850  builder.Save();
851  {
852  // 2. Draw an oval clip that applies to the image, which will get drawn
853  // in front of the image on the depth buffer.
854  builder.ClipOval(SkRect::MakeLTRB(-200, -200, 200, 200));
855 
856  Matrix result =
857  Matrix(1.0, 0.0, 0.0, 0.0, //
858  0.0, 1.0, 0.0, 0.0, //
859  0.0, 0.0, 1.0, 0.003, //
860  0.0, 0.0, 0.0, 1.0) *
861  Matrix::MakeRotationY({Radians{-1.0f + (time++ / 60.0f)}});
862 
863  // 3. Draw the rotating image with a perspective transform.
864  builder.Transform(FromImpellerMatrix(result));
865 
866  auto image =
867  DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
868  auto position = -SkPoint::Make(image->dimensions().fWidth,
869  image->dimensions().fHeight) *
870  0.5;
871  builder.DrawImage(image, position, {});
872  }
873  builder.Restore(); // Restore oval intersect clip.
874 
875  // 4. Draw a semi-translucent blue circle atop all previous draws.
876  DlPaint paint;
877  paint.setColor(DlColor::kBlue().modulateOpacity(0.4));
878  builder.DrawCircle({}, 230, paint);
879  }
880  builder.Restore(); // Restore translation.
881 
882  return builder.Build();
883  };
884  ASSERT_TRUE(OpenPlaygroundHere(callback));
885 }
886 
887 TEST_P(AiksTest, ImageColorSourceEffectTransform) {
888  // Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885
889 
890  DisplayListBuilder builder;
891  auto texture = DlImageImpeller::Make(CreateTextureForFixture("monkey.png"));
892 
893  DlPaint paint;
894  paint.setColor(DlColor::kWhite());
895  builder.DrawPaint(paint);
896 
897  // Translation
898  {
899  SkMatrix matrix = SkMatrix::Translate(50, 50);
900  DlPaint paint;
901  paint.setColorSource(std::make_shared<DlImageColorSource>(
902  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
903  DlImageSampling::kNearestNeighbor, &matrix));
904 
905  builder.DrawRect(SkRect::MakeLTRB(0, 0, 100, 100), paint);
906  }
907 
908  // Rotation/skew
909  {
910  builder.Save();
911  builder.Rotate(45);
912  DlPaint paint;
913 
914  Matrix impeller_matrix(1, -1, 0, 0, //
915  1, 1, 0, 0, //
916  0, 0, 1, 0, //
917  0, 0, 0, 1);
918  SkMatrix matrix = SkM44::ColMajor(impeller_matrix.m).asM33();
919  paint.setColorSource(std::make_shared<DlImageColorSource>(
920  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
921  DlImageSampling::kNearestNeighbor, &matrix));
922  builder.DrawRect(SkRect::MakeLTRB(100, 0, 200, 100), paint);
923  builder.Restore();
924  }
925 
926  // Scale
927  {
928  builder.Translate(100, 0);
929  builder.Scale(100, 100);
930  DlPaint paint;
931 
932  SkMatrix matrix = SkMatrix::Scale(0.005, 0.005);
933  paint.setColorSource(std::make_shared<DlImageColorSource>(
934  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
935  DlImageSampling::kNearestNeighbor, &matrix));
936 
937  builder.DrawRect(SkRect::MakeLTRB(0, 0, 1, 1), paint);
938  }
939 
940  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
941 }
942 
943 TEST_P(AiksTest, SubpassWithClearColorOptimization) {
944  DisplayListBuilder builder;
945 
946  // Use a non-srcOver blend mode to ensure that we don't detect this as an
947  // opacity peephole optimization.
948  DlPaint paint;
949  paint.setColor(DlColor::kBlue().modulateOpacity(0.5));
950  paint.setBlendMode(DlBlendMode::kSrc);
951 
952  SkRect bounds = SkRect::MakeLTRB(0, 0, 200, 200);
953  builder.SaveLayer(&bounds, &paint);
954 
955  paint.setColor(DlColor::kTransparent());
956  paint.setBlendMode(DlBlendMode::kSrc);
957  builder.DrawPaint(paint);
958  builder.Restore();
959 
960  paint.setColor(DlColor::kBlue());
961  paint.setBlendMode(DlBlendMode::kDstOver);
962  builder.SaveLayer(nullptr, &paint);
963  builder.Restore();
964 
965  // This playground should appear blank on CI since we are only drawing
966  // transparent black. If the clear color optimization is broken, the texture
967  // will be filled with NaNs and may produce a magenta texture on macOS or iOS.
968  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
969 }
970 
971 // Render a white circle at the top left corner of the screen.
972 TEST_P(AiksTest, MatrixImageFilterDoesntCullWhenTranslatedFromOffscreen) {
973  DisplayListBuilder builder;
974  builder.Scale(GetContentScale().x, GetContentScale().y);
975  builder.Translate(100, 100);
976  // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
977  // +300 translation applied by a SaveLayer image filter.
978  DlPaint paint;
979  SkMatrix translate = SkMatrix::Translate(300, 0);
980  paint.setImageFilter(
981  DlMatrixImageFilter::Make(translate, DlImageSampling::kLinear));
982  builder.SaveLayer(nullptr, &paint);
983 
984  DlPaint circle_paint;
985  circle_paint.setColor(DlColor::kGreen());
986  builder.DrawCircle({-300, 0}, 100, circle_paint);
987  builder.Restore();
988 
989  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
990 }
991 
992 // Render a white circle at the top left corner of the screen.
994  MatrixImageFilterDoesntCullWhenScaledAndTranslatedFromOffscreen) {
995  DisplayListBuilder builder;
996  builder.Scale(GetContentScale().x, GetContentScale().y);
997  builder.Translate(100, 100);
998  // Draw a circle in a SaveLayer at -300, but move it back on-screen with a
999  // +300 translation applied by a SaveLayer image filter.
1000 
1001  DlPaint paint;
1002  paint.setImageFilter(DlMatrixImageFilter::Make(
1003  SkMatrix::Translate(300, 0) * SkMatrix::Scale(2, 2),
1004  DlImageSampling::kNearestNeighbor));
1005  builder.SaveLayer(nullptr, &paint);
1006 
1007  DlPaint circle_paint;
1008  circle_paint.setColor(DlColor::kGreen());
1009  builder.DrawCircle({-150, 0}, 50, circle_paint);
1010  builder.Restore();
1011 
1012  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1013 }
1014 
1015 // This should be solid red, if you see a little red box this is broken.
1016 TEST_P(AiksTest, ClearColorOptimizationWhenSubpassIsBiggerThanParentPass) {
1017  SetWindowSize({400, 400});
1018  DisplayListBuilder builder;
1019 
1020  builder.Scale(GetContentScale().x, GetContentScale().y);
1021 
1022  DlPaint paint;
1023  paint.setColor(DlColor::kRed());
1024  builder.DrawRect(SkRect::MakeLTRB(200, 200, 300, 300), paint);
1025 
1026  paint.setImageFilter(DlMatrixImageFilter::Make(SkMatrix::Scale(2, 2),
1027  DlImageSampling::kLinear));
1028  builder.SaveLayer(nullptr, &paint);
1029  // Draw a rectangle that would fully cover the parent pass size, but not
1030  // the subpass that it is rendered in.
1031  paint.setColor(DlColor::kGreen());
1032  builder.DrawRect(SkRect::MakeLTRB(0, 0, 400, 400), paint);
1033  // Draw a bigger rectangle to force the subpass to be bigger.
1034 
1035  paint.setColor(DlColor::kRed());
1036  builder.DrawRect(SkRect::MakeLTRB(0, 0, 800, 800), paint);
1037  builder.Restore();
1038 
1039  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1040 }
1041 
1042 TEST_P(AiksTest, EmptySaveLayerIgnoresPaint) {
1043  DisplayListBuilder builder;
1044  builder.Scale(GetContentScale().x, GetContentScale().y);
1045 
1046  DlPaint paint;
1047  paint.setColor(DlColor::kRed());
1048  builder.DrawPaint(paint);
1049  builder.ClipRect(SkRect::MakeXYWH(100, 100, 200, 200));
1050  paint.setColor(DlColor::kBlue());
1051  builder.SaveLayer(nullptr, &paint);
1052  builder.Restore();
1053 
1054  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1055 }
1056 
1057 TEST_P(AiksTest, EmptySaveLayerRendersWithClear) {
1058  DisplayListBuilder builder;
1059  builder.Scale(GetContentScale().x, GetContentScale().y);
1060  auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
1061  builder.DrawImage(image, {10, 10}, {});
1062  builder.ClipRect(SkRect::MakeXYWH(100, 100, 200, 200));
1063 
1064  DlPaint paint;
1065  paint.setBlendMode(DlBlendMode::kClear);
1066  builder.SaveLayer(nullptr, &paint);
1067  builder.Restore();
1068 
1069  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1070 }
1071 
1073  CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) {
1074  DisplayListBuilder builder;
1075 
1076  DlPaint red;
1077  red.setColor(DlColor::kRed());
1078 
1079  DlPaint green;
1080  green.setColor(DlColor::kGreen());
1081 
1082  DlPaint blue;
1083  blue.setColor(DlColor::kBlue());
1084 
1085  DlPaint save;
1086  save.setColor(DlColor::kBlack().modulateOpacity(0.5));
1087 
1088  SkRect huge_bounds = SkRect::MakeXYWH(0, 0, 100000, 100000);
1089  builder.SaveLayer(&huge_bounds, &save);
1090 
1091  builder.DrawRect(SkRect::MakeXYWH(0, 0, 100, 100), red);
1092  builder.DrawRect(SkRect::MakeXYWH(10, 10, 100, 100), green);
1093  builder.DrawRect(SkRect::MakeXYWH(20, 20, 100, 100), blue);
1094 
1095  builder.Restore();
1096 
1097  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1098 }
1099 
1100 // This makes sure the WideGamut named tests use 16bit float pixel format.
1101 TEST_P(AiksTest, FormatWideGamut) {
1102  EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
1104 }
1105 
1106 TEST_P(AiksTest, FormatSRGB) {
1107  PixelFormat pixel_format =
1108  GetContext()->GetCapabilities()->GetDefaultColorFormat();
1109  EXPECT_TRUE(pixel_format == PixelFormat::kR8G8B8A8UNormInt ||
1110  pixel_format == PixelFormat::kB8G8R8A8UNormInt)
1111  << "pixel format: " << PixelFormatToString(pixel_format);
1112 }
1113 
1114 TEST_P(AiksTest, CoordinateConversionsAreCorrect) {
1115  DisplayListBuilder builder;
1116 
1117  // Render a texture directly.
1118  {
1119  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
1120 
1121  builder.Save();
1122  builder.Translate(100, 200);
1123  builder.Scale(0.5, 0.5);
1124  builder.DrawImage(image, SkPoint::Make(100.0, 100.0),
1125  DlImageSampling::kNearestNeighbor);
1126  builder.Restore();
1127  }
1128 
1129  // Render an offscreen rendered texture.
1130  {
1131  DlPaint alpha;
1132  alpha.setColor(DlColor::kRed().modulateOpacity(0.5));
1133 
1134  builder.SaveLayer(nullptr, &alpha);
1135 
1136  DlPaint paint;
1137  paint.setColor(DlColor::kRed());
1138  builder.DrawRect(SkRect::MakeXYWH(000, 000, 100, 100), paint);
1139  paint.setColor(DlColor::kGreen());
1140  builder.DrawRect(SkRect::MakeXYWH(020, 020, 100, 100), paint);
1141  paint.setColor(DlColor::kBlue());
1142  builder.DrawRect(SkRect::MakeXYWH(040, 040, 100, 100), paint);
1143 
1144  builder.Restore();
1145  }
1146 
1147  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1148 }
1149 
1150 TEST_P(AiksTest, CanPerformFullScreenMSAA) {
1151  DisplayListBuilder builder;
1152 
1153  DlPaint paint;
1154  paint.setColor(DlColor::kRed());
1155  builder.DrawCircle(SkPoint::Make(250, 250), 125, paint);
1156 
1157  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1158 }
1159 
1160 TEST_P(AiksTest, CanPerformSkew) {
1161  DisplayListBuilder builder;
1162 
1163  DlPaint red;
1164  red.setColor(DlColor::kRed());
1165  builder.Skew(2, 5);
1166  builder.DrawRect(SkRect::MakeXYWH(0, 0, 100, 100), red);
1167 
1168  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1169 }
1170 
1171 TEST_P(AiksTest, CanPerformSaveLayerWithBounds) {
1172  DisplayListBuilder builder;
1173 
1174  DlPaint save;
1175  save.setColor(DlColor::kBlack());
1176 
1177  SkRect save_bounds = SkRect::MakeXYWH(0, 0, 50, 50);
1178  builder.SaveLayer(&save_bounds, &save);
1179 
1180  DlPaint paint;
1181  paint.setColor(DlColor::kRed());
1182  builder.DrawRect(SkRect::MakeXYWH(0, 0, 100, 100), paint);
1183  paint.setColor(DlColor::kGreen());
1184  builder.DrawRect(SkRect::MakeXYWH(10, 10, 100, 100), paint);
1185  paint.setColor(DlColor::kBlue());
1186  builder.DrawRect(SkRect::MakeXYWH(20, 20, 100, 100), paint);
1187 
1188  builder.Restore();
1189 
1190  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1191 }
1192 
1193 TEST_P(AiksTest, FilledRoundRectPathsRenderCorrectly) {
1194  DisplayListBuilder builder;
1195  builder.Scale(GetContentScale().x, GetContentScale().y);
1196 
1197  DlPaint paint;
1198  const int color_count = 3;
1199  DlColor colors[color_count] = {
1200  DlColor::kBlue(),
1201  DlColor::kGreen(),
1202  DlColor::ARGB(1.0, 220.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f),
1203  };
1204 
1205  paint.setColor(DlColor::kWhite());
1206  builder.DrawPaint(paint);
1207 
1208  auto draw_rrect_as_path = [&builder](const SkRect& rect, Scalar x, Scalar y,
1209  const DlPaint& paint) {
1210  SkPath path;
1211  path.addRoundRect(rect, x, y);
1212  builder.DrawPath(path, paint);
1213  };
1214 
1215  int c_index = 0;
1216  for (int i = 0; i < 4; i++) {
1217  for (int j = 0; j < 4; j++) {
1218  paint.setColor(colors[(c_index++) % color_count]);
1219  draw_rrect_as_path(SkRect::MakeXYWH(i * 100 + 10, j * 100 + 20, 80, 80),
1220  i * 5 + 10, j * 5 + 10, paint);
1221  }
1222  }
1223  paint.setColor(colors[(c_index++) % color_count]);
1224  draw_rrect_as_path(SkRect::MakeXYWH(10, 420, 380, 80), 40, 40, paint);
1225  paint.setColor(colors[(c_index++) % color_count]);
1226  draw_rrect_as_path(SkRect::MakeXYWH(410, 20, 80, 380), 40, 40, paint);
1227 
1228  std::vector<DlColor> gradient_colors = {
1229  DlColor::RGBA(0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0),
1230  DlColor::RGBA(0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0),
1231  DlColor::RGBA(0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0),
1232  DlColor::RGBA(0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0),
1233  DlColor::RGBA(0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0),
1234  DlColor::RGBA(0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0),
1235  DlColor::RGBA(0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0)};
1236  std::vector<Scalar> stops = {
1237  0.0,
1238  (1.0 / 6.0) * 1,
1239  (1.0 / 6.0) * 2,
1240  (1.0 / 6.0) * 3,
1241  (1.0 / 6.0) * 4,
1242  (1.0 / 6.0) * 5,
1243  1.0,
1244  };
1245  auto texture = DlImageImpeller::Make(
1246  CreateTextureForFixture("airplane.jpg",
1247  /*enable_mipmapping=*/true));
1248 
1249  paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1250  paint.setColorSource(DlColorSource::MakeRadial(
1251  /*center=*/{550, 550},
1252  /*radius=*/75,
1253  /*stop_count=*/gradient_colors.size(),
1254  /*colors=*/gradient_colors.data(),
1255  /*stops=*/stops.data(),
1256  /*tile_mode=*/DlTileMode::kMirror));
1257  for (int i = 1; i <= 10; i++) {
1258  int j = 11 - i;
1259  draw_rrect_as_path(SkRect::MakeLTRB(550 - i * 20, 550 - j * 20, //
1260  550 + i * 20, 550 + j * 20),
1261  i * 10, j * 10, paint);
1262  }
1263  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1264  paint.setColorSource(DlColorSource::MakeRadial(
1265  /*center=*/{200, 650},
1266  /*radius=*/75,
1267  /*stop_count=*/gradient_colors.size(),
1268  /*colors=*/gradient_colors.data(),
1269  /*stops=*/stops.data(),
1270  /*tile_mode=*/DlTileMode::kMirror));
1271  draw_rrect_as_path(SkRect::MakeLTRB(100, 610, 300, 690), 40, 40, paint);
1272  draw_rrect_as_path(SkRect::MakeLTRB(160, 550, 240, 750), 40, 40, paint);
1273 
1274  auto matrix = SkMatrix::Translate(520, 20);
1275  paint.setColor(DlColor::kWhite().modulateOpacity(0.1));
1276  paint.setColorSource(std::make_shared<DlImageColorSource>(
1277  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1278  DlImageSampling::kMipmapLinear, &matrix));
1279  for (int i = 1; i <= 10; i++) {
1280  int j = 11 - i;
1281  draw_rrect_as_path(SkRect::MakeLTRB(720 - i * 20, 220 - j * 20, //
1282  720 + i * 20, 220 + j * 20),
1283  i * 10, j * 10, paint);
1284  }
1285  matrix = SkMatrix::Translate(800, 300);
1286  paint.setColor(DlColor::kWhite().modulateOpacity(0.5));
1287  paint.setColorSource(std::make_shared<DlImageColorSource>(
1288  texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
1289  DlImageSampling::kMipmapLinear, &matrix));
1290 
1291  draw_rrect_as_path(SkRect::MakeLTRB(800, 410, 1000, 490), 40, 40, paint);
1292  draw_rrect_as_path(SkRect::MakeLTRB(860, 350, 940, 550), 40, 40, paint);
1293 
1294  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1295 }
1296 
1297 TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
1298  auto callback = [&]() -> sk_sp<DisplayList> {
1299  DisplayListBuilder builder;
1300  builder.Scale(GetContentScale().x, GetContentScale().y);
1301 
1302  DlPaint alpha;
1303  alpha.setColor(DlColor::kRed().modulateOpacity(0.5));
1304 
1305  auto current = Point{25, 25};
1306  const auto offset = Point{25, 25};
1307  const auto size = Size(100, 100);
1308 
1309  static PlaygroundPoint point_a(Point(40, 40), 10, Color::White());
1310  static PlaygroundPoint point_b(Point(160, 160), 10, Color::White());
1311  auto [b0, b1] = DrawPlaygroundLine(point_a, point_b);
1312  SkRect bounds = SkRect::MakeLTRB(b0.x, b0.y, b1.x, b1.y);
1313 
1314  DlPaint stroke_paint;
1315  stroke_paint.setColor(DlColor::kYellow());
1316  stroke_paint.setStrokeWidth(5);
1317  stroke_paint.setDrawStyle(DlDrawStyle::kStroke);
1318  builder.DrawRect(bounds, stroke_paint);
1319 
1320  builder.SaveLayer(&bounds, &alpha);
1321 
1322  DlPaint paint;
1323  paint.setColor(DlColor::kRed());
1324  builder.DrawRect(
1325  SkRect::MakeXYWH(current.x, current.y, size.width, size.height), paint);
1326 
1327  paint.setColor(DlColor::kGreen());
1328  current += offset;
1329  builder.DrawRect(
1330  SkRect::MakeXYWH(current.x, current.y, size.width, size.height), paint);
1331 
1332  paint.setColor(DlColor::kBlue());
1333  current += offset;
1334  builder.DrawRect(
1335  SkRect::MakeXYWH(current.x, current.y, size.width, size.height), paint);
1336 
1337  builder.Restore();
1338 
1339  return builder.Build();
1340  };
1341 
1342  ASSERT_TRUE(OpenPlaygroundHere(callback));
1343 }
1344 
1345 TEST_P(AiksTest, SaveLayerDrawsBehindSubsequentEntities) {
1346  // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636
1347  DisplayListBuilder builder;
1348  DlPaint paint;
1349 
1350  paint.setColor(DlColor::kBlack());
1351  SkRect rect = SkRect::MakeXYWH(25, 25, 25, 25);
1352  builder.DrawRect(rect, paint);
1353 
1354  builder.Translate(10, 10);
1355 
1356  DlPaint save_paint;
1357  builder.SaveLayer(nullptr, &save_paint);
1358 
1359  paint.setColor(DlColor::kGreen());
1360  builder.DrawRect(rect, paint);
1361 
1362  builder.Restore();
1363 
1364  builder.Translate(10, 10);
1365  paint.setColor(DlColor::kRed());
1366  builder.DrawRect(rect, paint);
1367 
1368  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1369 }
1370 
1371 TEST_P(AiksTest, SiblingSaveLayerBoundsAreRespected) {
1372  DisplayListBuilder builder;
1373  DlPaint paint;
1374  SkRect rect = SkRect::MakeXYWH(0, 0, 1000, 1000);
1375 
1376  // Black, green, and red squares offset by [10, 10].
1377  {
1378  DlPaint save_paint;
1379  SkRect bounds = SkRect::MakeXYWH(25, 25, 25, 25);
1380  builder.SaveLayer(&bounds, &save_paint);
1381  paint.setColor(DlColor::kBlack());
1382  builder.DrawRect(rect, paint);
1383  builder.Restore();
1384  }
1385 
1386  {
1387  DlPaint save_paint;
1388  SkRect bounds = SkRect::MakeXYWH(35, 35, 25, 25);
1389  builder.SaveLayer(&bounds, &save_paint);
1390  paint.setColor(DlColor::kGreen());
1391  builder.DrawRect(rect, paint);
1392  builder.Restore();
1393  }
1394 
1395  {
1396  DlPaint save_paint;
1397  SkRect bounds = SkRect::MakeXYWH(45, 45, 25, 25);
1398  builder.SaveLayer(&bounds, &save_paint);
1399  paint.setColor(DlColor::kRed());
1400  builder.DrawRect(rect, paint);
1401  builder.Restore();
1402  }
1403 
1404  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1405 }
1406 
1407 TEST_P(AiksTest, CanRenderClippedLayers) {
1408  DisplayListBuilder builder;
1409 
1410  DlPaint paint;
1411  paint.setColor(DlColor::kWhite());
1412  builder.DrawPaint(paint);
1413 
1414  // Draw a green circle on the screen.
1415  {
1416  // Increase the clip depth for the savelayer to contend with.
1417  SkPath path = SkPath::Circle(100, 100, 50);
1418  builder.ClipPath(path);
1419 
1420  SkRect bounds = SkRect::MakeXYWH(50, 50, 100, 100);
1421  DlPaint save_paint;
1422  builder.SaveLayer(&bounds, &save_paint);
1423 
1424  // Fill the layer with white.
1425  paint.setColor(DlColor::kWhite());
1426  builder.DrawRect(SkRect::MakeSize(SkSize{400, 400}), paint);
1427  // Fill the layer with green, but do so with a color blend that can't be
1428  // collapsed into the parent pass.
1429  paint.setColor(DlColor::kGreen());
1430  paint.setBlendMode(DlBlendMode::kHardLight);
1431  builder.DrawRect(SkRect::MakeSize(SkSize{400, 400}), paint);
1432  }
1433 
1434  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1435 }
1436 
1437 TEST_P(AiksTest, SaveLayerFiltersScaleWithTransform) {
1438  DisplayListBuilder builder;
1439 
1440  builder.Scale(GetContentScale().x, GetContentScale().y);
1441  builder.Translate(100, 100);
1442 
1443  auto texture = DlImageImpeller::Make(CreateTextureForFixture("boston.jpg"));
1444  auto draw_image_layer = [&builder, &texture](const DlPaint& paint) {
1445  builder.SaveLayer(nullptr, &paint);
1446  builder.DrawImage(texture, {}, DlImageSampling::kLinear);
1447  builder.Restore();
1448  };
1449 
1450  DlPaint effect_paint;
1451  effect_paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 6));
1452  draw_image_layer(effect_paint);
1453 
1454  builder.Translate(300, 300);
1455  builder.Scale(3, 3);
1456  draw_image_layer(effect_paint);
1457 
1458  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1459 }
1460 
1461 TEST_P(AiksTest, FastEllipticalRRectMaskBlursRenderCorrectly) {
1462  DisplayListBuilder builder;
1463 
1464  builder.Scale(GetContentScale().x, GetContentScale().y);
1465  DlPaint paint;
1466  paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 1));
1467 
1468  DlPaint save_paint;
1469  save_paint.setColor(DlColor::kWhite());
1470  builder.DrawPaint(save_paint);
1471 
1472  paint.setColor(DlColor::kBlue());
1473  for (int i = 0; i < 5; i++) {
1474  Scalar y = i * 125;
1475  Scalar y_radius = i * 15;
1476  for (int j = 0; j < 5; j++) {
1477  Scalar x = j * 125;
1478  Scalar x_radius = j * 15;
1479  builder.DrawRRect(
1480  SkRRect::MakeRectXY(SkRect::MakeXYWH(x + 50, y + 50, 100.0f, 100.0f),
1481  x_radius, y_radius),
1482  paint);
1483  }
1484  }
1485 
1486  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1487 }
1488 
1489 TEST_P(AiksTest, PipelineBlendSingleParameter) {
1490  DisplayListBuilder builder;
1491 
1492  // Should render a green square in the middle of a blue circle.
1493  DlPaint paint;
1494  builder.SaveLayer(nullptr, &paint);
1495  {
1496  builder.Translate(100, 100);
1497  paint.setColor(DlColor::kBlue());
1498  builder.DrawCircle(SkPoint::Make(200, 200), 200, paint);
1499  builder.ClipRect(SkRect::MakeXYWH(100, 100, 200, 200));
1500 
1501  paint.setColor(DlColor::kGreen());
1502  paint.setBlendMode(DlBlendMode::kSrcOver);
1503  paint.setImageFilter(DlColorFilterImageFilter::Make(
1504  DlBlendColorFilter::Make(DlColor::kWhite(), DlBlendMode::kDst)));
1505  builder.DrawCircle(SkPoint::Make(200, 200), 200, paint);
1506  builder.Restore();
1507  }
1508 
1509  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1510 }
1511 
1512 // Creates an image matrix filter that scales large content such that it would
1513 // exceed the max texture size. See
1514 // https://github.com/flutter/flutter/issues/128912
1515 TEST_P(AiksTest, MassiveScalingMatrixImageFilter) {
1516  if (GetBackend() == PlaygroundBackend::kVulkan) {
1517  GTEST_SKIP() << "Swiftshader is running out of memory on this example.";
1518  }
1519  DisplayListBuilder builder(SkRect::MakeSize(SkSize::Make(1000, 1000)));
1520 
1521  auto filter = DlMatrixImageFilter::Make(SkMatrix::Scale(0.001, 0.001),
1522  DlImageSampling::kLinear);
1523 
1524  DlPaint paint;
1525  paint.setImageFilter(filter);
1526  builder.SaveLayer(nullptr, &paint);
1527  {
1528  DlPaint paint;
1529  paint.setColor(DlColor::kRed());
1530  builder.DrawRect(SkRect::MakeLTRB(0, 0, 100000, 100000), paint);
1531  }
1532  builder.Restore();
1533 
1534  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1535 }
1536 
1537 } // namespace testing
1538 } // namespace impeller
impeller::Matrix::m
Scalar m[16]
Definition: matrix.h:39
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::PlaygroundBackend::kVulkan
@ kVulkan
impeller::PixelFormatToString
constexpr const char * PixelFormatToString(PixelFormat format)
Definition: formats.h:140
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::PixelFormat::kB10G10R10A10XR
@ kB10G10R10A10XR
aiks_unittests.h
impeller::testing::AiksTest
AiksPlayground AiksTest
Definition: aiks_unittests.h:17
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:198
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
stroke_width
const Scalar stroke_width
Definition: stroke_path_geometry.cc:297
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:304
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:23
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
widgets.h
impeller::Color::White
static constexpr Color White()
Definition: color.h:263
impeller::Radians
Definition: scalar.h:43
flutter
Definition: dl_golden_blur_unittests.cc:15
impeller::Matrix::Transform
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:513
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:78
scalar.h
impeller::PlaygroundPoint
Definition: widgets.h:17
impeller::TPoint< Scalar >
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller::interop::ToSkMatrix
constexpr SkMatrix ToSkMatrix(const Matrix &matrix)
Definition: formats.h:81
impeller::DlScalar
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:21
impeller
Definition: allocation.cc:12
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::DrawPlaygroundLine
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition: widgets.cc:50
dl_image_impeller.h