Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Namespaces | Macros | Functions | Variables
aiks_blur_unittests.cc File Reference
#include "flutter/impeller/aiks/aiks_unittests.h"
#include "impeller/aiks/canvas.h"
#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
#include "impeller/entity/render_target_cache.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/playground/widgets.h"
#include "impeller/renderer/testing/mocks.h"
#include "third_party/imgui/imgui.h"

Go to the source code of this file.

Classes

struct  impeller::testing::MaskBlurTestConfig
 

Namespaces

namespace  impeller
 
namespace  impeller::testing
 

Macros

#define MASK_BLUR_VARIANT_TEST(config)
 
#define FLT_FORWARD(mock, real, method)
 

Functions

 impeller::testing::TEST_P (AiksTest, CanRenderMaskBlurHugeSigma)
 
 impeller::testing::TEST_P (AiksTest, CanRenderForegroundBlendWithMaskBlur)
 
 impeller::testing::TEST_P (AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur)
 
 impeller::testing::TEST_P (AiksTest, CanRenderBackdropBlurInteractive)
 
 impeller::testing::TEST_P (AiksTest, CanRenderBackdropBlur)
 
 impeller::testing::TEST_P (AiksTest, CanRenderBackdropBlurHugeSigma)
 
 impeller::testing::TEST_P (AiksTest, CanRenderClippedBlur)
 
 impeller::testing::TEST_P (AiksTest, ClippedBlurFilterRendersCorrectlyInteractive)
 
 impeller::testing::TEST_P (AiksTest, ClippedBlurFilterRendersCorrectly)
 
 impeller::testing::TEST_P (AiksTest, ClearBlendWithBlur)
 
 impeller::testing::TEST_P (AiksTest, BlurHasNoEdge)
 
 impeller::testing::TEST_P (AiksTest, BlurredRectangleWithShader)
 
 impeller::testing::TEST_P (AiksTest, MaskBlurWithZeroSigmaIsSkipped)
 
static Picture impeller::testing::MaskBlurVariantTest (const AiksTest &test_context, const MaskBlurTestConfig &config)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurAtPeripheryVertical)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurAtPeripheryHorizontal)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurWithoutDecalSupport)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurOneDimension)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurRotatedAndClipped)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurScaledAndClipped)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurRotatedAndClippedInteractive)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurSolidColorTinyMipMap)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurBackdropTinyMipMap)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurAnimatedBackdrop)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurStyleInnerGradient)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurStyleSolidGradient)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurStyleOuterGradient)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurStyleInner)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurStyleOuter)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurStyleSolid)
 
 impeller::testing::TEST_P (AiksTest, MaskBlurTexture)
 
 impeller::testing::TEST_P (AiksTest, GuassianBlurUpdatesMipmapContents)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurSetsMipCountOnPass)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurAllocatesCorrectMipCountRenderTarget)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurMipMapNestedLayer)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurMipMapImageFilter)
 
 impeller::testing::TEST_P (AiksTest, GaussianBlurMipMapSolidColor)
 
 impeller::testing::TEST_P (AiksTest, MaskBlurDoesntStretchContents)
 

Variables

static const std::map< std::string, MaskBlurTestConfigimpeller::testing::kPaintVariations
 

Macro Definition Documentation

◆ FLT_FORWARD

#define FLT_FORWARD (   mock,
  real,
  method 
)
Value:
EXPECT_CALL(*mock, method()) \
.WillRepeatedly(::testing::Return(real->method()));

Definition at line 489 of file aiks_blur_unittests.cc.

492 {
493 if (GetParam() != PlaygroundBackend::kMetal) {
494 GTEST_SKIP_(
495 "This backend doesn't yet support setting device capabilities.");
496 }
497 if (!WillRenderSomething()) {
498 // Sometimes these tests are run without playgrounds enabled which is
499 // pointless for this test since we are asserting that
500 // `SupportsDecalSamplerAddressMode` is called.
501 GTEST_SKIP_("This test requires playgrounds.");
502 }
503
504 std::shared_ptr<const Capabilities> old_capabilities =
505 GetContext()->GetCapabilities();
506 auto mock_capabilities = std::make_shared<MockCapabilities>();
507 EXPECT_CALL(*mock_capabilities, SupportsDecalSamplerAddressMode())
508 .Times(::testing::AtLeast(1))
509 .WillRepeatedly(::testing::Return(false));
510 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultColorFormat);
511 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultStencilFormat);
512 FLT_FORWARD(mock_capabilities, old_capabilities,
513 GetDefaultDepthStencilFormat);
514 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsOffscreenMSAA);
515 FLT_FORWARD(mock_capabilities, old_capabilities,
516 SupportsImplicitResolvingMSAA);
517 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsReadFromResolve);
518 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsFramebufferFetch);
519 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsSSBO);
520 FLT_FORWARD(mock_capabilities, old_capabilities, SupportsCompute);
521 FLT_FORWARD(mock_capabilities, old_capabilities,
522 SupportsTextureToTextureBlits);
523 FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultGlyphAtlasFormat);
524 ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());
525
526 auto texture = std::make_shared<Image>(CreateTextureForFixture("boston.jpg"));
527 Canvas canvas;
528 canvas.Scale(GetContentScale() * 0.5);
529 canvas.DrawPaint({.color = Color::Black()});
530 canvas.DrawImage(
531 texture, Point(200, 200),
532 {
533 .image_filter = ImageFilter::MakeBlur(
534 Sigma(20.0), Sigma(20.0), FilterContents::BlurStyle::kNormal,
535 Entity::TileMode::kDecal),
536 });
537 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
538}
539
540TEST_P(AiksTest, GaussianBlurOneDimension) {
541 Canvas canvas;
542
543 canvas.Scale(GetContentScale());
544 canvas.Scale({0.5, 0.5, 1.0});
545 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
546 canvas.DrawImage(std::make_shared<Image>(boston), Point(100, 100), Paint{});
547 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
548 ImageFilter::MakeBlur(Sigma(50.0), Sigma(0.0),
549 FilterContents::BlurStyle::kNormal,
550 Entity::TileMode::kClamp));
551 canvas.Restore();
552 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
553}
554
555// Smoketest to catch issues with the coverage hint.
556// Draws a rotated blurred image within a rectangle clip. The center of the clip
557// rectangle is the center of the rotated image. The entire area of the clip
558// rectangle should be filled with opaque colors output by the blur.
559TEST_P(AiksTest, GaussianBlurRotatedAndClipped) {
560 Canvas canvas;
561 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
562 Rect bounds =
563 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
564 Vector2 image_center = Vector2(bounds.GetSize() / 2);
565 Paint paint = {.image_filter =
566 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
567 FilterContents::BlurStyle::kNormal,
568 Entity::TileMode::kDecal)};
569 Vector2 clip_size = {150, 75};
570 Vector2 center = Vector2(1024, 768) / 2;
571 canvas.Scale(GetContentScale());
572 canvas.ClipRect(
573 Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size));
574 canvas.Translate({center.x, center.y, 0});
575 canvas.Scale({0.6, 0.6, 1});
576 canvas.Rotate(Degrees(25));
577
578 canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
579 /*dest=*/bounds.Shift(-image_center), paint);
580
581 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
582}
583
584TEST_P(AiksTest, GaussianBlurScaledAndClipped) {
585 Canvas canvas;
586 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
587 Rect bounds =
588 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
589 Vector2 image_center = Vector2(bounds.GetSize() / 2);
590 Paint paint = {.image_filter =
591 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
592 FilterContents::BlurStyle::kNormal,
593 Entity::TileMode::kDecal)};
594 Vector2 clip_size = {150, 75};
595 Vector2 center = Vector2(1024, 768) / 2;
596 canvas.Scale(GetContentScale());
597 canvas.ClipRect(
598 Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size));
599 canvas.Translate({center.x, center.y, 0});
600 canvas.Scale({0.6, 0.6, 1});
601
602 canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
603 /*dest=*/bounds.Shift(-image_center), paint);
604
605 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
606}
607
608TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
609 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
610
611 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
612 const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
613 const Entity::TileMode tile_modes[] = {
614 Entity::TileMode::kClamp, Entity::TileMode::kRepeat,
615 Entity::TileMode::kMirror, Entity::TileMode::kDecal};
616
617 static float rotation = 0;
618 static float scale = 0.6;
619 static int selected_tile_mode = 3;
620
621 if (AiksTest::ImGuiBegin("Controls", nullptr,
622 ImGuiWindowFlags_AlwaysAutoResize)) {
623 ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
624 ImGui::SliderFloat("Scale", &scale, 0, 2.0);
625 ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
626 sizeof(tile_mode_names) / sizeof(char*));
627 ImGui::End();
628 }
629
630 Canvas canvas;
631 Rect bounds =
632 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
633 Vector2 image_center = Vector2(bounds.GetSize() / 2);
634 Paint paint = {.image_filter =
635 ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
636 FilterContents::BlurStyle::kNormal,
637 tile_modes[selected_tile_mode])};
638 static PlaygroundPoint point_a(Point(362, 309), 20, Color::Red());
639 static PlaygroundPoint point_b(Point(662, 459), 20, Color::Red());
640 auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
641 Vector2 center = Vector2(1024, 768) / 2;
642 canvas.Scale(GetContentScale());
643 canvas.ClipRect(
644 Rect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
645 canvas.Translate({center.x, center.y, 0});
646 canvas.Scale({scale, scale, 1});
647 canvas.Rotate(Degrees(rotation));
648
649 canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
650 /*dest=*/bounds.Shift(-image_center), paint);
651 return canvas.EndRecordingAsPicture();
652 };
653
654 ASSERT_TRUE(OpenPlaygroundHere(callback));
655}
656
657// This addresses a bug where tiny blurs could result in mip maps that beyond
658// the limits for the textures used for blurring.
659// See also: b/323402168
660TEST_P(AiksTest, GaussianBlurSolidColorTinyMipMap) {
661 for (int32_t i = 1; i < 5; ++i) {
662 Canvas canvas;
663 Scalar fi = i;
664 canvas.DrawPath(
665 PathBuilder{}
666 .MoveTo({100, 100})
667 .LineTo({100.f + fi, 100.f + fi})
668 .TakePath(),
669 {.color = Color::Chartreuse(),
670 .image_filter = ImageFilter::MakeBlur(
671 Sigma(0.1), Sigma(0.1), FilterContents::BlurStyle::kNormal,
672 Entity::TileMode::kClamp)});
673
674 Picture picture = canvas.EndRecordingAsPicture();
675 std::shared_ptr<RenderTargetCache> cache =
676 std::make_shared<RenderTargetCache>(
677 GetContext()->GetResourceAllocator());
678 AiksContext aiks_context(GetContext(), nullptr, cache);
679 std::shared_ptr<Image> image = picture.ToImage(aiks_context, {1024, 768});
680 EXPECT_TRUE(image) << " length " << i;
681 }
682}
683
684// This addresses a bug where tiny blurs could result in mip maps that beyond
685// the limits for the textures used for blurring.
686// See also: b/323402168
687TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) {
688 for (int32_t i = 0; i < 5; ++i) {
689 Canvas canvas;
690 ISize clip_size = ISize(i, i);
691 canvas.ClipRect(
692 Rect::MakeXYWH(400, 400, clip_size.width, clip_size.height));
693 canvas.DrawCircle(
694 {400, 400}, 200,
695 {
696 .color = Color::Green(),
697 .image_filter = ImageFilter::MakeBlur(
698 Sigma(0.1), Sigma(0.1), FilterContents::BlurStyle::kNormal,
699 Entity::TileMode::kDecal),
700 });
701 canvas.Restore();
702
703 Picture picture = canvas.EndRecordingAsPicture();
704 std::shared_ptr<RenderTargetCache> cache =
705 std::make_shared<RenderTargetCache>(
706 GetContext()->GetResourceAllocator());
707 AiksContext aiks_context(GetContext(), nullptr, cache);
708 std::shared_ptr<Image> image = picture.ToImage(aiks_context, {1024, 768});
709 EXPECT_TRUE(image) << " clip rect " << i;
710 }
711}
712
713TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) {
714 // This test is for checking out how stable rendering is when content is
715 // translated underneath a blur. Animating under a blur can cause
716 // *shimmering* to happen as a result of pixel alignment.
717 // See also: https://github.com/flutter/flutter/issues/140193
718 auto boston = std::make_shared<Image>(
719 CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true));
720 ASSERT_TRUE(boston);
721 int64_t count = 0;
722 Scalar sigma = 20.0;
723 Scalar freq = 0.1;
724 Scalar amp = 50.0;
725 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
726 if (AiksTest::ImGuiBegin("Controls", nullptr,
727 ImGuiWindowFlags_AlwaysAutoResize)) {
728 ImGui::SliderFloat("Sigma", &sigma, 0, 200);
729 ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0);
730 ImGui::SliderFloat("Amplitude", &amp, 1, 100);
731 ImGui::End();
732 }
733
734 Canvas canvas;
735 canvas.Scale(GetContentScale());
736 Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60);
737 canvas.DrawImage(boston,
738 Point(1024 / 2 - boston->GetSize().width / 2,
739 (768 / 2 - boston->GetSize().height / 2) + y),
740 {});
741 static PlaygroundPoint point_a(Point(100, 100), 20, Color::Red());
742 static PlaygroundPoint point_b(Point(900, 700), 20, Color::Red());
743 auto [handle_a, handle_b] = DrawPlaygroundLine(point_a, point_b);
744 canvas.ClipRect(
745 Rect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
746 canvas.ClipRect(Rect::MakeLTRB(100, 100, 900, 700));
747 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
748 ImageFilter::MakeBlur(Sigma(sigma), Sigma(sigma),
749 FilterContents::BlurStyle::kNormal,
750 Entity::TileMode::kClamp));
751 count += 1;
752 return canvas.EndRecordingAsPicture();
753 };
754 ASSERT_TRUE(OpenPlaygroundHere(callback));
755}
756
757TEST_P(AiksTest, GaussianBlurStyleInnerGradient) {
758 Canvas canvas;
759 canvas.Scale(GetContentScale());
760
761 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
762
763 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
764 Color{0.7568, 0.2627, 0.2118, 1.0}};
765 std::vector<Scalar> stops = {0.0, 1.0};
766
767 Paint paint;
768 paint.color_source = ColorSource::MakeLinearGradient(
769 {0, 0}, {200, 200}, std::move(colors), std::move(stops),
770 Entity::TileMode::kMirror, {});
771 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
772 .style = FilterContents::BlurStyle::kInner,
773 .sigma = Sigma(30),
774 };
775 canvas.DrawPath(PathBuilder()
776 .MoveTo({200, 200})
777 .LineTo({300, 400})
778 .LineTo({100, 400})
779 .Close()
780 .TakePath(),
781 paint);
782
783 // Draw another thing to make sure the clip area is reset.
784 Paint red;
785 red.color = Color::Red();
786 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
787 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
788}
789
790TEST_P(AiksTest, GaussianBlurStyleSolidGradient) {
791 Canvas canvas;
792 canvas.Scale(GetContentScale());
793
794 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
795
796 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
797 Color{0.7568, 0.2627, 0.2118, 1.0}};
798 std::vector<Scalar> stops = {0.0, 1.0};
799
800 Paint paint;
801 paint.color_source = ColorSource::MakeLinearGradient(
802 {0, 0}, {200, 200}, std::move(colors), std::move(stops),
803 Entity::TileMode::kMirror, {});
804 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
805 .style = FilterContents::BlurStyle::kSolid,
806 .sigma = Sigma(30),
807 };
808 canvas.DrawPath(PathBuilder()
809 .MoveTo({200, 200})
810 .LineTo({300, 400})
811 .LineTo({100, 400})
812 .Close()
813 .TakePath(),
814 paint);
815
816 // Draw another thing to make sure the clip area is reset.
817 Paint red;
818 red.color = Color::Red();
819 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
820 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
821}
822
823TEST_P(AiksTest, GaussianBlurStyleOuterGradient) {
824 Canvas canvas;
825 canvas.Scale(GetContentScale());
826
827 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
828
829 std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
830 Color{0.7568, 0.2627, 0.2118, 1.0}};
831 std::vector<Scalar> stops = {0.0, 1.0};
832
833 Paint paint;
834 paint.color_source = ColorSource::MakeLinearGradient(
835 {0, 0}, {200, 200}, std::move(colors), std::move(stops),
836 Entity::TileMode::kMirror, {});
837 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
838 .style = FilterContents::BlurStyle::kOuter,
839 .sigma = Sigma(30),
840 };
841 canvas.DrawPath(PathBuilder()
842 .MoveTo({200, 200})
843 .LineTo({300, 400})
844 .LineTo({100, 400})
845 .Close()
846 .TakePath(),
847 paint);
848
849 // Draw another thing to make sure the clip area is reset.
850 Paint red;
851 red.color = Color::Red();
852 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
853 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
854}
855
856TEST_P(AiksTest, GaussianBlurStyleInner) {
857 Canvas canvas;
858 canvas.Scale(GetContentScale());
859
860 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
861
862 Paint paint;
863 paint.color = Color::Green();
864 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
865 .style = FilterContents::BlurStyle::kInner,
866 .sigma = Sigma(30),
867 };
868 canvas.DrawPath(PathBuilder()
869 .MoveTo({200, 200})
870 .LineTo({300, 400})
871 .LineTo({100, 400})
872 .Close()
873 .TakePath(),
874 paint);
875
876 // Draw another thing to make sure the clip area is reset.
877 Paint red;
878 red.color = Color::Red();
879 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
880
881 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
882}
883
884TEST_P(AiksTest, GaussianBlurStyleOuter) {
885 Canvas canvas;
886 canvas.Scale(GetContentScale());
887
888 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
889
890 Paint paint;
891 paint.color = Color::Green();
892 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
893 .style = FilterContents::BlurStyle::kOuter,
894 .sigma = Sigma(30),
895 };
896 canvas.DrawPath(PathBuilder()
897 .MoveTo({200, 200})
898 .LineTo({300, 400})
899 .LineTo({100, 400})
900 .Close()
901 .TakePath(),
902 paint);
903
904 // Draw another thing to make sure the clip area is reset.
905 Paint red;
906 red.color = Color::Red();
907 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
908
909 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
910}
911
912TEST_P(AiksTest, GaussianBlurStyleSolid) {
913 Canvas canvas;
914 canvas.Scale(GetContentScale());
915
916 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
917
918 Paint paint;
919 paint.color = Color::Green();
920 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
921 .style = FilterContents::BlurStyle::kSolid,
922 .sigma = Sigma(30),
923 };
924 canvas.DrawPath(PathBuilder()
925 .MoveTo({200, 200})
926 .LineTo({300, 400})
927 .LineTo({100, 400})
928 .Close()
929 .TakePath(),
930 paint);
931
932 // Draw another thing to make sure the clip area is reset.
933 Paint red;
934 red.color = Color::Red();
935 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
936
937 ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
938}
939
940TEST_P(AiksTest, MaskBlurTexture) {
941 Scalar sigma = 30;
942 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
943 if (AiksTest::ImGuiBegin("Controls", nullptr,
944 ImGuiWindowFlags_AlwaysAutoResize)) {
945 ImGui::SliderFloat("Sigma", &sigma, 0, 500);
946 ImGui::End();
947 }
948 Canvas canvas;
949 canvas.Scale(GetContentScale());
950 Paint paint;
951 paint.color = Color::Green();
952 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
953 .style = FilterContents::BlurStyle::kNormal,
954 .sigma = Sigma(sigma),
955 };
956 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
957 canvas.DrawImage(std::make_shared<Image>(boston), {200, 200}, paint);
958 Paint red;
959 red.color = Color::Red();
960 canvas.DrawRect(Rect::MakeXYWH(0, 0, 200, 200), red);
961 return canvas.EndRecordingAsPicture();
962 };
963 ASSERT_TRUE(OpenPlaygroundHere(callback));
964}
965
966TEST_P(AiksTest, GuassianBlurUpdatesMipmapContents) {
967 // This makes sure if mip maps are recycled across invocations of blurs the
968 // contents get updated each frame correctly. If they aren't updated the color
969 // inside the blur and outside the blur will be different.
970 //
971 // If there is some change to render target caching this could display a false
972 // positive in the future. Also, if the LOD that is rendered is 1 it could
973 // present a false positive.
974 int32_t count = 0;
975 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
976 Canvas canvas;
977 if (count++ == 0) {
978 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
979 } else {
980 canvas.DrawCircle({100, 100}, 50, {.color = Color::Chartreuse()});
981 }
982 canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), {20, 20});
983 canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
984 ImageFilter::MakeBlur(Sigma(30.0), Sigma(30.0),
985 FilterContents::BlurStyle::kNormal,
986 Entity::TileMode::kClamp));
987 canvas.Restore();
988 return canvas.EndRecordingAsPicture();
989 };
990
991 ASSERT_TRUE(OpenPlaygroundHere(callback));
992}
993
994TEST_P(AiksTest, GaussianBlurSetsMipCountOnPass) {
995 Canvas canvas;
996 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
997 canvas.SaveLayer({}, std::nullopt,
998 ImageFilter::MakeBlur(Sigma(3), Sigma(3),
999 FilterContents::BlurStyle::kNormal,
1000 Entity::TileMode::kClamp));
1001 canvas.Restore();
1002
1003 Picture picture = canvas.EndRecordingAsPicture();
1004 EXPECT_EQ(4, picture.pass->GetRequiredMipCount());
1005}
1006
1007TEST_P(AiksTest, GaussianBlurAllocatesCorrectMipCountRenderTarget) {
1008 size_t blur_required_mip_count =
1009 GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1010
1011 Canvas canvas;
1012 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
1013 canvas.SaveLayer({}, std::nullopt,
1014 ImageFilter::MakeBlur(Sigma(3), Sigma(3),
1015 FilterContents::BlurStyle::kNormal,
1016 Entity::TileMode::kClamp));
1017 canvas.Restore();
1018
1019 Picture picture = canvas.EndRecordingAsPicture();
1020 std::shared_ptr<RenderTargetCache> cache =
1021 std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1022 AiksContext aiks_context(GetContext(), nullptr, cache);
1023 picture.ToImage(aiks_context, {100, 100});
1024
1025 size_t max_mip_count = 0;
1026 for (auto it = cache->GetRenderTargetDataBegin();
1027 it != cache->GetRenderTargetDataEnd(); ++it) {
1028 max_mip_count = std::max(it->config.mip_count, max_mip_count);
1029 }
1030 EXPECT_EQ(max_mip_count, blur_required_mip_count);
1031}
1032
1033TEST_P(AiksTest, GaussianBlurMipMapNestedLayer) {
1034 fml::testing::LogCapture log_capture;
1035 size_t blur_required_mip_count =
1036 GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1037
1038 Canvas canvas;
1039 canvas.DrawPaint({.color = Color::Wheat()});
1040 canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
1041 canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
1042 canvas.SaveLayer({}, std::nullopt,
1043 ImageFilter::MakeBlur(Sigma(30), Sigma(30),
1044 FilterContents::BlurStyle::kNormal,
1045 Entity::TileMode::kClamp));
1046 canvas.DrawCircle({200, 200}, 50, {.color = Color::Chartreuse()});
1047
1048 Picture picture = canvas.EndRecordingAsPicture();
1049 std::shared_ptr<RenderTargetCache> cache =
1050 std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1051 AiksContext aiks_context(GetContext(), nullptr, cache);
1052 picture.ToImage(aiks_context, {100, 100});
1053
1054 size_t max_mip_count = 0;
1055 for (auto it = cache->GetRenderTargetDataBegin();
1056 it != cache->GetRenderTargetDataEnd(); ++it) {
1057 max_mip_count = std::max(it->config.mip_count, max_mip_count);
1058 }
1059 EXPECT_EQ(max_mip_count, blur_required_mip_count);
1060 // The log is FML_DLOG, so only check in debug builds.
1061#ifndef NDEBUG
1062 if (GetParam() != PlaygroundBackend::kOpenGLES) {
1063 EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1064 std::string::npos);
1065 } else {
1066 EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1067 std::string::npos);
1068 }
1069#endif
1070}
1071
1072TEST_P(AiksTest, GaussianBlurMipMapImageFilter) {
1073 size_t blur_required_mip_count =
1074 GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1075 fml::testing::LogCapture log_capture;
1076 Canvas canvas;
1077 canvas.SaveLayer(
1078 {.image_filter = ImageFilter::MakeBlur(Sigma(30), Sigma(30),
1079 FilterContents::BlurStyle::kNormal,
1080 Entity::TileMode::kClamp)});
1081 canvas.DrawCircle({200, 200}, 50, {.color = Color::Chartreuse()});
1082
1083 Picture picture = canvas.EndRecordingAsPicture();
1084 std::shared_ptr<RenderTargetCache> cache =
1085 std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1086 AiksContext aiks_context(GetContext(), nullptr, cache);
1087 picture.ToImage(aiks_context, {1024, 768});
1088
1089 size_t max_mip_count = 0;
1090 for (auto it = cache->GetRenderTargetDataBegin();
1091 it != cache->GetRenderTargetDataEnd(); ++it) {
1092 max_mip_count = std::max(it->config.mip_count, max_mip_count);
1093 }
1094 EXPECT_EQ(max_mip_count, blur_required_mip_count);
1095 // The log is FML_DLOG, so only check in debug builds.
1096#ifndef NDEBUG
1097 if (GetParam() != PlaygroundBackend::kOpenGLES) {
1098 EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1099 std::string::npos);
1100 } else {
1101 EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1102 std::string::npos);
1103 }
1104#endif
1105}
1106
1107TEST_P(AiksTest, GaussianBlurMipMapSolidColor) {
1108 size_t blur_required_mip_count =
1109 GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
1110 fml::testing::LogCapture log_capture;
1111 Canvas canvas;
1112 canvas.DrawPath(PathBuilder{}
1113 .MoveTo({100, 100})
1114 .LineTo({200, 100})
1115 .LineTo({150, 200})
1116 .LineTo({50, 200})
1117 .Close()
1118 .TakePath(),
1119 {.color = Color::Chartreuse(),
1120 .image_filter = ImageFilter::MakeBlur(
1121 Sigma(30), Sigma(30), FilterContents::BlurStyle::kNormal,
1122 Entity::TileMode::kClamp)});
1123
1124 Picture picture = canvas.EndRecordingAsPicture();
1125 std::shared_ptr<RenderTargetCache> cache =
1126 std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
1127 AiksContext aiks_context(GetContext(), nullptr, cache);
1128 picture.ToImage(aiks_context, {1024, 768});
1129
1130 size_t max_mip_count = 0;
1131 for (auto it = cache->GetRenderTargetDataBegin();
1132 it != cache->GetRenderTargetDataEnd(); ++it) {
1133 max_mip_count = std::max(it->config.mip_count, max_mip_count);
1134 }
1135 EXPECT_EQ(max_mip_count, blur_required_mip_count);
1136 // The log is FML_DLOG, so only check in debug builds.
1137#ifndef NDEBUG
1138 if (GetParam() != PlaygroundBackend::kOpenGLES) {
1139 EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1140 std::string::npos);
1141 } else {
1142 EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
1143 std::string::npos);
1144 }
1145#endif
1146}
1147
1148TEST_P(AiksTest, MaskBlurDoesntStretchContents) {
1149 Scalar sigma = 70;
1150 auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
1151 if (AiksTest::ImGuiBegin("Controls", nullptr,
1152 ImGuiWindowFlags_AlwaysAutoResize)) {
1153 ImGui::SliderFloat("Sigma", &sigma, 0, 500);
1154 ImGui::End();
1155 }
1156 Canvas canvas;
1157 canvas.Scale(GetContentScale());
1158 canvas.DrawPaint({.color = Color(0.1, 0.1, 0.1, 1.0)});
1159
1160 std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
1161 ColorSource image_source = ColorSource::MakeImage(
1162 boston, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {}, {});
1163
1164 canvas.Transform(Matrix::MakeTranslation({100, 100, 0}) *
1165 Matrix::MakeScale({0.5, 0.5, 1.0}));
1166 Paint paint = {
1167 .color_source = image_source,
1168 .mask_blur_descriptor =
1169 Paint::MaskBlurDescriptor{
1170 .style = FilterContents::BlurStyle::kNormal,
1171 .sigma = Sigma(sigma),
1172 },
1173 };
1174 canvas.DrawRect(
1175 Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
1176 paint);
1177
1178 return canvas.EndRecordingAsPicture();
1179 };
1180 ASSERT_TRUE(OpenPlaygroundHere(callback));
1181}
1182
1183} // namespace testing
1184} // namespace impeller
#define M_PI
int count
static bool ok(int result)
static SkScalar center(float pos0, float pos1)
#define FLT_FORWARD(mock, real, method)
const Paint & paint
sk_sp< SkImage > image
Definition examples.cpp:29
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlTexture * texture
double y
SK_API GrDirectContext * GetContext(const SkImage *src)
Optional< SkRect > bounds
Definition SkRecords.h:189
sk_sp< const SkPicture > picture
Definition SkRecords.h:299
PODArray< SkColor > colors
Definition SkRecords.h:276
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition switches.h:191
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer)
Point Vector2
Definition point.h:320
float Scalar
Definition scalar.h:18
std::tuple< Point, Point > DrawPlaygroundLine(PlaygroundPoint &point_a, PlaygroundPoint &point_b)
Definition widgets.cc:50
TRect< Scalar > Rect
Definition rect.h:746
TPoint< Scalar > Point
Definition point.h:316
TSize< int64_t > ISize
Definition size.h:138
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
void Close(PathBuilder *builder)
SK_API sk_sp< PrecompileShader > Picture()
const Scalar scale
std::string str() const
Definition logging.cc:122
#define EXPECT_TRUE(handle)
Definition unit_test.h:685

◆ MASK_BLUR_VARIANT_TEST

#define MASK_BLUR_VARIANT_TEST (   config)
Value:
TEST_P(AiksTest, MaskBlurVariantTest##config) { \
ASSERT_TRUE(OpenPlaygroundHere( \
MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \
}

Definition at line 432 of file aiks_blur_unittests.cc.

433 { \
434 ASSERT_TRUE(OpenPlaygroundHere( \
435 MaskBlurVariantTest(*this, kPaintVariations.at(#config)))); \
436 }
static const std::map< std::string, MaskBlurTestConfig > kPaintVariations
static Picture MaskBlurVariantTest(const AiksTest &test_context, const MaskBlurTestConfig &config)