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::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[] = {
46 
47 const char* BlendModeToString(BlendMode blend_mode) {
48  return kBlendModeNames[static_cast<std::underlying_type_t<BlendMode>>(
49  blend_mode)];
50 }
51 
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 
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();
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); });
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  });
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  });
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  });
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  });
271  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
272  return (d - s).Abs();
273  });
275  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
276  return d + s - 2.0f * d * s;
277  });
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 {
284  });
285  }
287  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
289  });
290  case BlendMode::kColor:
291  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
292  return SetLuminosity(s, Luminosity(d));
293  });
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 
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 
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 SPrintF("R=%.1f,G=%.1f,B=%.1f,A=%.1f", //
335  color.red, //
336  color.green, //
337  color.blue, //
338  color.alpha //
339  );
340 }
341 
342 } // namespace impeller
#define _IMPELLER_ASSERT_BLEND_MODE(blend_mode)
Definition: color.cc:20
#define _IMPELLER_BLEND_MODE_NAME_LIST(blend_mode)
Definition: color.cc:42
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
int32_t value
int32_t x
static constexpr const char * kBlendModeNames[]
Definition: color.cc:44
static constexpr Color Min(Color c, float threshold)
Definition: color.cc:55
static constexpr bool ValidateBlendModes()
Definition: color.cc:28
static constexpr Vector3 SetSaturation(Vector3 color, Scalar saturation)
Definition: color.cc:94
float Scalar
Definition: scalar.h:19
static Color DoColorBlendComponents(Color dst, Color src, const std::function< Scalar(Scalar, Scalar)> &blend_func)
Definition: color.cc:147
constexpr float kEhCloseEnough
Definition: constants.h:57
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:89
static constexpr Vector3 ToRGB(Color color)
Definition: color.cc:111
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
static constexpr Color FromRGB(Vector3 color, Scalar alpha)
Definition: color.cc:115
static constexpr Vector3 SetLuminosity(Vector3 color, Scalar luminosity)
Definition: color.cc:83
static constexpr Color ApplyBlendedColor(Color dst, Color src, Vector3 blend_result)
Definition: color.cc:124
BlendMode
Definition: color.h:58
std::string ColorToString(const Color &color)
Definition: color.cc:333
static Color DoColorBlend(Color dst, Color src, const std::function< Vector3(Vector3, Vector3)> &blend_rgb_func)
Definition: color.cc:139
static constexpr Vector3 ComponentChoose(Vector3 a, Vector3 b, Vector3 value, Scalar cutoff)
Definition: color.cc:101
static constexpr Vector3 ClipColor(Vector3 color)
Definition: color.cc:67
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:63
Scalar blue
Definition: color.h:138
static constexpr Color BlackTransparent()
Definition: color.h:270
Scalar alpha
Definition: color.h:143
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:311
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:301
Scalar red
Definition: color.h:128
constexpr Color Unpremultiply() const
Definition: color.h:216
constexpr Color()
Definition: color.h:145
Scalar green
Definition: color.h:133
constexpr Color Premultiply() const
Definition: color.h:212
Color SRGBToLinear() const
Convert the color from sRGB space to linear space.
Definition: color.cc:322
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
Scalar array[20]
Definition: color.h:118
Vector3 Min(const Vector3 &p) const
Definition: vector.h:70
Vector3 Max(const Vector3 &p) const
Definition: vector.h:74