diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 6abe67f6bf26a..33093e697fb32 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -145,6 +145,7 @@ ../../../flutter/impeller/display_list/aiks_dl_gradient_unittests.cc ../../../flutter/impeller/display_list/aiks_dl_opacity_unittests.cc ../../../flutter/impeller/display_list/aiks_dl_path_unittests.cc +../../../flutter/impeller/display_list/aiks_dl_unittests.cc ../../../flutter/impeller/display_list/aiks_dl_vertices_unittests.cc ../../../flutter/impeller/display_list/dl_golden_blur_unittests.cc ../../../flutter/impeller/display_list/dl_golden_unittests.cc diff --git a/display_list/dl_color.h b/display_list/dl_color.h index 18c22d7709ebf..239b49042e800 100644 --- a/display_list/dl_color.h +++ b/display_list/dl_color.h @@ -32,8 +32,9 @@ struct DlColor { static constexpr DlColor kLightGrey() {return DlColor(0xFFC0C0C0);}; static constexpr DlColor kAliceBlue() {return DlColor(0xFFF0F8FF);}; static constexpr DlColor kFuchsia() {return DlColor(0xFFFF00FF);}; - static constexpr DlColor kMaroon() {return DlColor(0xFF800000);} - static constexpr DlColor kSkyBlue() {return DlColor(0xFF87CEEB);} + static constexpr DlColor kMaroon() {return DlColor(0xFF800000);}; + static constexpr DlColor kSkyBlue() {return DlColor(0xFF87CEEB);}; + static constexpr DlColor kCornflowerBlue() {return DlColor(0xFF6495ED);}; // clang-format on constexpr bool isOpaque() const { return getAlpha() == 0xFF; } diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index cdf59f42a58ad..2d61b3fb0b59a 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -1758,143 +1758,6 @@ TEST_P(AiksTest, ClearColorOptimizationDoesNotApplyForBackdropFilters) { EXPECT_FALSE(actual_color.has_value()); } -TEST_P(AiksTest, CollapsedDrawPaintInSubpass) { - Canvas canvas; - canvas.DrawPaint( - {.color = Color::Yellow(), .blend_mode = BlendMode::kSource}); - canvas.SaveLayer({.blend_mode = BlendMode::kMultiply}); - canvas.DrawPaint({.color = Color::CornflowerBlue().WithAlpha(0.75), - .blend_mode = BlendMode::kSourceOver}); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) { - // Bug: https://github.com/flutter/flutter/issues/131576 - Canvas canvas; - canvas.DrawPaint( - {.color = Color::Yellow(), .blend_mode = BlendMode::kSource}); - canvas.SaveLayer({}, {}, - ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0), - FilterContents::BlurStyle::kNormal, - Entity::TileMode::kDecal)); - canvas.DrawPaint( - {.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSourceOver}); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, ColorMatrixFilterSubpassCollapseOptimization) { - Canvas canvas; - - canvas.SaveLayer({ - .color_filter = - ColorFilter::MakeMatrix({.array = - { - -1.0, 0, 0, 1.0, 0, // - 0, -1.0, 0, 1.0, 0, // - 0, 0, -1.0, 1.0, 0, // - 1.0, 1.0, 1.0, 1.0, 0 // - }}), - }); - - canvas.Translate({500, 300, 0}); - canvas.Rotate(Radians(2 * kPi / 3)); - canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()}); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, LinearToSrgbFilterSubpassCollapseOptimization) { - Canvas canvas; - - canvas.SaveLayer({ - .color_filter = ColorFilter::MakeLinearToSrgb(), - }); - - canvas.Translate({500, 300, 0}); - canvas.Rotate(Radians(2 * kPi / 3)); - canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()}); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, SrgbToLinearFilterSubpassCollapseOptimization) { - Canvas canvas; - - canvas.SaveLayer({ - .color_filter = ColorFilter::MakeSrgbToLinear(), - }); - - canvas.Translate({500, 300, 0}); - canvas.Rotate(Radians(2 * kPi / 3)); - canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()}); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, TranslucentSaveLayerDrawsCorrectly) { - Canvas canvas; - - canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()}); - - canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)}); - canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()}); - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, TranslucentSaveLayerWithBlendColorFilterDrawsCorrectly) { - Canvas canvas; - - canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()}); - - canvas.SaveLayer({ - .color = Color::Black().WithAlpha(0.5), - .color_filter = - ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red()), - }); - canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()}); - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterDrawsCorrectly) { - Canvas canvas; - - canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()}); - - canvas.SaveLayer({ - .color = Color::Black().WithAlpha(0.5), - .image_filter = ImageFilter::MakeFromColorFilter( - *ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red())), - }); - - canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()}); - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, TranslucentSaveLayerWithColorAndImageFilterDrawsCorrectly) { - Canvas canvas; - - canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300), {.color = Color::Blue()}); - - canvas.SaveLayer({ - .color = Color::Black().WithAlpha(0.5), - .color_filter = - ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red()), - }); - - canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()}); - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) { Canvas canvas; canvas.Scale(GetContentScale()); @@ -1984,144 +1847,6 @@ TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_P(AiksTest, ImageFilteredUnboundedSaveLayerWithUnboundedContents) { - Canvas canvas; - canvas.Scale(GetContentScale()); - - auto blur_filter = ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0}, - FilterContents::BlurStyle::kNormal, - Entity::TileMode::kDecal); - - canvas.SaveLayer({.image_filter = blur_filter}, std::nullopt); - { - // DrawPaint to verify correct behavior when the contents are unbounded. - canvas.DrawPaint({.color = Color::Yellow()}); - - // Contrasting rectangle to see interior blurring - canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175), - {.color = Color::Blue()}); - } - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) { - Canvas canvas; - - auto image = std::make_shared(CreateTextureForFixture("airplane.jpg")); - canvas.DrawImage(image, {100, 100}, {}); - - canvas.SaveLayer({.color = Color::Black().WithAlpha(0.5)}); - canvas.DrawImage(image, {100, 500}, {}); - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixColorFilterDrawsCorrectly) { - Canvas canvas; - - auto image = std::make_shared(CreateTextureForFixture("airplane.jpg")); - canvas.DrawImage(image, {100, 100}, {}); - - canvas.SaveLayer({ - .color = Color::Black().WithAlpha(0.5), - .color_filter = ColorFilter::MakeMatrix({.array = - { - 1, 0, 0, 0, 0, // - 0, 1, 0, 0, 0, // - 0, 0, 1, 0, 0, // - 0, 0, 0, 2, 0 // - }}), - }); - canvas.DrawImage(image, {100, 500}, {}); - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly) { - Canvas canvas; - - auto image = std::make_shared(CreateTextureForFixture("airplane.jpg")); - canvas.DrawImage(image, {100, 100}, {}); - - canvas.SaveLayer({ - .color = Color::Black().WithAlpha(0.5), - .image_filter = ImageFilter::MakeFromColorFilter( - *ColorFilter::MakeMatrix({.array = - { - 1, 0, 0, 0, 0, // - 0, 1, 0, 0, 0, // - 0, 0, 1, 0, 0, // - 0, 0, 0, 2, 0 // - }})), - }); - canvas.DrawImage(image, {100, 500}, {}); - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, - TranslucentSaveLayerWithColorFilterAndImageFilterDrawsCorrectly) { - Canvas canvas; - - auto image = std::make_shared(CreateTextureForFixture("airplane.jpg")); - canvas.DrawImage(image, {100, 100}, {}); - - canvas.SaveLayer({ - .color = Color::Black().WithAlpha(0.5), - .image_filter = ImageFilter::MakeFromColorFilter( - *ColorFilter::MakeMatrix({.array = - { - 1, 0, 0, 0, 0, // - 0, 1, 0, 0, 0, // - 0, 0.2, 1, 0, 0, // - 0, 0, 0, 0.5, 0 // - }})), - .color_filter = - ColorFilter::MakeBlend(BlendMode::kModulate, Color::Green()), - }); - canvas.DrawImage(image, {100, 500}, {}); - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, TranslucentSaveLayerWithAdvancedBlendModeDrawsCorrectly) { - Canvas canvas; - canvas.DrawRect(Rect::MakeXYWH(0, 0, 400, 400), {.color = Color::Red()}); - canvas.SaveLayer({ - .color = Color::Black().WithAlpha(0.5), - .blend_mode = BlendMode::kLighten, - }); - canvas.DrawCircle({200, 200}, 100, {.color = Color::Green()}); - canvas.Restore(); - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -/// This is a regression check for https://github.com/flutter/engine/pull/41129 -/// The entire screen is green if successful. If failing, no frames will render, -/// or the entire screen will be transparent black. -TEST_P(AiksTest, CanRenderTinyOverlappingSubpasses) { - Canvas canvas; - canvas.DrawPaint({.color = Color::Red()}); - - // Draw two overlapping subpixel circles. - canvas.SaveLayer({}); - canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()}); - canvas.Restore(); - canvas.SaveLayer({}); - canvas.DrawCircle({100, 100}, 0.1, {.color = Color::Yellow()}); - canvas.Restore(); - - canvas.DrawPaint({.color = Color::Green()}); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) { Canvas canvas; canvas.Scale(Vector2(1.618, 1.618)); @@ -2151,20 +1876,6 @@ TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) { ASSERT_EQ(entity[0].GetBlendMode(), BlendMode::kSource); } -TEST_P(AiksTest, CanRenderDestructiveSaveLayer) { - Canvas canvas; - - canvas.DrawPaint({.color = Color::Red()}); - // Draw an empty savelayer with a destructive blend mode, which will replace - // the entire red screen with fully transparent black, except for the green - // circle drawn within the layer. - canvas.SaveLayer({.blend_mode = BlendMode::kSource}); - canvas.DrawCircle({300, 300}, 100, {.color = Color::Green()}); - canvas.Restore(); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - // Regression test for https://github.com/flutter/flutter/issues/126701 . TEST_P(AiksTest, CanRenderClippedRuntimeEffects) { auto runtime_stages = @@ -2228,66 +1939,6 @@ TEST_P(AiksTest, DrawPaintTransformsBounds) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_P(AiksTest, CanDrawPoints) { - std::vector points = { - {0, 0}, // - {100, 100}, // - {100, 0}, // - {0, 100}, // - {0, 0}, // - {48, 48}, // - {52, 52}, // - }; - std::vector caps = { - PointStyle::kRound, - PointStyle::kSquare, - }; - Paint paint; - paint.color = Color::Yellow().WithAlpha(0.5); - - Paint background; - background.color = Color::Black(); - - Canvas canvas; - canvas.DrawPaint(background); - canvas.Translate({200, 200}); - canvas.DrawPoints(points, 10, paint, PointStyle::kRound); - canvas.Translate({150, 0}); - canvas.DrawPoints(points, 10, paint, PointStyle::kSquare); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, CanDrawPointsWithTextureMap) { - auto texture = CreateTextureForFixture("table_mountain_nx.png", - /*enable_mipmapping=*/true); - - std::vector points = { - {0, 0}, // - {100, 100}, // - {100, 0}, // - {0, 100}, // - {0, 0}, // - {48, 48}, // - {52, 52}, // - }; - std::vector caps = { - PointStyle::kRound, - PointStyle::kSquare, - }; - Paint paint; - paint.color_source = ColorSource::MakeImage(texture, Entity::TileMode::kClamp, - Entity::TileMode::kClamp, {}, {}); - - Canvas canvas; - canvas.Translate({200, 200}); - canvas.DrawPoints(points, 100, paint, PointStyle::kRound); - canvas.Translate({150, 0}); - canvas.DrawPoints(points, 100, paint, PointStyle::kSquare); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - // This currently renders solid blue, as the support for text color sources was // moved into DLDispatching. Path data requires the SkTextBlobs which are not // used in impeller::TextFrames. diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn index 7a0b2137bca5b..57e4c271b72a6 100644 --- a/impeller/display_list/BUILD.gn +++ b/impeller/display_list/BUILD.gn @@ -57,6 +57,7 @@ template("display_list_unittests_component") { "aiks_dl_gradient_unittests.cc", "aiks_dl_opacity_unittests.cc", "aiks_dl_path_unittests.cc", + "aiks_dl_unittests.cc", "aiks_dl_vertices_unittests.cc", "dl_golden_blur_unittests.cc", "dl_golden_unittests.cc", diff --git a/impeller/display_list/aiks_dl_unittests.cc b/impeller/display_list/aiks_dl_unittests.cc new file mode 100644 index 0000000000000..1daba1e9b4570 --- /dev/null +++ b/impeller/display_list/aiks_dl_unittests.cc @@ -0,0 +1,471 @@ + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "display_list/dl_sampling_options.h" +#include "display_list/dl_tile_mode.h" +#include "display_list/effects/dl_color_filter.h" +#include "display_list/effects/dl_color_source.h" +#include "display_list/effects/dl_image_filter.h" +#include "display_list/geometry/dl_geometry_types.h" +#include "display_list/image/dl_image.h" +#include "flutter/impeller/aiks/aiks_unittests.h" + +#include "flutter/display_list/dl_blend_mode.h" +#include "flutter/display_list/dl_builder.h" +#include "flutter/display_list/dl_color.h" +#include "flutter/display_list/dl_paint.h" +#include "flutter/testing/testing.h" +#include "imgui.h" +#include "impeller/display_list/dl_image_impeller.h" +#include "impeller/geometry/scalar.h" +#include "include/core/SkRSXform.h" +#include "include/core/SkRefCnt.h" + +namespace impeller { +namespace testing { + +using namespace flutter; + +namespace { +SkRect GetCullRect(ISize window_size) { + return SkRect::MakeSize(SkSize::Make(window_size.width, window_size.height)); +} +} // namespace + +TEST_P(AiksTest, CollapsedDrawPaintInSubpass) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColor(DlColor::kYellow()); + paint.setBlendMode(DlBlendMode::kSrc); + builder.DrawPaint(paint); + + DlPaint save_paint; + save_paint.setBlendMode(DlBlendMode::kMultiply); + builder.SaveLayer(nullptr, &save_paint); + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kCornflowerBlue().modulateOpacity(0.75f)); + builder.DrawPaint(draw_paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) { + // Bug: https://github.com/flutter/flutter/issues/131576 + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColor(DlColor::kYellow()); + paint.setBlendMode(DlBlendMode::kSrc); + builder.DrawPaint(paint); + + auto filter = DlBlurImageFilter::Make(20.0, 20.0, DlTileMode::kDecal); + builder.SaveLayer(nullptr, nullptr, filter.get()); + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kCornflowerBlue()); + builder.DrawPaint(draw_paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, ColorMatrixFilterSubpassCollapseOptimization) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + const float matrix[20] = { + -1.0, 0, 0, 1.0, 0, // + 0, -1.0, 0, 1.0, 0, // + 0, 0, -1.0, 1.0, 0, // + 1.0, 1.0, 1.0, 1.0, 0 // + }; + auto filter = DlMatrixColorFilter::Make(matrix); + + DlPaint paint; + paint.setColorFilter(filter); + builder.SaveLayer(nullptr, &paint); + + builder.Translate(500, 300); + builder.Rotate(120); // 120 deg + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 100, 200, 200), draw_paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, LinearToSrgbFilterSubpassCollapseOptimization) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColorFilter(DlLinearToSrgbGammaColorFilter::kInstance); + builder.SaveLayer(nullptr, &paint); + + builder.Translate(500, 300); + builder.Rotate(120); // 120 deg. + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 100, 200, 200), draw_paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, SrgbToLinearFilterSubpassCollapseOptimization) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColorFilter(DlLinearToSrgbGammaColorFilter::kInstance); + builder.SaveLayer(nullptr, &paint); + + builder.Translate(500, 300); + builder.Rotate(120); // 120 deg + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 100, 200, 200), draw_paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, TranslucentSaveLayerDrawsCorrectly) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 100, 300, 300), paint); + + DlPaint save_paint; + save_paint.setColor(DlColor::kBlack().withAlpha(128)); + builder.SaveLayer(nullptr, &save_paint); + builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), paint); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, TranslucentSaveLayerWithBlendColorFilterDrawsCorrectly) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 100, 300, 300), paint); + + DlPaint save_paint; + paint.setColor(DlColor::kBlack().withAlpha(128)); + paint.setColorFilter( + DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kDstOver)); + builder.SaveLayer(nullptr, &paint); + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), draw_paint); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterDrawsCorrectly) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 100, 300, 300), paint); + + DlPaint save_paint; + save_paint.setColor(DlColor::kBlack().withAlpha(128)); + save_paint.setImageFilter(DlColorFilterImageFilter::Make( + DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kDstOver))); + + builder.SaveLayer(nullptr, &save_paint); + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), draw_paint); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, TranslucentSaveLayerWithColorAndImageFilterDrawsCorrectly) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 100, 300, 300), paint); + + DlPaint save_paint; + save_paint.setColor(DlColor::kBlack().withAlpha(128)); + save_paint.setColorFilter( + DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kDstOver)); + builder.SaveLayer(nullptr, &save_paint); + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), draw_paint); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, ImageFilteredUnboundedSaveLayerWithUnboundedContents) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + builder.Scale(GetContentScale().x, GetContentScale().y); + + DlPaint save_paint; + save_paint.setImageFilter( + DlBlurImageFilter::Make(10.0, 10.0, DlTileMode::kDecal)); + builder.SaveLayer(nullptr, &save_paint); + + { + // DrawPaint to verify correct behavior when the contents are unbounded. + DlPaint draw_paint; + draw_paint.setColor(DlColor::kYellow()); + builder.DrawPaint(draw_paint); + + // Contrasting rectangle to see interior blurring + DlPaint draw_rect; + draw_rect.setColor(DlColor::kBlue()); + builder.DrawRect(SkRect::MakeLTRB(125, 125, 175, 175), draw_rect); + } + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg")); + builder.DrawImage(image, {100, 100}, DlImageSampling::kMipmapLinear); + + DlPaint paint; + paint.setColor(DlColor::kBlack().withAlpha(128)); + builder.SaveLayer(nullptr, &paint); + builder.DrawImage(image, {100, 500}, DlImageSampling::kMipmapLinear); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixColorFilterDrawsCorrectly) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg")); + builder.DrawImage(image, {100, 100}, {}); + + const float matrix[20] = { + 1, 0, 0, 0, 0, // + 0, 1, 0, 0, 0, // + 0, 0, 1, 0, 0, // + 0, 0, 0, 2, 0 // + }; + DlPaint paint; + paint.setColor(DlColor::kBlack().withAlpha(128)); + paint.setColorFilter(DlMatrixColorFilter::Make(matrix)); + builder.SaveLayer(nullptr, &paint); + builder.DrawImage(image, {100, 500}, {}); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg")); + builder.DrawImage(image, {100, 100}, {}); + + const float matrix[20] = { + 1, 0, 0, 0, 0, // + 0, 1, 0, 0, 0, // + 0, 0, 1, 0, 0, // + 0, 0, 0, 2, 0 // + }; + DlPaint paint; + paint.setColor(DlColor::kBlack().withAlpha(128)); + paint.setColorFilter(DlMatrixColorFilter::Make(matrix)); + builder.SaveLayer(nullptr, &paint); + builder.DrawImage(image, {100, 500}, {}); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, + TranslucentSaveLayerWithColorFilterAndImageFilterDrawsCorrectly) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg")); + builder.DrawImage(image, {100, 100}, {}); + + const float matrix[20] = { + 1, 0, 0, 0, 0, // + 0, 1, 0, 0, 0, // + 0, 0.2, 1, 0, 0, // + 0, 0, 0, 0.5, 0 // + }; + DlPaint paint; + paint.setColor(DlColor::kBlack().withAlpha(128)); + paint.setImageFilter( + DlColorFilterImageFilter::Make(DlMatrixColorFilter::Make(matrix))); + paint.setColorFilter( + DlBlendColorFilter::Make(DlColor::kGreen(), DlBlendMode::kModulate)); + builder.SaveLayer(nullptr, &paint); + builder.DrawImage(image, {100, 500}, {}); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, TranslucentSaveLayerWithAdvancedBlendModeDrawsCorrectly) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColor(DlColor::kRed()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 400, 400), paint); + + DlPaint save_paint; + save_paint.setAlpha(128); + save_paint.setBlendMode(DlBlendMode::kLighten); + builder.SaveLayer(nullptr, &save_paint); + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kGreen()); + builder.DrawCircle({200, 200}, 100, draw_paint); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +/// This is a regression check for https://github.com/flutter/engine/pull/41129 +/// The entire screen is green if successful. If failing, no frames will render, +/// or the entire screen will be transparent black. +TEST_P(AiksTest, CanRenderTinyOverlappingSubpasses) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColor(DlColor::kRed()); + builder.DrawPaint(paint); + + // Draw two overlapping subpixel circles. + builder.SaveLayer({}); + + DlPaint yellow_paint; + yellow_paint.setColor(DlColor::kYellow()); + builder.DrawCircle({100, 100}, 0.1, yellow_paint); + builder.Restore(); + builder.SaveLayer({}); + builder.DrawCircle({100, 100}, 0.1, yellow_paint); + builder.Restore(); + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kGreen()); + builder.DrawPaint(draw_paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanRenderDestructiveSaveLayer) { + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + + DlPaint paint; + paint.setColor(DlColor::kRed()); + builder.DrawPaint(paint); + // Draw an empty savelayer with a destructive blend mode, which will replace + // the entire red screen with fully transparent black, except for the green + // circle drawn within the layer. + + DlPaint save_paint; + save_paint.setBlendMode(DlBlendMode::kSrc); + builder.SaveLayer(nullptr, &save_paint); + + DlPaint draw_paint; + draw_paint.setColor(DlColor::kGreen()); + builder.DrawCircle({300, 300}, 100, draw_paint); + builder.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanDrawPoints) { + std::vector points = { + {0, 0}, // + {100, 100}, // + {100, 0}, // + {0, 100}, // + {0, 0}, // + {48, 48}, // + {52, 52}, // + }; + DlPaint paint_round; + paint_round.setColor(DlColor::kYellow().withAlpha(128)); + paint_round.setStrokeCap(DlStrokeCap::kRound); + paint_round.setStrokeWidth(20); + + DlPaint paint_square; + paint_square.setColor(DlColor::kYellow().withAlpha(128)); + paint_square.setStrokeCap(DlStrokeCap::kSquare); + paint_square.setStrokeWidth(20); + + DlPaint background; + background.setColor(DlColor::kBlack()); + + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + builder.DrawPaint(background); + builder.Translate(200, 200); + + builder.DrawPoints(DlCanvas::PointMode::kPoints, points.size(), points.data(), + paint_round); + builder.Translate(150, 0); + builder.DrawPoints(DlCanvas::PointMode::kPoints, points.size(), points.data(), + paint_square); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanDrawPointsWithTextureMap) { + auto texture = DlImageImpeller::Make( + CreateTextureForFixture("table_mountain_nx.png", + /*enable_mipmapping=*/true)); + + std::vector points = { + {0, 0}, // + {100, 100}, // + {100, 0}, // + {0, 100}, // + {0, 0}, // + {48, 48}, // + {52, 52}, // + }; + + auto image_src = std::make_shared( + texture, DlTileMode::kClamp, DlTileMode::kClamp); + + DlPaint paint_round; + paint_round.setStrokeCap(DlStrokeCap::kRound); + paint_round.setColorSource(image_src); + paint_round.setStrokeWidth(200); + + DlPaint paint_square; + paint_square.setStrokeCap(DlStrokeCap::kSquare); + paint_square.setColorSource(image_src); + paint_square.setStrokeWidth(200); + + DisplayListBuilder builder(GetCullRect(GetWindowSize())); + builder.Translate(200, 200); + + builder.DrawPoints(DlCanvas::PointMode::kPoints, points.size(), points.data(), + paint_round); + builder.Translate(150, 0); + builder.DrawPoints(DlCanvas::PointMode::kPoints, points.size(), points.data(), + paint_square); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +} // namespace testing +} // namespace impeller