| 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 /// Composite a blended color onto the destination.
197 /// All three parameters are unpremultiplied. Returns a premultiplied result.
198 ///
199 /// This routine is the same as `IPApplyBlendedColor` in the Impeller shader
200 /// library.
201 static constexpr inline Color ApplyBlendedColor(Color dst,
202 Color src,
203 Vector3 blend_result) {
204 dst = dst.Premultiply();
205 src =
206 // Use the blended color for areas where the source and destination
207 // colors overlap.
208 FromRGB(blend_result, src.alpha * dst.alpha).Premultiply() +
209 // Use the original source color for any remaining non-overlapping areas.
210 src.Premultiply() * (1.0f - dst.alpha);
211
212 // Source-over composite the blended source color atop the destination.
213 return src + dst * (1.0f - src.alpha);
214 }
215
216 static constexpr inline Color DoColorBlend(
217 Color dst,
218 Color src,
219 const std::function<Vector3(Vector3, Vector3)>& blend_rgb_func) {
220 const Vector3 blend_result = blend_rgb_func(ToRGB(dst), ToRGB(src));
221 return ApplyBlendedColor(dst, src, blend_result).Unpremultiply();
222 }
223
224 static constexpr inline Color DoColorBlendComponents(
225 Color dst,
226 Color src,
227 const std::function<Scalar(Scalar, Scalar)>& blend_func) {
228 Vector3 blend_result = Vector3(blend_func(dst.red, src.red), //
229 blend_func(dst.green, src.green), //
230 blend_func(dst.blue, src.blue)); //
231 return ApplyBlendedColor(dst, src, blend_result).Unpremultiply();
232 }
233
234 Color Color::Blend(Color src, BlendMode blend_mode) const {
235 Color dst = *this;
236
237 switch (blend_mode) {
238 case BlendMode::kClear:
239 return Color::BlackTransparent();
240 case BlendMode::kSource:
241 return src;
242 case BlendMode::kDestination:
243 return dst;
244 case BlendMode::kSourceOver:
245 // r = s + (1-sa)*d
246 return (src.Premultiply() + dst.Premultiply() * (1 - src.alpha))
247 .Unpremultiply();
248 case BlendMode::kDestinationOver:
249 // r = d + (1-da)*s
250 return (dst.Premultiply() + src.Premultiply() * (1 - dst.alpha))
251 .Unpremultiply();
252 case BlendMode::kSourceIn:
253 // r = s * da
254 return (src.Premultiply() * dst.alpha).Unpremultiply();
255 case BlendMode::kDestinationIn:
256 // r = d * sa
257 return (dst.Premultiply() * src.alpha).Unpremultiply();
258 case BlendMode::kSourceOut:
259 // r = s * ( 1- da)
260 return (src.Premultiply() * (1 - dst.alpha)).Unpremultiply();
261 case BlendMode::kDestinationOut:
262 // r = d * (1-sa)
263 return (dst.Premultiply() * (1 - src.alpha)).Unpremultiply();
264 case BlendMode::kSourceATop:
265 // r = s*da + d*(1-sa)
266 return (src.Premultiply() * dst.alpha +
267 dst.Premultiply() * (1 - src.alpha))
268 .Unpremultiply();
269 case BlendMode::kDestinationATop:
270 // r = d*sa + s*(1-da)
271 return (dst.Premultiply() * src.alpha +
272 src.Premultiply() * (1 - dst.alpha))
273 .Unpremultiply();
274 case BlendMode::kXor:
275 // r = s*(1-da) + d*(1-sa)
276 return (src.Premultiply() * (1 - dst.alpha) +
277 dst.Premultiply() * (1 - src.alpha))
278 .Unpremultiply();
279 case BlendMode::kPlus:
280 // r = min(s + d, 1)
281 return (Min(src.Premultiply() + dst.Premultiply(), 1)).Unpremultiply();
282 case BlendMode::kModulate:
283 // r = s*d
284 return (src.Premultiply() * dst.Premultiply()).Unpremultiply();
285 case BlendMode::kScreen: {
286 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
287 return s + d - s * d;
288 });
289 }
290 case BlendMode::kOverlay:
291 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
292 // The same as HardLight, but with the source and destination reversed.
293 Vector3 screen_src = 2.0 * d - 1.0;
294 Vector3 screen = screen_src + s - screen_src * s;
295 return ComponentChoose(s * (2.0 * d), //
296 screen, //
297 d, //
298 0.5);
299 });
300 case BlendMode::kDarken:
301 return DoColorBlend(
302 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Min(s); });
303 case BlendMode::kLighten:
304 return DoColorBlend(
305 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Max(s); });
306 case BlendMode::kColorDodge:
307 return DoColorBlendComponents(dst, src, [](Scalar d, Scalar s) -> Scalar {
308 if (d < kEhCloseEnough) {
309 return 0.0f;
310 }
311 if (1.0 - s < kEhCloseEnough) {
312 return 1.0f;
313 }
314 return std::min(1.0f, d / (1.0f - s));
315 });
316 case BlendMode::kColorBurn:
317 return DoColorBlendComponents(dst, src, [](Scalar d, Scalar s) -> Scalar {
318 if (1.0 - d < kEhCloseEnough) {
319 return 1.0f;
320 }
321 if (s < kEhCloseEnough) {
322 return 0.0f;
323 }
324 return 1.0f - std::min(1.0f, (1.0f - d) / s);
325 });
326 case BlendMode::kHardLight:
327 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
328 Vector3 screen_src = 2.0 * s - 1.0;
329 Vector3 screen = screen_src + d - screen_src * d;
330 return ComponentChoose(d * (2.0 * s), //
331 screen, //
332 s, //
333 0.5);
334 });
335 case BlendMode::kSoftLight:
336 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
337 Vector3 D = ComponentChoose(((16.0 * d - 12.0) * d + 4.0) * d, //
338 Vector3(std::sqrt(d.x), std::sqrt(d.y),
339 std::sqrt(d.z)), //
340 d, //
341 0.25);
342 return ComponentChoose(d - (1.0 - 2.0 * s) * d * (1.0 - d), //
343 d + (2.0 * s - 1.0) * (D - d), //
344 s, //
345 0.5);
346 });
347 case BlendMode::kDifference:
348 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
349 return (d - s).Abs();
350 });
351 case BlendMode::kExclusion:
352 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
353 return d + s - 2.0f * d * s;
354 });
355 case BlendMode::kMultiply:
356 return DoColorBlend(
357 dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d * s; });
358 case BlendMode::kHue: {
359 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
360 return SetLuminosity(SetSaturation(s, Saturation(d)), Luminosity(d));
361 });
362 }
363 case BlendMode::kSaturation:
364 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
365 return SetLuminosity(SetSaturation(d, Saturation(s)), Luminosity(d));
366 });
367 case BlendMode::kColor:
368 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
369 return SetLuminosity(s, Luminosity(d));
370 });
371 case BlendMode::kLuminosity:
372 return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
373 return SetLuminosity(d, Luminosity(s));
374 });
375 }
376 }
377
378 Color Color::ApplyColorMatrix(const ColorMatrix& color_matrix) const {
379 auto* c = color_matrix.array;
380 return Color(
381 c[0] * red + c[1] * green + c[2] * blue + c[3] * alpha + c[4],
382 c[5] * red + c[6] * green + c[7] * blue + c[8] * alpha + c[9],
383 c[10] * red + c[11] * green + c[12] * blue + c[13] * alpha + c[14],
384 c[15] * red + c[16] * green + c[17] * blue + c[18] * alpha + c[19])
385 .Clamp01();
386 }
387
388 Color Color::LinearToSRGB() const {
389 static auto conversion = [](Scalar component) {
390 if (component <= 0.0031308) {
391 return component * 12.92;
392 }
393 return 1.055 * pow(component, (1.0 / 2.4)) - 0.055;
394 };
395
396 return Color(conversion(red), conversion(green), conversion(blue), alpha);
397 }
398
399 Color Color::SRGBToLinear() const {
400 static auto conversion = [](Scalar component) {
401 if (component <= 0.04045) {
402 return component / 12.92;
403 }
404 return pow((component + 0.055) / 1.055, 2.4);
405 };
406
407 return Color(conversion(red), conversion(green), conversion(blue), alpha);
408 }
409
410 std::string ColorToString(const Color& color) {
411 return SPrintF("R=%.1f,G=%.1f,B=%.1f,A=%.1f", //
412 color.red, //
413 color.green, //
414 color.blue, //
415 color.alpha //
416 );
417 }
418
419 } // namespace impeller
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
impeller::BlendMode::kDestinationATop
@ kDestinationATop
impeller::ApplyBlendedColor
static constexpr Color ApplyBlendedColor(Color dst, Color src, Vector3 blend_result)
Definition: color.cc:201
impeller::ColorToString
std::string ColorToString(const Color &color)
Definition: color.cc:410
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:940
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::DoColorBlendComponents
static constexpr Color DoColorBlendComponents(Color dst, Color src, const std::function< Scalar(Scalar, Scalar)> &blend_func)
Definition: color.cc:224
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:926
impeller::BlendMode::kLighten
@ kLighten
impeller::BlendMode::kSoftLight
@ kSoftLight
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::BlendMode::kHardLight
@ kHardLight
impeller::DoColorBlend
static constexpr Color DoColorBlend(Color dst, Color src, const std::function< Vector3(Vector3, Vector3)> &blend_rgb_func)
Definition: color.cc:216
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:399
impeller::ColorHSB::alpha
Scalar alpha
Definition: color.h:945
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::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
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:935
impeller::ColorHSB::ColorHSB
constexpr ColorHSB(Scalar h, Scalar s, Scalar b, Scalar a)
Definition: color.h:947
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:272
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
color
DlColor color
Definition: dl_golden_blur_unittests.cc:23
impeller::BlendMode::kHue
@ kHue
impeller::kBlendModeNames
static constexpr const char * kBlendModeNames[]
Definition: color.cc:44
impeller::BlendMode::kXor
@ kXor
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:378
impeller::Color::LinearToSRGB
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:388
impeller
Definition: aiks_blend_unittests.cc:18
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:930
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:234