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 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;
231  return dst;
233  // r = s + (1-sa)*d
234  return (src.Premultiply() + dst.Premultiply() * (1 - src.alpha))
235  .Unpremultiply();
237  // r = d + (1-da)*s
238  return (dst.Premultiply() + src.Premultiply() * (1 - dst.alpha))
239  .Unpremultiply();
241  // r = s * da
242  return (src.Premultiply() * dst.alpha).Unpremultiply();
244  // r = d * sa
245  return (dst.Premultiply() * src.alpha).Unpremultiply();
247  // r = s * ( 1- da)
248  return (src.Premultiply() * (1 - dst.alpha)).Unpremultiply();
250  // r = d * (1-sa)
251  return (dst.Premultiply() * (1 - src.alpha)).Unpremultiply();
253  // r = s*da + d*(1-sa)
254  return (src.Premultiply() * dst.alpha +
255  dst.Premultiply() * (1 - src.alpha))
256  .Unpremultiply();
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();
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); });
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  });
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  });
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  });
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  });
336  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
337  return (d - s).Abs();
338  });
340  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
341  return d + s - 2.0f * d * s;
342  });
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 {
349  });
350  }
352  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
354  });
355  case BlendMode::kColor:
356  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
357  return SetLuminosity(s, Luminosity(d));
358  });
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 
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 
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::BlendMode
BlendMode
Definition: color.h:57
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
impeller::ColorToString
std::string ColorToString(const Color &color)
Definition: color.cc:398
impeller::Scalar
float Scalar
Definition: scalar.h:15
impeller::BlendMode::kMultiply
@ kMultiply
impeller::Color::Color
constexpr Color()
Definition: color.h:143
impeller::ColorHSB::brightness
Scalar brightness
Definition: color.h:924
impeller::ComponentChoose
static constexpr Vector3 ComponentChoose(Vector3 a, Vector3 b, Vector3 value, Scalar cutoff)
Definition: color.cc:178
impeller::Color
Definition: color.h:122
impeller::Color::Unpremultiply
constexpr Color Unpremultiply() const
Definition: color.h:216
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:55
impeller::Vector4
Definition: vector.h:229
impeller::ValidateBlendModes
static constexpr bool ValidateBlendModes()
Definition: color.cc:28
impeller::BlendMode::kSourceIn
@ kSourceIn
impeller::Color::alpha
Scalar alpha
Definition: color.h:141
impeller::BlendMode::kSaturation
@ kSaturation
impeller::BlendMode::kHue
@ kHue
impeller::BlendMode::kDestinationIn
@ kDestinationIn
impeller::BlendMode::kDarken
@ kDarken
impeller::BlendMode::kColor
@ kColor
impeller::Color::green
Scalar green
Definition: color.h:131
impeller::Vector3::x
Scalar x
Definition: vector.h:20
impeller::Min
static constexpr Color Min(Color c, float threshold)
Definition: color.cc:132
impeller::BlendMode::kLuminosity
@ kLuminosity
impeller::BlendMode::kXor
@ kXor
impeller::BlendMode::kOverlay
@ kOverlay
impeller::ColorHSB
Definition: color.h:910
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::BlendMode::kLighten
@ kLighten
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::BlendMode::kHardLight
@ kHardLight
impeller::BlendMode::kPlus
@ kPlus
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::BlendMode::kDestination
@ kDestination
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:929
impeller::SetSaturation
static constexpr Vector3 SetSaturation(Vector3 color, Scalar saturation)
Definition: color.cc:171
impeller::ColorMatrix::array
Scalar array[20]
Definition: color.h:116
impeller::SetLuminosity
static constexpr Vector3 SetLuminosity(Vector3 color, Scalar luminosity)
Definition: color.cc:160
impeller::Color::red
Scalar red
Definition: color.h:126
impeller::BlendMode::kDestinationATop
@ kDestinationATop
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:22
impeller::Luminosity
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:140
strings.h
impeller::BlendMode::kDestinationOut
@ kDestinationOut
impeller::Vector3::y
Scalar y
Definition: vector.h:21
impeller::ColorHSB::FromRGB
static ColorHSB FromRGB(Color rgb)
Definition: color.cc:52
impeller::BlendMode::kSoftLight
@ kSoftLight
IMPELLER_FOR_EACH_BLEND_MODE
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:17
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::kDifference
@ kDifference
_IMPELLER_ASSERT_BLEND_MODE
#define _IMPELLER_ASSERT_BLEND_MODE(blend_mode)
Definition: color.cc:20
impeller::ClipColor
static constexpr Vector3 ClipColor(Vector3 color)
Definition: color.cc:144
vector.h
impeller::BlendMode::kExclusion
@ kExclusion
constants.h
impeller::ColorHSB::saturation
Scalar saturation
Definition: color.h:919
impeller::BlendMode::kDestinationOver
@ kDestinationOver
impeller::ColorHSB::ColorHSB
constexpr ColorHSB(Scalar h, Scalar s, Scalar b, Scalar a)
Definition: color.h:931
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:260
impeller::BlendMode::kClear
@ kClear
impeller::BlendMode::kLast
@ kLast
impeller::Vector3::Min
constexpr Vector3 Min(const Vector3 &p) const
Definition: vector.h:67
color.h
impeller::FromRGB
static constexpr Color FromRGB(Vector3 color, Scalar alpha)
Definition: color.cc:192
impeller::BlendMode::kColorDodge
@ kColorDodge
impeller::BlendMode::kSourceATop
@ kSourceATop
impeller::kBlendModeNames
static constexpr const char * kBlendModeNames[]
Definition: color.cc:44
impeller::BlendMode::kModulate
@ kModulate
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:232
impeller::Color::blue
Scalar blue
Definition: color.h:136
impeller::ColorMatrix
Definition: color.h:115
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::BlendMode::kScreen
@ kScreen
impeller
Definition: aiks_context.cc:10
impeller::BlendMode::kSourceOut
@ kSourceOut
impeller::Color::Premultiply
constexpr Color Premultiply() const
Definition: color.h:212
impeller::BlendMode::kSource
@ kSource
impeller::Vector3
Definition: vector.h:17
impeller::ColorHSB::hue
Scalar hue
Definition: color.h:914
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:71
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