ge211  2021.5.1
A student game engine
ge211_color.cxx
1 #include "ge211_color.hxx"
2 
3 #include <algorithm>
4 #include <cmath>
5 #include <tuple>
6 
7 
8 namespace ge211 {
9 
10 template<class T, class U>
11 static T weighted_average(T t, double weight, U u) NOEXCEPT
12 {
13  auto f1 = static_cast<double>(t);
14  auto f2 = static_cast<double>(u);
15  double result = (1 - weight) * f1 + weight * f2;
16  return T(result);
17 }
18 
19 template<class Whole, class Field, class Goal>
20 static Whole adjust_field(Whole result, Field Whole::*field,
21  double weight, Goal goal) NOEXCEPT
22 {
23  result.*field = weighted_average(result.*field, weight, goal);
24  return result;
25 }
26 
27 Color
28 Color::from_rgba(double red, double green, double blue, double alpha) NOEXCEPT
29 {
30  return Color{uint8_t(255 * red),
31  uint8_t(255 * green),
32  uint8_t(255 * blue),
33  uint8_t(255 * alpha)};
34 }
35 
36 // Creates a color from the HSL/HSV-style hue, the chroma, an adjustment
37 // for brightness, and the alpha.
38 //
39 // From https://en.wikipedia.org/wiki/HSL_and_HSV
40 static Color from_hcma(double hue,
41  double C,
42  double m,
43  double alpha) NOEXCEPT
44 {
45  double H6 = std::fmod(hue, 360.0) / 60.0;
46  double X = C * (1 - std::fabs(std::fmod(H6, 2) - 1));
47 
48  double r1 = 0, g1 = 0, b1 = 0;
49 
50  if (H6 <= 1) {
51  r1 = C;
52  g1 = X;
53  } else if (H6 <= 2) {
54  r1 = X;
55  g1 = C;
56  } else if (H6 <= 3) {
57  g1 = C;
58  b1 = X;
59  } else if (H6 <= 4) {
60  g1 = X;
61  b1 = C;
62  } else if (H6 <= 5) {
63  b1 = C;
64  r1 = X;
65  } else {
66  b1 = X;
67  r1 = C;
68  }
69 
70  return Color::from_rgba(r1 + m, g1 + m, b1 + m, alpha);
71 }
72 
73 Color Color::from_hsla(double hue, double saturation, double lightness,
74  double alpha) NOEXCEPT
75 {
76  double C = (1 - std::fabs(2 * lightness - 1)) * saturation;
77  double m = lightness - 0.5 * C;
78  return from_hcma(hue, C, m, alpha);
79 }
80 
81 Color Color::from_hsva(double hue, double saturation, double value,
82  double alpha) NOEXCEPT
83 {
84  double C = value * saturation;
85  double m = value - C;
86  return from_hcma(hue, C, m, alpha);
87 }
88 
89 Color Color::blend(double weight, Color that) const NOEXCEPT
90 {
91  return Color{
92  weighted_average(red(), weight, that.red()),
93  weighted_average(green(), weight, that.green()),
94  weighted_average(blue(), weight, that.blue()),
95  weighted_average(alpha(), weight, that.alpha())
96  };
97 }
98 
99 Color Color::invert() const NOEXCEPT
100 {
101  return Color{uint8_t(~red_), uint8_t(~blue_), uint8_t(~green_), alpha_};
102 }
103 
104 Color Color::rotate_hue(double degrees) const NOEXCEPT
105 {
106  return to_hsva().rotate_hue(degrees).to_rgba();
107 }
108 
109 Color Color::lighten(double unit_amount) const NOEXCEPT
110 {
111  return to_hsla().lighten(unit_amount).to_rgba();
112 }
113 
114 Color Color::darken(double unit_amount) const NOEXCEPT
115 {
116  return to_hsla().darken(unit_amount).to_rgba();
117 }
118 
119 Color Color::saturate(double unit_amount) const NOEXCEPT
120 {
121  return to_hsla().saturate(unit_amount).to_rgba();
122 }
123 
124 Color Color::desaturate(double unit_amount) const NOEXCEPT
125 {
126  return to_hsla().desaturate(unit_amount).to_rgba();
127 }
128 
129 static std::tuple<double, double, double, double> to_HCMm(Color color) NOEXCEPT
130 {
131  double R = color.red() / 255.0;
132  double G = color.green() / 255.0;
133  double B = color.blue() / 255.0;
134 
135  double M = std::max(R, std::max(G, B));
136  double m = std::min(R, std::min(G, B));
137  double C = M - m;
138 
139  double H6 =
140  (M == R) ? std::fmod((G - B) / C, 6) :
141  (M == G) ? (B - R) / C + 2 :
142  (R - G) / C + 4;
143 
144  double H = 60 * H6;
145 
146  return std::make_tuple(H, C, M, m);
147 }
148 
149 Color::HSLA Color::to_hsla() const NOEXCEPT
150 {
151  double H, C, M, m;
152  std::tie(H, C, M, m) = to_HCMm(*this);
153 
154  double L = (M + m) / 2;
155  double S = (L == 1) ? 0 : C / (1 - std::fabs(2 * L - 1));
156 
157  return {H, S, L, alpha() / 255.0};
158 }
159 
160 Color::HSVA Color::to_hsva() const NOEXCEPT
161 {
162  double H, C, M, m;
163  std::tie(H, C, M, m) = to_HCMm(*this);
164 
165  double V = M;
166  double S = V == 0 ? 0 : C / V;
167 
168  return {H, S, V, alpha() / 255.0};
169 }
170 
171 SDL_Color Color::to_sdl_() const NOEXCEPT
172 {
173  SDL_Color result;
174  result.a = alpha_;
175  result.r = red_;
176  result.g = green_;
177  result.b = blue_;
178  return result;
179 }
180 
181 uint32_t Color::to_sdl_(const SDL_PixelFormat* format) const NOEXCEPT
182 {
183  return SDL_MapRGBA(format, red_, green_, blue_, alpha_);
184 }
185 
186 Color Color::fade_in(double unit_amount) const NOEXCEPT
187 {
188  return adjust_field(*this, &Color::alpha_, unit_amount, 255);
189 }
190 
191 Color Color::fade_out(double unit_amount) const NOEXCEPT
192 {
193  return adjust_field(*this, &Color::alpha_, unit_amount, 0);
194 }
195 
196 Color::HSLA::HSLA(double hue,
197  double saturation,
198  double lightness,
199  double alpha) NOEXCEPT
200  : hue{hue}
201  , saturation{saturation}
202  , lightness{lightness}
203  , alpha{alpha}
204 { }
205 
206 Color Color::HSLA::to_rgba() const NOEXCEPT
207 {
209 }
210 
211 Color::HSLA Color::HSLA::rotate_hue(double degrees) const NOEXCEPT
212 {
213  auto result = *this;
214  result.hue = std::fmod(result.hue + degrees, 360);
215  return result;
216 }
217 
218 Color::HSLA Color::HSLA::saturate(double unit_amount) const NOEXCEPT
219 {
220  return adjust_field(*this, &HSLA::saturation, unit_amount, 1.0);
221 }
222 
223 Color::HSLA Color::HSLA::desaturate(double unit_amount) const NOEXCEPT
224 {
225  return adjust_field(*this, &HSLA::saturation, unit_amount, 0.0);
226 }
227 
228 Color::HSLA Color::HSLA::lighten(double unit_amount) const NOEXCEPT
229 {
230  return adjust_field(*this, &HSLA::lightness, unit_amount, 1.0);
231 }
232 
233 Color::HSLA Color::HSLA::darken(double unit_amount) const NOEXCEPT
234 {
235  return adjust_field(*this, &HSLA::lightness, unit_amount, 0.0);
236 }
237 
238 Color::HSLA Color::HSLA::fade_in(double unit_amount) const NOEXCEPT
239 {
240  return adjust_field(*this, &HSLA::alpha, unit_amount, 1.0);
241 }
242 
243 Color::HSLA Color::HSLA::fade_out(double unit_amount) const NOEXCEPT
244 {
245  return adjust_field(*this, &HSLA::alpha, unit_amount, 0.0);
246 }
247 
248 Color Color::HSVA::to_rgba() const NOEXCEPT
249 {
250  return Color::from_hsva(hue, saturation, value, alpha);
251 }
252 
253 Color::HSVA::HSVA(double hue, double saturation,
254  double value, double alpha) NOEXCEPT
255  : hue(hue)
256  , saturation(saturation)
257  , value(value)
258  , alpha(alpha)
259 { }
260 
261 Color::HSVA Color::HSVA::rotate_hue(double degrees) const NOEXCEPT
262 {
263  auto result = *this;
264  result.hue = std::fmod(result.hue + degrees, 360);
265  return result;
266 }
267 
268 Color::HSVA Color::HSVA::saturate(double unit_amount) const NOEXCEPT
269 {
270  return adjust_field(*this, &HSVA::saturation, unit_amount, 1.0);
271 }
272 
273 Color::HSVA Color::HSVA::desaturate(double unit_amount) const NOEXCEPT
274 {
275  return adjust_field(*this, &HSVA::saturation, unit_amount, 0.0);
276 }
277 
278 Color::HSVA Color::HSVA::revalue(double unit_amount) const NOEXCEPT
279 {
280  return adjust_field(*this, &HSVA::value, unit_amount, 1.0);
281 }
282 
283 Color::HSVA Color::HSVA::devalue(double unit_amount) const NOEXCEPT
284 {
285  return adjust_field(*this, &HSVA::value, unit_amount, 0.0);
286 }
287 
288 Color::HSVA Color::HSVA::fade_in(double unit_amount) const NOEXCEPT
289 {
290  return adjust_field(*this, &HSVA::alpha, unit_amount, 1.0);
291 }
292 
293 Color::HSVA Color::HSVA::fade_out(double unit_amount) const NOEXCEPT
294 {
295  return adjust_field(*this, &HSVA::alpha, unit_amount, 0.0);
296 }
297 
298 }
ge211::Color::HSVA::rotate_hue
HSVA rotate_hue(double degrees) const
Returns a color by rotating the hue, leaving the other properties constant.
Definition: ge211_color.cxx:261
ge211::Color::HSLA::saturate
HSLA saturate(double unit_amount) const
Produces a fuller tone by saturating the color.
Definition: ge211_color.cxx:218
ge211::Color::to_hsla
HSLA to_hsla() const
Converts a color to the hue-saturation-lightness (HSL) color model.
Definition: ge211_color.cxx:149
ge211::Color::HSLA::saturation
double saturation
The fullness of the color, from 0.0 (grey) to 1.0 (fully saturated).
Definition: ge211_color.hxx:163
ge211::Color::HSVA::desaturate
HSVA desaturate(double unit_amount) const
Produces a weaker tone by desaturating the color.
Definition: ge211_color.cxx:273
ge211::Color::from_rgba
static Color from_rgba(double red, double green, double blue, double alpha=1.0)
Constructs a color with the given components.
Definition: ge211_color.cxx:28
ge211::Color::HSVA::revalue
HSVA revalue(double unit_amount) const
Produces a brighter color by increasing the value.
Definition: ge211_color.cxx:278
ge211::Color::HSVA::alpha
double alpha
The opacity of the color, from 0.0 (fully transparent) to 1.0 (fully opaque).
Definition: ge211_color.hxx:245
ge211::Color::HSLA::HSLA
HSLA(double hue, double saturation, double lightness, double alpha=1.0)
Constructs a hue-saturation-lightness-alpha color from its unit interval components.
Definition: ge211_color.cxx:196
ge211::Color::HSLA::desaturate
HSLA desaturate(double unit_amount) const
Produces a weaker tone by desaturating the color.
Definition: ge211_color.cxx:223
ge211::Color::lighten
Color lighten(double unit_amount) const
Produces a tint by lightening the color.
Definition: ge211_color.cxx:109
ge211::Color::HSVA::fade_in
HSVA fade_in(double unit_amount) const
Increases opacity of the color.
Definition: ge211_color.cxx:288
std::tuple
ge211::Color::fade_out
Color fade_out(double unit_amount) const
Decreases opacity of the color.
Definition: ge211_color.cxx:191
ge211::Color::HSLA::rotate_hue
HSLA rotate_hue(double degrees) const
Returns a color by rotating the hue, leaving the other properties.
Definition: ge211_color.cxx:211
ge211
The game engine namespace.
Definition: ge211.hxx:4
ge211::Color::HSVA::to_rgba
Color to_rgba() const
Converts color to the RGBA color model.
Definition: ge211_color.cxx:248
ge211::Color::HSVA
Representation for the hue-saturation-value-alpha color model.
Definition: ge211_color.hxx:230
ge211::Color::HSLA
Representation for the hue-saturation-lightness-alpha color model.
Definition: ge211_color.hxx:156
ge211::Color::HSLA::hue
double hue
The hue in degrees from 0 to 360.
Definition: ge211_color.hxx:159
ge211::Color::alpha
uint8_t alpha() const
Gets the alpha (opacity) component of a color.
Definition: ge211_color.hxx:100
ge211::Color::HSVA::devalue
HSVA devalue(double unit_amount) const
Produces a darker color by decreasing the value.
Definition: ge211_color.cxx:283
ge211::Color::HSLA::lighten
HSLA lighten(double unit_amount) const
Produces a tint by lightening the color.
Definition: ge211_color.cxx:228
ge211::Color::HSLA::fade_out
HSLA fade_out(double unit_amount) const
Decreases opacity of the color.
Definition: ge211_color.cxx:243
ge211::Color::HSLA::alpha
double alpha
The opacity of the color, from 0.0 (fully transparent) to 1.0 (fully opaque).
Definition: ge211_color.hxx:170
ge211::Color::blend
Color blend(double weight, Color that) const
Produces a blend of this color and that, with higher weight (between 0 and 1) increasing the level of...
Definition: ge211_color.cxx:89
ge211::Color::HSVA::fade_out
HSVA fade_out(double unit_amount) const
Decreases opacity of the color.
Definition: ge211_color.cxx:293
ge211::Color::HSVA::saturation
double saturation
The fullness of the color, from 0,0 (grey) to 1.0 (fully saturated).
Definition: ge211_color.hxx:237
ge211::Color::HSVA::saturate
HSVA saturate(double unit_amount) const
Produces a fuller tone by saturating the color.
Definition: ge211_color.cxx:268
ge211::Color::darken
Color darken(double unit_amount) const
Produces a shade by darkening the color.
Definition: ge211_color.cxx:114
ge211::Color::rotate_hue
Color rotate_hue(double degrees) const
Returns a color by rotating the hue, leaving the other properties constant.
Definition: ge211_color.cxx:104
ge211::Color::HSLA::fade_in
HSLA fade_in(double unit_amount) const
Increases opacity of the color.
Definition: ge211_color.cxx:238
ge211::Color
For representing colors.
Definition: ge211_color.hxx:24
ge211::Color::from_hsla
static Color from_hsla(double hue, double saturation, double lightness, double alpha=1)
Constructs a color given the hue, saturation, lightness, and alpha.
Definition: ge211_color.cxx:73
ge211::Color::from_hsva
static Color from_hsva(double hue, double saturation, double value, double alpha=1)
Constructs a color given the hue, saturation, value, and alpha.
Definition: ge211_color.cxx:81
ge211::Color::desaturate
Color desaturate(double unit_amount) const
Produces a weaker tone by desaturating the color.
Definition: ge211_color.cxx:124
ge211::Color::HSLA::to_rgba
Color to_rgba() const
Converts color to the RGBA color model.
Definition: ge211_color.cxx:206
ge211::Color::HSLA::lightness
double lightness
The lightness of the color, from 0.0 (black) to 1.0 (white).
Definition: ge211_color.hxx:166
ge211::Color::HSVA::value
double value
The brightness of the color, from 0.0 (black) to 1.0 (fully colored).
Definition: ge211_color.hxx:241
ge211::Color::HSLA::darken
HSLA darken(double unit_amount) const
Produces a shade by darkening the color.
Definition: ge211_color.cxx:233
ge211::Color::invert
Color invert() const
Returns the inverse of a color.
Definition: ge211_color.cxx:99
ge211::Color::HSVA::HSVA
HSVA(double hue, double saturation, double value, double alpha=1.0)
Constructs a hue-saturation-value-alpha color from its unit interval components.
Definition: ge211_color.cxx:253
ge211::Color::to_hsva
HSVA to_hsva() const
Converts a color to the hue-saturation-value (HSV) color model.
Definition: ge211_color.cxx:160
ge211::Color::saturate
Color saturate(double unit_amount) const
Produces a fuller tone by saturating the color.
Definition: ge211_color.cxx:119
ge211::Color::fade_in
Color fade_in(double unit_amount) const
Increases opacity of the color.
Definition: ge211_color.cxx:186