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 
6 
7 #include <algorithm>
8 #include <cmath>
9 #include <functional>
10 #include <sstream>
11 #include <type_traits>
12 
13 #include "impeller/base/strings.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.
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[] = {
46 
47 const char* BlendModeToString(BlendMode blend_mode) {
48  return kBlendModeNames[static_cast<std::underlying_type_t<BlendMode>>(
49  blend_mode)];
50 }
51 
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 
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;
243  return dst;
245  // r = s + (1-sa)*d
246  return (src.Premultiply() + dst.Premultiply() * (1 - src.alpha))
247  .Unpremultiply();
249  // r = d + (1-da)*s
250  return (dst.Premultiply() + src.Premultiply() * (1 - dst.alpha))
251  .Unpremultiply();
253  // r = s * da
254  return (src.Premultiply() * dst.alpha).Unpremultiply();
256  // r = d * sa
257  return (dst.Premultiply() * src.alpha).Unpremultiply();
259  // r = s * ( 1- da)
260  return (src.Premultiply() * (1 - dst.alpha)).Unpremultiply();
262  // r = d * (1-sa)
263  return (dst.Premultiply() * (1 - src.alpha)).Unpremultiply();
265  // r = s*da + d*(1-sa)
266  return (src.Premultiply() * dst.alpha +
267  dst.Premultiply() * (1 - src.alpha))
268  .Unpremultiply();
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();
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); });
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  });
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  });
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  });
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  });
348  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
349  return (d - s).Abs();
350  });
352  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
353  return d + s - 2.0f * d * s;
354  });
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 {
361  });
362  }
364  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
366  });
367  case BlendMode::kColor:
368  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
369  return SetLuminosity(s, Luminosity(d));
370  });
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 
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 
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: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::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:916
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: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::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:925
impeller::ColorHSB::ColorHSB
constexpr ColorHSB(Scalar h, Scalar s, Scalar b, Scalar a)
Definition: color.h:937
impeller::saturated::b
SI b
Definition: saturated_math.h:87
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::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_blur_unittests.cc:20
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:234