49 static constexpr float _clamp01(
float v) {
50 return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v);
57 Color(
void) : _r(0.0f), _g(0.0f), _b(0.0f), _a(1.0f) {}
63 : _r(_clamp01(
r)), _g(_clamp01(
g)), _b(_clamp01(
b)), _a(1.0f) {}
69 : _r(_clamp01(
r)), _g(_clamp01(
g)), _b(_clamp01(
b)), _a(_clamp01(
a)) {}
74 float r(
void)
const {
return _r; }
78 float g(
void)
const {
return _g; }
82 float b(
void)
const {
return _b; }
86 float a(
void)
const {
return _a; }
95 void setR(
float v) { _r = _clamp01(v); }
100 void setG(
float v) { _g = _clamp01(v); }
105 void setB(
float v) { _b = _clamp01(v); }
110 void setA(
float v) { _a = _clamp01(v); }
123 float u = _clamp01(t);
124 return Color(_r + (to._r - _r) * u, _g + (to._g - _g) * u,
125 _b + (to._b - _b) * u, _a + (to._a - _a) * u);
139 float outA = _a + bg._a * (1.0f - _a);
141 return Color(0, 0, 0, 0);
142 float outR = (_r * _a + bg._r * bg._a * (1.0f - _a)) / outA;
143 float outG = (_g * _a + bg._g * bg._a * (1.0f - _a)) / outA;
144 float outB = (_b * _a + bg._b * bg._a * (1.0f - _a)) / outA;
145 return Color(outR, outG, outB, outA);
170 auto to8 = [](
float v) -> uint8_t {
171 float cl = v <= 0.0f ? 0.0f : (v >= 1.0f ? 1.0f : v);
172 return static_cast<uint8_t
>(std::round(cl * 255.0f));
174 return RGBA8{to8(_r), to8(_g), to8(_b), to8(_a)};
181 auto toF = [](uint8_t v) ->
float {
182 return static_cast<float>(v) / 255.0f;
184 return Color(toF(
r), toF(
g), toF(
b), toF(
a));
191 std::string
toHex(
bool includeAlphaIfOpaque =
false)
const {
193 std::ostringstream oss;
194 oss <<
'#' << std::uppercase << std::hex << std::setfill(
'0')
195 << std::setw(2) <<
static_cast<int>(p.r) << std::setw(2)
196 <<
static_cast<int>(p.g) << std::setw(2) <<
static_cast<int>(p.b);
197 if (includeAlphaIfOpaque || p.a != 255) {
198 oss << std::setw(2) << static_cast<int>(p.a);
215 auto isHex = [](
char c) {
216 return (c >=
'0' && c <=
'9') || (c >=
'a' && c <=
'f') ||
217 (c >=
'A' && c <=
'F');
221 if (!s.empty() && s[0] ==
'#')
223 if (s.size() != 3 && s.size() != 4 && s.size() != 6 && s.size() != 8)
224 throw std::invalid_argument(
"Invalid hex color length");
227 throw std::invalid_argument(
"Invalid hex color character");
230 auto hexToByte = [](
const std::string &hh) -> uint8_t {
231 return static_cast<uint8_t
>(std::stoul(hh,
nullptr, 16));
234 if (s.size() == 3 || s.size() == 4) {
236 uint8_t
r = hexToByte(std::string{s[0], s[0]});
237 uint8_t
g = hexToByte(std::string{s[1], s[1]});
238 uint8_t
b = hexToByte(std::string{s[2], s[2]});
241 a = hexToByte(std::string{s[3], s[3]});
246 uint8_t
r = hexToByte(s.substr(0, 2));
247 uint8_t
g = hexToByte(s.substr(2, 2));
248 uint8_t
b = hexToByte(s.substr(4, 2));
251 a = hexToByte(s.substr(6, 2));
259 return _r == other._r && _g == other._g && _b == other._b && _a == other._a;
271 return Color(_clamp01(_r + o._r), _clamp01(_g + o._g), _clamp01(_b + o._b),
272 _clamp01(_a + o._a));
279 return Color(_clamp01(_r - o._r), _clamp01(_g - o._g), _clamp01(_b - o._b),
280 _clamp01(_a - o._a));
287 return Color(_clamp01(_r * s), _clamp01(_g * s), _clamp01(_b * s), _a);
Represents an RGBA color with floating-point channels in [0, 1].
void setB(float v)
Set blue component.
bool operator!=(const Color &other) const
Inequality operator.
Color withAlpha(float alpha) const
Return a copy with a different alpha (keeps RGB).
Color lerp(const Color &to, float t) const
Linear interpolation between two colors (component-wise).
static Color Green()
Opaque green (0,1,0,1)
float r(void) const
Red component in [0,1].
Color(float r, float g, float b)
Construct from RGB, alpha defaults to 1.0.
static Color Red()
Opaque red (1,0,0,1)
Color operator+(const Color &o) const
Component-wise addition (clamped).
Color operator*(float s) const
Scalar multiplication (scales RGB; alpha unchanged by default).
void setG(float v)
Set green component.
static Color Transparent()
Fully transparent black (0,0,0,0)
Color(float r, float g, float b, float a)
Construct from RGBA.
Color premultiplied() const
Premultiplied alpha representation.
void setA(float v)
Set alpha component.
static Color Blue()
Opaque blue (0,0,1,1)
static Color fromHex(const std::string &hex)
Parse from hexadecimal string.
float b(void) const
Blue component in [0,1].
void setR(float v)
Setters (values are clamped to [0,1]).
static Color White()
Opaque white (1,1,1,1)
static Color fromRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a=255)
Create from 8-bit per channel RGBA values.
Color(void)
Construct a fully-opaque black color (0,0,0,1).
RGBA8 toRGBA8() const
Convert to 8-bit per channel RGBA.
float g(void) const
Green component in [0,1].
float a(void) const
Alpha component in [0,1].
bool operator==(const Color &other) const
Equality operators (exact component-wise equality).
Color operator-(const Color &o) const
Component-wise subtraction (clamped to [0,1]).
static Color Black()
Common named colors (opaque unless specified).
std::string toHex(bool includeAlphaIfOpaque=false) const
Convert to hexadecimal string. Formats as "#RRGGBB" if a==1 else "#RRGGBBAA".
Color over(const Color &bg) const
Alpha blend this color over a background color. Uses standard source-over compositing in linear space...
8-bit RGBA packed representation.
uint8_t g
Green channel (0..255)
uint8_t r
Red channel (0..255)
uint8_t b
Blue channel (0..255)
uint8_t a
Alpha channel (0..255)