| Flutter Impeller |

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
5 #include "impeller/geometry/color.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <format>
10 #include <functional>
11 #include <sstream>
12 #include <type_traits>
13
14 #include "impeller/geometry/constants.h"
15 #include "impeller/geometry/scalar.h"
16 #include "impeller/geometry/vector.h"
17
18 namespace 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
28 static constexpr inline bool ValidateBlendModes() {
29 std::underlying_type_t<BlendMode> i = 0;
30 // Ensure the order of the blend modes match.
31 IMPELLER_FOR_EACH_BLEND_MODE(_IMPELLER_ASSERT_BLEND_MODE)
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 }
39 static_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
44 static constexpr const char* kBlendModeNames[] = {
45 IMPELLER_FOR_EACH_BLEND_MODE(_IMPELLER_BLEND_MODE_NAME_LIST)};
46
47 const char* BlendModeToString(BlendMode blend_mode) {
48 return kBlendModeNames[static_cast<std::underlying_type_t<BlendMode>>(
49 blend_mode)];
50 }
51
52 Color::Color(const Vector4& value)
53 : red(value.x), green(value.y), blue(value.z), alpha(value.w) {}
54
55 static 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
63 static constexpr inline Scalar Luminosity(Vector3 color) {
64 return color.x * 0.3f + color.y * 0.59f + color.z * 0.11f;
65 }
66
67 static 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
83 static constexpr inline Vector3 SetLuminosity(Vector3 color,
84 Scalar luminosity) {
85 Scalar relative_lum = luminosity - Luminosity(color);
86 return ClipColor(color + relative_lum);
87 }
88
89 static 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
94 static 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
101 static 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
111 static constexpr inline Vector3 ToRGB(Color color) {
112 return {color.red, color.green, color.blue};
113 }
114
115 static 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.
124 static 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
139 static 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
147 static inline Color DoColorBlendComponents(
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
157 Color Color::Blend(Color src, BlendMode blend_mode) const {
158 Color dst = *this;
159
160 switch (blend_mode) {
161 case BlendMode::kClear:
162 return Color::BlackTransparent();
163 case BlendMode::kSrc:
164 return src;
165 case BlendMode::kDst:
166 return dst;
167 case BlendMode::kSrcOver:
168 // r = s + (1-sa)*d
169 return (src.Premultiply() + dst.Premultiply() * (1 - src.alpha))
170 .Unpremultiply();
171 case BlendMode::kDstOver:
172 // r = d + (1-da)*s
173 return (dst.Premultiply() + src.Premultiply() * (1 - dst.alpha))
174 .Unpremultiply();
175 case BlendMode::kSrcIn:
176 // r = s * da
177 return (src.Premultiply() * dst.alpha).Unpremultiply();
178 case BlendMode::kDstIn:
179 // r = d * sa
180 return (dst.Premultiply() * src.alpha).Unpremultiply();
181 case BlendMode::kSrcOut:
182 // r = s * ( 1- da)
183 return (src.Premultiply() * (1 - dst.alpha)).Unpremultiply();
184 case BlendMode::kDstOut:
185 // r = d * (1-sa)
186 return (dst.Premultiply() * (1 - src.alpha)).Unpremultiply();
187 case BlendMode::kSrcATop:
188 // r = s*da + d*(1-sa)
189 return (src.Premultiply() * dst.alpha +
190 dst.Premultiply() * (1 - src.alpha))
191 .Unpremultiply();
192 case BlendMode::kDstATop:
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();
205 case BlendMode::kModulate:
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 }
213 case BlendMode::kOverlay:
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 });
223 case BlendMode::kDarken:
224 return DoColorBlend(
225 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Min(s); });
226 case BlendMode::kLighten:
227 return DoColorBlend(
228 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Max(s); });
229 case BlendMode::kColorDodge:
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 });
239 case BlendMode::kColorBurn:
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 });
249 case BlendMode::kHardLight:
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 });
258 case BlendMode::kSoftLight:
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 });
270 case BlendMode::kDifference:
271 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
272 return (d - s).Abs();
273 });
274 case BlendMode::kExclusion:
275 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
276 return d + s - 2.0f * d * s;
277 });
278 case BlendMode::kMultiply:
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 {
283 return SetLuminosity(SetSaturation(s, Saturation(d)), Luminosity(d));
284 });
285 }
286 case BlendMode::kSaturation:
287 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
288 return SetLuminosity(SetSaturation(d, Saturation(s)), Luminosity(d));
289 });
290 case BlendMode::kColor:
291 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
292 return SetLuminosity(s, Luminosity(d));
293 });
294 case BlendMode::kLuminosity:
295 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
296 return SetLuminosity(d, Luminosity(s));
297 });
298 }
299 }
300
301 Color 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
311 Color Color::LinearToSRGB() const {
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
322 Color Color::SRGBToLinear() const {
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
333 std::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
_IMPELLER_ASSERT_BLEND_MODE
#define _IMPELLER_ASSERT_BLEND_MODE(blend_mode)
Definition: color.cc:20
_IMPELLER_BLEND_MODE_NAME_LIST
#define _IMPELLER_BLEND_MODE_NAME_LIST(blend_mode)
Definition: color.cc:42
color.h
IMPELLER_FOR_EACH_BLEND_MODE
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
value
int32_t value
Definition: dl_golden_unittests.cc:459
x
int32_t x
Definition: dl_golden_unittests.cc:458
constants.h
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller
Definition: allocation.cc:12
impeller::kBlendModeNames
static constexpr const char * kBlendModeNames[]
Definition: color.cc:44
impeller::Min
static constexpr Color Min(Color c, float threshold)
Definition: color.cc:55
impeller::ValidateBlendModes
static constexpr bool ValidateBlendModes()
Definition: color.cc:28
impeller::SetSaturation
static constexpr Vector3 SetSaturation(Vector3 color, Scalar saturation)
Definition: color.cc:94
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::DoColorBlendComponents
static Color DoColorBlendComponents(Color dst, Color src, const std::function< Scalar(Scalar, Scalar)> &blend_func)
Definition: color.cc:147
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:57
impeller::Saturation
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:89
impeller::ToRGB
static constexpr Vector3 ToRGB(Color color)
Definition: color.cc:111
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
impeller::FromRGB
static constexpr Color FromRGB(Vector3 color, Scalar alpha)
Definition: color.cc:115
impeller::SetLuminosity
static constexpr Vector3 SetLuminosity(Vector3 color, Scalar luminosity)
Definition: color.cc:83
impeller::ApplyBlendedColor
static constexpr Color ApplyBlendedColor(Color dst, Color src, Vector3 blend_result)
Definition: color.cc:124
impeller::BlendMode
BlendMode
Definition: color.h:58
impeller::BlendMode::kSrcOut
@ kSrcOut
impeller::BlendMode::kExclusion
@ kExclusion
impeller::BlendMode::kSaturation
@ kSaturation
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::BlendMode::kPlus
@ kPlus
impeller::BlendMode::kLighten
@ kLighten
impeller::BlendMode::kHue
@ kHue
impeller::BlendMode::kDst
@ kDst
impeller::BlendMode::kDstIn
@ kDstIn
impeller::BlendMode::kModulate
@ kModulate
impeller::BlendMode::kMultiply
@ kMultiply
impeller::BlendMode::kColorDodge
@ kColorDodge
impeller::BlendMode::kScreen
@ kScreen
impeller::BlendMode::kSrcOver
@ kSrcOver
impeller::BlendMode::kXor
@ kXor
impeller::BlendMode::kSrc
@ kSrc
impeller::BlendMode::kLuminosity
@ kLuminosity
impeller::BlendMode::kSoftLight
@ kSoftLight
impeller::BlendMode::kDifference
@ kDifference
impeller::BlendMode::kOverlay
@ kOverlay
impeller::BlendMode::kSrcATop
@ kSrcATop
impeller::BlendMode::kDstATop
@ kDstATop
impeller::BlendMode::kDstOver
@ kDstOver
impeller::BlendMode::kLastMode
@ kLastMode
impeller::BlendMode::kColor
@ kColor
impeller::BlendMode::kHardLight
@ kHardLight
impeller::BlendMode::kDstOut
@ kDstOut
impeller::BlendMode::kDarken
@ kDarken
impeller::BlendMode::kSrcIn
@ kSrcIn
impeller::BlendMode::kClear
@ kClear
impeller::ColorToString
std::string ColorToString(const Color &color)
Definition: color.cc:333
impeller::DoColorBlend
static Color DoColorBlend(Color dst, Color src, const std::function< Vector3(Vector3, Vector3)> &blend_rgb_func)
Definition: color.cc:139
impeller::ComponentChoose
static constexpr Vector3 ComponentChoose(Vector3 a, Vector3 b, Vector3 value, Scalar cutoff)
Definition: color.cc:101
impeller::ClipColor
static constexpr Vector3 ClipColor(Vector3 color)
Definition: color.cc:67
impeller::Luminosity
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:63
scalar.h
impeller::Color
Definition: color.h:124
impeller::Color::blue
Scalar blue
Definition: color.h:138
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:270
impeller::Color::alpha
Scalar alpha
Definition: color.h:143
impeller::Color::LinearToSRGB
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:311
impeller::Color::ApplyColorMatrix
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:301
impeller::Color::red
Scalar red
Definition: color.h:128
impeller::Color::Unpremultiply
constexpr Color Unpremultiply() const
Definition: color.h:216
impeller::Color::Color
constexpr Color()
Definition: color.h:145
impeller::Color::green
Scalar green
Definition: color.h:133
impeller::Color::Premultiply
constexpr Color Premultiply() const
Definition: color.h:212
impeller::Color::SRGBToLinear
Color SRGBToLinear() const
Convert the color from sRGB space to linear space.
Definition: color.cc:322
impeller::Color::Blend
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
impeller::ColorMatrix
Definition: color.h:117
impeller::ColorMatrix::array
Scalar array[20]
Definition: color.h:118
impeller::Vector3
Definition: vector.h:20
impeller::Vector3::Min
Vector3 Min(const Vector3 &p) const
Definition: vector.h:70
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::Vector3::y
Scalar y
Definition: vector.h:24
impeller::Vector3::Max
Vector3 Max(const Vector3 &p) const
Definition: vector.h:74
impeller::Vector3::z
Scalar z
Definition: vector.h:25
impeller::Vector4
Definition: vector.h:232
vector.h