| 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 <functional>
10 #include <sstream>
11 #include <type_traits>
12
13 #include "impeller/base/strings.h"
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::kLast)) {
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 ColorHSB ColorHSB::FromRGB(Color rgb) {
53 Scalar R = rgb.red;
54 Scalar G = rgb.green;
55 Scalar B = rgb.blue;
56
57 Scalar v = 0.0;
58 Scalar x = 0.0;
59 Scalar f = 0.0;
60
61 int64_t i = 0;
62
63 x = fmin(R, G);
64 x = fmin(x, B);
65
66 v = fmax(R, G);
67 v = fmax(v, B);
68
69 if (v == x) {
70 return ColorHSB(0.0, 0.0, v, rgb.alpha);
71 }
72
73 f = (R == x) ? G - B : ((G == x) ? B - R : R - G);
74 i = (R == x) ? 3 : ((G == x) ? 5 : 1);
75
76 return ColorHSB(((i - f / (v - x)) / 6.0), (v - x) / v, v, rgb.alpha);
77 }
78
79 Color ColorHSB::ToRGBA() const {
80 Scalar h = hue * 6.0;
81 Scalar s = saturation;
82 Scalar v = brightness;
83
84 Scalar m = 0.0;
85 Scalar n = 0.0;
86 Scalar f = 0.0;
87
88 int64_t i = 0;
89
90 if (h == 0) {
91 h = 0.01;
92 }
93
94 if (h == 0.0) {
95 return Color(v, v, v, alpha);
96 }
97
98 i = static_cast<int64_t>(floor(h));
99
100 f = h - i;
101
102 if (!(i & 1)) {
103 f = 1 - f;
104 }
105
106 m = v * (1 - s);
107 n = v * (1 - s * f);
108
109 switch (i) {
110 case 6:
111 case 0:
112 return Color(v, n, m, alpha);
113 case 1:
114 return Color(n, v, m, alpha);
115 case 2:
116 return Color(m, v, n, alpha);
117 case 3:
118 return Color(m, n, v, alpha);
119 case 4:
120 return Color(n, m, v, alpha);
121 case 5:
122 return Color(v, m, n, alpha);
123 }
124 return Color(0, 0, 0, alpha);
125 }
126
127 Color::Color(const ColorHSB& hsbColor) : Color(hsbColor.ToRGBA()) {}
128
129 Color::Color(const Vector4& value)
130 : red(value.x), green(value.y), blue(value.z), alpha(value.w) {}
131
132 static constexpr inline Color Min(Color c, float threshold) {
133 return Color(std::min(c.red, threshold), std::min(c.green, threshold),
134 std::min(c.blue, threshold), std::min(c.alpha, threshold));
135 }
136
137 // The following HSV utilities correspond to the W3C blend definitions
138 // implemented in: impeller/compiler/shader_lib/impeller/blending.glsl
139
140 static constexpr inline Scalar Luminosity(Vector3 color) {
141 return color.x * 0.3f + color.y * 0.59f + color.z * 0.11f;
142 }
143
144 static constexpr inline Vector3 ClipColor(Vector3 color) {
145 Scalar lum = Luminosity(color);
146 Scalar mn = std::min(std::min(color.x, color.y), color.z);
147 Scalar mx = std::max(std::max(color.x, color.y), color.z);
148 // `lum - mn` and `mx - lum` will always be >= 0 in the following conditions,
149 // so adding a tiny value is enough to make these divisions safe.
150 if (mn < 0.0f) {
151 color = lum + (((color - lum) * lum) / (lum - mn + kEhCloseEnough));
152 }
153 if (mx > 1.0) {
154 color =
155 lum + (((color - lum) * (1.0f - lum)) / (mx - lum + kEhCloseEnough));
156 }
157 return color;
158 }
159
160 static constexpr inline Vector3 SetLuminosity(Vector3 color,
161 Scalar luminosity) {
162 Scalar relative_lum = luminosity - Luminosity(color);
163 return ClipColor(color + relative_lum);
164 }
165
166 static constexpr inline Scalar Saturation(Vector3 color) {
167 return std::max(std::max(color.x, color.y), color.z) -
168 std::min(std::min(color.x, color.y), color.z);
169 }
170
171 static constexpr inline Vector3 SetSaturation(Vector3 color,
172 Scalar saturation) {
173 Scalar mn = std::min(std::min(color.x, color.y), color.z);
174 Scalar mx = std::max(std::max(color.x, color.y), color.z);
175 return (mn < mx) ? ((color - mn) * saturation) / (mx - mn) : Vector3();
176 }
177
178 static constexpr inline Vector3 ComponentChoose(Vector3 a,
179 Vector3 b,
180 Vector3 value,
181 Scalar cutoff) {
182 return Vector3(value.x > cutoff ? b.x : a.x, //
183 value.y > cutoff ? b.y : a.y, //
184 value.z > cutoff ? b.z : a.z //
185 );
186 }
187
188 static constexpr inline Vector3 ToRGB(Color color) {
189 return {color.red, color.green, color.blue};
190 }
191
192 static constexpr inline Color FromRGB(Vector3 color, Scalar alpha) {
193 return {color.x, color.y, color.z, alpha};
194 }
195
196 static constexpr inline Color DoColorBlend(
197 Color d,
198 Color s,
199 const std::function<Vector3(Vector3, Vector3)>& blend_rgb_func) {
200 d = d.Premultiply();
201 s = s.Premultiply();
202 const Vector3 rgb = blend_rgb_func(ToRGB(d), ToRGB(s));
203 const Color blended = Color::Lerp(s, FromRGB(rgb, d.alpha), d.alpha);
204 return Color::Lerp(d, blended, s.alpha).Unpremultiply();
205 }
206
207 static constexpr inline Color DoColorBlendComponents(
208 Color d,
209 Color s,
210 const std::function<Scalar(Scalar, Scalar)>& blend_func) {
211 d = d.Premultiply();
212 s = s.Premultiply();
213 const Color blended = Color::Lerp(s,
214 Color(blend_func(d.red, s.red), //
215 blend_func(d.green, s.green), //
216 blend_func(d.blue, s.blue), //
217 d.alpha),
218 d.alpha);
219 return Color::Lerp(d, blended, s.alpha).Unpremultiply();
220 }
221
222 Color Color::Blend(Color src, BlendMode blend_mode) const {
223 Color dst = *this;
224
225 switch (blend_mode) {
226 case BlendMode::kClear:
227 return Color::BlackTransparent();
228 case BlendMode::kSource:
229 return src;
230 case BlendMode::kDestination:
231 return dst;
232 case BlendMode::kSourceOver:
233 // r = s + (1-sa)*d
234 return (src.Premultiply() + dst.Premultiply() * (1 - src.alpha))
235 .Unpremultiply();
236 case BlendMode::kDestinationOver:
237 // r = d + (1-da)*s
238 return (dst.Premultiply() + src.Premultiply() * (1 - dst.alpha))
239 .Unpremultiply();
240 case BlendMode::kSourceIn:
241 // r = s * da
242 return (src.Premultiply() * dst.alpha).Unpremultiply();
243 case BlendMode::kDestinationIn:
244 // r = d * sa
245 return (dst.Premultiply() * src.alpha).Unpremultiply();
246 case BlendMode::kSourceOut:
247 // r = s * ( 1- da)
248 return (src.Premultiply() * (1 - dst.alpha)).Unpremultiply();
249 case BlendMode::kDestinationOut:
250 // r = d * (1-sa)
251 return (dst.Premultiply() * (1 - src.alpha)).Unpremultiply();
252 case BlendMode::kSourceATop:
253 // r = s*da + d*(1-sa)
254 return (src.Premultiply() * dst.alpha +
255 dst.Premultiply() * (1 - src.alpha))
256 .Unpremultiply();
257 case BlendMode::kDestinationATop:
258 // r = d*sa + s*(1-da)
259 return (dst.Premultiply() * src.alpha +
260 src.Premultiply() * (1 - dst.alpha))
261 .Unpremultiply();
262 case BlendMode::kXor:
263 // r = s*(1-da) + d*(1-sa)
264 return (src.Premultiply() * (1 - dst.alpha) +
265 dst.Premultiply() * (1 - src.alpha))
266 .Unpremultiply();
267 case BlendMode::kPlus:
268 // r = min(s + d, 1)
269 return (Min(src.Premultiply() + dst.Premultiply(), 1)).Unpremultiply();
270 case BlendMode::kModulate:
271 // r = s*d
272 return (src.Premultiply() * dst.Premultiply()).Unpremultiply();
273 case BlendMode::kScreen: {
274 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
275 return s + d - s * d;
276 });
277 }
278 case BlendMode::kOverlay:
279 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
280 // The same as HardLight, but with the source and destination reversed.
281 Vector3 screen_src = 2.0 * d - 1.0;
282 Vector3 screen = screen_src + s - screen_src * s;
283 return ComponentChoose(s * (2.0 * d), //
284 screen, //
285 d, //
286 0.5);
287 });
288 case BlendMode::kDarken:
289 return DoColorBlend(
290 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Min(s); });
291 case BlendMode::kLighten:
292 return DoColorBlend(
293 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Max(s); });
294 case BlendMode::kColorDodge:
295 return DoColorBlendComponents(dst, src, [](Scalar d, Scalar s) -> Scalar {
296 if (d < kEhCloseEnough) {
297 return 0.0f;
298 }
299 if (1.0 - s < kEhCloseEnough) {
300 return 1.0f;
301 }
302 return std::min(1.0f, d / (1.0f - s));
303 });
304 case BlendMode::kColorBurn:
305 return DoColorBlendComponents(dst, src, [](Scalar d, Scalar s) -> Scalar {
306 if (1.0 - d < kEhCloseEnough) {
307 return 1.0f;
308 }
309 if (s < kEhCloseEnough) {
310 return 0.0f;
311 }
312 return 1.0f - std::min(1.0f, (1.0f - d) / s);
313 });
314 case BlendMode::kHardLight:
315 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
316 Vector3 screen_src = 2.0 * s - 1.0;
317 Vector3 screen = screen_src + d - screen_src * d;
318 return ComponentChoose(d * (2.0 * s), //
319 screen, //
320 s, //
321 0.5);
322 });
323 case BlendMode::kSoftLight:
324 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
325 Vector3 D = ComponentChoose(((16.0 * d - 12.0) * d + 4.0) * d, //
326 Vector3(std::sqrt(d.x), std::sqrt(d.y),
327 std::sqrt(d.z)), //
328 d, //
329 0.25);
330 return ComponentChoose(d - (1.0 - 2.0 * s) * d * (1.0 - d), //
331 d + (2.0 * s - 1.0) * (D - d), //
332 s, //
333 0.5);
334 });
335 case BlendMode::kDifference:
336 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
337 return (d - s).Abs();
338 });
339 case BlendMode::kExclusion:
340 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
341 return d + s - 2.0f * d * s;
342 });
343 case BlendMode::kMultiply:
344 return DoColorBlend(
345 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d * s; });
346 case BlendMode::kHue: {
347 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
348 return SetLuminosity(SetSaturation(s, Saturation(d)), Luminosity(d));
349 });
350 }
351 case BlendMode::kSaturation:
352 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
353 return SetLuminosity(SetSaturation(d, Saturation(s)), Luminosity(d));
354 });
355 case BlendMode::kColor:
356 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
357 return SetLuminosity(s, Luminosity(d));
358 });
359 case BlendMode::kLuminosity:
360 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
361 return SetLuminosity(d, Luminosity(s));
362 });
363 }
364 }
365
366 Color Color::ApplyColorMatrix(const ColorMatrix& color_matrix) const {
367 auto* c = color_matrix.array;
368 return Color(
369 c[0] * red + c[1] * green + c[2] * blue + c[3] * alpha + c[4],
370 c[5] * red + c[6] * green + c[7] * blue + c[8] * alpha + c[9],
371 c[10] * red + c[11] * green + c[12] * blue + c[13] * alpha + c[14],
372 c[15] * red + c[16] * green + c[17] * blue + c[18] * alpha + c[19])
373 .Clamp01();
374 }
375
376 Color Color::LinearToSRGB() const {
377 static auto conversion = [](Scalar component) {
378 if (component <= 0.0031308) {
379 return component * 12.92;
380 }
381 return 1.055 * pow(component, (1.0 / 2.4)) - 0.055;
382 };
383
384 return Color(conversion(red), conversion(green), conversion(blue), alpha);
385 }
386
387 Color Color::SRGBToLinear() const {
388 static auto conversion = [](Scalar component) {
389 if (component <= 0.04045) {
390 return component / 12.92;
391 }
392 return pow((component + 0.055) / 1.055, 2.4);
393 };
394
395 return Color(conversion(red), conversion(green), conversion(blue), alpha);
396 }
397
398 std::string ColorToString(const Color& color) {
399 return SPrintF("R=%.1f,G=%.1f,B=%.1f,A=%.1f", //
400 color.red, //
401 color.green, //
402 color.blue, //
403 color.alpha //
404 );
405 }
406
407 } // namespace impeller
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
impeller::BlendMode::kDestinationATop
@ kDestinationATop
impeller::ColorToString
std::string ColorToString(const Color &color)
Definition: color.cc:398
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Color::Color
constexpr Color()
Definition: color.h:145
impeller::ColorHSB::brightness
Scalar brightness
Definition: color.h:930
impeller::ComponentChoose
static constexpr Vector3 ComponentChoose(Vector3 a, Vector3 b, Vector3 value, Scalar cutoff)
Definition: color.cc:178
impeller::BlendMode
BlendMode
Definition: color.h:59
impeller::Color
Definition: color.h:124
impeller::Color::Unpremultiply
constexpr Color Unpremultiply() const
Definition: color.h:218
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
impeller::Vector4
Definition: vector.h:232
impeller::BlendMode::kLuminosity
@ kLuminosity
impeller::BlendMode::kSource
@ kSource
impeller::BlendMode::kColorDodge
@ kColorDodge
impeller::ValidateBlendModes
static constexpr bool ValidateBlendModes()
Definition: color.cc:28
impeller::BlendMode::kDestination
@ kDestination
impeller::BlendMode::kDarken
@ kDarken
impeller::BlendMode::kColor
@ kColor
impeller::Color::alpha
Scalar alpha
Definition: color.h:143
impeller::BlendMode::kDestinationOver
@ kDestinationOver
impeller::BlendMode::kPlus
@ kPlus
impeller::BlendMode::kOverlay
@ kOverlay
impeller::Color::green
Scalar green
Definition: color.h:133
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::BlendMode::kModulate
@ kModulate
impeller::Min
static constexpr Color Min(Color c, float threshold)
Definition: color.cc:132
impeller::BlendMode::kSourceOut
@ kSourceOut
impeller::BlendMode::kSaturation
@ kSaturation
impeller::BlendMode::kDifference
@ kDifference
impeller::ColorHSB
Definition: color.h:916
impeller::BlendMode::kLighten
@ kLighten
impeller::BlendMode::kSoftLight
@ kSoftLight
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::BlendMode::kHardLight
@ kHardLight
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::Color::SRGBToLinear
Color SRGBToLinear() const
Convert the color from sRGB space to linear space.
Definition: color.cc:387
impeller::ColorHSB::alpha
Scalar alpha
Definition: color.h:935
impeller::BlendMode::kClear
@ kClear
impeller::SetSaturation
static constexpr Vector3 SetSaturation(Vector3 color, Scalar saturation)
Definition: color.cc:171
impeller::ColorMatrix::array
Scalar array[20]
Definition: color.h:118
impeller::SetLuminosity
static constexpr Vector3 SetLuminosity(Vector3 color, Scalar luminosity)
Definition: color.cc:160
impeller::Color::red
Scalar red
Definition: color.h:128
impeller::DoColorBlendComponents
static constexpr Color DoColorBlendComponents(Color d, Color s, const std::function< Scalar(Scalar, Scalar)> &blend_func)
Definition: color.cc:207
impeller::Vector3::z
Scalar z
Definition: vector.h:25
impeller::Luminosity
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:140
strings.h
impeller::Vector3::y
Scalar y
Definition: vector.h:24
impeller::ColorHSB::FromRGB
static ColorHSB FromRGB(Color rgb)
Definition: color.cc:52
IMPELLER_FOR_EACH_BLEND_MODE
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
impeller::DoColorBlend
static constexpr Color DoColorBlend(Color d, Color s, const std::function< Vector3(Vector3, Vector3)> &blend_rgb_func)
Definition: color.cc:196
scalar.h
impeller::ColorHSB::ToRGBA
Color ToRGBA() const
Definition: color.cc:79
impeller::BlendMode::kDestinationIn
@ kDestinationIn
_IMPELLER_ASSERT_BLEND_MODE
#define _IMPELLER_ASSERT_BLEND_MODE(blend_mode)
Definition: color.cc:20
impeller::BlendMode::kExclusion
@ kExclusion
impeller::BlendMode::kDestinationOut
@ kDestinationOut
impeller::ClipColor
static constexpr Vector3 ClipColor(Vector3 color)
Definition: color.cc:144
vector.h
constants.h
impeller::ColorHSB::saturation
Scalar saturation
Definition: color.h:925
impeller::ColorHSB::ColorHSB
constexpr ColorHSB(Scalar h, Scalar s, Scalar b, Scalar a)
Definition: color.h:937
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:262
impeller::BlendMode::kSourceIn
@ kSourceIn
impeller::BlendMode::kScreen
@ kScreen
impeller::Vector3::Min
constexpr Vector3 Min(const Vector3 &p) const
Definition: vector.h:70
color.h
impeller::FromRGB
static constexpr Color FromRGB(Vector3 color, Scalar alpha)
Definition: color.cc:192
impeller::BlendMode::kLast
@ kLast
impeller::BlendMode::kHue
@ kHue
impeller::kBlendModeNames
static constexpr const char * kBlendModeNames[]
Definition: color.cc:44
impeller::BlendMode::kXor
@ kXor
impeller::Color::Lerp
constexpr static Color Lerp(Color a, Color b, Scalar t)
Return a color that is linearly interpolated between colors a and b, according to the value of t.
Definition: color.h:234
impeller::Color::blue
Scalar blue
Definition: color.h:138
impeller::ColorMatrix
Definition: color.h:117
impeller::Color::ApplyColorMatrix
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:366
impeller::Color::LinearToSRGB
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:376
impeller
Definition: aiks_context.cc:10
impeller::BlendMode::kSourceATop
@ kSourceATop
impeller::BlendMode::kMultiply
@ kMultiply
impeller::Color::Premultiply
constexpr Color Premultiply() const
Definition: color.h:214
impeller::Vector3
Definition: vector.h:20
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::ColorHSB::hue
Scalar hue
Definition: color.h:920
impeller::Saturation
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:166
impeller::Vector3::Max
constexpr Vector3 Max(const Vector3 &p) const
Definition: vector.h:74
impeller::ToRGB
static constexpr Vector3 ToRGB(Color color)
Definition: color.cc:188
_IMPELLER_BLEND_MODE_NAME_LIST
#define _IMPELLER_BLEND_MODE_NAME_LIST(blend_mode)
Definition: color.cc:42
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:222