utility 2026.1.9
A comprehensive C++ utilities library tailored for the development of modern desktop and extended reality (XR) applications.
Loading...
Searching...
No Matches
color.hpp
Go to the documentation of this file.
1/*
2 Copyright (c) 2026 ETIB Corporation
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22
31#pragma once
32
33#include <algorithm>
34#include <cstdint>
35#include <ostream>
36#include <stdexcept>
37#include <type_traits>
38#include <utility>
39
40namespace utility::graphic
41{
42
47 template<typename Type>
48 concept CanBeColorComponent = std::is_arithmetic<Type>::value
49 && (std::is_floating_point<Type>::value
50 || std::is_integral<Type>::value);
51
60 template<CanBeColorComponent Type> class Color
61 {
62 private:
63 protected:
67 Type _red;
68
72 Type _green;
73
77 Type _blue;
78
82 Type _alpha;
83
89 static Type clamp(Type value)
90 {
91 if constexpr (std::is_floating_point<Type>::value) {
92 return (std::max)(Type { 0 }, (std::min)(Type { 1 }, value));
93 } else {
94 return (std::max)(Type { 0 }, (std::min)(Type { 255 }, value));
95 }
96 }
97
106 static Type clampWide(long long value)
107 {
108 if constexpr (std::is_floating_point<Type>::value) {
109 return clamp(static_cast<Type>(value));
110 } else {
111 constexpr long long max = 255;
112 return static_cast<Type>(
113 (std::max)(0LL, (std::min)(max, value)));
114 }
115 }
116
121 static constexpr Type maxValue()
122 {
123 if constexpr (std::is_floating_point<Type>::value) {
124 return Type { 1 };
125 } else {
126 return Type { 255 };
127 }
128 }
129
130 public:
134 Color(void)
135 : _red(Type {})
136 , _green(Type {})
137 , _blue(Type {})
138 , _alpha(maxValue())
139 {
140 }
141
148 Color(Type red, Type green, Type blue)
149 : _red(clamp(red))
150 , _green(clamp(green))
151 , _blue(clamp(blue))
152 , _alpha(maxValue())
153 {
154 }
155
163 Color(Type red, Type green, Type blue, Type alpha)
164 : _red(clamp(red))
165 , _green(clamp(green))
166 , _blue(clamp(blue))
167 , _alpha(clamp(alpha))
168 {
169 }
170
175 Color(const Color &other)
176 : _red(other._red)
177 , _green(other._green)
178 , _blue(other._blue)
179 , _alpha(other._alpha)
180 {
181 }
182
187 Color(Color &&other) noexcept
188 : _red(std::move(other._red))
189 , _green(std::move(other._green))
190 , _blue(std::move(other._blue))
191 , _alpha(std::move(other._alpha))
192 {
193 }
194
200 Color &operator=(const Color &other)
201 {
202 if (this != &other) {
203 _red = other._red;
204 _green = other._green;
205 _blue = other._blue;
206 _alpha = other._alpha;
207 }
208 return *this;
209 }
210
216 Color &operator=(Color &&other) noexcept
217 {
218 if (this != &other) {
219 _red = std::move(other._red);
220 _green = std::move(other._green);
221 _blue = std::move(other._blue);
222 _alpha = std::move(other._alpha);
223 }
224 return *this;
225 }
226
227 // Destructor
228 ~Color(void) = default;
229
234 void setRed(Type red)
235 {
236 _red = clamp(red);
237 }
238
243 Type getRed(void) const
244 {
245 return _red;
246 }
247
252 void setGreen(Type green)
253 {
254 _green = clamp(green);
255 }
256
261 Type getGreen(void) const
262 {
263 return _green;
264 }
265
270 void setBlue(Type blue)
271 {
272 _blue = clamp(blue);
273 }
274
279 Type getBlue(void) const
280 {
281 return _blue;
282 }
283
288 void setAlpha(Type alpha)
289 {
290 _alpha = clamp(alpha);
291 }
292
297 Type getAlpha(void) const
298 {
299 return _alpha;
300 }
301
309 void setRGBA(Type red, Type green, Type blue, Type alpha)
310 {
311 _red = clamp(red);
312 _green = clamp(green);
313 _blue = clamp(blue);
314 _alpha = clamp(alpha);
315 }
316
322 bool operator==(const Color &other) const
323 {
324 return _red == other._red && _green == other._green
325 && _blue == other._blue && _alpha == other._alpha;
326 }
327
333 bool operator!=(const Color &other) const
334 {
335 return !(*this == other);
336 }
337
343 Color operator+(const Color &other) const
344 {
345 return Color(
346 clampWide(static_cast<long long>(_red) + other._red),
347 clampWide(static_cast<long long>(_green) + other._green),
348 clampWide(static_cast<long long>(_blue) + other._blue), _alpha);
349 }
350
356 Color operator-(const Color &other) const
357 {
358 return Color(
359 clampWide(static_cast<long long>(_red) - other._red),
360 clampWide(static_cast<long long>(_green) - other._green),
361 clampWide(static_cast<long long>(_blue) - other._blue), _alpha);
362 }
363
369 Color operator*(Type scalar) const
370 {
371 return Color(clampWide(static_cast<long long>(_red) * scalar),
372 clampWide(static_cast<long long>(_green) * scalar),
373 clampWide(static_cast<long long>(_blue) * scalar),
374 _alpha);
375 }
376
381 Color grayscale(void) const
382 {
383 Type luminance;
384 if constexpr (std::is_floating_point<Type>::value) {
385 luminance =
386 clamp(Type { 0.299 } * _red + Type { 0.587 } * _green
387 + Type { 0.114 } * _blue);
388 } else {
389 luminance =
390 clamp((299 * _red + 587 * _green + 114 * _blue) / 1000);
391 }
392 return Color(luminance, luminance, luminance, _alpha);
393 }
394
399 Color inverted(void) const
400 {
401 return Color(maxValue() - _red, maxValue() - _green,
402 maxValue() - _blue, _alpha);
403 }
404
411 Color lerp(const Color &other, Type t) const
412 {
413 t = clamp(t);
414 Type oneMinusT = maxValue() - t;
415
416 if constexpr (std::is_floating_point<Type>::value) {
417 return Color(oneMinusT * _red + t * other._red,
418 oneMinusT * _green + t * other._green,
419 oneMinusT * _blue + t * other._blue,
420 oneMinusT * _alpha + t * other._alpha);
421 } else {
422 return Color(
423 clamp((oneMinusT * _red + t * other._red) / maxValue()),
424 clamp((oneMinusT * _green + t * other._green) / maxValue()),
425 clamp((oneMinusT * _blue + t * other._blue) / maxValue()),
426 clamp((oneMinusT * _alpha + t * other._alpha)
427 / maxValue()));
428 }
429 }
430
436 Color blendOver(const Color &background) const
437 {
438 if constexpr (std::is_floating_point<Type>::value) {
439 Type a = _alpha;
440 Type oneMinusA = Type { 1 } - a;
441 return Color(a * _red + oneMinusA * background._red,
442 a * _green + oneMinusA * background._green,
443 a * _blue + oneMinusA * background._blue,
444 a + oneMinusA * background._alpha);
445 } else {
446 Type a = _alpha;
447 Type oneMinusA = maxValue() - a;
448 return Color(
449 clamp((a * _red + oneMinusA * background._red)
450 / maxValue()),
451 clamp((a * _green + oneMinusA * background._green)
452 / maxValue()),
453 clamp((a * _blue + oneMinusA * background._blue)
454 / maxValue()),
455 clamp((a + oneMinusA * background._alpha) / maxValue()));
456 }
457 }
458
464 Color lighter(Type factor) const
465 {
466 factor = clamp(factor);
467 return lerp(Color(maxValue(), maxValue(), maxValue(), _alpha),
468 factor);
469 }
470
476 Color darker(Type factor) const
477 {
478 factor = clamp(factor);
479 return lerp(Color(Type {}, Type {}, Type {}, _alpha), factor);
480 }
481
487 Color withAlpha(Type alpha) const
488 {
489 return Color(_red, _green, _blue, alpha);
490 }
491
496 bool isOpaque(void) const
497 {
498 return _alpha == maxValue();
499 }
500
505 bool isTransparent(void) const
506 {
507 return _alpha == Type {};
508 }
509
514 double alpha01(void) const
515 {
516 return static_cast<double>(_alpha)
517 / static_cast<double>(maxValue());
518 }
519
525 bool operator<(const Color &other) const
526 {
527 if (_red != other._red) {
528 return _red < other._red;
529 }
530 if (_green != other._green) {
531 return _green < other._green;
532 }
533 if (_blue != other._blue) {
534 return _blue < other._blue;
535 }
536 if (_alpha != other._alpha) {
537 return _alpha < other._alpha;
538 }
539 return false;
540 }
541 };
542
547
552
557
564 std::ostream &operator<<(std::ostream &stream, const Color32Bit &color);
565
572 std::ostream &operator<<(std::ostream &stream, const ColorFloat &color);
573
580 std::ostream &operator<<(std::ostream &stream, const ColorDouble &color);
581
582} // namespace utility::graphic
RGBA color with arithmetic type components.
Definition color.hpp:61
Type _green
Green component.
Definition color.hpp:72
Type getAlpha(void) const
Get the alpha (opacity) component.
Definition color.hpp:297
Type _blue
Blue component.
Definition color.hpp:77
Color operator-(const Color &other) const
Component-wise color subtraction (clamped).
Definition color.hpp:356
bool isOpaque(void) const
Check whether alpha is fully opaque.
Definition color.hpp:496
void setBlue(Type blue)
Set the blue component.
Definition color.hpp:270
void setAlpha(Type alpha)
Set the alpha (opacity) component.
Definition color.hpp:288
Color(void)
Default constructor creating opaque black.
Definition color.hpp:134
bool operator!=(const Color &other) const
Inequality comparison.
Definition color.hpp:333
Color darker(Type factor) const
Create a darker version of this color.
Definition color.hpp:476
Type getRed(void) const
Get the red component.
Definition color.hpp:243
void setRGBA(Type red, Type green, Type blue, Type alpha)
Set all RGBA components.
Definition color.hpp:309
Color blendOver(const Color &background) const
Alpha blend this color over another background color.
Definition color.hpp:436
void setGreen(Type green)
Set the green component.
Definition color.hpp:252
Color inverted(void) const
Invert the color (keeping alpha unchanged).
Definition color.hpp:399
Color lerp(const Color &other, Type t) const
Linearly interpolate between this color and another.
Definition color.hpp:411
Color(Color &&other) noexcept
Move constructor.
Definition color.hpp:187
static Type clamp(Type value)
Clamp a value to valid range based on type.
Definition color.hpp:89
Color lighter(Type factor) const
Create a lighter version of this color.
Definition color.hpp:464
Color(Type red, Type green, Type blue)
Construct color with RGB values (fully opaque).
Definition color.hpp:148
double alpha01(void) const
Get alpha normalized to [0, 1].
Definition color.hpp:514
Color operator*(Type scalar) const
Scalar multiplication of color components.
Definition color.hpp:369
void setRed(Type red)
Set the red component.
Definition color.hpp:234
Color operator+(const Color &other) const
Component-wise color addition (clamped).
Definition color.hpp:343
Color & operator=(const Color &other)
Copy assignment operator.
Definition color.hpp:200
Color withAlpha(Type alpha) const
Return a copy with a different alpha value.
Definition color.hpp:487
Type _alpha
Alpha (opacity) component.
Definition color.hpp:82
Color & operator=(Color &&other) noexcept
Move assignment operator.
Definition color.hpp:216
bool isTransparent(void) const
Check whether alpha is fully transparent.
Definition color.hpp:505
Type getGreen(void) const
Get the green component.
Definition color.hpp:261
static constexpr Type maxValue()
Get the maximum value for the type.
Definition color.hpp:121
Type getBlue(void) const
Get the blue component.
Definition color.hpp:279
Color(Type red, Type green, Type blue, Type alpha)
Construct color with RGBA values.
Definition color.hpp:163
bool operator==(const Color &other) const
Equality comparison.
Definition color.hpp:322
Type _red
Red component.
Definition color.hpp:67
bool operator<(const Color &other) const
Less-than operator for Color.
Definition color.hpp:525
Color grayscale(void) const
Convert color to grayscale using luminance formula.
Definition color.hpp:381
Color(const Color &other)
Copy constructor.
Definition color.hpp:175
static Type clampWide(long long value)
Clamp a wide accumulator into the component range.
Definition color.hpp:106
Concept to ensure the type can be used as a color component.
Definition color.hpp:48