mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Fix CPU Porter-Duff blends (flutter/engine#43217)
Part of https://github.com/flutter/flutter/issues/128606. * Fix all of the Porter-Duff blends (virtually all of the blends were wrong). Still working on getting all of the advanced/color blends fixed up. * Fill in missing ops for arithmetic types/remove Vector4 conversions. * Remove the original GeometryTest in favor of the much clearer goldens. Once we have all of the CPU blends matching up properly with the GPU blends, we can think about constructing a more intentional test that covers a select number of important cases with accompanying explanation. Top rectangles = GPU blends Bottom rectangles = CPU blends
This commit is contained in:
parent
b62a3603c1
commit
083d8dd6d4
@ -2085,8 +2085,9 @@ static Picture BlendModeTest(BlendMode blend_mode,
|
||||
const std::shared_ptr<Image>& src_image,
|
||||
const std::shared_ptr<Image>& dst_image) {
|
||||
Color destination_color = Color::CornflowerBlue().WithAlpha(0.75);
|
||||
auto source_colors =
|
||||
std::vector<Color>({Color::White(), Color::LimeGreen(), Color::Black()});
|
||||
auto source_colors = std::vector<Color>({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);
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -10,7 +10,9 @@
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
#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 <class T, class = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||
constexpr inline Color operator+(T value) const {
|
||||
auto v = static_cast<Scalar>(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 <class T, class = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||
constexpr inline Color operator-(T value) const {
|
||||
auto v = static_cast<Scalar>(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 <class T, class = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||
constexpr inline Color operator*(T value) const {
|
||||
auto v = static_cast<Scalar>(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 <class T, class = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||
constexpr inline Color operator/(T value) const {
|
||||
auto v = static_cast<Scalar>(value);
|
||||
return {red / v, green / v, blue / v, alpha / v};
|
||||
}
|
||||
|
||||
constexpr Color Premultiply() const {
|
||||
return {red * alpha, green * alpha, blue * alpha, alpha};
|
||||
}
|
||||
@ -835,21 +877,33 @@ 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);
|
||||
}
|
||||
|
||||
Color operator+(const Color& c) const;
|
||||
|
||||
Color operator-(const Color& c) const;
|
||||
|
||||
Color operator*(Scalar value) const;
|
||||
|
||||
constexpr bool IsTransparent() const { return alpha == 0.0f; }
|
||||
|
||||
constexpr bool IsOpaque() const { return alpha == 1.0f; }
|
||||
};
|
||||
|
||||
template <class T, class = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||
constexpr inline Color operator+(T value, const Color& c) {
|
||||
return c + static_cast<Scalar>(value);
|
||||
}
|
||||
|
||||
template <class T, class = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||
constexpr inline Color operator-(T value, const Color& c) {
|
||||
auto v = static_cast<Scalar>(value);
|
||||
return {v - c.red, v - c.green, v - c.blue, v - c.alpha};
|
||||
}
|
||||
|
||||
template <class T, class = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||
constexpr inline Color operator*(T value, const Color& c) {
|
||||
return c * static_cast<Scalar>(value);
|
||||
}
|
||||
|
||||
template <class T, class = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||
constexpr inline Color operator/(T value, const Color& c) {
|
||||
auto v = static_cast<Scalar>(value);
|
||||
return {v / c.red, v / c.green, v / c.blue, v / c.alpha};
|
||||
}
|
||||
|
||||
std::string ColorToString(const Color& color);
|
||||
|
||||
/**
|
||||
|
||||
@ -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 = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user