diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index 841222b6757c7..3a921289ccdd9 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -102,7 +102,7 @@ std::shared_ptr Paint::WithColorFilter( /// A color matrix which inverts colors. // clang-format off -constexpr ColorFilterContents::ColorMatrix kColorInversion = { +constexpr ColorMatrix kColorInversion = { .array = { -1.0, 0, 0, 1.0, 0, // 0, -1.0, 0, 1.0, 0, // diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index a714866e63fa7..ec9fa3021a37f 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -491,7 +491,7 @@ static std::optional ToColorFilterProc( } case flutter::DlColorFilterType::kMatrix: { const flutter::DlMatrixColorFilter* dl_matrix = filter->asMatrix(); - impeller::FilterContents::ColorMatrix color_matrix; + impeller::ColorMatrix color_matrix; dl_matrix->get_matrix(color_matrix.array); return [color_matrix](FilterInput::Ref input) { return ColorFilterContents::MakeColorMatrix({std::move(input)}, diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 706abfa826fd1..1a00060c47036 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -29,11 +29,6 @@ class FilterContents : public Contents { kInner, }; - // Domain is kRGBA, we may decide to support more color modes later. - struct ColorMatrix { - float array[20]; - }; - enum class MorphType { kDilate, kErode }; static std::shared_ptr MakeDirectionalGaussianBlur( diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 88a3861d0c702..bbe4ce286828a 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -1748,7 +1748,7 @@ TEST_P(EntityTest, ColorMatrixFilterCoverageIsCorrect) { fill->SetColor(Color::Coral()); // Set the color matrix filter. - FilterContents::ColorMatrix matrix = { + ColorMatrix matrix = { 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, // @@ -1775,7 +1775,7 @@ TEST_P(EntityTest, ColorMatrixFilterEditable) { auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { // UI state. - static FilterContents::ColorMatrix color_matrix = { + static ColorMatrix color_matrix = { 1, 0, 0, 0, 0, // 0, 3, 0, 0, 0, // 0, 0, 1, 0, 0, // @@ -1931,238 +1931,6 @@ TEST_P(EntityTest, SrgbToLinearFilter) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -TEST_P(EntityTest, TTTBlendColor) { - { - Color src = {1, 0, 0, 0.5}; - Color dst = {1, 0, 1, 1}; - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kClear), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSource), - Color(1, 0, 0, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestination), - Color(1, 0, 1, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOver), - Color(1.5, 0, 0.5, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOver), - Color(1, 0, 1, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceIn), - Color(1, 0, 0, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationIn), - Color(0.5, 0, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOut), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOut), - Color(0.5, 0, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceATop), - Color(1.5, 0, 0.5, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationATop), - Color(0.5, 0, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kXor), - Color(0.5, 0, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kPlus), Color(1, 0, 1, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kModulate), - Color(1, 0, 0, 0.5)); - } - - { - Color src = {1, 1, 0, 1}; - Color dst = {1, 0, 1, 1}; - - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kClear), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSource), - Color(1, 1, 0, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestination), - Color(1, 0, 1, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOver), - Color(1, 1, 0, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOver), - Color(1, 0, 1, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceIn), - Color(1, 1, 0, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationIn), - Color(1, 0, 1, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOut), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOut), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceATop), - Color(1, 1, 0, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationATop), - Color(1, 0, 1, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kXor), Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kPlus), Color(1, 1, 1, 1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kModulate), - Color(1, 0, 0, 1)); - } - - { - Color src = {1, 1, 0, 0.2}; - Color dst = {1, 1, 1, 0.5}; - - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kClear), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSource), - Color(1, 1, 0, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestination), - Color(1, 1, 1, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOver), - Color(1.8, 1.8, 0.8, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOver), - Color(1.5, 1.5, 1, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceIn), - Color(0.5, 0.5, 0, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationIn), - Color(0.2, 0.2, 0.2, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOut), - Color(0.5, 0.5, 0, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOut), - Color(0.8, 0.8, 0.8, 0.4)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceATop), - Color(1.3, 1.3, 0.8, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationATop), - Color(0.7, 0.7, 0.2, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kXor), - Color(1.3, 1.3, 0.8, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kPlus), - Color(1, 1, 1, 0.7)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kModulate), - Color(1, 1, 0, 0.1)); - } - - { - Color src = {1, 0.5, 0, 0.2}; - Color dst = {1, 1, 0.5, 0.5}; - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kClear), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSource), - Color(1, 0.5, 0, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestination), - Color(1, 1, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOver), - Color(1.8, 1.3, 0.4, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOver), - Color(1.5, 1.25, 0.5, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceIn), - Color(0.5, 0.25, 0, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationIn), - Color(0.2, 0.2, 0.1, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOut), - Color(0.5, 0.25, 0, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOut), - Color(0.8, 0.8, 0.4, 0.4)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceATop), - Color(1.3, 1.05, 0.4, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationATop), - Color(0.7, 0.45, 0.1, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kXor), - Color(1.3, 1.05, 0.4, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kPlus), - Color(1, 1, 0.5, 0.7)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kModulate), - Color(1, 0.5, 0, 0.1)); - } - - { - Color src = {0.5, 0.5, 0, 0.2}; - Color dst = {0, 1, 0.5, 0.5}; - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kClear), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSource), - Color(0.5, 0.5, 0, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestination), - Color(0, 1, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOver), - Color(0.5, 1.3, 0.4, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOver), - Color(0.25, 1.25, 0.5, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceIn), - Color(0.25, 0.25, 0, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationIn), - Color(0, 0.2, 0.1, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOut), - Color(0.25, 0.25, 0, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOut), - Color(0, 0.8, 0.4, 0.4)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceATop), - Color(0.25, 1.05, 0.4, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationATop), - Color(0.25, 0.45, 0.1, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kXor), - Color(0.25, 1.05, 0.4, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kPlus), - Color(0.5, 1, 0.5, 0.7)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kModulate), - Color(0, 0.5, 0, 0.1)); - } - - { - Color src = {0.5, 0.5, 0.2, 0.2}; - Color dst = {0.2, 1, 0.5, 0.5}; - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kClear), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSource), - Color(0.5, 0.5, 0.2, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestination), - Color(0.2, 1, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOver), - Color(0.66, 1.3, 0.6, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOver), - Color(0.45, 1.25, 0.6, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceIn), - Color(0.25, 0.25, 0.1, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationIn), - Color(0.04, 0.2, 0.1, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOut), - Color(0.25, 0.25, 0.1, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOut), - Color(0.16, 0.8, 0.4, 0.4)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceATop), - Color(0.41, 1.05, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationATop), - Color(0.29, 0.45, 0.2, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kXor), - Color(0.41, 1.05, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kPlus), - Color(0.7, 1, 0.7, 0.7)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kModulate), - Color(0.1, 0.5, 0.1, 0.1)); - } - - { - Color src = {0.5, 0.5, 0.2, 0.2}; - Color dst = {0.2, 0.2, 0.5, 0.5}; - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kClear), - Color(0, 0, 0, 0)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSource), - Color(0.5, 0.5, 0.2, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestination), - Color(0.2, 0.2, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOver), - Color(0.66, 0.66, 0.6, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOver), - Color(0.45, 0.45, 0.6, 0.6)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceIn), - Color(0.25, 0.25, 0.1, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationIn), - Color(0.04, 0.04, 0.1, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceOut), - Color(0.25, 0.25, 0.1, 0.1)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationOut), - Color(0.16, 0.16, 0.4, 0.4)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kSourceATop), - Color(0.41, 0.41, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kDestinationATop), - Color(0.29, 0.29, 0.2, 0.2)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kXor), - Color(0.41, 0.41, 0.5, 0.5)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kPlus), - Color(0.7, 0.7, 0.7, 0.7)); - ASSERT_EQ(Color::BlendColor(src, dst, BlendMode::kModulate), - Color(0.1, 0.1, 0.1, 0.1)); - } -} - TEST_P(EntityTest, AtlasContentsSubAtlas) { auto boston = CreateTextureForFixture("boston.jpg"); diff --git a/impeller/geometry/color.cc b/impeller/geometry/color.cc index d51ee111f5b4d..d4c88998b1de2 100644 --- a/impeller/geometry/color.cc +++ b/impeller/geometry/color.cc @@ -199,9 +199,9 @@ static constexpr Color FromRGB(Vector3 color, Scalar alpha) { return {color.x, color.y, color.z, alpha}; } -Color Color::BlendColor(const Color& src, - const Color& dst, - BlendMode blend_mode) { +Color Color::Blend(const Color& src, BlendMode blend_mode) const { + const Color& dst = *this; + static auto apply_rgb_srcover_alpha = [&](auto f) -> Color { return Color(f(src.red, dst.red), f(src.green, dst.green), f(src.blue, dst.blue), @@ -372,6 +372,38 @@ Color Color::BlendColor(const Color& src, } } +Color Color::ApplyColorMatrix(const ColorMatrix& color_matrix) const { + auto* c = color_matrix.array; + return Color( + c[0] * red + c[1] * green + c[2] * blue + c[3] * alpha + c[4], + c[5] * red + c[6] * green + c[7] * blue + c[8] * alpha + c[9], + c[10] * red + c[11] * green + c[12] * blue + c[13] * alpha + c[14], + c[15] * red + c[16] * green + c[17] * blue + c[18] * alpha + c[19]) + .Clamp01(); +} + +Color Color::LinearToSRGB() const { + static auto conversion = [](Scalar component) { + if (component <= 0.0031308) { + return component * 12.92; + } + return 1.055 * pow(component, (1.0 / 2.4)) - 0.055; + }; + + return Color(conversion(red), conversion(green), conversion(blue), alpha); +} + +Color Color::SRGBToLinear() const { + static auto conversion = [](Scalar component) { + if (component <= 0.04045) { + return component / 12.92; + } + return pow((component + 0.055) / 1.055, 2.4); + }; + + return Color(conversion(red), conversion(green), conversion(blue), alpha); +} + std::string ColorToString(const Color& color) { return SPrintF("R=%.1f,G=%.1f,B=%.1f,A=%.1f", // color.red, // diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 89fe96ef809f7..7991c6bc38a0d 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -93,6 +94,26 @@ enum class BlendMode { const char* BlendModeToString(BlendMode blend_mode); +/// 4x5 matrix for transforming the color and alpha components of a Bitmap. +/// +/// [ a, b, c, d, e, +/// f, g, h, i, j, +/// k, l, m, n, o, +/// p, q, r, s, t ] +/// +/// When applied to a color [R, G, B, A], the resulting color is computed as: +/// +/// R’ = a*R + b*G + c*B + d*A + e; +/// G’ = f*R + g*G + h*B + i*A + j; +/// B’ = k*R + l*G + m*B + n*A + o; +/// A’ = p*R + q*G + r*B + s*A + t; +/// +/// That resulting color [R’, G’, B’, A’] then has each channel clamped to the 0 +/// to 1 range. +struct ColorMatrix { + Scalar array[20]; +}; + /** * Represents a RGBA color */ @@ -166,12 +187,17 @@ struct Color { * @param t A value between 0.0f and 1.0f, inclusive. * @return constexpr Color */ - constexpr static Color lerp(Color a, Color b, Scalar t) { + constexpr static Color Lerp(Color a, Color b, Scalar t) { Scalar tt = 1.0f - t; return {a.red * tt + b.red * t, a.green * tt + b.green * t, a.blue * tt + b.blue * t, a.alpha * tt + b.alpha * t}; } + constexpr Color Clamp01() const { + return Color(std::clamp(red, 0.0f, 1.0f), std::clamp(green, 0.0f, 1.0f), + std::clamp(blue, 0.0f, 1.0f), std::clamp(alpha, 0.0f, 1.0f)); + } + /** * @brief Convert to R8G8B8A8 representation. * @@ -780,9 +806,34 @@ struct Color { }; } - static Color BlendColor(const Color& src, - const Color& dst, - BlendMode blend_mode); + /// @brief Blends an unpremultiplied destination color into a given + /// unpremultiplied source color to form a new unpremultiplied color. + /// + /// If either the source or destination are premultiplied, the result + /// will be incorrect. + Color Blend(const Color& source, BlendMode blend_mode) const; + + /// @brief A color filter that transforms colors through a 4x5 color matrix. + /// + /// This filter can be used to change the saturation of pixels, convert + /// from YUV to RGB, etc. + /// + /// Each channel of the output color is clamped to the 0 to 1 range. + /// + /// @see `ColorMatrix` + Color ApplyColorMatrix(const ColorMatrix& color_matrix) const; + + /// @brief Convert the color from linear space to sRGB space. + /// + /// The color is assumed to be unpremultiplied. If the color is + /// premultipled, the conversion output will be incorrect. + Color LinearToSRGB() const; + + /// @brief Convert the color from sRGB space to linear space. + /// + /// The color is assumed to be unpremultiplied. If the color is + /// premultipled, the conversion output will be incorrect. + Color SRGBToLinear() const; Color operator*(const Color& c) const { return Color(red * c.red, green * c.green, blue * c.blue, alpha * c.alpha); diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 951a405002373..cd59674492235 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -1411,20 +1411,40 @@ TEST(GeometryTest, ColorLerp) { Color a(0.0, 0.0, 0.0, 0.0); Color b(1.0, 1.0, 1.0, 1.0); - ASSERT_COLOR_NEAR(Color::lerp(a, b, 0.5), Color(0.5, 0.5, 0.5, 0.5)); - ASSERT_COLOR_NEAR(Color::lerp(a, b, 0.0), a); - ASSERT_COLOR_NEAR(Color::lerp(a, b, 1.0), b); - ASSERT_COLOR_NEAR(Color::lerp(a, b, 0.2), Color(0.2, 0.2, 0.2, 0.2)); + ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.5, 0.5, 0.5, 0.5)); + ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a); + ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b); + ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.2, 0.2, 0.2, 0.2)); } { Color a(0.2, 0.4, 1.0, 0.5); Color b(0.4, 1.0, 0.2, 0.3); - ASSERT_COLOR_NEAR(Color::lerp(a, b, 0.5), Color(0.3, 0.7, 0.6, 0.4)); - ASSERT_COLOR_NEAR(Color::lerp(a, b, 0.0), a); - ASSERT_COLOR_NEAR(Color::lerp(a, b, 1.0), b); - ASSERT_COLOR_NEAR(Color::lerp(a, b, 0.2), Color(0.24, 0.52, 0.84, 0.46)); + ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.3, 0.7, 0.6, 0.4)); + ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a); + ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b); + ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.24, 0.52, 0.84, 0.46)); + } +} + +TEST(GeometryTest, ColorClamp01) { + { + Color result = Color(0.5, 0.5, 0.5, 0.5).Clamp01(); + Color expected = Color(0.5, 0.5, 0.5, 0.5); + ASSERT_COLOR_NEAR(result, expected); + } + + { + Color result = Color(-1, -1, -1, -1).Clamp01(); + Color expected = Color(0, 0, 0, 0); + ASSERT_COLOR_NEAR(result, expected); + } + + { + Color result = Color(2, 2, 2, 2).Clamp01(); + Color expected = Color(1, 1, 1, 1); + ASSERT_COLOR_NEAR(result, expected); } } @@ -1448,6 +1468,247 @@ TEST(GeometryTest, ColorMakeRGBA8) { } } +TEST(GeometryTest, ColorBlend) { + { + Color src = {1, 0, 0, 0.5}; + Color dst = {1, 0, 1, 1}; + ASSERT_EQ(dst.Blend(src, BlendMode::kClear), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSource), Color(1, 0, 0, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestination), Color(1, 0, 1, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOver), Color(1.5, 0, 0.5, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOver), Color(1, 0, 1, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceIn), Color(1, 0, 0, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationIn), + Color(0.5, 0, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOut), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOut), + Color(0.5, 0, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceATop), Color(1.5, 0, 0.5, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationATop), + Color(0.5, 0, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kXor), Color(0.5, 0, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kPlus), Color(1, 0, 1, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kModulate), Color(1, 0, 0, 0.5)); + } + + { + Color src = {1, 1, 0, 1}; + Color dst = {1, 0, 1, 1}; + + ASSERT_EQ(dst.Blend(src, BlendMode::kClear), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSource), Color(1, 1, 0, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestination), Color(1, 0, 1, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOver), Color(1, 1, 0, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOver), Color(1, 0, 1, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceIn), Color(1, 1, 0, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationIn), Color(1, 0, 1, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOut), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOut), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceATop), Color(1, 1, 0, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationATop), Color(1, 0, 1, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kXor), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kPlus), Color(1, 1, 1, 1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kModulate), Color(1, 0, 0, 1)); + } + + { + Color src = {1, 1, 0, 0.2}; + Color dst = {1, 1, 1, 0.5}; + + ASSERT_EQ(dst.Blend(src, BlendMode::kClear), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSource), Color(1, 1, 0, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestination), Color(1, 1, 1, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOver), + Color(1.8, 1.8, 0.8, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOver), + Color(1.5, 1.5, 1, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceIn), Color(0.5, 0.5, 0, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationIn), + Color(0.2, 0.2, 0.2, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOut), Color(0.5, 0.5, 0, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOut), + Color(0.8, 0.8, 0.8, 0.4)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceATop), + Color(1.3, 1.3, 0.8, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationATop), + Color(0.7, 0.7, 0.2, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kXor), Color(1.3, 1.3, 0.8, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kPlus), Color(1, 1, 1, 0.7)); + ASSERT_EQ(dst.Blend(src, BlendMode::kModulate), Color(1, 1, 0, 0.1)); + } + + { + Color src = {1, 0.5, 0, 0.2}; + Color dst = {1, 1, 0.5, 0.5}; + ASSERT_EQ(dst.Blend(src, BlendMode::kClear), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSource), Color(1, 0.5, 0, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestination), Color(1, 1, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOver), + Color(1.8, 1.3, 0.4, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOver), + Color(1.5, 1.25, 0.5, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceIn), Color(0.5, 0.25, 0, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationIn), + Color(0.2, 0.2, 0.1, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOut), Color(0.5, 0.25, 0, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOut), + Color(0.8, 0.8, 0.4, 0.4)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceATop), + Color(1.3, 1.05, 0.4, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationATop), + Color(0.7, 0.45, 0.1, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kXor), Color(1.3, 1.05, 0.4, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kPlus), Color(1, 1, 0.5, 0.7)); + ASSERT_EQ(dst.Blend(src, BlendMode::kModulate), Color(1, 0.5, 0, 0.1)); + } + + { + Color src = {0.5, 0.5, 0, 0.2}; + Color dst = {0, 1, 0.5, 0.5}; + ASSERT_EQ(dst.Blend(src, BlendMode::kClear), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSource), Color(0.5, 0.5, 0, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestination), Color(0, 1, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOver), + Color(0.5, 1.3, 0.4, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOver), + Color(0.25, 1.25, 0.5, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceIn), Color(0.25, 0.25, 0, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationIn), + Color(0, 0.2, 0.1, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOut), Color(0.25, 0.25, 0, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOut), + Color(0, 0.8, 0.4, 0.4)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceATop), + Color(0.25, 1.05, 0.4, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationATop), + Color(0.25, 0.45, 0.1, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kXor), Color(0.25, 1.05, 0.4, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kPlus), Color(0.5, 1, 0.5, 0.7)); + ASSERT_EQ(dst.Blend(src, BlendMode::kModulate), Color(0, 0.5, 0, 0.1)); + } + + { + Color src = {0.5, 0.5, 0.2, 0.2}; + Color dst = {0.2, 1, 0.5, 0.5}; + ASSERT_EQ(dst.Blend(src, BlendMode::kClear), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSource), Color(0.5, 0.5, 0.2, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestination), Color(0.2, 1, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOver), + Color(0.66, 1.3, 0.6, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOver), + Color(0.45, 1.25, 0.6, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceIn), + Color(0.25, 0.25, 0.1, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationIn), + Color(0.04, 0.2, 0.1, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOut), + Color(0.25, 0.25, 0.1, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOut), + Color(0.16, 0.8, 0.4, 0.4)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceATop), + Color(0.41, 1.05, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationATop), + Color(0.29, 0.45, 0.2, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kXor), Color(0.41, 1.05, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kPlus), Color(0.7, 1, 0.7, 0.7)); + ASSERT_EQ(dst.Blend(src, BlendMode::kModulate), Color(0.1, 0.5, 0.1, 0.1)); + } + + { + Color src = {0.5, 0.5, 0.2, 0.2}; + Color dst = {0.2, 0.2, 0.5, 0.5}; + ASSERT_EQ(dst.Blend(src, BlendMode::kClear), Color(0, 0, 0, 0)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSource), Color(0.5, 0.5, 0.2, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestination), + Color(0.2, 0.2, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOver), + Color(0.66, 0.66, 0.6, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOver), + Color(0.45, 0.45, 0.6, 0.6)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceIn), + Color(0.25, 0.25, 0.1, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationIn), + Color(0.04, 0.04, 0.1, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceOut), + Color(0.25, 0.25, 0.1, 0.1)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationOut), + Color(0.16, 0.16, 0.4, 0.4)); + ASSERT_EQ(dst.Blend(src, BlendMode::kSourceATop), + Color(0.41, 0.41, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kDestinationATop), + Color(0.29, 0.29, 0.2, 0.2)); + ASSERT_EQ(dst.Blend(src, BlendMode::kXor), Color(0.41, 0.41, 0.5, 0.5)); + ASSERT_EQ(dst.Blend(src, BlendMode::kPlus), Color(0.7, 0.7, 0.7, 0.7)); + ASSERT_EQ(dst.Blend(src, BlendMode::kModulate), Color(0.1, 0.1, 0.1, 0.1)); + } +} + +TEST(GeometryTest, ColorApplyColorMatrix) { + { + ColorMatrix color_matrix = { + 1, 1, 1, 1, 1, // + 1, 1, 1, 1, 1, // + 1, 1, 1, 1, 1, // + 1, 1, 1, 1, 1, // + }; + auto result = Color::White().ApplyColorMatrix(color_matrix); + auto expected = Color(1, 1, 1, 1); + ASSERT_COLOR_NEAR(result, expected); + } + + { + ColorMatrix color_matrix = { + 0.1, 0, 0, 0, 0.01, // + 0, 0.2, 0, 0, 0.02, // + 0, 0, 0.3, 0, 0.03, // + 0, 0, 0, 0.4, 0.04, // + }; + auto result = Color::White().ApplyColorMatrix(color_matrix); + auto expected = Color(0.11, 0.22, 0.33, 0.44); + ASSERT_COLOR_NEAR(result, expected); + } +} + +TEST(GeometryTest, ColorLinearToSRGB) { + { + auto result = Color::White().LinearToSRGB(); + auto expected = Color(1, 1, 1, 1); + ASSERT_COLOR_NEAR(result, expected); + } + + { + auto result = Color::BlackTransparent().LinearToSRGB(); + auto expected = Color(0, 0, 0, 0); + ASSERT_COLOR_NEAR(result, expected); + } + + { + auto result = Color(0.2, 0.4, 0.6, 0.8).LinearToSRGB(); + auto expected = Color(0.484529, 0.665185, 0.797738, 0.8); + ASSERT_COLOR_NEAR(result, expected); + } +} + +TEST(GeometryTest, ColorSRGBToLinear) { + { + auto result = Color::White().SRGBToLinear(); + auto expected = Color(1, 1, 1, 1); + ASSERT_COLOR_NEAR(result, expected); + } + + { + auto result = Color::BlackTransparent().SRGBToLinear(); + auto expected = Color(0, 0, 0, 0); + ASSERT_COLOR_NEAR(result, expected); + } + + { + auto result = Color(0.2, 0.4, 0.6, 0.8).SRGBToLinear(); + auto expected = Color(0.0331048, 0.132868, 0.318547, 0.8); + ASSERT_COLOR_NEAR(result, expected); + } +} + #define _BLEND_MODE_NAME_CHECK(blend_mode) \ case BlendMode::k##blend_mode: \ ASSERT_STREQ(result, #blend_mode); \ @@ -2162,8 +2423,8 @@ TEST(GeometryTest, Gradient) { std::vector lerped_colors = { Color::Red(), Color::Blue(), - Color::lerp(Color::Blue(), Color::Green(), 0.3333), - Color::lerp(Color::Blue(), Color::Green(), 0.6666), + Color::Lerp(Color::Blue(), Color::Green(), 0.3333), + Color::Lerp(Color::Blue(), Color::Green(), 0.6666), Color::Green(), }; ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, lerped_colors); diff --git a/impeller/geometry/gradient.cc b/impeller/geometry/gradient.cc index becaf7c2c1f60..8bdfbe2cb2507 100644 --- a/impeller/geometry/gradient.cc +++ b/impeller/geometry/gradient.cc @@ -75,7 +75,7 @@ GradientData CreateGradientBuffer(const std::vector& colors, } else if (scaled_i < next_stop) { // We're still between the current stop and the next stop. auto t = (scaled_i - previous_stop) / (next_stop - previous_stop); - auto mixed_color = Color::lerp(previous_color, next_color, t); + auto mixed_color = Color::Lerp(previous_color, next_color, t); AppendColor(mixed_color, &data); } else { @@ -87,7 +87,7 @@ GradientData CreateGradientBuffer(const std::vector& colors, auto next_stop = stops[previous_color_index + 1]; auto t = (scaled_i - previous_stop) / (next_stop - previous_stop); - auto mixed_color = Color::lerp(previous_color, next_color, t); + auto mixed_color = Color::Lerp(previous_color, next_color, t); AppendColor(mixed_color, &data); }