Flutter Engine
 
Loading...
Searching...
No Matches
aiks_dl_blend_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 <memory>
6
14
26
27////////////////////////////////////////////////////////////////////////////////
28// This is for tests of Canvas that are interested the results of rendering
29// blends.
30////////////////////////////////////////////////////////////////////////////////
31
32namespace impeller {
33namespace testing {
34
35using namespace flutter;
36
37#define BLEND_MODE_TUPLE(blend_mode) {#blend_mode, BlendMode::k##blend_mode},
38
40 std::vector<const char*> blend_mode_names;
41 std::vector<BlendMode> blend_mode_values;
42};
43
45 std::vector<const char*> blend_mode_names;
46 std::vector<BlendMode> blend_mode_values;
47 {
48 const std::vector<std::tuple<const char*, BlendMode>> blends = {
50 assert(blends.size() ==
51 static_cast<size_t>(Entity::kLastAdvancedBlendMode) + 1);
52 for (const auto& [name, mode] : blends) {
53 blend_mode_names.push_back(name);
54 blend_mode_values.push_back(mode);
55 }
56 }
57
58 return {blend_mode_names, blend_mode_values};
59}
60
61TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer) {
62 DisplayListBuilder builder;
63
64 DlRect layer_rect = DlRect::MakeXYWH(0, 0, 500, 500);
65 builder.ClipRect(layer_rect);
66
67 DlPaint save_paint;
69 DlColor::RGBA(0, 1, 0, 0.5), DlBlendMode::kDifference));
70 builder.SaveLayer(layer_rect, &save_paint);
71
72 DlPaint paint;
74 builder.DrawPaint(paint);
76 builder.DrawRect(DlRect::MakeXYWH(100, 100, 300, 300), paint);
77 builder.Restore();
78
79 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
80}
81
82TEST_P(AiksTest, BlendModeShouldCoverWholeScreen) {
83 DisplayListBuilder builder;
84 DlPaint paint;
85
86 paint.setColor(DlColor::kRed());
87 builder.DrawPaint(paint);
88
89 paint.setBlendMode(DlBlendMode::kSrcOver);
90 builder.SaveLayer(std::nullopt, &paint);
91
93 builder.DrawRect(DlRect::MakeXYWH(100, 100, 400, 400), paint);
94
95 paint.setBlendMode(DlBlendMode::kSrc);
96 builder.SaveLayer(std::nullopt, &paint);
97
98 paint.setColor(DlColor::kBlue());
99 builder.DrawRect(DlRect::MakeXYWH(200, 200, 200, 200), paint);
100
101 builder.Restore();
102 builder.Restore();
103
104 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
105}
106
107TEST_P(AiksTest, CanDrawPaintWithAdvancedBlend) {
108 DisplayListBuilder builder;
109
110 builder.Scale(0.2, 0.2);
111 DlPaint paint;
115 builder.DrawPaint(paint);
116
118 Color::OrangeRed().blue, 0.5));
119 paint.setBlendMode(DlBlendMode::kHue);
120 builder.DrawPaint(paint);
121
122 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
123}
124
125TEST_P(AiksTest, DrawPaintWithAdvancedBlendOverFilter) {
126 DlPaint paint;
127 paint.setColor(DlColor::kBlack());
128 paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 60));
129
130 DisplayListBuilder builder;
131 paint.setColor(DlColor::kWhite());
132 builder.DrawPaint(paint);
133 paint.setColor(DlColor::kBlack());
134 builder.DrawCircle(DlPoint(300, 300), 200, paint);
135 paint.setColor(DlColor::kGreen());
136 paint.setBlendMode(DlBlendMode::kScreen);
137 builder.DrawPaint(paint);
138
139 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
140}
141
142TEST_P(AiksTest, DrawAdvancedBlendPartlyOffscreen) {
143 DisplayListBuilder builder;
144
145 DlPaint draw_paint;
146 draw_paint.setColor(DlColor::kBlue());
147 builder.DrawPaint(draw_paint);
148 builder.Scale(2, 2);
149 builder.ClipRect(DlRect::MakeLTRB(0, 0, 200, 200));
150
151 std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
152 DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0)};
153 std::vector<Scalar> stops = {0.0, 1.0};
154
155 DlPaint paint;
156 DlMatrix matrix = DlMatrix::MakeScale({0.3, 0.3, 1.0});
158 /*start_point=*/{0, 0}, //
159 /*end_point=*/{100, 100}, //
160 /*stop_count=*/colors.size(), //
161 /*colors=*/colors.data(), //
162 /*stops=*/stops.data(), //
163 /*tile_mode=*/DlTileMode::kRepeat, //
164 /*matrix=*/&matrix //
165 ));
166 paint.setBlendMode(DlBlendMode::kLighten);
167
168 builder.DrawCircle(DlPoint(100, 100), 100, paint);
169 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
170}
171
172TEST_P(AiksTest, PaintBlendModeIsRespected) {
173 DlPaint paint;
174 DisplayListBuilder builder;
175 // Default is kSourceOver.
176
177 paint.setColor(DlColor::RGBA(1, 0, 0, 0.5));
178 builder.DrawCircle(DlPoint(150, 200), 100, paint);
179
180 paint.setColor(DlColor::RGBA(0, 1, 0, 0.5));
181 builder.DrawCircle(DlPoint(250, 200), 100, paint);
182
183 paint.setBlendMode(DlBlendMode::kPlus);
184
185 paint.setColor(DlColor::kRed());
186 builder.DrawCircle(DlPoint(450, 250), 100, paint);
187
188 paint.setColor(DlColor::kGreen());
189 builder.DrawCircle(DlPoint(550, 250), 100, paint);
190
191 paint.setColor(DlColor::kBlue());
192 builder.DrawCircle(DlPoint(500, 150), 100, paint);
193
194 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
195}
196
197// Compare results with https://api.flutter.dev/flutter/dart-ui/BlendMode.html
198TEST_P(AiksTest, ColorFilterBlend) {
199 bool has_color_filter = true;
200 auto callback = [&]() -> sk_sp<DisplayList> {
201 if (AiksTest::ImGuiBegin("Controls", nullptr,
202 ImGuiWindowFlags_AlwaysAutoResize)) {
203 ImGui::Checkbox("has color filter", &has_color_filter);
204 ImGui::End();
205 }
206
207 DisplayListBuilder builder;
208 builder.Scale(GetContentScale().x, GetContentScale().y);
209
210 auto src_image =
211 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_src.png"));
212 auto dst_image =
213 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png"));
214
215 std::vector<DlBlendMode> blend_modes = {
216 DlBlendMode::kSrc, DlBlendMode::kSrcATop, DlBlendMode::kSrcOver,
217 DlBlendMode::kSrcIn, DlBlendMode::kSrcOut, DlBlendMode::kDst,
218 DlBlendMode::kDstATop, DlBlendMode::kDstOver, DlBlendMode::kDstIn,
219 DlBlendMode::kDstOut, DlBlendMode::kClear, DlBlendMode::kXor};
220
221 for (uint32_t i = 0; i < blend_modes.size(); ++i) {
222 builder.Save();
223 builder.Translate((i % 5) * 200, (i / 5) * 200);
224 builder.Scale(0.4, 0.4);
225 {
226 DlPaint dstPaint;
227 builder.DrawImage(dst_image, DlPoint(0, 0),
228 DlImageSampling::kMipmapLinear, &dstPaint);
229 }
230 {
231 DlPaint srcPaint;
232 srcPaint.setBlendMode(blend_modes[i]);
233 if (has_color_filter) {
234 std::shared_ptr<const DlColorFilter> color_filter =
235 DlColorFilter::MakeBlend(DlColor::RGBA(0.9, 0.5, 0.0, 1.0),
236 DlBlendMode::kSrcIn);
237 srcPaint.setColorFilter(color_filter);
238 }
239 builder.DrawImage(src_image, DlPoint(0, 0),
240 DlImageSampling::kMipmapLinear, &srcPaint);
241 }
242 builder.Restore();
243 }
244 return builder.Build();
245 };
246 ASSERT_TRUE(OpenPlaygroundHere(callback));
247}
248
249// Verification for: https://github.com/flutter/flutter/issues/155691
250TEST_P(AiksTest, ColorFilterAdvancedBlend) {
251 bool has_color_filter = true;
252 auto callback = [&]() -> sk_sp<DisplayList> {
253 if (AiksTest::ImGuiBegin("Controls", nullptr,
254 ImGuiWindowFlags_AlwaysAutoResize)) {
255 ImGui::Checkbox("has color filter", &has_color_filter);
256 ImGui::End();
257 }
258
259 DisplayListBuilder builder;
260 builder.Scale(GetContentScale().x, GetContentScale().y);
261
262 auto src_image =
263 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_src.png"));
264 auto dst_image =
265 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png"));
266
267 std::vector<DlBlendMode> blend_modes = {
268 DlBlendMode::kScreen, DlBlendMode::kOverlay,
269 DlBlendMode::kDarken, DlBlendMode::kLighten,
270 DlBlendMode::kColorDodge, DlBlendMode::kColorBurn,
271 DlBlendMode::kHardLight, DlBlendMode::kSoftLight,
272 DlBlendMode::kDifference, DlBlendMode::kExclusion,
273 DlBlendMode::kMultiply, DlBlendMode::kHue,
274 DlBlendMode::kSaturation, DlBlendMode::kColor,
275 DlBlendMode::kLuminosity,
276 };
277
278 for (uint32_t i = 0; i < blend_modes.size(); ++i) {
279 builder.Save();
280 builder.Translate((i % 5) * 200, (i / 5) * 200);
281 builder.Scale(0.4, 0.4);
282 {
283 DlPaint dstPaint;
284 builder.DrawImage(dst_image, DlPoint(0, 0),
285 DlImageSampling::kMipmapLinear, &dstPaint);
286 }
287 {
288 DlPaint srcPaint;
289 srcPaint.setBlendMode(blend_modes[i]);
290 if (has_color_filter) {
291 std::shared_ptr<const DlColorFilter> color_filter =
292 DlColorFilter::MakeBlend(DlColor::RGBA(0.9, 0.5, 0.0, 1.0),
293 DlBlendMode::kSrcIn);
294 srcPaint.setColorFilter(color_filter);
295 }
296 builder.DrawImage(src_image, DlPoint(0, 0),
297 DlImageSampling::kMipmapLinear, &srcPaint);
298 }
299 builder.Restore();
300 }
301 return builder.Build();
302 };
303 ASSERT_TRUE(OpenPlaygroundHere(callback));
304}
305
306// Variant of the https://github.com/flutter/flutter/issues/155691 test that
307// uses an advanced blend in the color filter and disables framebuffer fetch
308// to force usage of BlendFilterContents::CreateForegroundAdvancedBlend.
309TEST_P(AiksTest, ColorFilterAdvancedBlendNoFbFetch) {
310 if (GetParam() != PlaygroundBackend::kMetal) {
311 GTEST_SKIP()
312 << "This backend doesn't yet support setting device capabilities.";
313 }
314 if (!WillRenderSomething()) {
315 GTEST_SKIP() << "This test requires playgrounds.";
316 }
317
318 std::shared_ptr<const Capabilities> old_capabilities =
319 GetContext()->GetCapabilities();
320 auto mock_capabilities = std::make_shared<MockCapabilities>();
321 EXPECT_CALL(*mock_capabilities, SupportsFramebufferFetch())
322 .Times(::testing::AtLeast(1))
323 .WillRepeatedly(::testing::Return(false));
324 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultColorFormat);
325 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultStencilFormat);
326 FLT_FORWARD(mock_capabilities, old_capabilities,
327 GetDefaultDepthStencilFormat);
328 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsOffscreenMSAA);
329 FLT_FORWARD(mock_capabilities, old_capabilities,
330 SupportsImplicitResolvingMSAA);
331 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsReadFromResolve);
332 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsSSBO);
333 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsCompute);
334 FLT_FORWARD(mock_capabilities, old_capabilities,
335 SupportsTextureToTextureBlits);
336 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultGlyphAtlasFormat);
337 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsTriangleFan);
338 FLT_FORWARD(mock_capabilities, old_capabilities,
339 SupportsDecalSamplerAddressMode);
340 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsPrimitiveRestart);
341 FLT_FORWARD(mock_capabilities, old_capabilities, GetMinimumUniformAlignment);
342 ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());
343
344 bool has_color_filter = true;
345 auto callback = [&]() -> sk_sp<DisplayList> {
346 if (AiksTest::ImGuiBegin("Controls", nullptr,
347 ImGuiWindowFlags_AlwaysAutoResize)) {
348 ImGui::Checkbox("has color filter", &has_color_filter);
349 ImGui::End();
350 }
351
352 DisplayListBuilder builder;
353 builder.Scale(GetContentScale().x, GetContentScale().y);
354
355 auto src_image =
356 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_src.png"));
357 auto dst_image =
358 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png"));
359
360 std::vector<DlBlendMode> blend_modes = {
361 DlBlendMode::kScreen, DlBlendMode::kOverlay,
362 DlBlendMode::kDarken, DlBlendMode::kLighten,
363 DlBlendMode::kColorDodge, DlBlendMode::kColorBurn,
364 DlBlendMode::kHardLight, DlBlendMode::kSoftLight,
365 DlBlendMode::kDifference, DlBlendMode::kExclusion,
366 DlBlendMode::kMultiply, DlBlendMode::kHue,
367 DlBlendMode::kSaturation, DlBlendMode::kColor,
368 DlBlendMode::kLuminosity,
369 };
370
371 for (uint32_t i = 0; i < blend_modes.size(); ++i) {
372 builder.Save();
373 builder.Translate((i % 5) * 200, (i / 5) * 200);
374 builder.Scale(0.4, 0.4);
375 {
376 DlPaint dstPaint;
377 builder.DrawImage(dst_image, DlPoint(0, 0),
378 DlImageSampling::kMipmapLinear, &dstPaint);
379 }
380 {
381 DlPaint srcPaint;
382 srcPaint.setBlendMode(blend_modes[i]);
383 if (has_color_filter) {
384 std::shared_ptr<const DlColorFilter> color_filter =
385 DlColorFilter::MakeBlend(DlColor::RGBA(0.9, 0.5, 0.0, 1.0),
386 DlBlendMode::kMultiply);
387 srcPaint.setColorFilter(color_filter);
388 }
389 builder.DrawImage(src_image, DlPoint(0, 0),
390 DlImageSampling::kMipmapLinear, &srcPaint);
391 }
392 builder.Restore();
393 }
394 return builder.Build();
395 };
396 ASSERT_TRUE(OpenPlaygroundHere(callback));
397}
398
399// Bug: https://github.com/flutter/flutter/issues/142549
400TEST_P(AiksTest, BlendModePlusAlphaWideGamut) {
401 EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
403 auto texture = CreateTextureForFixture("airplane.jpg",
404 /*enable_mipmapping=*/true);
405
406 DisplayListBuilder builder;
407 DlPaint paint;
408 builder.Scale(GetContentScale().x, GetContentScale().y);
409
410 paint.setColor(DlColor::RGBA(0.9, 1, 0.9, 1.0));
411 builder.DrawPaint(paint);
412 builder.SaveLayer(std::nullopt);
413
414 paint.setBlendMode(DlBlendMode::kPlus);
415 paint.setColor(DlColor::kRed());
416
417 builder.DrawRect(DlRect::MakeXYWH(100, 100, 400, 400), paint);
418 paint.setColor(DlColor::kWhite());
419
420 auto rect = Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100);
421 builder.DrawImageRect(
423 DlRect::MakeWH(texture->GetSize().width, texture->GetSize().height),
424 DlRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), //
425 rect.GetRight(), rect.GetBottom()),
426 DlImageSampling::kMipmapLinear, &paint);
427 builder.Restore();
428 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
429}
430
431// Bug: https://github.com/flutter/flutter/issues/142549
432TEST_P(AiksTest, BlendModePlusAlphaColorFilterWideGamut) {
433 EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
435 auto texture = CreateTextureForFixture("airplane.jpg",
436 /*enable_mipmapping=*/true);
437
438 DisplayListBuilder builder;
439 builder.Scale(GetContentScale().x, GetContentScale().y);
440
441 DlPaint paint;
442 paint.setColor(DlColor::RGBA(0.1, 0.2, 0.1, 1.0));
443 builder.DrawPaint(paint);
444
445 DlPaint save_paint;
446 save_paint.setColorFilter(
447 DlColorFilter::MakeBlend(DlColor::RGBA(1, 0, 0, 1), DlBlendMode::kPlus));
448 builder.SaveLayer(std::nullopt, &save_paint);
449
450 paint.setColor(DlColor::kRed());
451 builder.DrawRect(DlRect::MakeXYWH(100, 100, 400, 400), paint);
452
453 paint.setColor(DlColor::kWhite());
454
455 auto rect = Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100);
456 builder.DrawImageRect(
458 DlRect::MakeWH(texture->GetSize().width, texture->GetSize().height),
459 DlRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), //
460 rect.GetRight(), rect.GetBottom()),
461 DlImageSampling::kMipmapLinear, &paint);
462 builder.Restore();
463
464 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
465}
466
467TEST_P(AiksTest, ForegroundBlendSubpassCollapseOptimization) {
468 DisplayListBuilder builder;
469
470 DlPaint save_paint;
471 save_paint.setColorFilter(
472 DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kColorDodge));
473 builder.SaveLayer(std::nullopt, &save_paint);
474
475 builder.Translate(500, 300);
476 builder.Rotate(120);
477
478 DlPaint paint;
479 paint.setColor(DlColor::kBlue());
480 builder.DrawRect(DlRect::MakeXYWH(100, 100, 200, 200), paint);
481
482 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
483}
484
485TEST_P(AiksTest, ClearBlend) {
486 DisplayListBuilder builder;
487
488 DlPaint blue;
489 blue.setColor(DlColor::kBlue());
490 builder.DrawRect(DlRect::MakeXYWH(0, 0, 600.0, 600.0), blue);
491
492 DlPaint clear;
493 clear.setBlendMode(DlBlendMode::kClear);
494
495 builder.DrawCircle(DlPoint(300.0, 300.0), 200.0, clear);
496
497 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
498}
499
500static sk_sp<DisplayList> BlendModeTest(Vector2 content_scale,
501 BlendMode blend_mode,
502 const sk_sp<DlImageImpeller>& src_image,
503 const sk_sp<DlImageImpeller>& dst_image,
504 Scalar src_alpha) {
505 if (AiksTest::ImGuiBegin("Controls", nullptr,
506 ImGuiWindowFlags_AlwaysAutoResize)) {
507 ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
508 ImGui::End();
509 }
510
511 Color destination_color = Color::CornflowerBlue().WithAlpha(0.75);
512 auto source_colors = std::vector<Color>({Color::White().WithAlpha(0.75),
514 Color::Black().WithAlpha(0.75)});
515
516 DisplayListBuilder builder;
517 {
518 DlPaint paint;
519 paint.setColor(DlColor::kBlack());
520 builder.DrawPaint(paint);
521 }
522 // TODO(bdero): Why does this cause the left image to double scale on high DPI
523 // displays.
524 // builder.Scale(content_scale);
525
526 //----------------------------------------------------------------------------
527 /// 1. Save layer blending (top squares).
528 ///
529
530 builder.Save();
531 for (const auto& color : source_colors) {
532 builder.Save();
533 {
534 builder.ClipRect(DlRect::MakeXYWH(25, 25, 100, 100));
535 // Perform the blend in a SaveLayer so that the initial backdrop color is
536 // fully transparent black. SourceOver blend the result onto the parent
537 // pass.
538 builder.SaveLayer(std::nullopt);
539 {
540 DlPaint draw_paint;
541 draw_paint.setColor(
542 DlColor::RGBA(destination_color.red, destination_color.green,
543 destination_color.blue, destination_color.alpha));
544 builder.DrawPaint(draw_paint);
545
546 // Draw the source color in an offscreen pass and blend it to the parent
547 // pass.
548 DlPaint save_paint;
549 save_paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
550 builder.SaveLayer(std::nullopt, &save_paint);
551 { //
552 DlPaint paint;
553 paint.setColor(
554 DlColor::RGBA(color.red, color.green, color.blue, color.alpha));
555 builder.DrawRect(DlRect::MakeXYWH(25, 25, 100, 100), paint);
556 }
557 builder.Restore();
558 }
559 builder.Restore();
560 }
561 builder.Restore();
562 builder.Translate(100, 0);
563 }
564 builder.RestoreToCount(0);
565
566 //----------------------------------------------------------------------------
567 /// 2. CPU blend modes (bottom squares).
568 ///
569
570 builder.Save();
571 builder.Translate(0, 100);
572 // Perform the blend in a SaveLayer so that the initial backdrop color is
573 // fully transparent black. SourceOver blend the result onto the parent pass.
574 builder.SaveLayer(std::nullopt);
575 for (const auto& color : source_colors) {
576 // Simply write the CPU blended color to the pass.
577 DlPaint paint;
578 auto dest = destination_color.Blend(color, blend_mode);
579 paint.setColor(DlColor::RGBA(dest.red, dest.green, dest.blue, dest.alpha));
580 paint.setBlendMode(DlBlendMode::kSrcOver);
581 builder.DrawRect(DlRect::MakeXYWH(25, 25, 100, 100), paint);
582 builder.Translate(100, 0);
583 }
584 builder.Restore();
585 builder.Restore();
586
587 //----------------------------------------------------------------------------
588 /// 3. Image blending (bottom images).
589 ///
590 /// Compare these results with the images in the Flutter blend mode
591 /// documentation: https://api.flutter.dev/flutter/dart-ui/BlendMode.html
592 ///
593
594 builder.Translate(0, 250);
595
596 // Draw grid behind the images.
597 {
598 DlPaint paint;
599 paint.setColor(DlColor::RGBA(41 / 255.0, 41 / 255.0, 41 / 255.0, 1));
600 builder.DrawRect(DlRect::MakeLTRB(0, 0, 800, 400), paint);
601 }
602
603 DlPaint square_paint;
604 square_paint.setColor(DlColor::RGBA(15 / 255.0, 15 / 255.0, 15 / 255.0, 1));
605 for (int y = 0; y < 400 / 8; y++) {
606 for (int x = 0; x < 800 / 16; x++) {
607 builder.DrawRect(DlRect::MakeXYWH(x * 16 + (y % 2) * 8, y * 8, 8, 8),
608 square_paint);
609 }
610 }
611
612 // Uploaded image source (left image).
613 DlPaint paint;
614 paint.setBlendMode(DlBlendMode::kSrcOver);
615 builder.Save();
616 builder.SaveLayer(std::nullopt, &paint);
617 {
618 builder.DrawImage(dst_image, DlPoint(0, 0), DlImageSampling::kMipmapLinear,
619 &paint);
620
621 paint.setColor(DlColor::kWhite().withAlpha(src_alpha * 255));
622 paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
623 builder.DrawImage(src_image, DlPoint(0, 0), DlImageSampling::kMipmapLinear,
624 &paint);
625 }
626 builder.Restore();
627 builder.Restore();
628
629 // Rendered image source (right image).
630 builder.Save();
631
632 DlPaint save_paint;
633 builder.SaveLayer(std::nullopt, &save_paint);
634 {
635 builder.DrawImage(dst_image, DlPoint(400, 0),
636 DlImageSampling::kMipmapLinear, nullptr);
637
638 DlPaint save_paint;
639 save_paint.setColor(DlColor::kWhite().withAlpha(src_alpha * 255));
640 save_paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
641 builder.SaveLayer(std::nullopt, &save_paint);
642 {
643 builder.DrawImage(src_image, DlPoint(400, 0),
644 DlImageSampling::kMipmapLinear, nullptr);
645 }
646 builder.Restore();
647 }
648 builder.Restore();
649 builder.Restore();
650
651 return builder.Build();
652}
653
654#define BLEND_MODE_TEST(blend_mode) \
655 TEST_P(AiksTest, BlendMode##blend_mode) { \
656 auto src_image = \
657 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_src.png")); \
658 auto dst_image = \
659 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png")); \
660 auto callback = [&]() -> sk_sp<DisplayList> { \
661 return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
662 src_image, dst_image, /*src_alpha=*/1.0); \
663 }; \
664 OpenPlaygroundHere(callback); \
665 }
667
668#define BLEND_MODE_SRC_ALPHA_TEST(blend_mode) \
669 TEST_P(AiksTest, BlendModeSrcAlpha##blend_mode) { \
670 auto src_image = \
671 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_src.png")); \
672 auto dst_image = \
673 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png")); \
674 auto callback = [&]() -> sk_sp<DisplayList> { \
675 return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
676 src_image, dst_image, /*src_alpha=*/0.5); \
677 }; \
678 OpenPlaygroundHere(callback); \
679 }
681
682TEST_P(AiksTest, CanDrawPaintMultipleTimesInteractive) {
683 auto modes = GetBlendModeSelection();
684
685 auto callback = [&]() -> sk_sp<DisplayList> {
686 static Color background = Color::MediumTurquoise();
687 static Color foreground = Color::Color::OrangeRed().WithAlpha(0.5);
688 static int current_blend_index = 3;
689
690 if (AiksTest::ImGuiBegin("Controls", nullptr,
691 ImGuiWindowFlags_AlwaysAutoResize)) {
692 ImGui::ColorEdit4("Background", reinterpret_cast<float*>(&background));
693 ImGui::ColorEdit4("Foreground", reinterpret_cast<float*>(&foreground));
694 ImGui::ListBox("Blend mode", &current_blend_index,
695 modes.blend_mode_names.data(),
696 modes.blend_mode_names.size());
697 ImGui::End();
698 }
699
700 DisplayListBuilder builder;
701 builder.Scale(0.2, 0.2);
702 DlPaint paint;
703 paint.setColor(DlColor(background.ToARGB()));
704 builder.DrawPaint(paint);
705
706 paint.setColor(DlColor(foreground.ToARGB()));
707 paint.setBlendMode(static_cast<DlBlendMode>(current_blend_index));
708 builder.DrawPaint(paint);
709 return builder.Build();
710 };
711 ASSERT_TRUE(OpenPlaygroundHere(callback));
712}
713
714TEST_P(AiksTest, ForegroundPipelineBlendAppliesTransformCorrectly) {
715 auto texture = CreateTextureForFixture("airplane.jpg",
716 /*enable_mipmapping=*/true);
717
718 DisplayListBuilder builder;
719 builder.Rotate(30);
720
721 DlPaint image_paint;
723 DlColor::RGBA(255.0f / 255.0f, 165.0f / 255.0f, 0.0f / 255.0f, 1.0f),
724 DlBlendMode::kSrcIn));
725
727 DlImageSampling::kMipmapLinear, &image_paint);
728
729 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
730}
731
732TEST_P(AiksTest, ForegroundAdvancedBlendAppliesTransformCorrectly) {
733 auto texture = CreateTextureForFixture("airplane.jpg",
734 /*enable_mipmapping=*/true);
735
736 DisplayListBuilder builder;
737 builder.Rotate(30);
738
739 DlPaint image_paint;
741 DlColor::RGBA(255.0f / 255.0f, 165.0f / 255.0f, 0.0f / 255.0f, 1.0f),
742 DlBlendMode::kColorDodge));
743
745 DlImageSampling::kMipmapLinear, &image_paint);
746
747 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
748}
749
750TEST_P(AiksTest, FramebufferAdvancedBlendCoverage) {
751 auto texture = CreateTextureForFixture("airplane.jpg",
752 /*enable_mipmapping=*/true);
753
754 // Draw with an advanced blend that can use FramebufferBlendContents and
755 // verify that the scale transform is correctly applied to the image.
756 DisplayListBuilder builder;
757
758 DlPaint paint;
759 paint.setColor(
760 DlColor::RGBA(169.0f / 255.0f, 169.0f / 255.0f, 169.0f / 255.0f, 1.0f));
761 builder.DrawPaint(paint);
762 builder.Scale(0.4, 0.4);
763
764 DlPaint image_paint;
765 image_paint.setBlendMode(DlBlendMode::kMultiply);
766
768 DlImageSampling::kMipmapLinear, &image_paint);
769
770 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
771}
772
773TEST_P(AiksTest, ColorWheel) {
774 // Compare with https://fiddle.skia.org/c/@BlendModes
775
777
778 auto draw_color_wheel = [](DisplayListBuilder& builder) -> void {
779 /// color_wheel_sampler: r=0 -> fuchsia, r=2pi/3 -> yellow, r=4pi/3 ->
780 /// cyan domain: r >= 0 (because modulo used is non euclidean)
781 auto color_wheel_sampler = [](Radians r) {
782 Scalar x = r.radians / k2Pi + 1;
783
784 // https://www.desmos.com/calculator/6nhjelyoaj
785 auto color_cycle = [](Scalar x) {
786 Scalar cycle = std::fmod(x, 6.0f);
787 return std::max(0.0f, std::min(1.0f, 2 - std::abs(2 - cycle)));
788 };
789 return Color(color_cycle(6 * x + 1), //
790 color_cycle(6 * x - 1), //
791 color_cycle(6 * x - 3), //
792 1);
793 };
794
795 DlPaint paint;
796 paint.setBlendMode(DlBlendMode::kSrcOver);
797
798 // Draw a fancy color wheel for the backdrop.
799 // https://www.desmos.com/calculator/xw7kafthwd
800 const int max_dist = 900;
801 for (int i = 0; i <= 900; i++) {
802 Radians r(kPhi / k2Pi * i);
803 Scalar distance = r.radians / std::powf(4.12, 0.0026 * r.radians);
804 Scalar normalized_distance = static_cast<Scalar>(i) / max_dist;
805
806 auto color = color_wheel_sampler(r).WithAlpha(1.0f - normalized_distance);
807 paint.setColor(
808 DlColor::RGBA(color.red, color.green, color.blue, color.alpha));
809 DlPoint position = DlPoint(distance * std::sin(r.radians),
810 -distance * std::cos(r.radians));
811
812 builder.DrawCircle(position, 9 + normalized_distance * 3, paint);
813 }
814 };
815
816 auto callback = [&]() -> sk_sp<DisplayList> {
817 // UI state.
818 static bool cache_the_wheel = true;
819 static int current_blend_index = 3;
820 static float dst_alpha = 1;
821 static float src_alpha = 1;
822 static DlColor color0 = DlColor::kRed();
823 static DlColor color1 = DlColor::kGreen();
824 static DlColor color2 = DlColor::kBlue();
825
826 if (AiksTest::ImGuiBegin("Controls", nullptr,
827 ImGuiWindowFlags_AlwaysAutoResize)) {
828 ImGui::Checkbox("Cache the wheel", &cache_the_wheel);
829 ImGui::ListBox("Blending mode", &current_blend_index,
830 blend_modes.blend_mode_names.data(),
831 blend_modes.blend_mode_names.size());
832 ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
833 ImGui::ColorEdit4("Color A", reinterpret_cast<float*>(&color0));
834 ImGui::ColorEdit4("Color B", reinterpret_cast<float*>(&color1));
835 ImGui::ColorEdit4("Color C", reinterpret_cast<float*>(&color2));
836 ImGui::SliderFloat("Destination alpha", &dst_alpha, 0, 1);
837 ImGui::End();
838 }
839
840 DisplayListBuilder builder;
841
842 DlPaint paint;
843 paint.setColor(DlColor::kWhite().withAlpha(dst_alpha * 255));
844 paint.setBlendMode(DlBlendMode::kSrc);
845 builder.SaveLayer(std::nullopt, &paint);
846 {
847 DlPaint paint;
848 paint.setColor(DlColor::kWhite());
849 builder.DrawPaint(paint);
850
851 builder.SaveLayer(std::nullopt, nullptr);
852 builder.Scale(GetContentScale().x, GetContentScale().y);
853 builder.Translate(500, 400);
854 builder.Scale(3, 3);
855 draw_color_wheel(builder);
856 builder.Restore();
857 }
858 builder.Restore();
859
860 builder.Scale(GetContentScale().x, GetContentScale().y);
861 builder.Translate(500, 400);
862 builder.Scale(3, 3);
863
864 // Draw 3 circles to a subpass and blend it in.
865 DlPaint save_paint;
866 save_paint.setColor(DlColor::kWhite().withAlpha(src_alpha * 255));
867 save_paint.setBlendMode(static_cast<DlBlendMode>(
868 blend_modes.blend_mode_values[current_blend_index]));
869 builder.SaveLayer(std::nullopt, &save_paint);
870 {
871 DlPaint paint;
872 paint.setBlendMode(DlBlendMode::kPlus);
873 const Scalar x = std::sin(k2Pi / 3);
874 const Scalar y = -std::cos(k2Pi / 3);
875 paint.setColor(color0);
876 builder.DrawCircle(DlPoint(-x * 45, y * 45), 65, paint);
877 paint.setColor(color1);
878 builder.DrawCircle(DlPoint(0, -45), 65, paint);
879 paint.setColor(color2);
880 builder.DrawCircle(DlPoint(x * 45, y * 45), 65, paint);
881 }
882 builder.Restore();
883
884 return builder.Build();
885 };
886
887 ASSERT_TRUE(OpenPlaygroundHere(callback));
888}
889
890TEST_P(AiksTest, DestructiveBlendColorFilterFloodsClip) {
891 DisplayListBuilder builder;
892
893 DlPaint paint;
894 paint.setColor(DlColor::kBlue());
895 builder.DrawPaint(paint);
896
897 DlPaint save_paint;
898 save_paint.setColorFilter(
899 DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kSrc));
900 builder.SaveLayer(std::nullopt, &save_paint);
901 builder.Restore();
902
903 // Should be solid red as the destructive color filter floods the clip.
904 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
905}
906
907TEST_P(AiksTest, AdvancedBlendColorFilterWithDestinationOpacity) {
908 DisplayListBuilder builder;
909
911
912 DlPaint save_paint;
913 save_paint.setOpacity(0.3);
915 DlBlendMode::kSaturation));
916 builder.SaveLayer(std::nullopt, &save_paint);
917 builder.DrawRect(DlRect::MakeXYWH(100, 100, 300, 300),
919 builder.DrawRect(DlRect::MakeXYWH(200, 200, 300, 300),
921 builder.Restore();
922
923 // Should be solid red as the destructive color filter floods the clip.
924 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
925}
926
927TEST_P(AiksTest, EmulatedAdvancedBlendRestore) {
928 DisplayListBuilder builder;
929
931 builder.Save();
932 builder.ClipRect(DlRect::MakeLTRB(100, 100, 400, 300));
933
934 // Draw should apply the clip, even though it is an advanced blend.
935 builder.DrawRect(DlRect::MakeLTRB(0, 0, 400, 300),
936 DlPaint()
937 .setColor(DlColor::kRed())
938 .setBlendMode(DlBlendMode::kDifference));
939 // This color should not show if clip is still functional.
940 builder.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100),
941 DlPaint().setColor(DlColor::kBlue()));
942 builder.Restore();
943
944 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
945}
946
947} // namespace testing
948} // namespace impeller
#define BLEND_MODE_TUPLE(blend_mode)
#define BLEND_MODE_SRC_ALPHA_TEST(blend_mode)
#define BLEND_MODE_TEST(blend_mode)
void ClipRect(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void DrawImageRect(const sk_sp< DlImage > &image, const DlRect &src, const DlRect &dst, DlImageSampling sampling, const DlPaint *paint=nullptr, DlSrcRectConstraint constraint=DlSrcRectConstraint::kFast) override
void DrawImage(const sk_sp< DlImage > &image, const DlPoint &point, DlImageSampling sampling, const DlPaint *paint=nullptr) override
void DrawCircle(const DlPoint &center, DlScalar radius, const DlPaint &paint) override
void SaveLayer(const std::optional< DlRect > &bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr, std::optional< int64_t > backdrop_id=std::nullopt) override
void Rotate(DlScalar degrees) override
void Scale(DlScalar sx, DlScalar sy) override
void Translate(DlScalar tx, DlScalar ty) override
void DrawPaint(const DlPaint &paint) override
sk_sp< DisplayList > Build()
Definition dl_builder.cc:66
void DrawRect(const DlRect &rect, const DlPaint &paint) override
static std::shared_ptr< DlMaskFilter > Make(DlBlurStyle style, SkScalar sigma, bool respect_ctm=true)
static std::shared_ptr< const DlColorFilter > MakeBlend(DlColor color, DlBlendMode mode)
static std::shared_ptr< DlColorSource > MakeLinear(const DlPoint start_point, const DlPoint end_point, uint32_t stop_count, const DlColor *colors, const float *stops, DlTileMode tile_mode, const DlMatrix *matrix=nullptr)
DlPaint & setColor(DlColor color)
Definition dl_paint.h:70
DlPaint & setBlendMode(DlBlendMode mode)
Definition dl_paint.h:85
DlPaint & setMaskFilter(std::nullptr_t filter)
Definition dl_paint.h:185
DlPaint & setOpacity(DlScalar opacity)
Definition dl_paint.h:78
DlPaint & setColorFilter(std::nullptr_t filter)
Definition dl_paint.h:149
DlPaint & setColorSource(std::nullptr_t source)
Definition dl_paint.h:131
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
static constexpr BlendMode kLastAdvancedBlendMode
Definition entity.h:29
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition color.h:19
int32_t x
FlutterDesktopBinaryReply callback
FlTexture * texture
#define FLT_FORWARD(mock, real, method)
Definition mocks.h:20
double y
DEF_SWITCHES_START aot vmservice shared library name
Definition switch_defs.h:27
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
impeller::Point DlPoint
static sk_sp< DisplayList > BlendModeTest(Vector2 content_scale, BlendMode blend_mode, const sk_sp< DlImageImpeller > &src_image, const sk_sp< DlImageImpeller > &dst_image, Scalar src_alpha)
TEST_P(AiksTest, DrawAtlasNoColor)
static BlendModeSelection GetBlendModeSelection()
constexpr float k2Pi
Definition constants.h:29
float Scalar
Definition scalar.h:19
BlendMode
Definition color.h:58
constexpr float kPhi
Definition constants.h:54
static constexpr DlColor kWhite()
Definition dl_color.h:70
static constexpr DlColor kBlue()
Definition dl_color.h:73
static constexpr DlColor RGBA(DlScalar r, DlScalar g, DlScalar b, DlScalar a)
Construct a 32 bit color from floating point R, G, B, and A color channels.
Definition dl_color.h:48
static constexpr DlColor kBlack()
Definition dl_color.h:69
static constexpr DlColor kMaroon()
Definition dl_color.h:82
static constexpr DlColor kTransparent()
Definition dl_color.h:68
static constexpr DlColor kRed()
Definition dl_color.h:71
static constexpr DlColor kGreen()
Definition dl_color.h:72
DlColor withAlpha(uint8_t alpha) const
Definition dl_color.h:120
Scalar blue
Definition color.h:138
uint32_t ToARGB() const
Convert to ARGB 32 bit color.
Definition color.h:259
static constexpr Color LimeGreen()
Definition color.h:602
Scalar alpha
Definition color.h:143
static constexpr Color Black()
Definition color.h:266
static constexpr Color CornflowerBlue()
Definition color.h:342
static constexpr Color MediumTurquoise()
Definition color.h:646
static constexpr Color White()
Definition color.h:264
constexpr Color WithAlpha(Scalar new_alpha) const
Definition color.h:278
static constexpr Color OrangeRed()
Definition color.h:694
Scalar red
Definition color.h:128
Scalar green
Definition color.h:133
Color Blend(Color source, BlendMode blend_mode) const
Blends an unpremultiplied destination color into a given unpremultiplied source color to form a new u...
Definition color.cc:157
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
Scalar radians
Definition scalar.h:45
static constexpr TRect MakeWH(Type width, Type height)
Definition rect.h:140
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition rect.h:618
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129