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 <type_traits>
11 
15 
16 namespace impeller {
17 
18 #define _IMPELLER_ASSERT_BLEND_MODE(blend_mode) \
19  auto enum_##blend_mode = static_cast<std::underlying_type_t<BlendMode>>( \
20  BlendMode::k##blend_mode); \
21  if (i != enum_##blend_mode) { \
22  return false; \
23  } \
24  ++i;
25 
26 static constexpr inline bool ValidateBlendModes() {
27  std::underlying_type_t<BlendMode> i = 0;
28  // Ensure the order of the blend modes match.
30  // Ensure the total number of blend modes match.
31  if (i - 1 !=
32  static_cast<std::underlying_type_t<BlendMode>>(BlendMode::kLastMode)) {
33  return false;
34  }
35  return true;
36 }
37 static_assert(ValidateBlendModes(),
38  "IMPELLER_FOR_EACH_BLEND_MODE must match impeller::BlendMode.");
39 
40 #define _IMPELLER_BLEND_MODE_NAME_LIST(blend_mode) #blend_mode,
41 
42 static constexpr const char* kBlendModeNames[] = {
44 
45 const char* BlendModeToString(BlendMode blend_mode) {
46  return kBlendModeNames[static_cast<std::underlying_type_t<BlendMode>>(
47  blend_mode)];
48 }
49 
51  : red(value.x), green(value.y), blue(value.z), alpha(value.w) {}
52 
53 static constexpr inline Color Min(Color c, float threshold) {
54  return Color(std::min(c.red, threshold), std::min(c.green, threshold),
55  std::min(c.blue, threshold), std::min(c.alpha, threshold));
56 }
57 
58 // The following HSV utilities correspond to the W3C blend definitions
59 // implemented in: impeller/compiler/shader_lib/impeller/blending.glsl
60 
61 static constexpr inline Scalar Luminosity(Vector3 color) {
62  return color.x * 0.3f + color.y * 0.59f + color.z * 0.11f;
63 }
64 
65 static constexpr inline Vector3 ClipColor(Vector3 color) {
66  Scalar lum = Luminosity(color);
67  Scalar mn = std::min(std::min(color.x, color.y), color.z);
68  Scalar mx = std::max(std::max(color.x, color.y), color.z);
69  // `lum - mn` and `mx - lum` will always be >= 0 in the following conditions,
70  // so adding a tiny value is enough to make these divisions safe.
71  if (mn < 0.0f) {
72  color = lum + (((color - lum) * lum) / (lum - mn + kEhCloseEnough));
73  }
74  if (mx > 1.0) {
75  color =
76  lum + (((color - lum) * (1.0f - lum)) / (mx - lum + kEhCloseEnough));
77  }
78  return color;
79 }
80 
81 static constexpr inline Vector3 SetLuminosity(Vector3 color,
82  Scalar luminosity) {
83  Scalar relative_lum = luminosity - Luminosity(color);
84  return ClipColor(color + relative_lum);
85 }
86 
87 static constexpr inline Scalar Saturation(Vector3 color) {
88  return std::max(std::max(color.x, color.y), color.z) -
89  std::min(std::min(color.x, color.y), color.z);
90 }
91 
92 static constexpr inline Vector3 SetSaturation(Vector3 color,
93  Scalar saturation) {
94  Scalar mn = std::min(std::min(color.x, color.y), color.z);
95  Scalar mx = std::max(std::max(color.x, color.y), color.z);
96  return (mn < mx) ? ((color - mn) * saturation) / (mx - mn) : Vector3();
97 }
98 
99 static constexpr inline Vector3 ComponentChoose(Vector3 a,
100  Vector3 b,
101  Vector3 value,
102  Scalar cutoff) {
103  return Vector3(value.x > cutoff ? b.x : a.x, //
104  value.y > cutoff ? b.y : a.y, //
105  value.z > cutoff ? b.z : a.z //
106  );
107 }
108 
109 static constexpr inline Vector3 ToRGB(Color color) {
110  return {color.red, color.green, color.blue};
111 }
112 
113 static constexpr inline Color FromRGB(Vector3 color, Scalar alpha) {
114  return {color.x, color.y, color.z, alpha};
115 }
116 
117 /// Composite a blended color onto the destination.
118 /// All three parameters are unpremultiplied. Returns a premultiplied result.
119 ///
120 /// This routine is the same as `IPApplyBlendedColor` in the Impeller shader
121 /// library.
122 static constexpr inline Color ApplyBlendedColor(Color dst,
123  Color src,
124  Vector3 blend_result) {
125  dst = dst.Premultiply();
126  src =
127  // Use the blended color for areas where the source and destination
128  // colors overlap.
129  FromRGB(blend_result, src.alpha * dst.alpha).Premultiply() +
130  // Use the original source color for any remaining non-overlapping areas.
131  src.Premultiply() * (1.0f - dst.alpha);
132 
133  // Source-over composite the blended source color atop the destination.
134  return src + dst * (1.0f - src.alpha);
135 }
136 
137 static inline Color DoColorBlend(
138  Color dst,
139  Color src,
140  const std::function<Vector3(Vector3, Vector3)>& blend_rgb_func) {
141  const Vector3 blend_result = blend_rgb_func(ToRGB(dst), ToRGB(src));
142  return ApplyBlendedColor(dst, src, blend_result).Unpremultiply();
143 }
144 
146  Color dst,
147  Color src,
148  const std::function<Scalar(Scalar, Scalar)>& blend_func) {
149  Vector3 blend_result = Vector3(blend_func(dst.red, src.red), //
150  blend_func(dst.green, src.green), //
151  blend_func(dst.blue, src.blue)); //
152  return ApplyBlendedColor(dst, src, blend_result).Unpremultiply();
153 }
154 
155 Color Color::Blend(Color src, BlendMode blend_mode) const {
156  Color dst = *this;
157 
158  switch (blend_mode) {
159  case BlendMode::kClear:
160  return Color::BlackTransparent();
161  case BlendMode::kSrc:
162  return src;
163  case BlendMode::kDst:
164  return dst;
165  case BlendMode::kSrcOver:
166  // r = s + (1-sa)*d
167  return (src.Premultiply() + dst.Premultiply() * (1 - src.alpha))
168  .Unpremultiply();
169  case BlendMode::kDstOver:
170  // r = d + (1-da)*s
171  return (dst.Premultiply() + src.Premultiply() * (1 - dst.alpha))
172  .Unpremultiply();
173  case BlendMode::kSrcIn:
174  // r = s * da
175  return (src.Premultiply() * dst.alpha).Unpremultiply();
176  case BlendMode::kDstIn:
177  // r = d * sa
178  return (dst.Premultiply() * src.alpha).Unpremultiply();
179  case BlendMode::kSrcOut:
180  // r = s * ( 1- da)
181  return (src.Premultiply() * (1 - dst.alpha)).Unpremultiply();
182  case BlendMode::kDstOut:
183  // r = d * (1-sa)
184  return (dst.Premultiply() * (1 - src.alpha)).Unpremultiply();
185  case BlendMode::kSrcATop:
186  // r = s*da + d*(1-sa)
187  return (src.Premultiply() * dst.alpha +
188  dst.Premultiply() * (1 - src.alpha))
189  .Unpremultiply();
190  case BlendMode::kDstATop:
191  // r = d*sa + s*(1-da)
192  return (dst.Premultiply() * src.alpha +
193  src.Premultiply() * (1 - dst.alpha))
194  .Unpremultiply();
195  case BlendMode::kXor:
196  // r = s*(1-da) + d*(1-sa)
197  return (src.Premultiply() * (1 - dst.alpha) +
198  dst.Premultiply() * (1 - src.alpha))
199  .Unpremultiply();
200  case BlendMode::kPlus:
201  // r = min(s + d, 1)
202  return (Min(src.Premultiply() + dst.Premultiply(), 1)).Unpremultiply();
204  // r = s*d
205  return (src.Premultiply() * dst.Premultiply()).Unpremultiply();
206  case BlendMode::kScreen: {
207  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
208  return s + d - s * d;
209  });
210  }
211  case BlendMode::kOverlay:
212  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
213  // The same as HardLight, but with the source and destination reversed.
214  Vector3 screen_src = 2.0 * d - 1.0;
215  Vector3 screen = screen_src + s - screen_src * s;
216  return ComponentChoose(s * (2.0 * d), //
217  screen, //
218  d, //
219  0.5);
220  });
221  case BlendMode::kDarken:
222  return DoColorBlend(
223  dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Min(s); });
224  case BlendMode::kLighten:
225  return DoColorBlend(
226  dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d.Max(s); });
228  return DoColorBlendComponents(dst, src, [](Scalar d, Scalar s) -> Scalar {
229  if (d < kEhCloseEnough) {
230  return 0.0f;
231  }
232  if (1.0 - s < kEhCloseEnough) {
233  return 1.0f;
234  }
235  return std::min(1.0f, d / (1.0f - s));
236  });
238  return DoColorBlendComponents(dst, src, [](Scalar d, Scalar s) -> Scalar {
239  if (1.0 - d < kEhCloseEnough) {
240  return 1.0f;
241  }
242  if (s < kEhCloseEnough) {
243  return 0.0f;
244  }
245  return 1.0f - std::min(1.0f, (1.0f - d) / s);
246  });
248  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
249  Vector3 screen_src = 2.0 * s - 1.0;
250  Vector3 screen = screen_src + d - screen_src * d;
251  return ComponentChoose(d * (2.0 * s), //
252  screen, //
253  s, //
254  0.5);
255  });
257  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
258  Vector3 D = ComponentChoose(((16.0 * d - 12.0) * d + 4.0) * d, //
259  Vector3(std::sqrt(d.x), std::sqrt(d.y),
260  std::sqrt(d.z)), //
261  d, //
262  0.25);
263  return ComponentChoose(d - (1.0 - 2.0 * s) * d * (1.0 - d), //
264  d + (2.0 * s - 1.0) * (D - d), //
265  s, //
266  0.5);
267  });
269  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
270  return (d - s).Abs();
271  });
273  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
274  return d + s - 2.0f * d * s;
275  });
277  return DoColorBlend(
278  dst, src, [](Vector3 d, Vector3 s) -> Vector3 { return d * s; });
279  case BlendMode::kHue: {
280  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
282  });
283  }
285  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
287  });
288  case BlendMode::kColor:
289  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
290  return SetLuminosity(s, Luminosity(d));
291  });
293  return DoColorBlend(dst, src, [](Vector3 d, Vector3 s) -> Vector3 {
294  return SetLuminosity(d, Luminosity(s));
295  });
296  }
297 }
298 
299 Color Color::ApplyColorMatrix(const ColorMatrix& color_matrix) const {
300  auto* c = color_matrix.array;
301  return Color(
302  c[0] * red + c[1] * green + c[2] * blue + c[3] * alpha + c[4],
303  c[5] * red + c[6] * green + c[7] * blue + c[8] * alpha + c[9],
304  c[10] * red + c[11] * green + c[12] * blue + c[13] * alpha + c[14],
305  c[15] * red + c[16] * green + c[17] * blue + c[18] * alpha + c[19])
306  .Clamp01();
307 }
308 
310  static auto conversion = [](Scalar component) {
311  if (component <= 0.0031308) {
312  return component * 12.92;
313  }
314  return 1.055 * pow(component, (1.0 / 2.4)) - 0.055;
315  };
316 
317  return Color(conversion(red), conversion(green), conversion(blue), alpha);
318 }
319 
321  static auto conversion = [](Scalar component) {
322  if (component <= 0.04045) {
323  return component / 12.92;
324  }
325  return pow((component + 0.055) / 1.055, 2.4);
326  };
327 
328  return Color(conversion(red), conversion(green), conversion(blue), alpha);
329 }
330 
331 } // namespace impeller
#define _IMPELLER_ASSERT_BLEND_MODE(blend_mode)
Definition: color.cc:18
#define _IMPELLER_BLEND_MODE_NAME_LIST(blend_mode)
Definition: color.cc:40
#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:42
static constexpr Color Min(Color c, float threshold)
Definition: color.cc:53
static constexpr bool ValidateBlendModes()
Definition: color.cc:26
static constexpr Vector3 SetSaturation(Vector3 color, Scalar saturation)
Definition: color.cc:92
float Scalar
Definition: scalar.h:19
static Color DoColorBlendComponents(Color dst, Color src, const std::function< Scalar(Scalar, Scalar)> &blend_func)
Definition: color.cc:145
constexpr float kEhCloseEnough
Definition: constants.h:57
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:87
static constexpr Vector3 ToRGB(Color color)
Definition: color.cc:109
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:45
static constexpr Color FromRGB(Vector3 color, Scalar alpha)
Definition: color.cc:113
static constexpr Vector3 SetLuminosity(Vector3 color, Scalar luminosity)
Definition: color.cc:81
static constexpr Color ApplyBlendedColor(Color dst, Color src, Vector3 blend_result)
Definition: color.cc:122
BlendMode
Definition: color.h:58
static Color DoColorBlend(Color dst, Color src, const std::function< Vector3(Vector3, Vector3)> &blend_rgb_func)
Definition: color.cc:137
static constexpr Vector3 ComponentChoose(Vector3 a, Vector3 b, Vector3 value, Scalar cutoff)
Definition: color.cc:99
static constexpr Vector3 ClipColor(Vector3 color)
Definition: color.cc:65
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:61
Scalar blue
Definition: color.h:138
static constexpr Color BlackTransparent()
Definition: color.h:275
Scalar alpha
Definition: color.h:143
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:309
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:299
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:320
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:155
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