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 
52 Color::Color(const Vector4& value)
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 constexpr 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 
147 static constexpr inline Color DoColorBlendComponents(
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::kSource:
164  return src;
166  return dst;
168  // r = s + (1-sa)*d
169  return (src.Premultiply() + dst.Premultiply() * (1 - src.alpha))
170  .Unpremultiply();
172  // r = d + (1-da)*s
173  return (dst.Premultiply() + src.Premultiply() * (1 - dst.alpha))
174  .Unpremultiply();
176  // r = s * da
177  return (src.Premultiply() * dst.alpha).Unpremultiply();
179  // r = d * sa
180  return (dst.Premultiply() * src.alpha).Unpremultiply();
182  // r = s * ( 1- da)
183  return (src.Premultiply() * (1 - dst.alpha)).Unpremultiply();
185  // r = d * (1-sa)
186  return (dst.Premultiply() * (1 - src.alpha)).Unpremultiply();
188  // r = s*da + d*(1-sa)
189  return (src.Premultiply() * dst.alpha +
190  dst.Premultiply() * (1 - src.alpha))
191  .Unpremultiply();
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
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:124
impeller::ColorToString
std::string ColorToString(const Color &color)
Definition: color.cc:333
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Color::Color
constexpr Color()
Definition: color.h:144
impeller::ComponentChoose
static constexpr Vector3 ComponentChoose(Vector3 a, Vector3 b, Vector3 value, Scalar cutoff)
Definition: color.cc:101
impeller::BlendMode
BlendMode
Definition: color.h:58
impeller::Color
Definition: color.h:123
impeller::Color::Unpremultiply
constexpr Color Unpremultiply() const
Definition: color.h:215
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:142
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:147
impeller::Color::green
Scalar green
Definition: color.h:132
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:55
impeller::BlendMode::kSourceOut
@ kSourceOut
impeller::BlendMode::kSaturation
@ kSaturation
impeller::BlendMode::kDifference
@ kDifference
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:139
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:322
impeller::BlendMode::kClear
@ kClear
impeller::SetSaturation
static constexpr Vector3 SetSaturation(Vector3 color, Scalar saturation)
Definition: color.cc:94
impeller::ColorMatrix::array
Scalar array[20]
Definition: color.h:117
impeller::SetLuminosity
static constexpr Vector3 SetLuminosity(Vector3 color, Scalar luminosity)
Definition: color.cc:83
impeller::Color::red
Scalar red
Definition: color.h:127
impeller::Vector3::z
Scalar z
Definition: vector.h:25
impeller::Luminosity
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:63
strings.h
impeller::Vector3::y
Scalar y
Definition: vector.h:24
IMPELLER_FOR_EACH_BLEND_MODE
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
scalar.h
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:67
vector.h
constants.h
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:269
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:115
impeller::BlendMode::kLast
@ kLast
color
DlColor color
Definition: dl_golden_blur_unittests.cc:24
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:137
impeller::ColorMatrix
Definition: color.h:116
impeller::Color::ApplyColorMatrix
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:301
impeller::Color::LinearToSRGB
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:311
impeller
Definition: allocation.cc:12
impeller::BlendMode::kSourceATop
@ kSourceATop
impeller::BlendMode::kMultiply
@ kMultiply
impeller::Color::Premultiply
constexpr Color Premultiply() const
Definition: color.h:211
impeller::Vector3
Definition: vector.h:20
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Saturation
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:89
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:111
_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:157