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