| 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"
12 #include "flutter/impeller/display_list/aiks_unittests.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/impeller/display_list/dl_image_impeller.h"
19 #include "flutter/impeller/geometry/scalar.h"
20 #include "flutter/testing/display_list_testing.h"
21 #include "flutter/testing/testing.h"
22 #include "impeller/playground/widgets.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.
993 TEST_P(AiksTest,
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
1072 TEST_P(AiksTest,
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(),
1103 PixelFormat::kB10G10R10A10XR);
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