#pragma once #include "config.hpp" #include #include #include #include #include #include #include #include #include namespace color_picker_math { struct Hsv { float h = 0.0f; // [0, 360) float s = 0.0f; // [0, 1] float v = 0.0f; // [0, 1] }; inline Hsv rgbToHsv(BYTE red, BYTE green, BYTE blue) { const float r = static_cast(red) / 255.0f; const float g = static_cast(green) / 255.0f; const float b = static_cast(blue) / 255.0f; const float maximum = std::max({r, g, b}); const float minimum = std::min({r, g, b}); const float delta = maximum - minimum; Hsv result; result.v = maximum; result.s = maximum <= 0.0f ? 0.0f : delta / maximum; if (delta <= 0.00001f) { result.h = 0.0f; } else if (maximum == r) { result.h = 60.0f * std::fmod((g - b) / delta, 6.0f); } else if (maximum == g) { result.h = 60.0f * (((b - r) / delta) + 2.0f); } else { result.h = 60.0f * (((r - g) / delta) + 4.0f); } if (result.h < 0.0f) result.h += 360.0f; if (result.h >= 360.0f) result.h = std::fmod(result.h, 360.0f); return result; } inline COLORREF hsvToRgb(float hue, float saturation, float value) { hue = std::fmod(hue, 360.0f); if (hue < 0.0f) hue += 360.0f; saturation = std::clamp(saturation, 0.0f, 1.0f); value = std::clamp(value, 0.0f, 1.0f); const float chroma = value * saturation; const float section = hue / 60.0f; const float x = chroma * (1.0f - std::fabs(std::fmod(section, 2.0f) - 1.0f)); const float m = value - chroma; float r = 0.0f; float g = 0.0f; float b = 0.0f; if (section < 1.0f) { r = chroma; g = x; } else if (section < 2.0f) { r = x; g = chroma; } else if (section < 3.0f) { g = chroma; b = x; } else if (section < 4.0f) { g = x; b = chroma; } else if (section < 5.0f) { r = x; b = chroma; } else { r = chroma; b = x; } const auto channel = [m](float component) -> BYTE { return static_cast(std::clamp(static_cast(std::lround((component + m) * 255.0f)), 0, 255)); }; return RGB(channel(r), channel(g), channel(b)); } inline std::uint32_t toArgb(COLORREF color, BYTE alpha) { return (static_cast(alpha) << 24u) | (static_cast(GetRValue(color)) << 16u) | (static_cast(GetGValue(color)) << 8u) | static_cast(GetBValue(color)); } inline COLORREF colorFromArgb(std::uint32_t argb) { return RGB((argb >> 16u) & 0xFFu, (argb >> 8u) & 0xFFu, argb & 0xFFu); } inline BYTE alphaFromArgb(std::uint32_t argb) { return static_cast((argb >> 24u) & 0xFFu); } inline std::wstring hexRgba(COLORREF color, BYTE alpha) { std::wostringstream stream; stream << L'#' << std::uppercase << std::hex << std::setfill(L'0') << std::setw(2) << static_cast(GetRValue(color)) << std::setw(2) << static_cast(GetGValue(color)) << std::setw(2) << static_cast(GetBValue(color)) << std::setw(2) << static_cast(alpha); return stream.str(); } inline bool parseHexRgba(std::wstring text, COLORREF& color, BYTE& alpha) { if (!text.empty() && text.front() == L'#') text.erase(text.begin()); if (text.size() != 6 && text.size() != 8) return false; for (wchar_t ch : text) { if (!std::iswxdigit(static_cast(ch))) return false; } wchar_t* end = nullptr; const unsigned long long value = std::wcstoull(text.c_str(), &end, 16); if (!end || *end != L'\0') return false; if (text.size() == 6) { color = RGB((value >> 16u) & 0xFFu, (value >> 8u) & 0xFFu, value & 0xFFu); alpha = 255; } else { color = RGB((value >> 24u) & 0xFFu, (value >> 16u) & 0xFFu, (value >> 8u) & 0xFFu); alpha = static_cast(value & 0xFFu); } return true; } } // namespace color_picker_math