diff --git a/src/Color.cpp b/src/Color.cpp new file mode 100644 index 0000000..6829a4e --- /dev/null +++ b/src/Color.cpp @@ -0,0 +1,124 @@ +#include "Color.hpp" +#include +#include +#include +#include +#include + +Color::Color(const RGB& rgb) : _rgb {rgb} {} + +Color::Color(const HSB& hsb) { + if (hsb.s <= 0) { + _rgb.r = _rgb.g = _rgb.b = hsb.b; + } else { + double hi = hsb.h / 60; + int i = static_cast(hi); + double f = hi - i; + double p = hsb.b * (1.0 - hsb.s); + double q = hsb.b * (1.0 - (hsb.s * f)); + double t = hsb.b * (1.0 - (hsb.s * (1.0 -f))); + switch (i) { + case 0: + _rgb.r = hsb.b; + _rgb.g = t; + _rgb.b = p; + break; + case 1: + _rgb.r = q; + _rgb.g = hsb.b; + _rgb.b = p; + break; + case 2: + _rgb.r = p; + _rgb.g = hsb.b; + _rgb.b = t; + break; + case 3: + _rgb.r = p; + _rgb.g = q; + _rgb.b = hsb.b; + break; + case 4: + _rgb.r = t; + _rgb.g = p; + _rgb.b = hsb.b; + break; + case 5: + default: + _rgb.r = hsb.b; + _rgb.g = p; + _rgb.b = q; + break; + } + } +} + +Color::operator HSB() const { + HSB hsb; + double rgb_max = std::max({_rgb.r, _rgb.g, _rgb.b}); + double rgb_min = std::min({_rgb.r, _rgb.g, _rgb.b}); + double delta = rgb_max - rgb_min; + if (delta < 0.00001) { + hsb.h = 0; + hsb.s = 0; + hsb.b = rgb_max; + return hsb; + } + if (_rgb.r >= rgb_max) { // between yellow and magenta + hsb.h = (_rgb.g - _rgb.b) / delta; + } else if (_rgb.g >= rgb_max) { // between cyan and yellow + hsb.h = 2.0 + (_rgb.b - _rgb.r) / delta; + } else { // between magenta and cyan + hsb.h = 4.0 + (_rgb.r - _rgb.g) / delta; + } + hsb.h *= 60; + hsb.s = rgb_max > 0.0 ? delta / rgb_max : 0; + hsb.b = rgb_max; + return hsb; +} + +Color::Color(const std::string& hex) { + if (hex[0] != '#') throw std::runtime_error {"invalid hex color - must start with '#'"}; + if (hex.size() != 7) throw std::runtime_error {"invalid hex color - must be 7 chars"}; + unsigned int val; + + std::istringstream iss {hex.substr(1, 2)}; + iss >> std::hex >> val; + _rgb.r = val / 255.0; + + iss.str(hex.substr(3, 2)); + iss.seekg(0); + iss >> std::hex >> val; + _rgb.g = val / 255.0; + + iss.str(hex.substr(5, 2)); + iss.seekg(0); + iss >> std::hex >> val; + _rgb.b = val / 255.0; +} + +Color& Color::operator=(const RGB& rgb) { + _rgb = rgb; + return *this; +} + +Color& Color::operator=(const HSB& hsb) { + Color tmp {hsb}; + _rgb = tmp._rgb; + return *this; +} + +Color& Color::operator=(const std::string& hex) { + Color tmp {hex}; + _rgb = tmp._rgb; + return *this; +} + +std::string Color::hex() const { + std::ostringstream oss; + oss << '#' << std::hex << + static_cast(round(_rgb.r * 255)) << + static_cast(round(_rgb.g * 255)) << + static_cast(round(_rgb.b * 255)); + return oss.str(); +} diff --git a/src/Color.hpp b/src/Color.hpp new file mode 100644 index 0000000..12f5429 --- /dev/null +++ b/src/Color.hpp @@ -0,0 +1,36 @@ +#ifndef _Color_H_ +#define _Color_H_ + +#include + +struct RGB { + double r; // 0..1 + double g; // 0..1 + double b; // 0..1 +}; + +struct HSB { + double h; // 0..360 + double s; // 0..1 + double b; // 0..1 +}; + +class Color { + public: + Color(const RGB& rgb); + Color(const HSB& hsb); + Color(const std::string& hex); + + operator RGB() const { return _rgb; } + operator HSB() const; + std::string hex() const; + + Color& operator=(const RGB& rgb); + Color& operator=(const HSB& hsb); + Color& operator=(const std::string& hex); + + private: + RGB _rgb; +}; + +#endif // _Color_H_