From 673ff9c03bfab7765525ffe9f48151f5da3786b3 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 12 Jun 2024 18:49:57 -0700 Subject: [PATCH] [Impeller] Move Gradient tests to display list. (flutter/engine#53345) Migrate almost all of the gradient unittests to display list. Removes tests for disabled dithering and the dithering flag, as it has not been possible to disable dithering on gradients for months and months. Part of https://github.com/flutter/flutter/issues/142054 --- .../flutter/ci/licenses_golden/excluded_files | 1 + .../impeller/aiks/aiks_gradient_unittests.cc | 801 +---------------- .../flutter/impeller/aiks/aiks_playground.cc | 19 + .../flutter/impeller/aiks/aiks_playground.h | 4 + engine/src/flutter/impeller/aiks/paint.h | 1 - .../flutter/impeller/display_list/BUILD.gn | 1 + .../aiks_dl_gradient_unittests.cc | 832 ++++++++++++++++++ .../golden_tests/golden_playground_test.h | 4 + .../golden_playground_test_mac.cc | 19 + .../golden_playground_test_stub.cc | 5 + .../testing/impeller_golden_tests_output.txt | 12 - 11 files changed, 886 insertions(+), 813 deletions(-) create mode 100644 engine/src/flutter/impeller/display_list/aiks_dl_gradient_unittests.cc diff --git a/engine/src/flutter/ci/licenses_golden/excluded_files b/engine/src/flutter/ci/licenses_golden/excluded_files index f212b2b37ad..97f87f2cf6a 100644 --- a/engine/src/flutter/ci/licenses_golden/excluded_files +++ b/engine/src/flutter/ci/licenses_golden/excluded_files @@ -141,6 +141,7 @@ ../../../flutter/impeller/compiler/switches_unittests.cc ../../../flutter/impeller/core/allocator_unittests.cc ../../../flutter/impeller/display_list/aiks_dl_clip_unittests.cc +../../../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/dl_golden_unittests.cc diff --git a/engine/src/flutter/impeller/aiks/aiks_gradient_unittests.cc b/engine/src/flutter/impeller/aiks/aiks_gradient_unittests.cc index ce0e8d31a71..bf6abdad96c 100644 --- a/engine/src/flutter/impeller/aiks/aiks_gradient_unittests.cc +++ b/engine/src/flutter/impeller/aiks/aiks_gradient_unittests.cc @@ -22,616 +22,6 @@ namespace impeller { namespace testing { -namespace { -void CanRenderLinearGradient(AiksTest* aiks_test, Entity::TileMode tile_mode) { - Canvas canvas; - canvas.Scale(aiks_test->GetContentScale()); - Paint paint; - canvas.Translate({100.0f, 0, 0}); - - std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, - Color{0.1294, 0.5882, 0.9529, 0.0}}; - std::vector stops = {0.0, 1.0}; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {}); - - paint.color = Color(1.0, 1.0, 1.0, 1.0); - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} -} // namespace - -TEST_P(AiksTest, CanRenderLinearGradientClamp) { - CanRenderLinearGradient(this, Entity::TileMode::kClamp); -} -TEST_P(AiksTest, CanRenderLinearGradientRepeat) { - CanRenderLinearGradient(this, Entity::TileMode::kRepeat); -} -TEST_P(AiksTest, CanRenderLinearGradientMirror) { - CanRenderLinearGradient(this, Entity::TileMode::kMirror); -} -TEST_P(AiksTest, CanRenderLinearGradientDecal) { - CanRenderLinearGradient(this, Entity::TileMode::kDecal); -} - -TEST_P(AiksTest, CanRenderLinearGradientDecalWithColorFilter) { - Canvas canvas; - canvas.Scale(GetContentScale()); - Paint paint; - canvas.Translate({100.0f, 0, 0}); - - std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, - Color{0.1294, 0.5882, 0.9529, 0.0}}; - std::vector stops = {0.0, 1.0}; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {200, 200}, std::move(colors), std::move(stops), - Entity::TileMode::kDecal, {}); - // Overlay the gradient with 25% green. This should appear as the entire - // rectangle being drawn with 25% green, including the border area outside the - // decal gradient. - paint.color_filter = ColorFilter::MakeBlend(BlendMode::kSourceOver, - Color::Green().WithAlpha(0.25)); - - paint.color = Color(1.0, 1.0, 1.0, 1.0); - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -static void CanRenderLinearGradientWithDithering(AiksTest* aiks_test, - bool use_dithering) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - - // 0xffcccccc --> 0xff333333, taken from - // https://github.com/flutter/flutter/issues/118073#issue-1521699748 - std::vector colors = {Color{0.8, 0.8, 0.8, 1.0}, - Color{0.2, 0.2, 0.2, 1.0}}; - std::vector stops = {0.0, 1.0}; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {800, 500}, std::move(colors), std::move(stops), - Entity::TileMode::kClamp, {}); - paint.dither = use_dithering; - canvas.DrawRect(Rect::MakeXYWH(0, 0, 800, 500), paint); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, CanRenderLinearGradientWithDitheringDisabled) { - CanRenderLinearGradientWithDithering(this, false); -} - -TEST_P(AiksTest, CanRenderLinearGradientWithDitheringEnabled) { - CanRenderLinearGradientWithDithering(this, true); -} // namespace - -static void CanRenderRadialGradientWithDithering(AiksTest* aiks_test, - bool use_dithering) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - - // #FFF -> #000 - std::vector colors = {Color{1.0, 1.0, 1.0, 1.0}, - Color{0.0, 0.0, 0.0, 1.0}}; - std::vector stops = {0.0, 1.0}; - - paint.color_source = ColorSource::MakeRadialGradient( - {600, 600}, 600, std::move(colors), std::move(stops), - Entity::TileMode::kClamp, {}); - paint.dither = use_dithering; - canvas.DrawRect(Rect::MakeXYWH(0, 0, 1200, 1200), paint); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, CanRenderRadialGradientWithDitheringDisabled) { - CanRenderRadialGradientWithDithering(this, false); -} - -TEST_P(AiksTest, CanRenderRadialGradientWithDitheringEnabled) { - CanRenderRadialGradientWithDithering(this, true); -} - -static void CanRenderSweepGradientWithDithering(AiksTest* aiks_test, - bool use_dithering) { - Canvas canvas; - canvas.Scale(aiks_test->GetContentScale()); - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - - // #FFF -> #000 - std::vector colors = {Color{1.0, 1.0, 1.0, 1.0}, - Color{0.0, 0.0, 0.0, 1.0}}; - std::vector stops = {0.0, 1.0}; - - paint.color_source = ColorSource::MakeSweepGradient( - {100, 100}, Degrees(45), Degrees(135), std::move(colors), - std::move(stops), Entity::TileMode::kMirror, {}); - paint.dither = use_dithering; - - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, CanRenderSweepGradientWithDitheringDisabled) { - CanRenderSweepGradientWithDithering(this, false); -} - -TEST_P(AiksTest, CanRenderSweepGradientWithDitheringEnabled) { - CanRenderSweepGradientWithDithering(this, true); -} - -static void CanRenderConicalGradientWithDithering(AiksTest* aiks_test, - bool use_dithering) { - Canvas canvas; - canvas.Scale(aiks_test->GetContentScale()); - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - - // #FFF -> #000 - std::vector colors = {Color{1.0, 1.0, 1.0, 1.0}, - Color{0.0, 0.0, 0.0, 1.0}}; - std::vector stops = {0.0, 1.0}; - - paint.color_source = ColorSource::MakeConicalGradient( - {100, 100}, 100, std::move(colors), std::move(stops), {0, 1}, 0, - Entity::TileMode::kMirror, {}); - paint.dither = use_dithering; - - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, CanRenderConicalGradientWithDitheringDisabled) { - CanRenderConicalGradientWithDithering(this, false); -} - -TEST_P(AiksTest, CanRenderConicalGradientWithDitheringEnabled) { - CanRenderConicalGradientWithDithering(this, true); -} - -namespace { -void CanRenderLinearGradientWithOverlappingStops(AiksTest* aiks_test, - Entity::TileMode tile_mode) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - - std::vector colors = { - Color{0.9568, 0.2627, 0.2118, 1.0}, Color{0.9568, 0.2627, 0.2118, 1.0}, - Color{0.1294, 0.5882, 0.9529, 1.0}, Color{0.1294, 0.5882, 0.9529, 1.0}}; - std::vector stops = {0.0, 0.5, 0.5, 1.0}; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {500, 500}, std::move(colors), std::move(stops), tile_mode, {}); - - paint.color = Color(1.0, 1.0, 1.0, 1.0); - canvas.DrawRect(Rect::MakeXYWH(0, 0, 500, 500), paint); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} -} // namespace - -// Only clamp is necessary. All tile modes are the same output. -TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStopsClamp) { - CanRenderLinearGradientWithOverlappingStops(this, Entity::TileMode::kClamp); -} - -namespace { -void CanRenderLinearGradientManyColors(AiksTest* aiks_test, - Entity::TileMode tile_mode) { - Canvas canvas; - canvas.Scale(aiks_test->GetContentScale()); - Paint paint; - canvas.Translate({100, 100, 0}); - - std::vector colors = { - Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}, - Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}, - Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}, - Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}, - Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}, - Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}, - Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}}; - std::vector stops = { - 0.0, - (1.0 / 6.0) * 1, - (1.0 / 6.0) * 2, - (1.0 / 6.0) * 3, - (1.0 / 6.0) * 4, - (1.0 / 6.0) * 5, - 1.0, - }; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {}); - - paint.color = Color(1.0, 1.0, 1.0, 1.0); - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - canvas.Restore(); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} -} // namespace - -TEST_P(AiksTest, CanRenderLinearGradientManyColorsClamp) { - CanRenderLinearGradientManyColors(this, Entity::TileMode::kClamp); -} -TEST_P(AiksTest, CanRenderLinearGradientManyColorsRepeat) { - CanRenderLinearGradientManyColors(this, Entity::TileMode::kRepeat); -} -TEST_P(AiksTest, CanRenderLinearGradientManyColorsMirror) { - CanRenderLinearGradientManyColors(this, Entity::TileMode::kMirror); -} -TEST_P(AiksTest, CanRenderLinearGradientManyColorsDecal) { - CanRenderLinearGradientManyColors(this, Entity::TileMode::kDecal); -} - -namespace { -void CanRenderLinearGradientWayManyColors(AiksTest* aiks_test, - Entity::TileMode tile_mode) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - auto color = Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}; - std::vector colors; - std::vector stops; - auto current_stop = 0.0; - for (int i = 0; i < 2000; i++) { - colors.push_back(color); - stops.push_back(current_stop); - current_stop += 1 / 2000.0; - } - stops[2000 - 1] = 1.0; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {}); - - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} -} // namespace - -// Only test clamp on purpose since they all look the same. -TEST_P(AiksTest, CanRenderLinearGradientWayManyColorsClamp) { - CanRenderLinearGradientWayManyColors(this, Entity::TileMode::kClamp); -} - -TEST_P(AiksTest, CanRenderLinearGradientManyColorsUnevenStops) { - auto callback = [&](AiksContext& renderer) -> std::optional { - const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; - const Entity::TileMode tile_modes[] = { - Entity::TileMode::kClamp, Entity::TileMode::kRepeat, - Entity::TileMode::kMirror, Entity::TileMode::kDecal}; - - static int selected_tile_mode = 0; - static Matrix matrix = { - 1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1 // - }; - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, - sizeof(tile_mode_names) / sizeof(char*)); - std::string label = "##1"; - for (int i = 0; i < 4; i++) { - ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, - &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0); - label[2]++; - } - ImGui::End(); - } - - Canvas canvas; - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - auto tile_mode = tile_modes[selected_tile_mode]; - - std::vector colors = { - Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}, - Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}, - Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}, - Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}, - Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}, - Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}, - Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}}; - std::vector stops = { - 0.0, 2.0 / 62.0, 4.0 / 62.0, 8.0 / 62.0, 16.0 / 62.0, 32.0 / 62.0, 1.0, - }; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {}); - - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - return canvas.EndRecordingAsPicture(); - }; - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -TEST_P(AiksTest, CanRenderLinearGradientMaskBlur) { - Canvas canvas; - - Paint paint = { - .color = Color::White(), - .color_source = ColorSource::MakeLinearGradient( - {200, 200}, {400, 400}, - {Color::Red(), Color::White(), Color::Red(), Color::White(), - Color::Red(), Color::White(), Color::Red(), Color::White(), - Color::Red(), Color::White(), Color::Red()}, - {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}, - Entity::TileMode::kClamp, {}), - .mask_blur_descriptor = - Paint::MaskBlurDescriptor{ - .style = FilterContents::BlurStyle::kNormal, - .sigma = Sigma(20), - }, - }; - - canvas.DrawCircle({300, 300}, 200, paint); - canvas.DrawRect(Rect::MakeLTRB(100, 300, 500, 600), paint); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, CanRenderRadialGradient) { - auto callback = [&](AiksContext& renderer) -> std::optional { - const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; - const Entity::TileMode tile_modes[] = { - Entity::TileMode::kClamp, Entity::TileMode::kRepeat, - Entity::TileMode::kMirror, Entity::TileMode::kDecal}; - - static int selected_tile_mode = 0; - static Matrix matrix = { - 1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1 // - }; - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, - sizeof(tile_mode_names) / sizeof(char*)); - std::string label = "##1"; - for (int i = 0; i < 4; i++) { - ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, - &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0); - label[2]++; - } - ImGui::End(); - } - - Canvas canvas; - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - auto tile_mode = tile_modes[selected_tile_mode]; - - std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, - Color{0.1294, 0.5882, 0.9529, 1.0}}; - std::vector stops = {0.0, 1.0}; - - paint.color_source = ColorSource::MakeRadialGradient( - {100, 100}, 100, std::move(colors), std::move(stops), tile_mode, {}); - - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - return canvas.EndRecordingAsPicture(); - }; - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -TEST_P(AiksTest, CanRenderRadialGradientManyColors) { - auto callback = [&](AiksContext& renderer) -> std::optional { - const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; - const Entity::TileMode tile_modes[] = { - Entity::TileMode::kClamp, Entity::TileMode::kRepeat, - Entity::TileMode::kMirror, Entity::TileMode::kDecal}; - - static int selected_tile_mode = 0; - static Matrix matrix = { - 1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1 // - }; - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, - sizeof(tile_mode_names) / sizeof(char*)); - std::string label = "##1"; - for (int i = 0; i < 4; i++) { - ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, - &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0); - label[2]++; - } - ImGui::End(); - } - - Canvas canvas; - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - auto tile_mode = tile_modes[selected_tile_mode]; - - std::vector colors = { - Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}, - Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}, - Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}, - Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}, - Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}, - Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}, - Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}}; - std::vector stops = { - 0.0, - (1.0 / 6.0) * 1, - (1.0 / 6.0) * 2, - (1.0 / 6.0) * 3, - (1.0 / 6.0) * 4, - (1.0 / 6.0) * 5, - 1.0, - }; - - paint.color_source = ColorSource::MakeRadialGradient( - {100, 100}, 100, std::move(colors), std::move(stops), tile_mode, {}); - - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - return canvas.EndRecordingAsPicture(); - }; - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -namespace { -void CanRenderSweepGradient(AiksTest* aiks_test, Entity::TileMode tile_mode) { - Canvas canvas; - canvas.Scale(aiks_test->GetContentScale()); - Paint paint; - canvas.Translate({100, 100, 0}); - - std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, - Color{0.1294, 0.5882, 0.9529, 1.0}}; - std::vector stops = {0.0, 1.0}; - - paint.color_source = ColorSource::MakeSweepGradient( - {100, 100}, Degrees(45), Degrees(135), std::move(colors), - std::move(stops), tile_mode, {}); - - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} -} // namespace - -TEST_P(AiksTest, CanRenderSweepGradientClamp) { - CanRenderSweepGradient(this, Entity::TileMode::kClamp); -} -TEST_P(AiksTest, CanRenderSweepGradientRepeat) { - CanRenderSweepGradient(this, Entity::TileMode::kRepeat); -} -TEST_P(AiksTest, CanRenderSweepGradientMirror) { - CanRenderSweepGradient(this, Entity::TileMode::kMirror); -} -TEST_P(AiksTest, CanRenderSweepGradientDecal) { - CanRenderSweepGradient(this, Entity::TileMode::kDecal); -} - -namespace { -void CanRenderSweepGradientManyColors(AiksTest* aiks_test, - Entity::TileMode tile_mode) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0, 100.0, 0}); - - std::vector colors = { - Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}, - Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}, - Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}, - Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}, - Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}, - Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}, - Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}}; - std::vector stops = { - 0.0, - (1.0 / 6.0) * 1, - (1.0 / 6.0) * 2, - (1.0 / 6.0) * 3, - (1.0 / 6.0) * 4, - (1.0 / 6.0) * 5, - 1.0, - }; - - paint.color_source = ColorSource::MakeSweepGradient( - {100, 100}, Degrees(45), Degrees(135), std::move(colors), - std::move(stops), tile_mode, {}); - - canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint); - ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} -} // namespace - -TEST_P(AiksTest, CanRenderSweepGradientManyColorsClamp) { - CanRenderSweepGradientManyColors(this, Entity::TileMode::kClamp); -} -TEST_P(AiksTest, CanRenderSweepGradientManyColorsRepeat) { - CanRenderSweepGradientManyColors(this, Entity::TileMode::kRepeat); -} -TEST_P(AiksTest, CanRenderSweepGradientManyColorsMirror) { - CanRenderSweepGradientManyColors(this, Entity::TileMode::kMirror); -} -TEST_P(AiksTest, CanRenderSweepGradientManyColorsDecal) { - CanRenderSweepGradientManyColors(this, Entity::TileMode::kDecal); -} - -TEST_P(AiksTest, CanRenderConicalGradient) { - Scalar size = 256; - Canvas canvas; - Paint paint; - paint.color = Color::White(); - canvas.DrawRect(Rect::MakeXYWH(0, 0, size * 3, size * 3), paint); - std::vector colors = {Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF), - Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF), - Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF), - Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF)}; - std::vector stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0}; - std::array, 8> array{ - std::make_tuple(Point{size / 2.f, size / 2.f}, 0.f, - Point{size / 2.f, size / 2.f}, size / 2.f), - std::make_tuple(Point{size / 2.f, size / 2.f}, size / 4.f, - Point{size / 2.f, size / 2.f}, size / 2.f), - std::make_tuple(Point{size / 4.f, size / 4.f}, 0.f, - Point{size / 2.f, size / 2.f}, size / 2.f), - std::make_tuple(Point{size / 4.f, size / 4.f}, size / 2.f, - Point{size / 2.f, size / 2.f}, 0), - std::make_tuple(Point{size / 4.f, size / 4.f}, size / 4.f, - Point{size / 2.f, size / 2.f}, size / 2.f), - std::make_tuple(Point{size / 4.f, size / 4.f}, size / 16.f, - Point{size / 2.f, size / 2.f}, size / 8.f), - std::make_tuple(Point{size / 4.f, size / 4.f}, size / 8.f, - Point{size / 2.f, size / 2.f}, size / 16.f), - std::make_tuple(Point{size / 8.f, size / 8.f}, size / 8.f, - Point{size / 2.f, size / 2.f}, size / 8.f), - }; - for (int i = 0; i < 8; i++) { - canvas.Save(); - canvas.Translate({(i % 3) * size, i / 3 * size, 0}); - paint.color_source = ColorSource::MakeConicalGradient( - std::get<0>(array[i]), std::get<1>(array[i]), colors, stops, - std::get<2>(array[i]), std::get<3>(array[i]), Entity::TileMode::kClamp, - {}); - canvas.DrawRect(Rect::MakeXYWH(0, 0, size, size), paint); - canvas.Restore(); - } - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, CanRenderGradientDecalWithBackground) { - std::vector colors = {Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF), - Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF), - Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF), - Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF)}; - std::vector stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0}; - - std::array color_sources = { - ColorSource::MakeLinearGradient({0, 0}, {100, 100}, colors, stops, - Entity::TileMode::kDecal, {}), - ColorSource::MakeRadialGradient({100, 100}, 100, colors, stops, - Entity::TileMode::kDecal, {}), - ColorSource::MakeSweepGradient({100, 100}, Degrees(45), Degrees(135), - colors, stops, Entity::TileMode::kDecal, - {}), - }; - - Canvas canvas; - Paint paint; - paint.color = Color::White(); - canvas.DrawRect(Rect::MakeLTRB(0, 0, 605, 205), paint); - for (int i = 0; i < 3; i++) { - canvas.Save(); - canvas.Translate({i * 200.0f, 0, 0}); - paint.color_source = color_sources[i]; - canvas.DrawRect(Rect::MakeLTRB(0, 0, 200, 200), paint); - canvas.Restore(); - } - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - #define APPLY_COLOR_FILTER_GRADIENT_TEST(name) \ TEST_P(AiksTest, name##GradientApplyColorFilter) { \ auto contents = name##GradientContents(); \ @@ -651,194 +41,5 @@ APPLY_COLOR_FILTER_GRADIENT_TEST(Radial); APPLY_COLOR_FILTER_GRADIENT_TEST(Conical); APPLY_COLOR_FILTER_GRADIENT_TEST(Sweep); -TEST_P(AiksTest, GradientStrokesRenderCorrectly) { - // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2 - auto callback = [&](AiksContext& renderer) -> std::optional { - static float scale = 3; - static bool add_circle_clip = true; - const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; - const Entity::TileMode tile_modes[] = { - Entity::TileMode::kClamp, Entity::TileMode::kRepeat, - Entity::TileMode::kMirror, Entity::TileMode::kDecal}; - static int selected_tile_mode = 0; - static float alpha = 1; - - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::SliderFloat("Scale", &scale, 0, 6); - ImGui::Checkbox("Circle clip", &add_circle_clip); - ImGui::SliderFloat("Alpha", &alpha, 0, 1); - ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, - sizeof(tile_mode_names) / sizeof(char*)); - ImGui::End(); - } - - Canvas canvas; - canvas.Scale(GetContentScale()); - Paint paint; - paint.color = Color::White(); - canvas.DrawPaint(paint); - - paint.style = Paint::Style::kStroke; - paint.color = Color(1.0, 1.0, 1.0, alpha); - paint.stroke_width = 10; - auto tile_mode = tile_modes[selected_tile_mode]; - - std::vector colors = {Color{0.9568, 0.2627, 0.2118, 1.0}, - Color{0.1294, 0.5882, 0.9529, 1.0}}; - std::vector stops = {0.0, 1.0}; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {50, 50}, std::move(colors), std::move(stops), tile_mode, {}); - - Path path = PathBuilder{} - .MoveTo({20, 20}) - .QuadraticCurveTo({60, 20}, {60, 60}) - .Close() - .MoveTo({60, 20}) - .QuadraticCurveTo({60, 60}, {20, 60}) - .TakePath(); - - canvas.Scale(Vector2(scale, scale)); - - if (add_circle_clip) { - static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20, - Color::Red()); - static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20, - Color::Red()); - auto [handle_a, handle_b] = - DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b); - - auto screen_to_canvas = canvas.GetCurrentTransform().Invert(); - Point point_a = screen_to_canvas * handle_a * GetContentScale(); - Point point_b = screen_to_canvas * handle_b * GetContentScale(); - - Point middle = (point_a + point_b) / 2; - auto radius = point_a.GetDistance(middle); - canvas.ClipPath(PathBuilder{}.AddCircle(middle, radius).TakePath()); - } - - for (auto join : {Join::kBevel, Join::kRound, Join::kMiter}) { - paint.stroke_join = join; - for (auto cap : {Cap::kButt, Cap::kSquare, Cap::kRound}) { - paint.stroke_cap = cap; - canvas.DrawPath(path, paint); - canvas.Translate({80, 0}); - } - canvas.Translate({-240, 60}); - } - - return canvas.EndRecordingAsPicture(); - }; - - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - -// Draws two gradients that should look identical (except that one is an RRECT). -TEST_P(AiksTest, FastGradientTestHorizontal) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0f, 0, 0}); - - std::vector colors = {Color::Red(), Color::Blue(), Color::Green()}; - std::vector stops = {0.0, 0.1, 1.0}; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {300, 0}, std::move(colors), std::move(stops), - Entity::TileMode::kClamp, {}); - - paint.color = Color(1.0, 1.0, 1.0, 1.0); - canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300), paint); - canvas.Translate({400, 0, 0}); - canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), Size(4, 4), paint); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -// Draws two gradients that should look identical (except that one is an RRECT). -TEST_P(AiksTest, FastGradientTestVertical) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0f, 0, 0}); - - std::vector colors = {Color::Red(), Color::Blue(), Color::Green()}; - std::vector stops = {0.0, 0.1, 1.0}; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 0}, {0, 300}, std::move(colors), std::move(stops), - Entity::TileMode::kClamp, {}); - - paint.color = Color(1.0, 1.0, 1.0, 1.0); - canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300), paint); - canvas.Translate({400, 0, 0}); - canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), Size(4, 4), paint); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -// Draws two gradients that should look identical (except that one is an RRECT). -TEST_P(AiksTest, FastGradientTestHorizontalReversed) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0f, 0, 0}); - - std::vector colors = {Color::Red(), Color::Blue(), Color::Green()}; - std::vector stops = {0.0, 0.1, 1.0}; - - paint.color_source = ColorSource::MakeLinearGradient( - {300, 0}, {0, 0}, std::move(colors), std::move(stops), - Entity::TileMode::kClamp, {}); - - paint.color = Color(1.0, 1.0, 1.0, 1.0); - canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300), paint); - canvas.Translate({400, 0, 0}); - canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), Size(4, 4), paint); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -// Draws two gradients that should look identical (except that one is an RRECT). -TEST_P(AiksTest, FastGradientTestVerticalReversed) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0f, 0, 0}); - - std::vector colors = {Color::Red(), Color::Blue(), Color::Green()}; - std::vector stops = {0.0, 0.1, 1.0}; - - paint.color_source = ColorSource::MakeLinearGradient( - {0, 300}, {0, 0}, std::move(colors), std::move(stops), - Entity::TileMode::kClamp, {}); - - paint.color = Color(1.0, 1.0, 1.0, 1.0); - canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300), paint); - canvas.Translate({400, 0, 0}); - canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), Size(4, 4), paint); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - -TEST_P(AiksTest, VerifyNonOptimizedGradient) { - Canvas canvas; - Paint paint; - canvas.Translate({100.0f, 0, 0}); - - std::vector colors = {Color::Red(), Color::Blue(), Color::Green()}; - std::vector stops = {0.0, 0.1, 1.0}; - - // Inset the start and end point to verify that we do not apply - // the fast gradient condition. - paint.color_source = ColorSource::MakeLinearGradient( - {0, 150}, {0, 100}, std::move(colors), std::move(stops), - Entity::TileMode::kRepeat, {}); - - paint.color = Color(1.0, 1.0, 1.0, 1.0); - canvas.DrawRect(Rect::MakeXYWH(0, 0, 300, 300), paint); - canvas.Translate({400, 0, 0}); - canvas.DrawRRect(Rect::MakeXYWH(0, 0, 300, 300), Size(4, 4), paint); - - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - } // namespace testing -} // namespace impeller +} // namespace impeller \ No newline at end of file diff --git a/engine/src/flutter/impeller/aiks/aiks_playground.cc b/engine/src/flutter/impeller/aiks/aiks_playground.cc index df533a434aa..82f76bf3372 100644 --- a/engine/src/flutter/impeller/aiks/aiks_playground.cc +++ b/engine/src/flutter/impeller/aiks/aiks_playground.cc @@ -81,4 +81,23 @@ bool AiksPlayground::OpenPlaygroundHere( return OpenPlaygroundHere(std::move(picture)); } +bool AiksPlayground::OpenPlaygroundHere( + const AiksDlPlaygroundCallback& callback) { + AiksContext renderer(GetContext(), typographer_context_); + + if (!renderer.IsValid()) { + return false; + } + + return Playground::OpenPlaygroundHere( + [&renderer, &callback](RenderTarget& render_target) -> bool { + auto display_list = callback(); + DlDispatcher dispatcher; + display_list->Dispatch(dispatcher); + Picture picture = dispatcher.EndRecordingAsPicture(); + + return renderer.Render(picture, render_target, true); + }); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/aiks/aiks_playground.h b/engine/src/flutter/impeller/aiks/aiks_playground.h index c2e95325202..bb0fddfd1b5 100644 --- a/engine/src/flutter/impeller/aiks/aiks_playground.h +++ b/engine/src/flutter/impeller/aiks/aiks_playground.h @@ -19,6 +19,8 @@ class AiksPlayground : public PlaygroundTest { using AiksPlaygroundCallback = std::function(AiksContext& renderer)>; + using AiksDlPlaygroundCallback = std::function()>; + AiksPlayground(); ~AiksPlayground(); @@ -32,6 +34,8 @@ class AiksPlayground : public PlaygroundTest { bool OpenPlaygroundHere(AiksPlaygroundCallback callback); + bool OpenPlaygroundHere(const AiksDlPlaygroundCallback& callback); + bool OpenPlaygroundHere(const sk_sp& list); static bool ImGuiBegin(const char* name, diff --git a/engine/src/flutter/impeller/aiks/paint.h b/engine/src/flutter/impeller/aiks/paint.h index 66049e1c0ac..7aa6da5be32 100644 --- a/engine/src/flutter/impeller/aiks/paint.h +++ b/engine/src/flutter/impeller/aiks/paint.h @@ -63,7 +63,6 @@ struct Paint { Color color = Color::Black(); ColorSource color_source; - bool dither = false; Scalar stroke_width = 0.0; Cap stroke_cap = Cap::kButt; diff --git a/engine/src/flutter/impeller/display_list/BUILD.gn b/engine/src/flutter/impeller/display_list/BUILD.gn index 4c344867984..449128bccfd 100644 --- a/engine/src/flutter/impeller/display_list/BUILD.gn +++ b/engine/src/flutter/impeller/display_list/BUILD.gn @@ -53,6 +53,7 @@ template("display_list_unittests_component") { target_name = invoker.target_name predefined_sources = [ "aiks_dl_clip_unittests.cc", + "aiks_dl_gradient_unittests.cc", "aiks_dl_opacity_unittests.cc", "aiks_dl_path_unittests.cc", "dl_golden_unittests.cc", diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_gradient_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_gradient_unittests.cc new file mode 100644 index 00000000000..bea1fa77aef --- /dev/null +++ b/engine/src/flutter/impeller/display_list/aiks_dl_gradient_unittests.cc @@ -0,0 +1,832 @@ +// 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/display_list.h" +#include "display_list/dl_blend_mode.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_mask_filter.h" +#include "flutter/impeller/aiks/aiks_unittests.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 "impeller/playground/widgets.h" +#include "include/core/SkPath.h" +#include "include/core/SkRRect.h" +#include "include/core/SkRect.h" + +using namespace flutter; +//////////////////////////////////////////////////////////////////////////////// +// This is for tests of Canvas that are interested the results of rendering +// gradients. +//////////////////////////////////////////////////////////////////////////////// + +namespace impeller { +namespace testing { + +namespace { + +/// Test body for linear gradient tile mode tests (ex. +/// CanRenderLinearGradientClamp). +void CanRenderLinearGradient(AiksTest* aiks_test, DlTileMode tile_mode) { + DisplayListBuilder builder; + Point scale = aiks_test->GetContentScale(); + builder.Scale(scale.x, scale.y); + DlPaint paint; + builder.Translate(100.0f, 0); + + std::vector colors = { + DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()), + DlColor(Color{0.1294, 0.5882, 0.9529, 0.0}.ToARGB())}; + std::vector stops = {0.0, 1.0}; + + auto gradient = DlColorSource::MakeLinear( + {0, 0}, {200, 200}, 2, colors.data(), stops.data(), tile_mode); + paint.setColorSource(gradient); + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} + +Matrix ToMatrix(const SkMatrix& m) { + return Matrix{ + // clang-format off + m[0], m[3], 0, m[6], + m[1], m[4], 0, m[7], + 0, 0, 1, 0, + m[2], m[5], 0, m[8], + // clang-format on + }; +} +} // namespace + +TEST_P(AiksTest, CanRenderLinearGradientClamp) { + CanRenderLinearGradient(this, DlTileMode::kClamp); +} +TEST_P(AiksTest, CanRenderLinearGradientRepeat) { + CanRenderLinearGradient(this, DlTileMode::kRepeat); +} +TEST_P(AiksTest, CanRenderLinearGradientMirror) { + CanRenderLinearGradient(this, DlTileMode::kMirror); +} +TEST_P(AiksTest, CanRenderLinearGradientDecal) { + CanRenderLinearGradient(this, DlTileMode::kDecal); +} + +TEST_P(AiksTest, CanRenderLinearGradientDecalWithColorFilter) { + DisplayListBuilder builder; + Point scale = GetContentScale(); + builder.Scale(scale.x, scale.y); + DlPaint paint; + builder.Translate(100.0f, 0); + + std::vector colors = { + DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()), + DlColor(Color{0.1294, 0.5882, 0.9529, 0.0}.ToARGB())}; + std::vector stops = {0.0, 1.0}; + + paint.setColorSource(DlColorSource::MakeLinear( + {0, 0}, {200, 200}, 2, colors.data(), stops.data(), DlTileMode::kDecal)); + // Overlay the gradient with 25% green. This should appear as the entire + // rectangle being drawn with 25% green, including the border area outside the + // decal gradient. + paint.setColorFilter(DlBlendColorFilter::Make(DlColor::kGreen().withAlpha(64), + DlBlendMode::kSrcOver)); + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +static void CanRenderLinearGradientWithDithering(AiksTest* aiks_test) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0, 100.0); + + // 0xffcccccc --> 0xff333333, taken from + // https://github.com/flutter/flutter/issues/118073#issue-1521699748 + std::vector colors = {DlColor(0xFFCCCCCC), DlColor(0xFF333333)}; + std::vector stops = {0.0, 1.0}; + + paint.setColorSource(DlColorSource::MakeLinear( + {0, 0}, {800, 500}, 2, colors.data(), stops.data(), DlTileMode::kClamp)); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 800, 500), paint); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanRenderLinearGradientWithDitheringEnabled) { + CanRenderLinearGradientWithDithering(this); +} + +static void CanRenderRadialGradientWithDithering(AiksTest* aiks_test) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0, 100.0); + + // #FFF -> #000 + std::vector colors = {DlColor(Color{1.0, 1.0, 1.0, 1.0}.ToARGB()), + DlColor(Color{0.0, 0.0, 0.0, 1.0}.ToARGB())}; + std::vector stops = {0.0, 1.0}; + + paint.setColorSource(DlColorSource::MakeRadial( + {600, 600}, 600, 2, colors.data(), stops.data(), DlTileMode::kClamp)); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 1200, 1200), paint); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanRenderRadialGradientWithDitheringEnabled) { + CanRenderRadialGradientWithDithering(this); +} + +static void CanRenderSweepGradientWithDithering(AiksTest* aiks_test) { + DisplayListBuilder builder; + builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y); + DlPaint paint; + builder.Translate(100.0, 100.0); + + // #FFF -> #000 + std::vector colors = {DlColor(Color{1.0, 1.0, 1.0, 1.0}.ToARGB()), + DlColor(Color{0.0, 0.0, 0.0, 1.0}.ToARGB())}; + std::vector stops = {0.0, 1.0}; + + paint.setColorSource(DlColorSource::MakeSweep( + {100, 100}, /*start=*/45, /*end=*/135, 2, colors.data(), stops.data(), + DlTileMode::kMirror)); + + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanRenderSweepGradientWithDitheringEnabled) { + CanRenderSweepGradientWithDithering(this); +} + +static void CanRenderConicalGradientWithDithering(AiksTest* aiks_test) { + DisplayListBuilder builder; + builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y); + DlPaint paint; + builder.Translate(100.0, 100.0); + + // #FFF -> #000 + std::vector colors = {DlColor(Color{1.0, 1.0, 1.0, 1.0}.ToARGB()), + DlColor(Color{0.0, 0.0, 0.0, 1.0}.ToARGB())}; + std::vector stops = {0.0, 1.0}; + + paint.setColorSource(DlColorSource::MakeConical({0, 1}, 0, {100, 100}, 100, 2, + colors.data(), stops.data(), + DlTileMode::kMirror)); + + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanRenderConicalGradientWithDitheringEnabled) { + CanRenderConicalGradientWithDithering(this); +} + +namespace { +void CanRenderLinearGradientWithOverlappingStops(AiksTest* aiks_test, + DlTileMode tile_mode) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0, 100.0); + + std::vector colors = { + DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()), + DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()), + DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB()), + DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())}; + std::vector stops = {0.0, 0.5, 0.5, 1.0}; + + paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {500, 500}, + stops.size(), colors.data(), + stops.data(), tile_mode)); + + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 500, 500), paint); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} +} // namespace + +// Only clamp is necessary. All tile modes are the same output. +TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStopsClamp) { + CanRenderLinearGradientWithOverlappingStops(this, DlTileMode::kClamp); +} + +namespace { +void CanRenderLinearGradientManyColors(AiksTest* aiks_test, + DlTileMode tile_mode) { + DisplayListBuilder builder; + builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y); + DlPaint paint; + builder.Translate(100, 100); + + std::vector colors = { + DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()), + DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())}; + std::vector stops = { + 0.0, + (1.0 / 6.0) * 1, + (1.0 / 6.0) * 2, + (1.0 / 6.0) * 3, + (1.0 / 6.0) * 4, + (1.0 / 6.0) * 5, + 1.0, + }; + + paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {200, 200}, + stops.size(), colors.data(), + stops.data(), tile_mode)); + + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + builder.Restore(); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} +} // namespace + +TEST_P(AiksTest, CanRenderLinearGradientManyColorsClamp) { + CanRenderLinearGradientManyColors(this, DlTileMode::kClamp); +} +TEST_P(AiksTest, CanRenderLinearGradientManyColorsRepeat) { + CanRenderLinearGradientManyColors(this, DlTileMode::kRepeat); +} +TEST_P(AiksTest, CanRenderLinearGradientManyColorsMirror) { + CanRenderLinearGradientManyColors(this, DlTileMode::kMirror); +} +TEST_P(AiksTest, CanRenderLinearGradientManyColorsDecal) { + CanRenderLinearGradientManyColors(this, DlTileMode::kDecal); +} + +namespace { +void CanRenderLinearGradientWayManyColors(AiksTest* aiks_test, + DlTileMode tile_mode) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0, 100.0); + auto color = DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()); + std::vector colors; + std::vector stops; + auto current_stop = 0.0; + for (int i = 0; i < 2000; i++) { + colors.push_back(color); + stops.push_back(current_stop); + current_stop += 1 / 2000.0; + } + stops[2000 - 1] = 1.0; + + paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {200, 200}, + stops.size(), colors.data(), + stops.data(), tile_mode)); + + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} +} // namespace + +// Only test clamp on purpose since they all look the same. +TEST_P(AiksTest, CanRenderLinearGradientWayManyColorsClamp) { + CanRenderLinearGradientWayManyColors(this, DlTileMode::kClamp); +} + +TEST_P(AiksTest, CanRenderLinearGradientManyColorsUnevenStops) { + auto callback = [&]() -> sk_sp { + const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; + const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat, + DlTileMode::kMirror, DlTileMode::kDecal}; + + static int selected_tile_mode = 0; + static Matrix matrix; + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, + sizeof(tile_mode_names) / sizeof(char*)); + std::string label = "##1"; + for (int i = 0; i < 4; i++) { + ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, + &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0); + label[2]++; + } + ImGui::End(); + } + + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0, 100.0); + auto tile_mode = tile_modes[selected_tile_mode]; + + std::vector colors = { + DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()), + DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())}; + std::vector stops = { + 0.0, 2.0 / 62.0, 4.0 / 62.0, 8.0 / 62.0, 16.0 / 62.0, 32.0 / 62.0, 1.0, + }; + + paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {200, 200}, + stops.size(), colors.data(), + stops.data(), tile_mode)); + + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + return builder.Build(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(AiksTest, CanRenderLinearGradientMaskBlur) { + DisplayListBuilder builder; + + std::vector colors = { + DlColor::kRed(), DlColor::kWhite(), DlColor::kRed(), DlColor::kWhite(), + DlColor::kRed(), DlColor::kWhite(), DlColor::kRed(), DlColor::kWhite(), + DlColor::kRed(), DlColor::kWhite(), DlColor::kRed()}; + std::vector stops = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, + 0.6, 0.7, 0.8, 0.9, 1.0}; + + DlPaint paint; + paint.setColor(DlColor::kWhite()); + paint.setColorSource(DlColorSource::MakeLinear( + {200, 200}, {400, 400}, stops.size(), colors.data(), stops.data(), + DlTileMode::kClamp)); + paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20)); + + builder.DrawCircle({300, 300}, 200, paint); + builder.DrawRect(SkRect::MakeLTRB(100, 300, 500, 600), paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanRenderRadialGradient) { + auto callback = [&]() -> sk_sp { + const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; + const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat, + DlTileMode::kMirror, DlTileMode::kDecal}; + + static int selected_tile_mode = 0; + static Matrix matrix; + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, + sizeof(tile_mode_names) / sizeof(char*)); + std::string label = "##1"; + for (int i = 0; i < 4; i++) { + ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, + &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0); + label[2]++; + } + ImGui::End(); + } + + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0, 100.0); + auto tile_mode = tile_modes[selected_tile_mode]; + + std::vector colors = { + DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()), + DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())}; + std::vector stops = {0.0, 1.0}; + + paint.setColorSource(DlColorSource::MakeRadial( + {100, 100}, 100, 2, colors.data(), stops.data(), tile_mode)); + + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + return builder.Build(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +TEST_P(AiksTest, CanRenderRadialGradientManyColors) { + auto callback = [&]() -> sk_sp { + const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; + const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat, + DlTileMode::kMirror, DlTileMode::kDecal}; + + static int selected_tile_mode = 0; + static Matrix matrix = { + 1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1 // + }; + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, + sizeof(tile_mode_names) / sizeof(char*)); + std::string label = "##1"; + for (int i = 0; i < 4; i++) { + ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, + &(matrix.vec[i]), 4, NULL, NULL, "%.2f", 0); + label[2]++; + } + ImGui::End(); + } + + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0, 100.0); + auto tile_mode = tile_modes[selected_tile_mode]; + + std::vector colors = { + DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()), + DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())}; + std::vector stops = { + 0.0, + (1.0 / 6.0) * 1, + (1.0 / 6.0) * 2, + (1.0 / 6.0) * 3, + (1.0 / 6.0) * 4, + (1.0 / 6.0) * 5, + 1.0, + }; + + paint.setColorSource(DlColorSource::MakeRadial( + {100, 100}, 100, stops.size(), colors.data(), stops.data(), tile_mode)); + + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + return builder.Build(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +namespace { +void CanRenderSweepGradient(AiksTest* aiks_test, DlTileMode tile_mode) { + DisplayListBuilder builder; + builder.Scale(aiks_test->GetContentScale().x, aiks_test->GetContentScale().y); + DlPaint paint; + builder.Translate(100, 100); + + std::vector colors = { + DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()), + DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())}; + std::vector stops = {0.0, 1.0}; + + paint.setColorSource(DlColorSource::MakeSweep( + {100, 100}, /*start=*/45, /*end=*/135, /*stop_count=*/2, colors.data(), + stops.data(), tile_mode)); + + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} +} // namespace + +TEST_P(AiksTest, CanRenderSweepGradientClamp) { + CanRenderSweepGradient(this, DlTileMode::kClamp); +} +TEST_P(AiksTest, CanRenderSweepGradientRepeat) { + CanRenderSweepGradient(this, DlTileMode::kRepeat); +} +TEST_P(AiksTest, CanRenderSweepGradientMirror) { + CanRenderSweepGradient(this, DlTileMode::kMirror); +} +TEST_P(AiksTest, CanRenderSweepGradientDecal) { + CanRenderSweepGradient(this, DlTileMode::kDecal); +} + +namespace { +void CanRenderSweepGradientManyColors(AiksTest* aiks_test, + DlTileMode tile_mode) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0, 100.0); + + std::vector colors = { + DlColor(Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0}.ToARGB()), + DlColor(Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0}.ToARGB()), + DlColor(Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}.ToARGB())}; + std::vector stops = { + 0.0, + (1.0 / 6.0) * 1, + (1.0 / 6.0) * 2, + (1.0 / 6.0) * 3, + (1.0 / 6.0) * 4, + (1.0 / 6.0) * 5, + 1.0, + }; + + paint.setColorSource(DlColorSource::MakeSweep({100, 100}, 45, 135, + stops.size(), colors.data(), + stops.data(), tile_mode)); + + builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint); + ASSERT_TRUE(aiks_test->OpenPlaygroundHere(builder.Build())); +} +} // namespace + +TEST_P(AiksTest, CanRenderSweepGradientManyColorsClamp) { + CanRenderSweepGradientManyColors(this, DlTileMode::kClamp); +} +TEST_P(AiksTest, CanRenderSweepGradientManyColorsRepeat) { + CanRenderSweepGradientManyColors(this, DlTileMode::kRepeat); +} +TEST_P(AiksTest, CanRenderSweepGradientManyColorsMirror) { + CanRenderSweepGradientManyColors(this, DlTileMode::kMirror); +} +TEST_P(AiksTest, CanRenderSweepGradientManyColorsDecal) { + CanRenderSweepGradientManyColors(this, DlTileMode::kDecal); +} + +TEST_P(AiksTest, CanRenderConicalGradient) { + Scalar size = 256; + DisplayListBuilder builder; + DlPaint paint; + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, size * 3, size * 3), paint); + std::vector colors = { + DlColor(Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF).ToARGB()), + DlColor(Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF).ToARGB()), + DlColor(Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF).ToARGB()), + DlColor(Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF).ToARGB())}; + std::vector stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0}; + std::array, 8> array{ + std::make_tuple(SkPoint::Make(size / 2.f, size / 2.f), 0.f, + SkPoint::Make(size / 2.f, size / 2.f), size / 2.f), + std::make_tuple(SkPoint::Make(size / 2.f, size / 2.f), size / 4.f, + SkPoint::Make(size / 2.f, size / 2.f), size / 2.f), + std::make_tuple(SkPoint::Make(size / 4.f, size / 4.f), 0.f, + SkPoint::Make(size / 2.f, size / 2.f), size / 2.f), + std::make_tuple(SkPoint::Make(size / 4.f, size / 4.f), size / 2.f, + SkPoint::Make(size / 2.f, size / 2.f), 0), + std::make_tuple(SkPoint::Make(size / 4.f, size / 4.f), size / 4.f, + SkPoint::Make(size / 2.f, size / 2.f), size / 2.f), + std::make_tuple(SkPoint::Make(size / 4.f, size / 4.f), size / 16.f, + SkPoint::Make(size / 2.f, size / 2.f), size / 8.f), + std::make_tuple(SkPoint::Make(size / 4.f, size / 4.f), size / 8.f, + SkPoint::Make(size / 2.f, size / 2.f), size / 16.f), + std::make_tuple(SkPoint::Make(size / 8.f, size / 8.f), size / 8.f, + SkPoint::Make(size / 2.f, size / 2.f), size / 8.f), + }; + for (int i = 0; i < 8; i++) { + builder.Save(); + builder.Translate((i % 3) * size, i / 3 * size); + paint.setColorSource(DlColorSource::MakeConical( + std::get<2>(array[i]), std::get<3>(array[i]), std::get<0>(array[i]), + std::get<1>(array[i]), stops.size(), colors.data(), stops.data(), + DlTileMode::kClamp)); + builder.DrawRect(SkRect::MakeXYWH(0, 0, size, size), paint); + builder.Restore(); + } + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, CanRenderGradientDecalWithBackground) { + std::vector colors = { + DlColor(Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF).ToARGB()), + DlColor(Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF).ToARGB()), + DlColor(Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF).ToARGB()), + DlColor(Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF).ToARGB())}; + std::vector stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0}; + + std::array, 3> color_sources = { + DlColorSource::MakeLinear({0, 0}, {100, 100}, stops.size(), colors.data(), + stops.data(), DlTileMode::kDecal), + DlColorSource::MakeRadial({100, 100}, 100, stops.size(), colors.data(), + stops.data(), DlTileMode::kDecal), + DlColorSource::MakeSweep({100, 100}, 45, 135, stops.size(), colors.data(), + stops.data(), DlTileMode::kDecal), + }; + + DisplayListBuilder builder; + DlPaint paint; + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeLTRB(0, 0, 605, 205), paint); + for (int i = 0; i < 3; i++) { + builder.Save(); + builder.Translate(i * 200.0f, 0); + paint.setColorSource(color_sources[i]); + builder.DrawRect(SkRect::MakeLTRB(0, 0, 200, 200), paint); + builder.Restore(); + } + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, GradientStrokesRenderCorrectly) { + // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2 + auto callback = [&]() -> sk_sp { + static float scale = 3; + static bool add_circle_clip = true; + const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"}; + const DlTileMode tile_modes[] = {DlTileMode::kClamp, DlTileMode::kRepeat, + DlTileMode::kMirror, DlTileMode::kDecal}; + static int selected_tile_mode = 0; + static float alpha = 1; + + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::SliderFloat("Scale", &scale, 0, 6); + ImGui::Checkbox("Circle clip", &add_circle_clip); + ImGui::SliderFloat("Alpha", &alpha, 0, 1); + ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names, + sizeof(tile_mode_names) / sizeof(char*)); + ImGui::End(); + } + + DisplayListBuilder builder; + builder.Scale(GetContentScale().x, GetContentScale().y); + DlPaint paint; + paint.setColor(DlColor::kWhite()); + builder.DrawPaint(paint); + + paint.setDrawStyle(DlDrawStyle::kStroke); + paint.setColor(DlColor::kWhite().withAlpha(alpha * 255)); + paint.setStrokeWidth(10); + auto tile_mode = tile_modes[selected_tile_mode]; + + std::vector colors = { + DlColor(Color{0.9568, 0.2627, 0.2118, 1.0}.ToARGB()), + DlColor(Color{0.1294, 0.5882, 0.9529, 1.0}.ToARGB())}; + std::vector stops = {0.0, 1.0}; + + paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {50, 50}, + stops.size(), colors.data(), + stops.data(), tile_mode)); + + SkPath path; + path.moveTo(20, 20); + path.quadTo({60, 20}, {60, 60}); + path.close(); + path.moveTo(60, 20); + path.quadTo({60, 60}, {20, 60}); + + builder.Scale(scale, scale); + + if (add_circle_clip) { + static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20, + Color::Red()); + static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20, + Color::Red()); + auto [handle_a, handle_b] = + DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b); + + SkMatrix screen_to_canvas; + if (!builder.GetTransform().invert(&screen_to_canvas)) { + return nullptr; + } + Matrix ip_matrix = ToMatrix(screen_to_canvas); + Point point_a = ip_matrix * handle_a * GetContentScale(); + Point point_b = ip_matrix * handle_b * GetContentScale(); + + Point middle = (point_a + point_b) / 2; + auto radius = point_a.GetDistance(middle); + SkPath circle; + circle.addCircle(middle.x, middle.y, radius); + builder.ClipPath(circle); + } + + for (auto join : + {DlStrokeJoin::kBevel, DlStrokeJoin::kRound, DlStrokeJoin::kMiter}) { + paint.setStrokeJoin(join); + for (auto cap : + {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) { + paint.setStrokeCap(cap); + builder.DrawPath(path, paint); + builder.Translate(80, 0); + } + builder.Translate(-240, 60); + } + + return builder.Build(); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + +// Draws two gradients that should look identical (except that one is an RRECT). +TEST_P(AiksTest, FastGradientTestHorizontal) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0f, 0); + + std::vector colors = {DlColor::kRed(), DlColor::kBlue(), + DlColor::kGreen()}; + std::vector stops = {0.0, 0.1, 1.0}; + + paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {300, 0}, stops.size(), + colors.data(), stops.data(), + DlTileMode::kClamp)); + + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint); + builder.Translate(400, 0); + builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4), + paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +// Draws two gradients that should look identical (except that one is an RRECT). +TEST_P(AiksTest, FastGradientTestVertical) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0f, 0); + + std::vector colors = {DlColor::kRed(), DlColor::kBlue(), + DlColor::kGreen()}; + std::vector stops = {0.0, 0.1, 1.0}; + + paint.setColorSource(DlColorSource::MakeLinear({0, 0}, {0, 300}, stops.size(), + colors.data(), stops.data(), + DlTileMode::kClamp)); + + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint); + builder.Translate(400, 0); + builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4), + paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +// Draws two gradients that should look identical (except that one is an RRECT). +TEST_P(AiksTest, FastGradientTestHorizontalReversed) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0f, 0); + + std::vector colors = {DlColor::kRed(), DlColor::kBlue(), + DlColor::kGreen()}; + std::vector stops = {0.0, 0.1, 1.0}; + + paint.setColorSource(DlColorSource::MakeLinear({300, 0}, {0, 0}, stops.size(), + colors.data(), stops.data(), + DlTileMode::kClamp)); + + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint); + builder.Translate(400, 0); + builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4), + paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +// Draws two gradients that should look identical (except that one is an RRECT). +TEST_P(AiksTest, FastGradientTestVerticalReversed) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0f, 0); + + std::vector colors = {DlColor::kRed(), DlColor::kBlue(), + DlColor::kGreen()}; + std::vector stops = {0.0, 0.1, 1.0}; + + paint.setColorSource(DlColorSource::MakeLinear({0, 300}, {0, 0}, stops.size(), + colors.data(), stops.data(), + DlTileMode::kClamp)); + + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint); + builder.Translate(400, 0); + builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4), + paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, VerifyNonOptimizedGradient) { + DisplayListBuilder builder; + DlPaint paint; + builder.Translate(100.0f, 0); + + std::vector colors = {DlColor::kRed(), DlColor::kBlue(), + DlColor::kGreen()}; + std::vector stops = {0.0, 0.1, 1.0}; + + // Inset the start and end point to verify that we do not apply + // the fast gradient condition. + paint.setColorSource( + DlColorSource::MakeLinear({0, 150}, {0, 100}, stops.size(), colors.data(), + stops.data(), DlTileMode::kRepeat)); + + paint.setColor(DlColor::kWhite()); + builder.DrawRect(SkRect::MakeXYWH(0, 0, 300, 300), paint); + builder.Translate(400, 0); + builder.DrawRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 300, 300), 4, 4), + paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test.h b/engine/src/flutter/impeller/golden_tests/golden_playground_test.h index 5fdf6720db7..8b480b29c61 100644 --- a/engine/src/flutter/impeller/golden_tests/golden_playground_test.h +++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test.h @@ -28,6 +28,8 @@ class GoldenPlaygroundTest using AiksPlaygroundCallback = std::function(AiksContext& renderer)>; + using AiksDlPlaygroundCallback = std::function()>; + GoldenPlaygroundTest(); ~GoldenPlaygroundTest() override; @@ -45,6 +47,8 @@ class GoldenPlaygroundTest bool OpenPlaygroundHere(AiksPlaygroundCallback callback); + bool OpenPlaygroundHere(const AiksDlPlaygroundCallback& callback); + bool OpenPlaygroundHere(const sk_sp& list); static bool ImGuiBegin(const char* name, diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc index f90aaa91ed6..5a85e5d269a 100644 --- a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc +++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc @@ -210,6 +210,25 @@ bool GoldenPlaygroundTest::OpenPlaygroundHere(Picture picture) { return SaveScreenshot(std::move(screenshot)); } +bool GoldenPlaygroundTest::OpenPlaygroundHere( + const AiksDlPlaygroundCallback& callback) { + AiksContext renderer(GetContext(), typographer_context_); + + std::optional picture; + std::unique_ptr screenshot; + for (int i = 0; i < 2; ++i) { + auto display_list = callback(); + DlDispatcher dispatcher; + display_list->Dispatch(dispatcher); + Picture picture = dispatcher.EndRecordingAsPicture(); + + screenshot = pimpl_->screenshotter->MakeScreenshot(renderer, picture, + pimpl_->window_size); + } + + return SaveScreenshot(std::move(screenshot)); +} + bool GoldenPlaygroundTest::OpenPlaygroundHere( AiksPlaygroundCallback callback) { // NOLINT(performance-unnecessary-value-param) diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test_stub.cc b/engine/src/flutter/impeller/golden_tests/golden_playground_test_stub.cc index bfdb0495e36..0d6b8174709 100644 --- a/engine/src/flutter/impeller/golden_tests/golden_playground_test_stub.cc +++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test_stub.cc @@ -37,6 +37,11 @@ bool GoldenPlaygroundTest::OpenPlaygroundHere( return false; } +bool GoldenPlaygroundTest::OpenPlaygroundHere( + const AiksDlPlaygroundCallback& callback) { + return false; +} + bool GoldenPlaygroundTest::OpenPlaygroundHere( const sk_sp& list) { return false; diff --git a/engine/src/flutter/testing/impeller_golden_tests_output.txt b/engine/src/flutter/testing/impeller_golden_tests_output.txt index 86e979f0a8d..79412098cf5 100644 --- a/engine/src/flutter/testing/impeller_golden_tests_output.txt +++ b/engine/src/flutter/testing/impeller_golden_tests_output.txt @@ -268,9 +268,6 @@ impeller_Play_AiksTest_CanRenderColorFilterWithInvertColors_Vulkan.png impeller_Play_AiksTest_CanRenderColoredRect_Metal.png impeller_Play_AiksTest_CanRenderColoredRect_OpenGLES.png impeller_Play_AiksTest_CanRenderColoredRect_Vulkan.png -impeller_Play_AiksTest_CanRenderConicalGradientWithDitheringDisabled_Metal.png -impeller_Play_AiksTest_CanRenderConicalGradientWithDitheringDisabled_OpenGLES.png -impeller_Play_AiksTest_CanRenderConicalGradientWithDitheringDisabled_Vulkan.png impeller_Play_AiksTest_CanRenderConicalGradientWithDitheringEnabled_Metal.png impeller_Play_AiksTest_CanRenderConicalGradientWithDitheringEnabled_OpenGLES.png impeller_Play_AiksTest_CanRenderConicalGradientWithDitheringEnabled_Vulkan.png @@ -364,9 +361,6 @@ impeller_Play_AiksTest_CanRenderLinearGradientRepeat_Vulkan.png impeller_Play_AiksTest_CanRenderLinearGradientWayManyColorsClamp_Metal.png impeller_Play_AiksTest_CanRenderLinearGradientWayManyColorsClamp_OpenGLES.png impeller_Play_AiksTest_CanRenderLinearGradientWayManyColorsClamp_Vulkan.png -impeller_Play_AiksTest_CanRenderLinearGradientWithDitheringDisabled_Metal.png -impeller_Play_AiksTest_CanRenderLinearGradientWithDitheringDisabled_OpenGLES.png -impeller_Play_AiksTest_CanRenderLinearGradientWithDitheringDisabled_Vulkan.png impeller_Play_AiksTest_CanRenderLinearGradientWithDitheringEnabled_Metal.png impeller_Play_AiksTest_CanRenderLinearGradientWithDitheringEnabled_OpenGLES.png impeller_Play_AiksTest_CanRenderLinearGradientWithDitheringEnabled_Vulkan.png @@ -388,9 +382,6 @@ impeller_Play_AiksTest_CanRenderQuadraticStrokeWithInstantTurn_Vulkan.png impeller_Play_AiksTest_CanRenderRadialGradientManyColors_Metal.png impeller_Play_AiksTest_CanRenderRadialGradientManyColors_OpenGLES.png impeller_Play_AiksTest_CanRenderRadialGradientManyColors_Vulkan.png -impeller_Play_AiksTest_CanRenderRadialGradientWithDitheringDisabled_Metal.png -impeller_Play_AiksTest_CanRenderRadialGradientWithDitheringDisabled_OpenGLES.png -impeller_Play_AiksTest_CanRenderRadialGradientWithDitheringDisabled_Vulkan.png impeller_Play_AiksTest_CanRenderRadialGradientWithDitheringEnabled_Metal.png impeller_Play_AiksTest_CanRenderRadialGradientWithDitheringEnabled_OpenGLES.png impeller_Play_AiksTest_CanRenderRadialGradientWithDitheringEnabled_Vulkan.png @@ -439,9 +430,6 @@ impeller_Play_AiksTest_CanRenderSweepGradientMirror_Vulkan.png impeller_Play_AiksTest_CanRenderSweepGradientRepeat_Metal.png impeller_Play_AiksTest_CanRenderSweepGradientRepeat_OpenGLES.png impeller_Play_AiksTest_CanRenderSweepGradientRepeat_Vulkan.png -impeller_Play_AiksTest_CanRenderSweepGradientWithDitheringDisabled_Metal.png -impeller_Play_AiksTest_CanRenderSweepGradientWithDitheringDisabled_OpenGLES.png -impeller_Play_AiksTest_CanRenderSweepGradientWithDitheringDisabled_Vulkan.png impeller_Play_AiksTest_CanRenderSweepGradientWithDitheringEnabled_Metal.png impeller_Play_AiksTest_CanRenderSweepGradientWithDitheringEnabled_OpenGLES.png impeller_Play_AiksTest_CanRenderSweepGradientWithDitheringEnabled_Vulkan.png