Fix HSV to RGB conversion once and for all

This commit is contained in:
2025-04-22 17:11:24 -04:00
parent 8f49ba5e49
commit fe25751b07
4 changed files with 156 additions and 60 deletions

View File

@@ -3,6 +3,7 @@
#include <cstdint>
#include <string>
#include <format>
#include <Color4.hpp>
// Gets set to whatever your terminal emulator is configured for.
// This means black can be shown as purple if configured that way.

View File

@@ -32,40 +32,40 @@ public:
public:
/// The default constructor does not initialize any members.
constexpr Color4() = default;
Color4() = default;
/// Constructs a new Color4 from a Color3 and an optional alpha value.
constexpr explicit Color4(const Color3& color3, u8 alpha = 255);
explicit Color4(const Color3& color3, u8 alpha = 255);
/// Constructs a new Color4 from red, green, blue channel values, and an optional alpha value.
constexpr Color4(u8 red, u8 green, u8 blue, u8 alpha = 255);
Color4(u8 red, u8 green, u8 blue, u8 alpha = 255);
/// Constructs a new Color4 from an RGB structure and an optional alpha value.
constexpr explicit Color4(const RGB& rgb, u8 alpha = 255);
explicit Color4(const RGB& rgb, u8 alpha = 255);
/// Constructs a new Color4 from an RGBA structure.
constexpr explicit Color4(const RGBA& rgba);
explicit Color4(const RGBA& rgba);
/// Constructs a new Color4 from a floating-point RGB structure and an optional alpha value.
/// @note: Normalizes the color values from ranges [0, 1] to [0, 255].
constexpr explicit Color4(const RGBf& rgb, float alpha = 1.0f);
explicit Color4(const RGBf& rgb, float alpha = 1.0f);
/// Constructs a new Color4 from a floating-point RGBA structure.
/// /// @note: Normalizes the color values from ranges [0, 1] to [0, 255].
constexpr explicit Color4(const RGBAf& rgba);
explicit Color4(const RGBAf& rgba);
/// Constructs a new Color4 from an HSV structure.
constexpr explicit Color4(const HSV& hsv, float alpha = 1.f);
explicit Color4(const HSV& hsv, float alpha = 1.f);
/// Constructs a new Color4 from an HSVA structure.
constexpr explicit Color4(const HSVA& hsva);
explicit Color4(const HSVA& hsva);
/// TODO: HSL to RGB constructor
constexpr explicit Color4(const HSL& hsl, float alpha = 1.f);
explicit Color4(const HSL& hsl, float alpha = 1.f);
// TODO: LCH to RGB constructor
constexpr explicit Color4(const LCH& lch, float alpha = 1.f);
explicit Color4(const LCH& lch, float alpha = 1.f);
// TODO: LCHA to RGB constructor
constexpr explicit Color4(const LCHA& lcha);
explicit Color4(const LCHA& lcha);
static Color4 FromColor3(const Color3& color3, u8 alpha = 255);
@@ -73,10 +73,11 @@ public:
static Color4 FromHexA(const std::string& hexACode);
static Color4 FromHSV(float hue, float saturation, float value, float alpha = 1.f) {
// TODO: implement
return {};
}
static Color4 FromHSV(const HSV& hsv, float alpha = 1.f);
/// @param hue The hue value represented in degrees [0-360]
static Color4 FromHSV(float hue, float saturation, float value, float alpha = 1.f);
static Color4 FromHSL(float hue, float saturation, float lightness, float alpha = 1.f);
static Color4 FromNormalized(float red, float green, float blue, float alpha = 1.f);
static Color4 FromLCH(float l, float c, float h, float alpha = 1.f);

View File

@@ -21,5 +21,15 @@ int main()
mcolor::printAnsiColorTable();
mcolor::printRGBConsoleTest();
for (float i = 0; i < 360; i+=10.f)
{
HSVA hsva {i, 1.f, 1.f};
Color4 c = Color4(hsva);
std::cout << std::format("{}hue:{} rgb: {},{},{}", toEscapeCode(c), i, c.r, c.g, c.b) << std::endl;
}
return 0;
}

View File

@@ -24,63 +24,84 @@ void print_builtin_color_list() {
#endif
}
constexpr Color4::Color4(const RGB &rgb, u8 alpha): Color4(rgb.r, rgb.g, rgb.b, alpha) {}
Color4::Color4(const RGB &rgb, u8 alpha): Color4(rgb.r, rgb.g, rgb.b, alpha) {}
constexpr Color4::Color4(const RGBA &rgba): Color4(rgba.r, rgba.g, rgba.b, rgba.a) {}
Color4::Color4(const RGBA &rgba): Color4(rgba.r, rgba.g, rgba.b, rgba.a) {}
constexpr Color4::Color4(const RGBf &rgb, float alpha): Color4(rgb.r * 255, rgb.g * 255, rgb.b * 255, alpha * 255) { }
Color4::Color4(const RGBf &rgb, float alpha): Color4(rgb.r * 255, rgb.g * 255, rgb.b * 255, alpha * 255) { }
constexpr Color4::Color4(const RGBAf &rgba): Color4(rgba.r * 255, rgba.g * 255, rgba.b * 255, rgba.a * 255) { }
Color4::Color4(const RGBAf &rgba): Color4(rgba.r * 255, rgba.g * 255, rgba.b * 255, rgba.a * 255) { }
constexpr Color4::Color4(const HSV &hsv, float alpha) {
float h = hsv.h;
float s = hsv.s;
float v = hsv.v;
float M = 255*hsv.v;
float m = M*(1-hsv.s);
Color4::Color4(const HSV &hsv, float alpha) {
float z = (M-m)*(1 - std::abs( std::fmod(hsv.h/60.f, 2) - 1));
float hue = hsv.h;
float saturation = hsv.s;
float value = hsv.v;
if (0 <= h <= 60) {
r = M;
g = z + m;
b = m;
}
if (60 <= h < 120) {
r = z + m;
g = M;
b = m;
}
if (120 <= h < 180) {
r = m;
g = M;
b = z + m;
float hh, p, q, t, ff;
long i;
float rn, gn, bn;
if (saturation <= 0.0) { // < is bogus, just shuts up warnings.
r = value*255;
g = value*255;
b = value*255;
return;
//return Color4(RGBAf{rn, bn, gn, alpha});
}
if (180 <= h < 240) {
r = m;
g = z + m;
b = M;
hh = hue;
if (hh >= 360.f) hh = 0;
hh /= 60.f;
i = (long)hh;
ff = hh - i;
p = value * (1.f - saturation);
q = value * (1.f - (saturation * ff));
t = value * (1.f - (saturation * (1.f - ff)));
switch(i) {
case 0:
rn = value;
gn = t;
bn = p;
break;
case 1:
rn = q;
gn = value;
bn = p;
break;
case 2:
rn = p;
gn = value;
bn = t;
break;
case 3:
rn = p;
gn = q;
bn = value;
break;
case 4:
rn = t;
gn = p;
bn = value;
break;
case 5:
default:
rn = value;
gn = p;
bn = q;
break;
}
this->r = rn * 255;
this->g = gn * 255;
this->b = bn * 255;
this->a = alpha * 255;
//return Color4(RGBAf{rn, gn, bn, alpha});
}
if (240 <= h < 300) {
r = z + m;
g = m;
b = M;
}
Color4::Color4(const Color3 &color3, u8 alpha) {r = color3.r; g = color3.g; b = color3.b; a = alpha;}
if (300 <= h < 360) {
r = M;
g = m;
b = z + m;
}
a = alpha;
}
constexpr Color4::Color4(const Color3 &color3, u8 alpha) {r = color3.r; g = color3.g; b = color3.b; a = alpha;}
constexpr Color4::Color4(u8 red, u8 green, u8 blue, u8 alpha): r(red), g(green), b(blue), a(alpha) {
Color4::Color4(u8 red, u8 green, u8 blue, u8 alpha): r(red), g(green), b(blue), a(alpha) {
#if !defined(WIN32)
list.push_back(*this);
#endif
@@ -356,3 +377,66 @@ std::string Color4::ToHexAlpha() const {
std::string as = decimal_to_hex(a);
return "#" + rs + gs + bs + as;
}
Color4 Color4::FromHSV(float hue, float saturation, float value, float alpha) {
float hh, p, q, t, ff;
long i;
float rn, gn, bn;
if (saturation <= 0.0) { // < is bogus, just shuts up warnings.
rn = value;
bn = value;
gn = value;
return Color4(RGBAf{rn, bn, gn, alpha});
}
hh = hue;
if (hh >= 360.f) hh = 0;
hh /= 60.f;
i = (long)hh;
ff = hh - i;
p = value * (1.f - saturation);
q = value * (1.f - (saturation * ff));
t = value * (1.f - (saturation * (1.f - ff)));
switch(i) {
case 0:
rn = value;
gn = t;
bn = p;
break;
case 1:
rn = q;
gn = value;
bn = p;
break;
case 2:
rn = p;
gn = value;
bn = t;
break;
case 3:
rn = p;
gn = q;
bn = value;
break;
case 4:
rn = t;
gn = p;
bn = value;
break;
case 5:
default:
rn = value;
gn = p;
bn = q;
break;
}
return Color4(RGBAf{rn, gn, bn, alpha});
}
Color4 Color4::FromHSV(const HSV &hsv, float alpha) {
return FromHSV(hsv.h, hsv.s, hsv.v, alpha);
}
Color4::Color4(const HSVA &hsva) : Color4(HSV{hsva.h,hsva.s,hsva.v}, hsva.a) {}