5#include "fifechan/color.hpp"
14 Color::Color(
int color) :
r((color >> 16) & 0xFF),
g((color >> 8) & 0xFF),
b((color >> 0) & 0xFF) { }
18 r(static_cast<std::uint8_t>(red)),
19 g(static_cast<std::uint8_t>(green)),
20 b(static_cast<std::uint8_t>(blue)),
21 a(static_cast<std::uint8_t>(alpha))
26 Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) :
r(red),
g(green),
b(blue),
a(alpha) { }
31 if (colorString[0] ==
'#') {
33 parseHex(colorString.substr(1));
34 }
else if (colorString.find(
"rgba") != std::string::npos) {
36 parseRGBA(colorString);
39 parseRGB(colorString);
43 void Color::parseHex(std::string
const & hex)
45 if (hex.size() != 6) {
46 throw std::invalid_argument(
"Hex code must be 6 characters long.");
49 r =
static_cast<uint8_t
>(std::stoi(hex.substr(0, 2),
nullptr, 16));
50 g =
static_cast<uint8_t
>(std::stoi(hex.substr(2, 2),
nullptr, 16));
51 b =
static_cast<uint8_t
>(std::stoi(hex.substr(4, 2),
nullptr, 16));
55 void Color::parseRGB(std::string
const & rgbString)
58 if (!rgbString.starts_with(
"rgb(") || !rgbString.ends_with(
")")) {
59 throw std::invalid_argument(
"Invalid RGB format. Expected: rgb(r,g,b)");
63 std::string
const values = rgbString.substr(4, rgbString.size() - 5);
66 std::vector<int> components = parseColorComponents(values);
67 if (components.size() != 3) {
68 throw std::invalid_argument(
"Invalid RGB format. Expected 3 components (r,g,b)");
71 r =
static_cast<uint8_t
>(components[0]);
72 g =
static_cast<uint8_t
>(components[1]);
73 b =
static_cast<uint8_t
>(components[2]);
77 void Color::parseRGBA(std::string
const & rgbaString)
80 if (!rgbaString.starts_with(
"rgba(") || !rgbaString.ends_with(
")")) {
81 throw std::invalid_argument(
"Invalid RGBA format. Expected: rgba(r,g,b,a)");
85 std::string
const values = rgbaString.substr(5, rgbaString.size() - 6);
88 std::vector<int> components = parseColorComponents(values,
true);
89 if (components.size() != 4) {
90 throw std::invalid_argument(
"Invalid RGBA format. Expected 4 components (r,g,b,a)");
93 r =
static_cast<uint8_t
>(components[0]);
94 g =
static_cast<uint8_t
>(components[1]);
95 b =
static_cast<uint8_t
>(components[2]);
96 a =
static_cast<uint8_t
>(components[3]);
99 std::vector<int> Color::parseColorComponents(std::string
const & colorString,
bool withAlpha)
101 std::vector<int> colors;
104 size_t const firstComma = colorString.find(
',');
105 if (firstComma == std::string::npos) {
106 throw std::invalid_argument(
"Invalid format. Expected comma after first value.");
109 size_t const secondComma = colorString.find(
',', firstComma + 1);
110 if (secondComma == std::string::npos) {
111 throw std::invalid_argument(
"Invalid format. Expected comma after second value.");
114 size_t const thirdComma = colorString.find(
',', secondComma + 1);
115 if (withAlpha && thirdComma == std::string::npos) {
116 throw std::invalid_argument(
"Invalid format. Expected comma after third value.");
120 std::string
const redStr = colorString.substr(0, firstComma);
121 std::string
const greenStr = colorString.substr(firstComma + 1, secondComma - firstComma - 1);
122 std::string
const blueStr = colorString.substr(secondComma + 1, thirdComma - secondComma - 1);
124 std::string
const alphaStr = withAlpha ? colorString.substr(thirdComma + 1) :
"255";
129 int const red = std::strtol(redStr.c_str(), &end, 10);
131 throw std::invalid_argument(
"Invalid red value.");
134 int const green = std::strtol(greenStr.c_str(), &end, 10);
136 throw std::invalid_argument(
"Invalid green value.");
139 int const blue = std::strtol(blueStr.c_str(), &end, 10);
141 throw std::invalid_argument(
"Invalid blue value.");
144 int const alpha = std::strtol(alphaStr.c_str(), &end, 10);
145 if (withAlpha && *end !=
'\0') {
146 throw std::invalid_argument(
"Invalid alpha value.");
150 colors.push_back(red);
151 colors.push_back(green);
152 colors.push_back(blue);
154 colors.push_back(alpha);
164 static_cast<uint8_t
>(std::clamp(
r + color.
r, 0, 255)),
165 static_cast<uint8_t
>(std::clamp(
g + color.
g, 0, 255)),
166 static_cast<uint8_t
>(std::clamp(
b + color.
b, 0, 255)),
174 static_cast<uint8_t
>(std::max(0,
r - color.
r)),
175 static_cast<uint8_t
>(std::max(0,
g - color.
g)),
176 static_cast<uint8_t
>(std::max(0,
b - color.
b)),
184 static_cast<uint8_t
>(std::clamp(
r + value, 0.0F, 255.0F)),
185 static_cast<uint8_t
>(std::clamp(
g + value, 0.0F, 255.0F)),
186 static_cast<uint8_t
>(std::clamp(
b + value, 0.0F, 255.0F)),
193 static_cast<uint8_t
>(std::clamp(
static_cast<int>(
r - value), 0, 255)),
194 static_cast<uint8_t
>(std::clamp(
static_cast<int>(
g - value), 0, 255)),
195 static_cast<uint8_t
>(std::clamp(
static_cast<int>(
b - value), 0, 255)),
203 static_cast<uint8_t
>(std::clamp(
static_cast<int>(
r * value), 0, 255)),
204 static_cast<uint8_t
>(std::clamp(
static_cast<int>(
g * value), 0, 255)),
205 static_cast<uint8_t
>(std::clamp(
static_cast<int>(
b * value), 0, 255)),
212 r = std::clamp(
static_cast<int>(
r) +
static_cast<int>(color.
r), 0, 255);
213 g = std::clamp(
static_cast<int>(
g) +
static_cast<int>(color.
g), 0, 255);
214 b = std::clamp(
static_cast<int>(
b) +
static_cast<int>(color.
b), 0, 255);
221 r = std::max(0,
static_cast<int>(
r) -
static_cast<int>(color.
r));
222 g = std::max(0,
static_cast<int>(
g) -
static_cast<int>(color.
g));
223 b = std::max(0,
static_cast<int>(
b) -
static_cast<int>(color.
b));
230 r = std::clamp(
static_cast<int>(
r * value), 0, 255);
231 g = std::clamp(
static_cast<int>(
g * value), 0, 255);
232 b = std::clamp(
static_cast<int>(
b * value), 0, 255);
241 static_cast<uint8_t
>(std::min(255,
static_cast<int>(
r + (
r * percentage)))),
242 static_cast<uint8_t
>(std::min(255,
static_cast<int>(
g + (
g * percentage)))),
243 static_cast<uint8_t
>(std::min(255,
static_cast<int>(
b + (
b * percentage)))),
251 static_cast<uint8_t
>(std::max(0,
static_cast<int>(
r - (
r * percentage)))),
252 static_cast<uint8_t
>(std::max(0,
static_cast<int>(
g - (
g * percentage)))),
253 static_cast<uint8_t
>(std::max(0,
static_cast<int>(
b - (
b * percentage)))),
260 auto const gray =
static_cast<uint8_t
>((
r * 0.3) + (
g * 0.59) + (
b * 0.11));
261 return Color{gray, gray, gray,
a};
267 auto blendChannel = [](uint8_t c1, uint8_t c2, uint8_t a1, uint8_t a2) {
280 int const maxAlpha = 255;
281 int const inverseA2 = maxAlpha - a2;
282 int const scaledC1 = c1 * a1 * inverseA2;
283 int const scaledC2 = c2 * a2 * maxAlpha;
285 return static_cast<uint8_t
>((scaledC1 + scaledC2) / (maxAlpha * maxAlpha));
288 auto const blendedAlpha =
static_cast<uint8_t
>(
a + (other.
a * (255 -
a) / 255));
289 auto const blendedRed = blendChannel(
r, other.
r,
a, other.
a);
290 auto const blendedGreen = blendChannel(
g, other.
g,
a, other.
a);
291 auto const blendedBlue = blendChannel(
b, other.
b,
a, other.
a);
293 return Color{blendedRed, blendedGreen, blendedBlue, blendedAlpha};
298 return r == color.
r &&
g == color.
g &&
b == color.
b &&
a == color.
a;
303 return !(*
this == color);
308 out <<
"Color [r = " <<
static_cast<int>(color.
r) <<
", g = " <<
static_cast<int>(color.
g)
309 <<
", b = " <<
static_cast<int>(color.
b) <<
", a = " <<
static_cast<int>(color.
a) <<
"]";
315 std::stringstream ss;
316 ss <<
"#" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(
r) << std::setw(2)
317 << std::setfill(
'0') <<
static_cast<int>(
g) << std::setw(2) << std::setfill(
'0') <<
static_cast<int>(
b);
323 std::stringstream ss;
324 ss <<
"rgb(" <<
static_cast<int>(
r) <<
"," <<
static_cast<int>(
g) <<
"," <<
static_cast<int>(
b) <<
")";
330 std::stringstream ss;
331 ss <<
"rgba(" <<
static_cast<int>(
r) <<
"," <<
static_cast<int>(
g) <<
"," <<
static_cast<int>(
b) <<
","
332 <<
static_cast<int>(
a) <<
")";
uint8_t a
Alpha color component (0-255).
Color & operator+=(Color const &color)
Adds the RGB values of another color to this color.
Color toGrayScale() const
Converts the color to grayscale.
std::string toRGBString() const
Returns the color in the form "rgb(r,g,b)".
std::string toHexString() const
Returns the color as a hexadecimal string in the form "#RRGGBB".
friend std::ostream & operator<<(std::ostream &out, Color const &color)
Output operator for output.
std::string toRGBAString() const
Returns the color in the form "rgba(r,g,b,a)".
Color operator+(Color const &color) const
Adds the RGB values of two colors together.
Color lighten(float percentage) const
Lightens the color by a percentage.
Color & operator*=(float value)
Multiplies the RGB values of this color with a float value.
Color operator*(float value) const
Multiplies the RGB values of a color with a float value.
uint8_t b
Blue color component (0-255).
Color darken(float percentage) const
Darkens the color by a percentage.
bool operator==(Color const &color) const
Compares two colors.
uint8_t g
Green color component (0-255).
bool operator!=(Color const &color) const
Compares two colors.
Color operator-(Color const &color) const
Subtracts the RGB values of one color from another.
Color()=default
Constructor.
Color blendWith(Color const &other) const
Blends the color with another color using alpha blending.
uint8_t r
Red color component (0-255).
Color & operator-=(Color const &color)
Subtracts the RGB values of another color from this color.