/** * @file color.hpp * @author Gustice * @brief * * @copyright Copyright (c) 2024 */ #pragma once #include #include #include #include /// No Waveform definition extern uint8_t const *const noWave; /** * @brief Color class * * @details Defines color data and common operations with color objects * \li Supports RGB with overlapping white channel (e.g. SK6812-LED) * \li Supports Operator overloading with '+' and '*' (Combined operation with MixWith) * \li Return formatted data stream that can be directly sent to an LED */ class Color { private: uint8_t red; uint8_t green; uint8_t blue; uint8_t white; public: struct Rgbw { uint8_t red; uint8_t green; uint8_t blue; uint8_t white; }; struct Rgb { uint8_t red; uint8_t green; uint8_t blue; }; /// @brief Construct a new Color object /// @details Default color will be Black \ref _ctBlack Color(void) { set(0, 0, 0, 0); }; /// @brief Copy-Construct a new Color object Color(const Color & c) { red = c.red; green = c.green; blue = c.blue; white = c.white; }; /// @brief Construct a new Color object according to rgb color-channel-values /// @details This constructor also takes an overlapping white channel /// @param r Red channel /// @param g Green channel /// @param b Blue channel /// @param w White channel (zero by default if not assigned) Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { set(r, g, b, w); }; /// @brief Set the Color object according to rgb color-channel values /// @param r Red channel /// @param g Green channel /// @param b Blue channel /// @param w White channel (zero by default if not assigned) void set(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { red = r; green = g; blue = b; white = w; } /// @brief Returns Color as Color_t structure /// @return Color_t Color template T getColor(void) const { if constexpr (std::is_same::value) { return {red, green, blue, white}; } else if constexpr (std::is_same::value) { return {red, green, blue}; } else { throw "wrong type"; } } uint8_t getRed() { return red; }; uint8_t getGreen() { return green; }; uint8_t getBlue() { return blue; }; uint8_t getWhite() { return white; }; /// @brief Scaling and overlaying with given color. /// @param c2 Color 2 /// @param k scaling factor /// @return Overlayed Color Color mixWith(const Color c2, uint8_t k = 0x7F) { auto t(c2); *this = (*this * (UINT8_MAX - k)) + (t * k); return *this; } /// @brief Writes Color as formatted byte stream to location /// @param color template void writeByteStreamTo(uint8_t raw[sizeof(T)]) const { if constexpr (std::is_same::value) { raw[0] = blue; raw[1] = green; raw[2] = red; raw[3] = white; } else if constexpr (std::is_same::value) { raw[0] = blue; raw[1] = green; raw[2] = red; } else { throw "wrong type"; } } /// @brief Assign overload Color operator=(const Color &c2) { red = c2.red; green = c2.green; blue = c2.blue; white = c2.white; return *this; } /// @brief Checks if both color values are equal bool operator==(Color const &c2) const { return ((blue == c2.blue) && (green == c2.green) && (red == c2.red) && (white == c2.white)); } /// @brief Checks if both color values are not equal bool operator!=(Color const &c2) const { return ((blue != c2.blue) || (green != c2.green) || (red != c2.red) || (white != c2.white)); } /// @brief Scale color by scaling each color-channel by given factor /// @param c1 Color /// @param k Scale-factor uint8_t.Max is full-scale /// @return Scaled Color Color operator*(uint8_t k) { red = (uint8_t)((uint16_t)red * k / 0xFF); green = (uint8_t)((uint16_t)green * k / 0xFF); blue = (uint8_t)((uint16_t)blue * k / 0xFF); white = (uint8_t)((uint16_t)white * k / 0xFF); return *this; } /// @brief Overlaying to colors my adding each color channel value /// @note Colors channels should not overlap since there is no automatic ratio correction. /// Use *-Operator first in order to scale each color value a a way that no overflow can occur /// @param c2 Color 2 /// @return Overlayed Color Color operator+(Color c2) { red = red + c2.red; green = green + c2.green; blue = blue + c2.blue; white = white + c2.white; return *this; } }; static_assert(sizeof(Color) == 4 && "Color Type is to big"); /// Null color object extern const Color *noColor; /// Used old color agreement (if no color pointer) extern const Color *oldColor; /// Black color object constant extern const Color CBlack; /// Red color object constant extern const Color CRed; /// Green color object constant extern const Color CGreen; /// Blue color object constant extern const Color CBlue; /// Magenta color object constant extern const Color CMagenta; /// Cyan color object constant extern const Color CCyan; /// Yellow color object constant extern const Color CYellow; /// White color object constant extern const Color CWhite; /// Warm white color object constant (with higher red channel value) extern const Color CWarmWhite; /// Black color object constant (with higher green channel value) extern const Color CColdWhite; /// Black color object constant (with higher blue channel value) extern const Color CSpookyWhite;