diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 508bc23e2d0c5..05ebee6ccb8f9 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -2085,8 +2085,9 @@ static Picture BlendModeTest(BlendMode blend_mode, const std::shared_ptr& src_image, const std::shared_ptr& dst_image) { Color destination_color = Color::CornflowerBlue().WithAlpha(0.75); - auto source_colors = - std::vector({Color::White(), Color::LimeGreen(), Color::Black()}); + auto source_colors = std::vector({Color::White().WithAlpha(0.75), + Color::LimeGreen().WithAlpha(0.75), + Color::Black().WithAlpha(0.75)}); Canvas canvas; @@ -2111,7 +2112,7 @@ static Picture BlendModeTest(BlendMode blend_mode, // pass. canvas.SaveLayer({.blend_mode = blend_mode}); { // - canvas.DrawRect({50, 50, 100, 100}, {.color = color.WithAlpha(0.75)}); + canvas.DrawRect({50, 50, 100, 100}, {.color = color}); } canvas.Restore(); } @@ -2135,9 +2136,9 @@ static Picture BlendModeTest(BlendMode blend_mode, // canvas.DrawPaint({.color = destination_color}); for (auto& color : source_colors) { // Simply write the CPU blended color to the pass. - canvas.DrawRect({50, 50, 100, 100}, {.color = destination_color.Blend( - color.WithAlpha(0.75), blend_mode), - .blend_mode = BlendMode::kSource}); + canvas.DrawRect({50, 50, 100, 100}, + {.color = destination_color.Blend(color, blend_mode), + .blend_mode = BlendMode::kSourceOver}); canvas.Translate(Vector2(100, 0)); } canvas.RestoreToCount(0); diff --git a/impeller/geometry/color.cc b/impeller/geometry/color.cc index d4c88998b1de2..79ed191c8446d 100644 --- a/impeller/geometry/color.cc +++ b/impeller/geometry/color.cc @@ -123,18 +123,6 @@ Color ColorHSB::ToRGBA() const { return Color(0, 0, 0, alpha); } -Color Color::operator+(const Color& c) const { - return Color(Vector4(*this) + Vector4(c)); -} - -Color Color::operator-(const Color& c) const { - return Color(Vector4(*this) - Vector4(c)); -} - -Color Color::operator*(Scalar value) const { - return Color(red * value, green * value, blue * value, alpha * value); -} - Color::Color(const ColorHSB& hsbColor) : Color(hsbColor.ToRGBA()) {} Color::Color(const Vector4& value) @@ -218,47 +206,57 @@ Color Color::Blend(const Color& src, BlendMode blend_mode) const { return dst; case BlendMode::kSourceOver: // r = s + (1-sa)*d - return src + dst * (1 - src.alpha); + return (src.Premultiply() + dst.Premultiply() * (1 - src.alpha)) + .Unpremultiply(); case BlendMode::kDestinationOver: // r = d + (1-da)*s - return dst + src * (1 - dst.alpha); + return (dst.Premultiply() + src.Premultiply() * (1 - dst.alpha)) + .Unpremultiply(); case BlendMode::kSourceIn: // r = s * da - return src * dst.alpha; + return (src.Premultiply() * dst.alpha).Unpremultiply(); case BlendMode::kDestinationIn: // r = d * sa - return dst * src.alpha; + return (dst.Premultiply() * src.alpha).Unpremultiply(); case BlendMode::kSourceOut: // r = s * ( 1- da) - return src * (1 - dst.alpha); + return (src.Premultiply() * (1 - dst.alpha)).Unpremultiply(); case BlendMode::kDestinationOut: // r = d * (1-sa) - return dst * (1 - src.alpha); + return (dst.Premultiply() * (1 - src.alpha)).Unpremultiply(); case BlendMode::kSourceATop: // r = s*da + d*(1-sa) - return src * dst.alpha + dst * (1 - src.alpha); + return (src.Premultiply() * dst.alpha + + dst.Premultiply() * (1 - src.alpha)) + .Unpremultiply(); case BlendMode::kDestinationATop: // r = d*sa + s*(1-da) - return dst * src.alpha + src * (1 - dst.alpha); + return (dst.Premultiply() * src.alpha + + src.Premultiply() * (1 - dst.alpha)) + .Unpremultiply(); case BlendMode::kXor: // r = s*(1-da) + d*(1-sa) - return src * (1 - dst.alpha) + dst * (1 - src.alpha); + return (src.Premultiply() * (1 - dst.alpha) + + dst.Premultiply() * (1 - src.alpha)) + .Unpremultiply(); case BlendMode::kPlus: // r = min(s + d, 1) - return Min(src + dst, 1); + return (Min(src.Premultiply() + dst.Premultiply(), 1)).Unpremultiply(); case BlendMode::kModulate: // r = s*d - return src * dst; + return (src.Premultiply() * dst.Premultiply()).Unpremultiply(); case BlendMode::kScreen: { // r = s + d - s*d - return src + dst - src * dst; + auto s = src.Premultiply(); + auto d = dst.Premultiply(); + return (s + d - s * d).Unpremultiply(); } case BlendMode::kOverlay: return apply_rgb_srcover_alpha([&](auto s, auto d) { - if (d * 2 < dst.alpha) { + if (d * 2 <= dst.alpha) { return 2 * s * d; } - return src.alpha * dst.alpha - 2 * (dst.alpha - s) * (src.alpha - d); + return src.alpha * dst.alpha - 2 * (dst.alpha - d) * (src.alpha - s); }); case BlendMode::kDarken: { return apply_rgb_srcover_alpha([&](auto s, auto d) { @@ -313,13 +311,13 @@ Color Color::Blend(const Color& src, BlendMode blend_mode) const { std::sqrt(dst_rgb.z)), // dst_rgb, // 0.25); - Color blended = - FromRGB(ComponentChoose( - dst_rgb - (1.0 - 2.0 * src) * dst * (1.0 - dst_rgb), // - dst_rgb + (2.0 * src_rgb - 1.0) * (d - dst_rgb), // - src_rgb, // - 0.5), - dst.alpha); + Color blended = FromRGB( + ComponentChoose( + dst_rgb - (1.0 - 2.0 * src_rgb) * dst_rgb * (1.0 - dst_rgb), // + dst_rgb + (2.0 * src_rgb - 1.0) * (d - dst_rgb), // + src_rgb, // + 0.5), + dst.alpha); return blended + dst * (1 - blended.alpha); } case BlendMode::kDifference: diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 7991c6bc38a0d..77bb9790d7664 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -10,7 +10,9 @@ #include #include #include + #include "impeller/geometry/scalar.h" +#include "impeller/geometry/type_traits.h" #define IMPELLER_FOR_EACH_BLEND_MODE(V) \ V(Clear) \ @@ -162,11 +164,51 @@ struct Color { 0xFFFFFFFF; } - constexpr bool operator==(const Color& c) const { + constexpr inline bool operator==(const Color& c) const { return ScalarNearlyEqual(red, c.red) && ScalarNearlyEqual(green, c.green) && ScalarNearlyEqual(blue, c.blue) && ScalarNearlyEqual(alpha, c.alpha); } + constexpr inline Color operator+(const Color& c) const { + return {red + c.red, green + c.green, blue + c.blue, alpha + c.alpha}; + } + + template >> + constexpr inline Color operator+(T value) const { + auto v = static_cast(value); + return {red + v, green + v, blue + v, alpha + v}; + } + + constexpr inline Color operator-(const Color& c) const { + return {red - c.red, green - c.green, blue - c.blue, alpha - c.alpha}; + } + + template >> + constexpr inline Color operator-(T value) const { + auto v = static_cast(value); + return {red - v, green - v, blue - v, alpha - v}; + } + + constexpr inline Color operator*(const Color& c) const { + return {red * c.red, green * c.green, blue * c.blue, alpha * c.alpha}; + } + + template >> + constexpr inline Color operator*(T value) const { + auto v = static_cast(value); + return {red * v, green * v, blue * v, alpha * v}; + } + + constexpr inline Color operator/(const Color& c) const { + return {red * c.red, green * c.green, blue * c.blue, alpha * c.alpha}; + } + + template >> + constexpr inline Color operator/(T value) const { + auto v = static_cast(value); + return {red / v, green / v, blue / v, alpha / v}; + } + constexpr Color Premultiply() const { return {red * alpha, green * alpha, blue * alpha, alpha}; } @@ -835,20 +877,32 @@ struct Color { /// 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); - } + constexpr bool IsTransparent() const { return alpha == 0.0f; } - Color operator+(const Color& c) const; + constexpr bool IsOpaque() const { return alpha == 1.0f; } +}; - Color operator-(const Color& c) const; +template >> +constexpr inline Color operator+(T value, const Color& c) { + return c + static_cast(value); +} - Color operator*(Scalar value) const; +template >> +constexpr inline Color operator-(T value, const Color& c) { + auto v = static_cast(value); + return {v - c.red, v - c.green, v - c.blue, v - c.alpha}; +} - constexpr bool IsTransparent() const { return alpha == 0.0f; } +template >> +constexpr inline Color operator*(T value, const Color& c) { + return c * static_cast(value); +} - constexpr bool IsOpaque() const { return alpha == 1.0f; } -}; +template >> +constexpr inline Color operator/(T value, const Color& c) { + auto v = static_cast(value); + return {v / c.red, v / c.green, v / c.blue, v / c.alpha}; +} std::string ColorToString(const Color& color); diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 3a123c657ce61..2b9875bf67bd0 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -1479,181 +1479,6 @@ 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 = {