Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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 // Must be called before any methods that use the context to ensure that
402 // this test is always run with wide gamut support.
403 if (!EnsureContextSupportsWideGamut()) {
404 GTEST_SKIP() << "This backend doesn't yet support wide gamut.";
405 }
406
407 EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
409 auto texture = CreateTextureForFixture("airplane.jpg",
410 /*enable_mipmapping=*/true);
411
412 DisplayListBuilder builder;
413 DlPaint paint;
414 builder.Scale(GetContentScale().x, GetContentScale().y);
415
416 paint.setColor(DlColor::RGBA(0.9, 1, 0.9, 1.0));
417 builder.DrawPaint(paint);
418 builder.SaveLayer(std::nullopt);
419
420 paint.setBlendMode(DlBlendMode::kPlus);
421 paint.setColor(DlColor::kRed());
422
423 builder.DrawRect(DlRect::MakeXYWH(100, 100, 400, 400), paint);
424 paint.setColor(DlColor::kWhite());
425
426 auto rect = Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100);
427 builder.DrawImageRect(
429 DlRect::MakeWH(texture->GetSize().width, texture->GetSize().height),
430 DlRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), //
431 rect.GetRight(), rect.GetBottom()),
432 DlImageSampling::kMipmapLinear, &paint);
433 builder.Restore();
434 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
435}
436
437// Bug: https://github.com/flutter/flutter/issues/142549
438TEST_P(AiksTest, BlendModePlusAlphaColorFilterWideGamut) {
439 // Must be called before any methods that use the context to ensure that
440 // this test is always run with wide gamut support.
441 if (!EnsureContextSupportsWideGamut()) {
442 GTEST_SKIP() << "This backend doesn't yet support wide gamut.";
443 }
444
445 EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
447 auto texture = CreateTextureForFixture("airplane.jpg",
448 /*enable_mipmapping=*/true);
449
450 DisplayListBuilder builder;
451 builder.Scale(GetContentScale().x, GetContentScale().y);
452
453 DlPaint paint;
454 paint.setColor(DlColor::RGBA(0.1, 0.2, 0.1, 1.0));
455 builder.DrawPaint(paint);
456
457 DlPaint save_paint;
458 save_paint.setColorFilter(
459 DlColorFilter::MakeBlend(DlColor::RGBA(1, 0, 0, 1), DlBlendMode::kPlus));
460 builder.SaveLayer(std::nullopt, &save_paint);
461
462 paint.setColor(DlColor::kRed());
463 builder.DrawRect(DlRect::MakeXYWH(100, 100, 400, 400), paint);
464
465 paint.setColor(DlColor::kWhite());
466
467 auto rect = Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100);
468 builder.DrawImageRect(
470 DlRect::MakeWH(texture->GetSize().width, texture->GetSize().height),
471 DlRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), //
472 rect.GetRight(), rect.GetBottom()),
473 DlImageSampling::kMipmapLinear, &paint);
474 builder.Restore();
475
476 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
477}
478
479TEST_P(AiksTest, ForegroundBlendSubpassCollapseOptimization) {
480 DisplayListBuilder builder;
481
482 DlPaint save_paint;
483 save_paint.setColorFilter(
484 DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kColorDodge));
485 builder.SaveLayer(std::nullopt, &save_paint);
486
487 builder.Translate(500, 300);
488 builder.Rotate(120);
489
490 DlPaint paint;
491 paint.setColor(DlColor::kBlue());
492 builder.DrawRect(DlRect::MakeXYWH(100, 100, 200, 200), paint);
493
494 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
495}
496
497TEST_P(AiksTest, ClearBlend) {
498 DisplayListBuilder builder;
499
500 DlPaint blue;
501 blue.setColor(DlColor::kBlue());
502 builder.DrawRect(DlRect::MakeXYWH(0, 0, 600.0, 600.0), blue);
503
504 DlPaint clear;
505 clear.setBlendMode(DlBlendMode::kClear);
506
507 builder.DrawCircle(DlPoint(300.0, 300.0), 200.0, clear);
508
509 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
510}
511
512static sk_sp<DisplayList> BlendModeTest(Vector2 content_scale,
513 BlendMode blend_mode,
514 const sk_sp<DlImageImpeller>& src_image,
515 const sk_sp<DlImageImpeller>& dst_image,
516 Scalar src_alpha) {
517 if (AiksTest::ImGuiBegin("Controls", nullptr,
518 ImGuiWindowFlags_AlwaysAutoResize)) {
519 ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
520 ImGui::End();
521 }
522
523 Color destination_color = Color::CornflowerBlue().WithAlpha(0.75);
524 auto source_colors = std::vector<Color>({Color::White().WithAlpha(0.75),
526 Color::Black().WithAlpha(0.75)});
527
528 DisplayListBuilder builder;
529 {
530 DlPaint paint;
531 paint.setColor(DlColor::kBlack());
532 builder.DrawPaint(paint);
533 }
534 // TODO(bdero): Why does this cause the left image to double scale on high DPI
535 // displays.
536 // builder.Scale(content_scale);
537
538 //----------------------------------------------------------------------------
539 /// 1. Save layer blending (top squares).
540 ///
541
542 builder.Save();
543 for (const auto& color : source_colors) {
544 builder.Save();
545 {
546 builder.ClipRect(DlRect::MakeXYWH(25, 25, 100, 100));
547 // Perform the blend in a SaveLayer so that the initial backdrop color is
548 // fully transparent black. SourceOver blend the result onto the parent
549 // pass.
550 builder.SaveLayer(std::nullopt);
551 {
552 DlPaint draw_paint;
553 draw_paint.setColor(
554 DlColor::RGBA(destination_color.red, destination_color.green,
555 destination_color.blue, destination_color.alpha));
556 builder.DrawPaint(draw_paint);
557
558 // Draw the source color in an offscreen pass and blend it to the parent
559 // pass.
560 DlPaint save_paint;
561 save_paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
562 builder.SaveLayer(std::nullopt, &save_paint);
563 { //
564 DlPaint paint;
565 paint.setColor(
566 DlColor::RGBA(color.red, color.green, color.blue, color.alpha));
567 builder.DrawRect(DlRect::MakeXYWH(25, 25, 100, 100), paint);
568 }
569 builder.Restore();
570 }
571 builder.Restore();
572 }
573 builder.Restore();
574 builder.Translate(100, 0);
575 }
576 builder.RestoreToCount(0);
577
578 //----------------------------------------------------------------------------
579 /// 2. CPU blend modes (bottom squares).
580 ///
581
582 builder.Save();
583 builder.Translate(0, 100);
584 // Perform the blend in a SaveLayer so that the initial backdrop color is
585 // fully transparent black. SourceOver blend the result onto the parent pass.
586 builder.SaveLayer(std::nullopt);
587 for (const auto& color : source_colors) {
588 // Simply write the CPU blended color to the pass.
589 DlPaint paint;
590 auto dest = destination_color.Blend(color, blend_mode);
591 paint.setColor(DlColor::RGBA(dest.red, dest.green, dest.blue, dest.alpha));
592 paint.setBlendMode(DlBlendMode::kSrcOver);
593 builder.DrawRect(DlRect::MakeXYWH(25, 25, 100, 100), paint);
594 builder.Translate(100, 0);
595 }
596 builder.Restore();
597 builder.Restore();
598
599 //----------------------------------------------------------------------------
600 /// 3. Image blending (bottom images).
601 ///
602 /// Compare these results with the images in the Flutter blend mode
603 /// documentation: https://api.flutter.dev/flutter/dart-ui/BlendMode.html
604 ///
605
606 builder.Translate(0, 250);
607
608 // Draw grid behind the images.
609 {
610 DlPaint paint;
611 paint.setColor(DlColor::RGBA(41 / 255.0, 41 / 255.0, 41 / 255.0, 1));
612 builder.DrawRect(DlRect::MakeLTRB(0, 0, 800, 400), paint);
613 }
614
615 DlPaint square_paint;
616 square_paint.setColor(DlColor::RGBA(15 / 255.0, 15 / 255.0, 15 / 255.0, 1));
617 for (int y = 0; y < 400 / 8; y++) {
618 for (int x = 0; x < 800 / 16; x++) {
619 builder.DrawRect(DlRect::MakeXYWH(x * 16 + (y % 2) * 8, y * 8, 8, 8),
620 square_paint);
621 }
622 }
623
624 // Uploaded image source (left image).
625 DlPaint paint;
626 paint.setBlendMode(DlBlendMode::kSrcOver);
627 builder.Save();
628 builder.SaveLayer(std::nullopt, &paint);
629 {
630 builder.DrawImage(dst_image, DlPoint(0, 0), DlImageSampling::kMipmapLinear,
631 &paint);
632
633 paint.setColor(DlColor::kWhite().withAlpha(src_alpha * 255));
634 paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
635 builder.DrawImage(src_image, DlPoint(0, 0), DlImageSampling::kMipmapLinear,
636 &paint);
637 }
638 builder.Restore();
639 builder.Restore();
640
641 // Rendered image source (right image).
642 builder.Save();
643
644 DlPaint save_paint;
645 builder.SaveLayer(std::nullopt, &save_paint);
646 {
647 builder.DrawImage(dst_image, DlPoint(400, 0),
648 DlImageSampling::kMipmapLinear, nullptr);
649
650 DlPaint save_paint;
651 save_paint.setColor(DlColor::kWhite().withAlpha(src_alpha * 255));
652 save_paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
653 builder.SaveLayer(std::nullopt, &save_paint);
654 {
655 builder.DrawImage(src_image, DlPoint(400, 0),
656 DlImageSampling::kMipmapLinear, nullptr);
657 }
658 builder.Restore();
659 }
660 builder.Restore();
661 builder.Restore();
662
663 return builder.Build();
664}
665
666#define BLEND_MODE_TEST(blend_mode) \
667 TEST_P(AiksTest, BlendMode##blend_mode) { \
668 auto src_image = \
669 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_src.png")); \
670 auto dst_image = \
671 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png")); \
672 auto callback = [&]() -> sk_sp<DisplayList> { \
673 return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
674 src_image, dst_image, /*src_alpha=*/1.0); \
675 }; \
676 OpenPlaygroundHere(callback); \
677 }
679
680#define BLEND_MODE_SRC_ALPHA_TEST(blend_mode) \
681 TEST_P(AiksTest, BlendModeSrcAlpha##blend_mode) { \
682 auto src_image = \
683 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_src.png")); \
684 auto dst_image = \
685 DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png")); \
686 auto callback = [&]() -> sk_sp<DisplayList> { \
687 return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
688 src_image, dst_image, /*src_alpha=*/0.5); \
689 }; \
690 OpenPlaygroundHere(callback); \
691 }
693
694TEST_P(AiksTest, CanDrawPaintMultipleTimesInteractive) {
695 auto modes = GetBlendModeSelection();
696
697 auto callback = [&]() -> sk_sp<DisplayList> {
698 static Color background = Color::MediumTurquoise();
699 static Color foreground = Color::Color::OrangeRed().WithAlpha(0.5);
700 static int current_blend_index = 3;
701
702 if (AiksTest::ImGuiBegin("Controls", nullptr,
703 ImGuiWindowFlags_AlwaysAutoResize)) {
704 ImGui::ColorEdit4("Background", reinterpret_cast<float*>(&background));
705 ImGui::ColorEdit4("Foreground", reinterpret_cast<float*>(&foreground));
706 ImGui::ListBox("Blend mode", &current_blend_index,
707 modes.blend_mode_names.data(),
708 modes.blend_mode_names.size());
709 ImGui::End();
710 }
711
712 DisplayListBuilder builder;
713 builder.Scale(0.2, 0.2);
714 DlPaint paint;
715 paint.setColor(DlColor(background.ToARGB()));
716 builder.DrawPaint(paint);
717
718 paint.setColor(DlColor(foreground.ToARGB()));
719 paint.setBlendMode(static_cast<DlBlendMode>(current_blend_index));
720 builder.DrawPaint(paint);
721 return builder.Build();
722 };
723 ASSERT_TRUE(OpenPlaygroundHere(callback));
724}
725
726TEST_P(AiksTest, ForegroundPipelineBlendAppliesTransformCorrectly) {
727 auto texture = CreateTextureForFixture("airplane.jpg",
728 /*enable_mipmapping=*/true);
729
730 DisplayListBuilder builder;
731 builder.Rotate(30);
732
733 DlPaint image_paint;
735 DlColor::RGBA(255.0f / 255.0f, 165.0f / 255.0f, 0.0f / 255.0f, 1.0f),
736 DlBlendMode::kSrcIn));
737
739 DlImageSampling::kMipmapLinear, &image_paint);
740
741 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
742}
743
744TEST_P(AiksTest, ForegroundAdvancedBlendAppliesTransformCorrectly) {
745 auto texture = CreateTextureForFixture("airplane.jpg",
746 /*enable_mipmapping=*/true);
747
748 DisplayListBuilder builder;
749 builder.Rotate(30);
750
751 DlPaint image_paint;
753 DlColor::RGBA(255.0f / 255.0f, 165.0f / 255.0f, 0.0f / 255.0f, 1.0f),
754 DlBlendMode::kColorDodge));
755
757 DlImageSampling::kMipmapLinear, &image_paint);
758
759 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
760}
761
762TEST_P(AiksTest, FramebufferAdvancedBlendCoverage) {
763 auto texture = CreateTextureForFixture("airplane.jpg",
764 /*enable_mipmapping=*/true);
765
766 // Draw with an advanced blend that can use FramebufferBlendContents and
767 // verify that the scale transform is correctly applied to the image.
768 DisplayListBuilder builder;
769
770 DlPaint paint;
771 paint.setColor(
772 DlColor::RGBA(169.0f / 255.0f, 169.0f / 255.0f, 169.0f / 255.0f, 1.0f));
773 builder.DrawPaint(paint);
774 builder.Scale(0.4, 0.4);
775
776 DlPaint image_paint;
777 image_paint.setBlendMode(DlBlendMode::kMultiply);
778
780 DlImageSampling::kMipmapLinear, &image_paint);
781
782 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
783}
784
785TEST_P(AiksTest, ColorWheel) {
786 // Compare with https://fiddle.skia.org/c/@BlendModes
787
789
790 auto draw_color_wheel = [](DisplayListBuilder& builder) -> void {
791 /// color_wheel_sampler: r=0 -> fuchsia, r=2pi/3 -> yellow, r=4pi/3 ->
792 /// cyan domain: r >= 0 (because modulo used is non euclidean)
793 auto color_wheel_sampler = [](Radians r) {
794 Scalar x = r.radians / k2Pi + 1;
795
796 // https://www.desmos.com/calculator/6nhjelyoaj
797 auto color_cycle = [](Scalar x) {
798 Scalar cycle = std::fmod(x, 6.0f);
799 return std::max(0.0f, std::min(1.0f, 2 - std::abs(2 - cycle)));
800 };
801 return Color(color_cycle(6 * x + 1), //
802 color_cycle(6 * x - 1), //
803 color_cycle(6 * x - 3), //
804 1);
805 };
806
807 DlPaint paint;
808 paint.setBlendMode(DlBlendMode::kSrcOver);
809
810 // Draw a fancy color wheel for the backdrop.
811 // https://www.desmos.com/calculator/xw7kafthwd
812 const int max_dist = 900;
813 for (int i = 0; i <= 900; i++) {
814 Radians r(kPhi / k2Pi * i);
815 Scalar distance = r.radians / std::powf(4.12, 0.0026 * r.radians);
816 Scalar normalized_distance = static_cast<Scalar>(i) / max_dist;
817
818 auto color = color_wheel_sampler(r).WithAlpha(1.0f - normalized_distance);
819 paint.setColor(
820 DlColor::RGBA(color.red, color.green, color.blue, color.alpha));
821 DlPoint position = DlPoint(distance * std::sin(r.radians),
822 -distance * std::cos(r.radians));
823
824 builder.DrawCircle(position, 9 + normalized_distance * 3, paint);
825 }
826 };
827
828 auto callback = [&]() -> sk_sp<DisplayList> {
829 // UI state.
830 static bool cache_the_wheel = true;
831 static int current_blend_index = 3;
832 static float dst_alpha = 1;
833 static float src_alpha = 1;
834 static DlColor color0 = DlColor::kRed();
835 static DlColor color1 = DlColor::kGreen();
836 static DlColor color2 = DlColor::kBlue();
837
838 if (AiksTest::ImGuiBegin("Controls", nullptr,
839 ImGuiWindowFlags_AlwaysAutoResize)) {
840 ImGui::Checkbox("Cache the wheel", &cache_the_wheel);
841 ImGui::ListBox("Blending mode", &current_blend_index,
842 blend_modes.blend_mode_names.data(),
843 blend_modes.blend_mode_names.size());
844 ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
845 ImGui::ColorEdit4("Color A", reinterpret_cast<float*>(&color0));
846 ImGui::ColorEdit4("Color B", reinterpret_cast<float*>(&color1));
847 ImGui::ColorEdit4("Color C", reinterpret_cast<float*>(&color2));
848 ImGui::SliderFloat("Destination alpha", &dst_alpha, 0, 1);
849 ImGui::End();
850 }
851
852 DisplayListBuilder builder;
853
854 DlPaint paint;
855 paint.setColor(DlColor::kWhite().withAlpha(dst_alpha * 255));
856 paint.setBlendMode(DlBlendMode::kSrc);
857 builder.SaveLayer(std::nullopt, &paint);
858 {
859 DlPaint paint;
860 paint.setColor(DlColor::kWhite());
861 builder.DrawPaint(paint);
862
863 builder.SaveLayer(std::nullopt, nullptr);
864 builder.Scale(GetContentScale().x, GetContentScale().y);
865 builder.Translate(500, 400);
866 builder.Scale(3, 3);
867 draw_color_wheel(builder);
868 builder.Restore();
869 }
870 builder.Restore();
871
872 builder.Scale(GetContentScale().x, GetContentScale().y);
873 builder.Translate(500, 400);
874 builder.Scale(3, 3);
875
876 // Draw 3 circles to a subpass and blend it in.
877 DlPaint save_paint;
878 save_paint.setColor(DlColor::kWhite().withAlpha(src_alpha * 255));
879 save_paint.setBlendMode(static_cast<DlBlendMode>(
880 blend_modes.blend_mode_values[current_blend_index]));
881 builder.SaveLayer(std::nullopt, &save_paint);
882 {
883 DlPaint paint;
884 paint.setBlendMode(DlBlendMode::kPlus);
885 const Scalar x = std::sin(k2Pi / 3);
886 const Scalar y = -std::cos(k2Pi / 3);
887 paint.setColor(color0);
888 builder.DrawCircle(DlPoint(-x * 45, y * 45), 65, paint);
889 paint.setColor(color1);
890 builder.DrawCircle(DlPoint(0, -45), 65, paint);
891 paint.setColor(color2);
892 builder.DrawCircle(DlPoint(x * 45, y * 45), 65, paint);
893 }
894 builder.Restore();
895
896 return builder.Build();
897 };
898
899 ASSERT_TRUE(OpenPlaygroundHere(callback));
900}
901
902TEST_P(AiksTest, DestructiveBlendColorFilterFloodsClip) {
903 DisplayListBuilder builder;
904
905 DlPaint paint;
906 paint.setColor(DlColor::kBlue());
907 builder.DrawPaint(paint);
908
909 DlPaint save_paint;
910 save_paint.setColorFilter(
911 DlColorFilter::MakeBlend(DlColor::kRed(), DlBlendMode::kSrc));
912 builder.SaveLayer(std::nullopt, &save_paint);
913 builder.Restore();
914
915 // Should be solid red as the destructive color filter floods the clip.
916 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
917}
918
919TEST_P(AiksTest, AdvancedBlendColorFilterWithDestinationOpacity) {
920 DisplayListBuilder builder;
921
923
924 DlPaint save_paint;
925 save_paint.setOpacity(0.3);
927 DlBlendMode::kSaturation));
928 builder.SaveLayer(std::nullopt, &save_paint);
929 builder.DrawRect(DlRect::MakeXYWH(100, 100, 300, 300),
931 builder.DrawRect(DlRect::MakeXYWH(200, 200, 300, 300),
933 builder.Restore();
934
935 // Should be solid red as the destructive color filter floods the clip.
936 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
937}
938
939TEST_P(AiksTest, EmulatedAdvancedBlendRestore) {
940 DisplayListBuilder builder;
941
943 builder.Save();
944 builder.ClipRect(DlRect::MakeLTRB(100, 100, 400, 300));
945
946 // Draw should apply the clip, even though it is an advanced blend.
947 builder.DrawRect(DlRect::MakeLTRB(0, 0, 400, 300),
948 DlPaint()
949 .setColor(DlColor::kRed())
950 .setBlendMode(DlBlendMode::kDifference));
951 // This color should not show if clip is still functional.
952 builder.DrawRect(DlRect::MakeLTRB(0, 0, 100, 100),
953 DlPaint().setColor(DlColor::kBlue()));
954 builder.Restore();
955
956 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
957}
958
959} // namespace testing
960} // 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()
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
#define FLT_FORWARD(mock, real, method)
Definition mocks.h:22
FlTexture * texture
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:607
Scalar alpha
Definition color.h:143
static constexpr Color Black()
Definition color.h:271
static constexpr Color CornflowerBlue()
Definition color.h:347
static constexpr Color MediumTurquoise()
Definition color.h:651
static constexpr Color White()
Definition color.h:269
constexpr Color WithAlpha(Scalar new_alpha) const
Definition color.h:283
static constexpr Color OrangeRed()
Definition color.h:699
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:155
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:652
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129