Flutter Engine
 
Loading...
Searching...
No Matches
color.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <algorithm>
8#include <cmath>
9#include <format>
10#include <functional>
11#include <sstream>
12#include <type_traits>
13
17
18namespace impeller {
19
20#define _IMPELLER_ASSERT_BLEND_MODE(blend_mode) \
21 auto enum_##blend_mode = static_cast<std::underlying_type_t<BlendMode>>( \
22 BlendMode::k##blend_mode); \
23 if (i != enum_##blend_mode) { \
24 return false; \
25 } \
26 ++i;
27
28static constexpr inline bool ValidateBlendModes() {
29 std::underlying_type_t<BlendMode> i = 0;
30 // Ensure the order of the blend modes match.
32 // Ensure the total number of blend modes match.
33 if (i - 1 !=
34 static_cast<std::underlying_type_t<BlendMode>>(BlendMode::kLastMode)) {
35 return false;
36 }
37 return true;
38}
39static_assert(ValidateBlendModes(),
40 "IMPELLER_FOR_EACH_BLEND_MODE must match impeller::BlendMode.");
41
42#define _IMPELLER_BLEND_MODE_NAME_LIST(blend_mode) #blend_mode,
43
46
47const char* BlendModeToString(BlendMode blend_mode) {
48 return kBlendModeNames[static_cast<std::underlying_type_t<BlendMode>>(
49 blend_mode)];
50}
51
52Color::Color(const Vector4& value)
53 : red(value.x), green(value.y), blue(value.z), alpha(value.w) {}
54
55static constexpr inline Color Min(Color c, float threshold) {
56 return Color(std::min(c.red, threshold), std::min(c.green, threshold),
57 std::min(c.blue, threshold), std::min(c.alpha, threshold));
58}
59
60// The following HSV utilities correspond to the W3C blend definitions
61// implemented in: impeller/compiler/shader_lib/impeller/blending.glsl
62
63static constexpr inline Scalar Luminosity(Vector3 color) {
64 return color.x * 0.3f + color.y * 0.59f + color.z * 0.11f;
65}
66
67static constexpr inline Vector3 ClipColor(Vector3 color) {
68 Scalar lum = Luminosity(color);
69 Scalar mn = std::min(std::min(color.x, color.y), color.z);
70 Scalar mx = std::max(std::max(color.x, color.y), color.z);
71 // `lum - mn` and `mx - lum` will always be >= 0 in the following conditions,
72 // so adding a tiny value is enough to make these divisions safe.
73 if (mn < 0.0f) {
74 color = lum + (((color - lum) * lum) / (lum - mn + kEhCloseEnough));
75 }
76 if (mx > 1.0) {
77 color =
78 lum + (((color - lum) * (1.0f - lum)) / (mx - lum + kEhCloseEnough));
79 }
80 return color;
81}
82
83static constexpr inline Vector3 SetLuminosity(Vector3 color,
84 Scalar luminosity) {
85 Scalar relative_lum = luminosity - Luminosity(color);
86 return ClipColor(color + relative_lum);
87}
88
89static constexpr inline Scalar Saturation(Vector3 color) {
90 return std::max(std::max(color.x, color.y), color.z) -
91 std::min(std::min(color.x, color.y), color.z);
92}
93
94static constexpr inline Vector3 SetSaturation(Vector3 color,
95 Scalar saturation) {
96 Scalar mn = std::min(std::min(color.x, color.y), color.z);
97 Scalar mx = std::max(std::max(color.x, color.y), color.z);
98 return (mn < mx) ? ((color - mn) * saturation) / (mx - mn) : Vector3();
99}
100
101static constexpr inline Vector3 ComponentChoose(Vector3 a,
102 Vector3 b,
103 Vector3 value,
104 Scalar cutoff) {
105 return Vector3(value.x > cutoff ? b.x : a.x, //
106 value.y > cutoff ? b.y : a.y, //
107 value.z > cutoff ? b.z : a.z //
108 );
109}
110
111static constexpr inline Vector3 ToRGB(Color color) {
112 return {color.red, color.green, color.blue};
113}
114
115static constexpr inline Color FromRGB(Vector3 color, Scalar alpha) {
116 return {color.x, color.y, color.z, alpha};
117}
118
119/// Composite a blended color onto the destination.
120/// All three parameters are unpremultiplied. Returns a premultiplied result.
121///
122/// This routine is the same as `IPApplyBlendedColor` in the Impeller shader
123/// library.
124static constexpr inline Color ApplyBlendedColor(Color dst,
125 Color src,
126 Vector3 blend_result) {
127 dst = dst.Premultiply();
128 src =
129 // Use the blended color for areas where the source and destination
130 // colors overlap.
131 FromRGB(blend_result, src.alpha * dst.alpha).Premultiply() +
132 // Use the original source color for any remaining non-overlapping areas.
133 src.Premultiply() * (1.0f - dst.alpha);
134
135 // Source-over composite the blended source color atop the destination.
136 return src + dst * (1.0f - src.alpha);
137}
138
139static inline Color DoColorBlend(
140 Color dst,
141 Color src,
142 const std::function<Vector3(Vector3, Vector3)>& blend_rgb_func) {
143 const Vector3 blend_result = blend_rgb_func(ToRGB(dst), ToRGB(src));
144 return ApplyBlendedColor(dst, src, blend_result).Unpremultiply();
145}
146
148 Color dst,
149 Color src,
150 const std::function<Scalar(Scalar, Scalar)>& blend_func) {
151 Vector3 blend_result = Vector3(blend_func(dst.red, src.red), //
152 blend_func(dst.green, src.green), //
153 blend_func(dst.blue, src.blue)); //
154 return ApplyBlendedColor(dst, src, blend_result).Unpremultiply();
155}
156
157Color Color::Blend(Color src, BlendMode blend_mode) const {
158 Color dst = *this;
159
160 switch (blend_mode) {
163 case BlendMode::kSrc:
164 return src;
165 case BlendMode::kDst:
166 return dst;
168 // r = s + (1-sa)*d
169 return (src.Premultiply() + dst.Premultiply() * (1 - src.alpha))
170 .Unpremultiply();
172 // r = d + (1-da)*s
173 return (dst.Premultiply() + src.Premultiply() * (1 - dst.alpha))
174 .Unpremultiply();
176 // r = s * da
177 return (src.Premultiply() * dst.alpha).Unpremultiply();
179 // r = d * sa
180 return (dst.Premultiply() * src.alpha).Unpremultiply();
182 // r = s * ( 1- da)
183 return (src.Premultiply() * (1 - dst.alpha)).Unpremultiply();
185 // r = d * (1-sa)
186 return (dst.Premultiply() * (1 - src.alpha)).Unpremultiply();
188 // r = s*da + d*(1-sa)
189 return (src.Premultiply() * dst.alpha +
190 dst.Premultiply() * (1 - src.alpha))
191 .Unpremultiply();
193 // r = d*sa + s*(1-da)
194 return (dst.Premultiply() * src.alpha +
195 src.Premultiply() * (1 - dst.alpha))
196 .Unpremultiply();
197 case BlendMode::kXor:
198 // r = s*(1-da) + d*(1-sa)
199 return (src.Premultiply() * (1 - dst.alpha) +
200 dst.Premultiply() * (1 - src.alpha))
201 .Unpremultiply();
202 case BlendMode::kPlus:
203 // r = min(s + d, 1)
204 return (Min(src.Premultiply() + dst.Premultiply(), 1)).Unpremultiply();
206 // r = s*d
207 return (src.Premultiply() * dst.Premultiply()).Unpremultiply();
208 case BlendMode::kScreen: {
209 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
210 return s + d - s * d;
211 });
212 }
214 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
215 // The same as HardLight, but with the source and destination reversed.
216 Vector3 screen_src = 2.0 * d - 1.0;
217 Vector3 screen = screen_src + s - screen_src * s;
218 return ComponentChoose(s * (2.0 * d), //
219 screen, //
220 d, //
221 0.5);
222 });
224 return DoColorBlend(
225 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Min(s); });
227 return DoColorBlend(
228 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Max(s); });
230 return DoColorBlendComponents(dst, src, [](Scalar d, Scalar s) -> Scalar {
231 if (d < kEhCloseEnough) {
232 return 0.0f;
233 }
234 if (1.0 - s < kEhCloseEnough) {
235 return 1.0f;
236 }
237 return std::min(1.0f, d / (1.0f - s));
238 });
240 return DoColorBlendComponents(dst, src, [](Scalar d, Scalar s) -> Scalar {
241 if (1.0 - d < kEhCloseEnough) {
242 return 1.0f;
243 }
244 if (s < kEhCloseEnough) {
245 return 0.0f;
246 }
247 return 1.0f - std::min(1.0f, (1.0f - d) / s);
248 });
250 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
251 Vector3 screen_src = 2.0 * s - 1.0;
252 Vector3 screen = screen_src + d - screen_src * d;
253 return ComponentChoose(d * (2.0 * s), //
254 screen, //
255 s, //
256 0.5);
257 });
259 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
260 Vector3 D = ComponentChoose(((16.0 * d - 12.0) * d + 4.0) * d, //
261 Vector3(std::sqrt(d.x), std::sqrt(d.y),
262 std::sqrt(d.z)), //
263 d, //
264 0.25);
265 return ComponentChoose(d - (1.0 - 2.0 * s) * d * (1.0 - d), //
266 d + (2.0 * s - 1.0) * (D - d), //
267 s, //
268 0.5);
269 });
271 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
272 return (d - s).Abs();
273 });
275 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
276 return d + s - 2.0f * d * s;
277 });
279 return DoColorBlend(
280 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d * s; });
281 case BlendMode::kHue: {
282 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
284 });
285 }
287 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
289 });
291 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
292 return SetLuminosity(s, Luminosity(d));
293 });
295 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
296 return SetLuminosity(d, Luminosity(s));
297 });
298 }
299}
300
301Color Color::ApplyColorMatrix(const ColorMatrix& color_matrix) const {
302 auto* c = color_matrix.array;
303 return Color(
304 c[0] * red + c[1] * green + c[2] * blue + c[3] * alpha + c[4],
305 c[5] * red + c[6] * green + c[7] * blue + c[8] * alpha + c[9],
306 c[10] * red + c[11] * green + c[12] * blue + c[13] * alpha + c[14],
307 c[15] * red + c[16] * green + c[17] * blue + c[18] * alpha + c[19])
308 .Clamp01();
309}
310
312 static auto conversion = [](Scalar component) {
313 if (component <= 0.0031308) {
314 return component * 12.92;
315 }
316 return 1.055 * pow(component, (1.0 / 2.4)) - 0.055;
317 };
318
319 return Color(conversion(red), conversion(green), conversion(blue), alpha);
320}
321
323 static auto conversion = [](Scalar component) {
324 if (component <= 0.04045) {
325 return component / 12.92;
326 }
327 return pow((component + 0.055) / 1.055, 2.4);
328 };
329
330 return Color(conversion(red), conversion(green), conversion(blue), alpha);
331}
332
333std::string ColorToString(const Color& color) {
334 return std::format("R={:.1f},G={:.1f},B={:.1f},A={:.1f}", color.red,
335 color.green, color.blue, color.alpha);
336}
337
338} // namespace impeller
#define _IMPELLER_ASSERT_BLEND_MODE(blend_mode)
Definition color.cc:20
#define _IMPELLER_BLEND_MODE_NAME_LIST(blend_mode)
Definition color.cc:42
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition color.h:19
int32_t value
int32_t x
auto & d
Definition main.cc:28
double y
static constexpr const char * kBlendModeNames[]
Definition color.cc:44
static constexpr Color Min(Color c, float threshold)
Definition color.cc:55
static constexpr bool ValidateBlendModes()
Definition color.cc:28
static constexpr Vector3 SetSaturation(Vector3 color, Scalar saturation)
Definition color.cc:94
float Scalar
Definition scalar.h:19
static Color DoColorBlendComponents(Color dst, Color src, const std::function< Scalar(Scalar, Scalar)> &blend_func)
Definition color.cc:147
constexpr float kEhCloseEnough
Definition constants.h:57
static constexpr Scalar Saturation(Vector3 color)
Definition color.cc:89
static constexpr Vector3 ToRGB(Color color)
Definition color.cc:111
const char * BlendModeToString(BlendMode blend_mode)
Definition color.cc:47
static constexpr Color FromRGB(Vector3 color, Scalar alpha)
Definition color.cc:115
static constexpr Vector3 SetLuminosity(Vector3 color, Scalar luminosity)
Definition color.cc:83
static constexpr Color ApplyBlendedColor(Color dst, Color src, Vector3 blend_result)
Definition color.cc:124
BlendMode
Definition color.h:58
std::string ColorToString(const Color &color)
Definition color.cc:333
static Color DoColorBlend(Color dst, Color src, const std::function< Vector3(Vector3, Vector3)> &blend_rgb_func)
Definition color.cc:139
static constexpr Vector3 ComponentChoose(Vector3 a, Vector3 b, Vector3 value, Scalar cutoff)
Definition color.cc:101
static constexpr Vector3 ClipColor(Vector3 color)
Definition color.cc:67
static constexpr Scalar Luminosity(Vector3 color)
Definition color.cc:63
Scalar blue
Definition color.h:138
static constexpr Color BlackTransparent()
Definition color.h:270
Scalar alpha
Definition color.h:143
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition color.cc:311
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition color.cc:301
Scalar red
Definition color.h:128
constexpr Color Unpremultiply() const
Definition color.h:216
constexpr Color()
Definition color.h:145
Scalar green
Definition color.h:133
constexpr Color Premultiply() const
Definition color.h:212
Color SRGBToLinear() const
Convert the color from sRGB space to linear space.
Definition color.cc:322
Color Blend(Color source, BlendMode blend_mode) const
Blends an unpremultiplied destination color into a given unpremultiplied source color to form a new u...
Definition color.cc:157
Scalar array[20]
Definition color.h:118
Vector3 Min(const Vector3 &p) const
Definition vector.h:70
Vector3 Max(const Vector3 &p) const
Definition vector.h:74