diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b3aaa8..c5da54b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,14 @@ set(headers src/cleanuphelper.h + src/color_utils.h src/cppbitmask.h - src/crc32builder.h src/cppflags.h src/cppmacros.h src/cppoverloadutils.h src/cppsignal.h src/cpptypesafeenum.h src/cpputils.h + src/crc32builder.h src/delayedconstruction.h src/numberparsing.h src/refwhenneeded.h @@ -15,6 +16,7 @@ set(headers ) set(sources + src/color_utils.cpp src/strutils.cpp ) diff --git a/src/color_utils.cpp b/src/color_utils.cpp new file mode 100644 index 0000000..f266a4f --- /dev/null +++ b/src/color_utils.cpp @@ -0,0 +1,30 @@ +#include "color_utils.h" + +// 3rdparty lib includes +#include + +namespace cpputils { +std::string toString(ColorHelper color) +{ + return fmt::format("#{:02X}{:02X}{:02X}", color.r, color.g, color.b); +} + +tl::expected parseColor(std::string_view str) +{ + // input may be "#FFF" or "#FFFFFF" or "#FFFFFFFF" + + if (ColorHelper helper; std::sscanf(str.data(), "#%2hhx%2hhx%2hhx", &helper.r, &helper.g, &helper.b) == 3) + return helper; + + if (ColorHelper helper; std::sscanf(str.data(), "#%1hhx%1hhx%1hhx", &helper.r, &helper.g, &helper.b) == 3) + { + helper.r <<= 4; + helper.g <<= 4; + helper.b <<= 4; + return helper; + } + + return tl::make_unexpected(fmt::format("invalid color {}", str)); +} + +} // namespace cpputils diff --git a/src/color_utils.h b/src/color_utils.h new file mode 100644 index 0000000..75457ff --- /dev/null +++ b/src/color_utils.h @@ -0,0 +1,143 @@ +#pragma once + +// system includes +#include +#include +#include + +// 3rdparty lib includes +#include + +namespace cpputils { +struct ColorHelper +{ + constexpr ColorHelper() = default; + constexpr ColorHelper(uint8_t _r, uint8_t _g, uint8_t _b) : r{_r}, g{_g}, b{_b} {} + constexpr ColorHelper(const ColorHelper &other) = default; + + constexpr ColorHelper &operator=(const ColorHelper &other) = default; + + constexpr bool operator==(const ColorHelper &other) const { return r == other.r && g == other.g && b == other.b; } + constexpr bool operator!=(const ColorHelper &other) const { return r != other.r || g != other.g || b != other.b; } + + uint8_t r{}; + uint8_t g{}; + uint8_t b{}; +}; + +namespace { +constexpr ColorHelper black{}; +constexpr ColorHelper red{255, 0, 0}; +constexpr ColorHelper blue{0, 0, 255}; +constexpr ColorHelper yellow{255, 255, 0}; +constexpr ColorHelper cyan{0, 255, 255}; +constexpr ColorHelper green{0, 255, 0}; +constexpr ColorHelper white{255, 255, 255}; +constexpr ColorHelper pink{255, 0, 255}; +} // namespace + +struct HsvColor +{ + constexpr HsvColor() = default; + constexpr HsvColor(uint8_t _h, uint8_t _s, uint8_t _v) : h{_h}, s{_s}, v{_v} {} + constexpr HsvColor(const HsvColor &other) = default; + + constexpr HsvColor &operator=(const HsvColor &other) = default; + + constexpr bool operator==(const HsvColor &other) const { return h == other.h && s == other.s && v == other.v; } + constexpr bool operator!=(const HsvColor &other) const { return h != other.h || s != other.s || v != other.v; } + + uint8_t h{}; + uint8_t s{}; + uint8_t v{}; +}; + +std::string toString(ColorHelper color); +tl::expected parseColor(std::string_view str); + +inline uint32_t colorToNumber(ColorHelper color) +{ + union { + uint32_t temp; + struct { + // reverse order to be reverse compatible with existing configs + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t x; + }; + } helper; + helper.b = color.b; + helper.g = color.g; + helper.r = color.r; + helper.x = 0; + return helper.temp; +} + +inline ColorHelper numberToColor(uint32_t number) +{ + union { + uint32_t temp; + struct { + // reverse order to be reverse compatible with existing configs + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t x; + }; + } helper; + helper.temp = number; + return ColorHelper{helper.r, helper.g, helper.b}; +} + +constexpr inline ColorHelper interpolateColor(ColorHelper left, ColorHelper right, uint8_t pos) +{ + int16_t leftAmount = pos; + int16_t rightAmount = 255 - pos; + + return ColorHelper ( + (left.r * leftAmount / 255) + (right.r * rightAmount / 255), + (left.g * leftAmount / 255) + (right.g * rightAmount / 255), + (left.b * leftAmount / 255) + (right.b * rightAmount / 255) + ); +} + +constexpr inline ColorHelper HsvToRgb(HsvColor hsv) +{ + if (hsv.s == 0) + return ColorHelper{hsv.v, hsv.v, hsv.v}; + + const uint8_t region = hsv.h / 43; + const uint8_t remainder = (hsv.h - (region * 43)) * 6; + + const uint8_t p = (hsv.v * (255 - hsv.s)) >> 8; + const uint8_t q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8; + const uint8_t t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8; + + switch (region) + { + case 0: return ColorHelper{hsv.v, t, p }; + case 1: return ColorHelper{q, hsv.v, p }; + case 2: return ColorHelper{p, hsv.v, t }; + case 3: return ColorHelper{p, q, hsv.v}; + case 4: return ColorHelper{t, p, hsv.v}; + default: return ColorHelper{hsv.v, p, q }; + } +} + +template +constexpr inline std::array generateRainbowColors() +{ + std::array colors; + + for (int i = 0; i < COUNT; i++) + { + HsvColor hsvColor{uint8_t(i * 256.f / COUNT), 240, 255}; + ColorHelper rgbColor = HsvToRgb(hsvColor); + colors[i] = rgbColor; + } + + return colors; +} + +} // namespace cpputils diff --git a/src/strutils.cpp b/src/strutils.cpp index 0ccee4e..6e4c4e1 100644 --- a/src/strutils.cpp +++ b/src/strutils.cpp @@ -5,20 +5,6 @@ #include namespace cpputils { -std::string toString(bool val) { return val ? "true" : "false"; } -std::string toString(int8_t val) { return std::to_string(val); } -std::string toString(uint8_t val) { return std::to_string(val); } -std::string toString(int16_t val) { return std::to_string(val); } -std::string toString(uint16_t val) { return std::to_string(val); } -std::string toString(int32_t val) { return std::to_string(val); } -std::string toString(uint32_t val) { return std::to_string(val); } -std::string toString(int64_t val) { return std::to_string(val); } -std::string toString(uint64_t val) { return std::to_string(val); } -std::string toString(float val) { return std::to_string(val); } -std::string toString(double val) { return std::to_string(val); } -std::string toString(const std::string &val) { return val; } -std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } - bool stringEqualsIgnoreCase(std::string_view a, std::string_view b) { return a == b; // HACK for now... diff --git a/src/strutils.h b/src/strutils.h index ee840ce..3d0546f 100644 --- a/src/strutils.h +++ b/src/strutils.h @@ -6,19 +6,30 @@ #include namespace cpputils { -std::string toString(bool val); -std::string toString(int8_t val); -std::string toString(uint8_t val); -std::string toString(int16_t val); -std::string toString(uint16_t val); -std::string toString(int32_t val); -std::string toString(uint32_t val); -std::string toString(int64_t val); -std::string toString(uint64_t val); -std::string toString(float val); -std::string toString(double val); -std::string toString(const std::string &val); -std::string toString(std::optional val); +inline std::string toString(bool val) { return val ? "true" : "false"; } +inline std::string toString(int8_t val) { return std::to_string(val); } +inline std::string toString(uint8_t val) { return std::to_string(val); } +inline std::string toString(int16_t val) { return std::to_string(val); } +inline std::string toString(uint16_t val) { return std::to_string(val); } +inline std::string toString(int32_t val) { return std::to_string(val); } +inline std::string toString(uint32_t val) { return std::to_string(val); } +inline std::string toString(int64_t val) { return std::to_string(val); } +inline std::string toString(uint64_t val) { return std::to_string(val); } +inline std::string toString(float val) { return std::to_string(val); } +inline std::string toString(double val) { return std::to_string(val); } +inline std::string toString(const std::string &val) { return val; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(std::optional val) { if (val) return toString(*val); else return {}; } +inline std::string toString(const std::optional &val) { if (val) return toString(*val); else return {}; } bool stringEqualsIgnoreCase(std::string_view a, std::string_view b); bool stringStartsWith(std::string_view fullString, std::string_view begin);