From e37122b5a27bd0b1517dd03c858ce76cfbd07833 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 17 Aug 2023 14:15:33 -0700 Subject: [PATCH 1/5] [Impeller] match Skia gradient clamping behavior (and document) --- impeller/display_list/dl_dispatcher.cc | 10 +--------- lib/ui/painting.dart | 9 ++++++--- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 4ddaee6eb1795..557ca9e764cbe 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -279,17 +279,9 @@ static void ConvertStops(T* gradient, auto* dl_colors = gradient->colors(); auto* dl_stops = gradient->stops(); - if (dl_stops[0] != 0.0) { - colors->emplace_back(skia_conversions::ToColor(dl_colors[0])); - stops->emplace_back(0); - } for (auto i = 0; i < gradient->stop_count(); i++) { colors->emplace_back(skia_conversions::ToColor(dl_colors[i])); - stops->emplace_back(dl_stops[i]); - } - if (stops->back() != 1.0) { - colors->emplace_back(colors->back()); - stops->emplace_back(1.0); + stops->emplace_back(std::clamp(dl_stops[i], 0.0f, 1.0f)); } } diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 2849c4b1c178a..fa27d48ea6eb6 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -4200,7 +4200,8 @@ base class Gradient extends Shader { /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and - /// `color` must therefore only have two entries). + /// `color` must therefore only have two entries). The color stop values will + /// be clamped to the range from 0.0 to 1.0. /// /// The behavior before `from` and after `to` is described by the `tileMode` /// argument. For details, see the [TileMode] enum. @@ -4242,7 +4243,8 @@ base class Gradient extends Shader { /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and - /// `color` must therefore only have two entries). + /// `color` must therefore only have two entries). The color stop values will + /// be clamped to the range from 0.0 to 1.0. /// /// The behavior before and after the radius is described by the `tileMode` /// argument. For details, see the [TileMode] enum. @@ -4304,7 +4306,8 @@ base class Gradient extends Shader { /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and - /// `color` must therefore only have two entries). + /// `color` must therefore only have two entries). The color stop values will + /// be clamped to the range from 0.0 to 1.0. /// /// The behavior before `startAngle` and after `endAngle` is described by the /// `tileMode` argument. For details, see the [TileMode] enum. From 9a33cf46ce9183d991e874f5495163566b47745c Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 17 Aug 2023 17:38:12 -0700 Subject: [PATCH 2/5] clean up conversion utilities --- impeller/display_list/dl_dispatcher.cc | 24 +-- impeller/display_list/skia_conversions.cc | 24 +++ impeller/display_list/skia_conversions.h | 18 +++ .../skia_conversions_unittests.cc | 139 ++++++++++++++++++ 4 files changed, 185 insertions(+), 20 deletions(-) diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 557ca9e764cbe..3bda262b82208 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -269,22 +269,6 @@ static std::vector ToColors(const flutter::DlColor colors[], int count) { return result; } -// Convert display list colors + stops into impeller colors and stops, taking -// care to ensure that the stops always start with 0.0 and end with 1.0. -template -static void ConvertStops(T* gradient, - std::vector* colors, - std::vector* stops) { - FML_DCHECK(gradient->stop_count() >= 2); - - auto* dl_colors = gradient->colors(); - auto* dl_stops = gradient->stops(); - for (auto i = 0; i < gradient->stop_count(); i++) { - colors->emplace_back(skia_conversions::ToColor(dl_colors[i])); - stops->emplace_back(std::clamp(dl_stops[i], 0.0f, 1.0f)); - } -} - static std::optional ToColorSourceType( flutter::DlColorSourceType type) { switch (type) { @@ -341,7 +325,7 @@ void DlDispatcher::setColorSource(const flutter::DlColorSource* source) { auto end_point = skia_conversions::ToPoint(linear->end_point()); std::vector colors; std::vector stops; - ConvertStops(linear, &colors, &stops); + skia_conversions::ConvertStops(linear, colors, stops); auto tile_mode = ToTileMode(linear->tile_mode()); auto matrix = ToMatrix(linear->matrix()); @@ -362,7 +346,7 @@ void DlDispatcher::setColorSource(const flutter::DlColorSource* source) { SkScalar focus_radius = conical_gradient->start_radius(); std::vector colors; std::vector stops; - ConvertStops(conical_gradient, &colors, &stops); + skia_conversions::ConvertStops(conical_gradient, colors, stops); auto tile_mode = ToTileMode(conical_gradient->tile_mode()); auto matrix = ToMatrix(conical_gradient->matrix()); @@ -380,7 +364,7 @@ void DlDispatcher::setColorSource(const flutter::DlColorSource* source) { auto radius = radialGradient->radius(); std::vector colors; std::vector stops; - ConvertStops(radialGradient, &colors, &stops); + skia_conversions::ConvertStops(radialGradient, colors, stops); auto tile_mode = ToTileMode(radialGradient->tile_mode()); auto matrix = ToMatrix(radialGradient->matrix()); @@ -399,7 +383,7 @@ void DlDispatcher::setColorSource(const flutter::DlColorSource* source) { auto end_angle = Degrees(sweepGradient->end()); std::vector colors; std::vector stops; - ConvertStops(sweepGradient, &colors, &stops); + skia_conversions::ConvertStops(sweepGradient, colors, stops); auto tile_mode = ToTileMode(sweepGradient->tile_mode()); auto matrix = ToMatrix(sweepGradient->matrix()); diff --git a/impeller/display_list/skia_conversions.cc b/impeller/display_list/skia_conversions.cc index 0fdeeab6adbd3..ca573b1492e8f 100644 --- a/impeller/display_list/skia_conversions.cc +++ b/impeller/display_list/skia_conversions.cc @@ -185,5 +185,29 @@ std::optional ToPixelFormat(SkColorType type) { return std::nullopt; } +void ConvertStops(const flutter::DlGradientColorSourceBase* gradient, + std::vector& colors, + std::vector& stops) { + FML_DCHECK(gradient->stop_count() >= 2); + + auto* dl_colors = gradient->colors(); + auto* dl_stops = gradient->stops(); + if (dl_stops[0] != 0.0) { + colors.emplace_back(skia_conversions::ToColor(dl_colors[0])); + stops.emplace_back(0); + } + for (auto i = 0; i < gradient->stop_count(); i++) { + colors.emplace_back(skia_conversions::ToColor(dl_colors[i])); + stops.emplace_back(std::clamp(dl_stops[i], 0.0f, 1.0f)); + } + if (dl_stops[gradient->stop_count() - 1] != 1.0) { + colors.emplace_back(colors.back()); + stops.emplace_back(1.0); + } + for (auto i = 1; i < gradient->stop_count(); i++) { + stops[i] = std::clamp(stops[i], stops[i - 1], stops[i]); + } +} + } // namespace skia_conversions } // namespace impeller diff --git a/impeller/display_list/skia_conversions.h b/impeller/display_list/skia_conversions.h index c56cf298a932f..2f74e3d9a347c 100644 --- a/impeller/display_list/skia_conversions.h +++ b/impeller/display_list/skia_conversions.h @@ -5,6 +5,7 @@ #pragma once #include "display_list/dl_color.h" +#include "display_list/effects/dl_color_source.h" #include "impeller/core/formats.h" #include "impeller/geometry/color.h" #include "impeller/geometry/path.h" @@ -46,5 +47,22 @@ Path PathDataFromTextBlob(const sk_sp& blob); std::optional ToPixelFormat(SkColorType type); +// Convert display list colors + stops into impeller colors and stops, taking +// care to ensure that the stops always start with 0.0 and end with 1.0. +// +// The general process is: +// * Ensure that the first gradient stop value is 0.0. If not, insert a new +// stop with a value of 0.0 and use the first gradient color. +// * Ensure the last gradient stop value is 0.0. If not, insert a new stop +// with a value of 1.0 and use the last gradient color. +// * Clamp all gradient values between the values of 0.0 and 1.0. +// * For all stop values, ensure that the values are monotonically increasing +// by clamping each value to a minimum of the previous stop value and itself. +// For example, with stop values of 0.0 0.5 0.4 1.0, we would clamp such that +// the values were 0.0 0.5 0.5 1.0. +void ConvertStops(const flutter::DlGradientColorSourceBase* gradient, + std::vector& colors, + std::vector& stops); + } // namespace skia_conversions } // namespace impeller diff --git a/impeller/display_list/skia_conversions_unittests.cc b/impeller/display_list/skia_conversions_unittests.cc index c714a2fb6c760..43114e7f6b2c7 100644 --- a/impeller/display_list/skia_conversions_unittests.cc +++ b/impeller/display_list/skia_conversions_unittests.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "display_list/dl_color.h" +#include "display_list/dl_tile_mode.h" #include "flutter/testing/testing.h" #include "impeller/display_list/skia_conversions.h" #include "impeller/geometry/scalar.h" @@ -23,5 +25,142 @@ TEST(SkiaConversionsTest, ToColor) { ASSERT_TRUE(ScalarNearlyEqual(converted_color.blue, 0x20 * (1.0f / 255))); } +TEST(SkiaConversionsTest, GradientStopConversion) { + // Typical gradient. + { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed(), + flutter::DlColor::kGreen()}; + std::vector stops = {0.0, 0.5, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 3, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); + } + + // Missing 0.0 first stop gradient. + { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed()}; + std::vector stops = {0.5, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 2, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // First color is inserted as blue. + ASSERT_TRUE(ScalarNearlyEqual(converted_colors[0].blue, 1.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); + } + + // Missing 1.0 last stop gradient. + { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed()}; + std::vector stops = {0.0, .5}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 2, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // Last color is inserted as red. + ASSERT_TRUE(ScalarNearlyEqual(converted_colors[2].red, 1.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); + } + + // Values greater than 1.0. + { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kGreen(), + flutter::DlColor::kRed()}; + std::vector stops = {0.0, 100, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 3, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // Value is clamped to 1.0 + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 1.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); + } + + // Non monotonic values. + { + std::vector colors = { + flutter::DlColor::kBlue(), flutter::DlColor::kGreen(), + flutter::DlColor::kGreen(), flutter::DlColor::kRed()}; + std::vector stops = {0.0, 0.5, 0.4, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 4, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // Value is clamped to 0.5 + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[3], 1.0f)); + } +} + } // namespace testing } // namespace impeller From 2889fc0280d3eec8377257a76b38fcbb36d3514b Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 17 Aug 2023 17:48:25 -0700 Subject: [PATCH 3/5] fix doc comment. --- impeller/display_list/skia_conversions.h | 27 ++++++++++++------------ 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/impeller/display_list/skia_conversions.h b/impeller/display_list/skia_conversions.h index 2f74e3d9a347c..8eb3e16d512c8 100644 --- a/impeller/display_list/skia_conversions.h +++ b/impeller/display_list/skia_conversions.h @@ -47,19 +47,20 @@ Path PathDataFromTextBlob(const sk_sp& blob); std::optional ToPixelFormat(SkColorType type); -// Convert display list colors + stops into impeller colors and stops, taking -// care to ensure that the stops always start with 0.0 and end with 1.0. -// -// The general process is: -// * Ensure that the first gradient stop value is 0.0. If not, insert a new -// stop with a value of 0.0 and use the first gradient color. -// * Ensure the last gradient stop value is 0.0. If not, insert a new stop -// with a value of 1.0 and use the last gradient color. -// * Clamp all gradient values between the values of 0.0 and 1.0. -// * For all stop values, ensure that the values are monotonically increasing -// by clamping each value to a minimum of the previous stop value and itself. -// For example, with stop values of 0.0 0.5 0.4 1.0, we would clamp such that -// the values were 0.0 0.5 0.5 1.0. +/// @brief Convert display list colors + stops into impeller colors and stops, +/// taking care to ensure that the stops monotonically increase from 0.0 to 1.0. +/// +/// The general process is: +/// * Ensure that the first gradient stop value is 0.0. If not, insert a new +/// stop with a value of 0.0 and use the first gradient color as this new +/// stops color. +/// * Ensure the last gradient stop value is 1.0. If not, insert a new stop +/// with a value of 1.0 and use the last gradient color as this stops color. +/// * Clamp all gradient values between the values of 0.0 and 1.0. +/// * For all stop values, ensure that the values are monotonically increasing +/// by clamping each value to a minimum of the previous stop value and itself. +/// For example, with stop values of 0.0, 0.5, 0.4, 1.0, we would clamp such +/// that the values were 0.0, 0.5, 0.5, 1.0. void ConvertStops(const flutter::DlGradientColorSourceBase* gradient, std::vector& colors, std::vector& stops); From a2684bdd45da1ddb276f6bb1a1d731e2e5d460d7 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 27 Sep 2023 09:50:14 -0700 Subject: [PATCH 4/5] update docs --- lib/ui/painting.dart | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index fbf354515799a..638a257301d3b 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -4191,8 +4191,11 @@ base class Gradient extends Shader { /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and - /// `color` must therefore only have two entries). The color stop values will - /// be clamped to the range from 0.0 to 1.0. + /// `color` must therefore only have two entries). Stop values less than 0.0 + /// will be rounded up to 0.0 and stop values greater than 1.0 will be rounded + /// down to 1.0. Each stop value must be greater than or equal to the previous + /// stop value. Stop values that do not meet this criteria will be rounded up + /// to the previous stop value. /// /// The behavior before `from` and after `to` is described by the `tileMode` /// argument. For details, see the [TileMode] enum. @@ -4234,8 +4237,11 @@ base class Gradient extends Shader { /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and - /// `color` must therefore only have two entries). The color stop values will - /// be clamped to the range from 0.0 to 1.0. + /// `color` must therefore only have two entries). Stop values less than 0.0 + /// will be rounded up to 0.0 and stop values greater than 1.0 will be rounded + /// down to 1.0. Each stop value must be greater than or equal to the previous + /// stop value. Stop values that do not meet this criteria will be rounded up + /// to the previous stop value. /// /// The behavior before and after the radius is described by the `tileMode` /// argument. For details, see the [TileMode] enum. @@ -4297,8 +4303,11 @@ base class Gradient extends Shader { /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 /// that specifies where `color[i]` begins in the gradient. If `colorStops` is /// not provided, then only two stops, at 0.0 and 1.0, are implied (and - /// `color` must therefore only have two entries). The color stop values will - /// be clamped to the range from 0.0 to 1.0. + /// `color` must therefore only have two entries). Stop values less than 0.0 + /// will be rounded up to 0.0 and stop values greater than 1.0 will be rounded + /// down to 1.0. Each stop value must be greater than or equal to the previous + /// stop value. Stop values that do not meet this criteria will be rounded up + /// to the previous stop value. /// /// The behavior before `startAngle` and after `endAngle` is described by the /// `tileMode` argument. For details, see the [TileMode] enum. From 6efecf208c5fdfe144571b7b08979d6877a845e3 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 27 Sep 2023 11:37:16 -0700 Subject: [PATCH 5/5] split up tests. --- .../skia_conversions_unittests.cc | 260 +++++++++--------- 1 file changed, 127 insertions(+), 133 deletions(-) diff --git a/impeller/display_list/skia_conversions_unittests.cc b/impeller/display_list/skia_conversions_unittests.cc index 7800f9e4785d1..eaf1f571ab7f5 100644 --- a/impeller/display_list/skia_conversions_unittests.cc +++ b/impeller/display_list/skia_conversions_unittests.cc @@ -27,139 +27,133 @@ TEST(SkiaConversionsTest, ToColor) { TEST(SkiaConversionsTest, GradientStopConversion) { // Typical gradient. - { - std::vector colors = {flutter::DlColor::kBlue(), - flutter::DlColor::kRed(), - flutter::DlColor::kGreen()}; - std::vector stops = {0.0, 0.5, 1.0}; - const auto gradient = - flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // - SkPoint::Make(1.0, 1.0), // - 3, // - colors.data(), // - stops.data(), // - flutter::DlTileMode::kClamp, // - nullptr // - ); - - std::vector converted_colors; - std::vector converted_stops; - skia_conversions::ConvertStops(gradient.get(), converted_colors, - converted_stops); - - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); - } - - // Missing 0.0 first stop gradient. - { - std::vector colors = {flutter::DlColor::kBlue(), - flutter::DlColor::kRed()}; - std::vector stops = {0.5, 1.0}; - const auto gradient = - flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // - SkPoint::Make(1.0, 1.0), // - 2, // - colors.data(), // - stops.data(), // - flutter::DlTileMode::kClamp, // - nullptr // - ); - - std::vector converted_colors; - std::vector converted_stops; - skia_conversions::ConvertStops(gradient.get(), converted_colors, - converted_stops); - - // First color is inserted as blue. - ASSERT_TRUE(ScalarNearlyEqual(converted_colors[0].blue, 1.0f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); - } - - // Missing 1.0 last stop gradient. - { - std::vector colors = {flutter::DlColor::kBlue(), - flutter::DlColor::kRed()}; - std::vector stops = {0.0, .5}; - const auto gradient = - flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // - SkPoint::Make(1.0, 1.0), // - 2, // - colors.data(), // - stops.data(), // - flutter::DlTileMode::kClamp, // - nullptr // - ); - - std::vector converted_colors; - std::vector converted_stops; - skia_conversions::ConvertStops(gradient.get(), converted_colors, - converted_stops); - - // Last color is inserted as red. - ASSERT_TRUE(ScalarNearlyEqual(converted_colors[2].red, 1.0f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); - } - - // Values greater than 1.0. - { - std::vector colors = {flutter::DlColor::kBlue(), - flutter::DlColor::kGreen(), - flutter::DlColor::kRed()}; - std::vector stops = {0.0, 100, 1.0}; - const auto gradient = - flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // - SkPoint::Make(1.0, 1.0), // - 3, // - colors.data(), // - stops.data(), // - flutter::DlTileMode::kClamp, // - nullptr // - ); - - std::vector converted_colors; - std::vector converted_stops; - skia_conversions::ConvertStops(gradient.get(), converted_colors, - converted_stops); - - // Value is clamped to 1.0 - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 1.0f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); - } - - // Non monotonic values. - { - std::vector colors = { - flutter::DlColor::kBlue(), flutter::DlColor::kGreen(), - flutter::DlColor::kGreen(), flutter::DlColor::kRed()}; - std::vector stops = {0.0, 0.5, 0.4, 1.0}; - const auto gradient = - flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // - SkPoint::Make(1.0, 1.0), // - 4, // - colors.data(), // - stops.data(), // - flutter::DlTileMode::kClamp, // - nullptr // - ); - - std::vector converted_colors; - std::vector converted_stops; - skia_conversions::ConvertStops(gradient.get(), converted_colors, - converted_stops); - - // Value is clamped to 0.5 - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 0.5f)); - ASSERT_TRUE(ScalarNearlyEqual(converted_stops[3], 1.0f)); - } + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed(), + flutter::DlColor::kGreen()}; + std::vector stops = {0.0, 0.5, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 3, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); +} + +TEST(SkiaConversionsTest, GradientMissing0) { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed()}; + std::vector stops = {0.5, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 2, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // First color is inserted as blue. + ASSERT_TRUE(ScalarNearlyEqual(converted_colors[0].blue, 1.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); +} + +TEST(SkiaConversionsTest, GradientMissingLastValue) { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed()}; + std::vector stops = {0.0, .5}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 2, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // Last color is inserted as red. + ASSERT_TRUE(ScalarNearlyEqual(converted_colors[2].red, 1.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); +} + +TEST(SkiaConversionsTest, GradientStopGreaterThan1) { + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kGreen(), + flutter::DlColor::kRed()}; + std::vector stops = {0.0, 100, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 3, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // Value is clamped to 1.0 + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 1.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 1.0f)); +} + +TEST(SkiaConversionsTest, GradientConversionNonMonotonic) { + std::vector colors = { + flutter::DlColor::kBlue(), flutter::DlColor::kGreen(), + flutter::DlColor::kGreen(), flutter::DlColor::kRed()}; + std::vector stops = {0.0, 0.5, 0.4, 1.0}; + const auto gradient = + flutter::DlColorSource::MakeLinear(SkPoint::Make(0, 0), // + SkPoint::Make(1.0, 1.0), // + 4, // + colors.data(), // + stops.data(), // + flutter::DlTileMode::kClamp, // + nullptr // + ); + + std::vector converted_colors; + std::vector converted_stops; + skia_conversions::ConvertStops(gradient.get(), converted_colors, + converted_stops); + + // Value is clamped to 0.5 + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[0], 0.0f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[1], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[2], 0.5f)); + ASSERT_TRUE(ScalarNearlyEqual(converted_stops[3], 1.0f)); } } // namespace testing