#pragma once #include #include #include #include "zeus/CVector4f.hpp" #include "zeus/Global.hpp" #include "zeus/Math.hpp" #undef min #undef max #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define COLOR(rgba) \ (zeus::Comp32)(((rgba)&0x000000FF) << 24 | ((rgba)&0x0000FF00) << 8 | ((rgba)&0x00FF0000) >> 8 | \ ((rgba)&0xFF000000) >> 24) #else #define COLOR(rgba) (zeus::Comp32)(rgba) #endif namespace zeus { using Comp8 = uint8_t; using Comp32 = uint32_t; constexpr float OneOver255 = 1.f / 255.f; union RGBA32 { struct { Comp8 r, g, b, a; }; Comp32 rgba; }; class CVector4f; class CColor { public: simd mSimd; constexpr CColor() : mSimd(1.f) {} constexpr CColor(float rgb, float a = 1.0) : mSimd(rgb, rgb, rgb, a) {} constexpr CColor(float r, float g, float b, float a = 1.0f) : mSimd(r, g, b, a) {} constexpr CColor(Comp32 rgba) : mSimd(((COLOR(rgba) >> 0) & 0xff) * OneOver255, ((COLOR(rgba) >> 8) & 0xff) * OneOver255, ((COLOR(rgba) >> 16) & 0xff) * OneOver255, ((COLOR(rgba) >> 24) & 0xff) * OneOver255) {} constexpr CColor(const Comp8* rgba) : mSimd(rgba[0] * OneOver255, rgba[1] * OneOver255, rgba[2] * OneOver255, rgba[3] * OneOver255) {} constexpr CColor(const CVector4f& other) : mSimd(other.mSimd) {} template constexpr CColor(const simd& s) : mSimd(s) {} CColor& operator=(const CVector4f& other) { mSimd = other.mSimd; return *this; } #if ZE_ATHENA_TYPES [[nodiscard]] static CColor ReadRGBABig(athena::io::IStreamReader& reader) { CColor ret; ret.readRGBABig(reader); return ret; } void readRGBABig(athena::io::IStreamReader& reader) { simd_floats f; f[0] = reader.readFloatBig(); f[1] = reader.readFloatBig(); f[2] = reader.readFloatBig(); f[3] = reader.readFloatBig(); mSimd.copy_from(f); } void readBGRABig(athena::io::IStreamReader& reader) { simd_floats f; f[2] = reader.readFloatBig(); f[1] = reader.readFloatBig(); f[0] = reader.readFloatBig(); f[3] = reader.readFloatBig(); mSimd.copy_from(f); } void writeRGBABig(athena::io::IStreamWriter& writer) const { simd_floats f(mSimd); writer.writeFloatBig(f[0]); writer.writeFloatBig(f[1]); writer.writeFloatBig(f[2]); writer.writeFloatBig(f[3]); } void writeBGRABig(athena::io::IStreamWriter& writer) const { simd_floats f(mSimd); writer.writeFloatBig(f[2]); writer.writeFloatBig(f[1]); writer.writeFloatBig(f[0]); writer.writeFloatBig(f[3]); } void writeRGBA8(athena::io::IStreamWriter& writer) const { simd_floats f(mSimd); writer.writeUByte(atUint8(f[0] * 255)); writer.writeUByte(atUint8(f[1] * 255)); writer.writeUByte(atUint8(f[2] * 255)); writer.writeUByte(atUint8(f[3] * 255)); } #endif [[nodiscard]] bool operator==(const CColor& rhs) const { return (r() == rhs.r() && g() == rhs.g() && b() == rhs.b() && a() == rhs.a()); } [[nodiscard]] bool operator!=(const CColor& rhs) const { return !(*this == rhs); } [[nodiscard]] CColor operator+(const CColor& rhs) const { return CColor(mSimd + rhs.mSimd).Clamp(); } [[nodiscard]] CColor operator-(const CColor& rhs) const { return CColor(mSimd - rhs.mSimd).Clamp(); } [[nodiscard]] CColor operator*(const CColor& rhs) const { return CColor(mSimd * rhs.mSimd).Clamp(); } [[nodiscard]] CColor operator/(const CColor& rhs) const { return CColor(mSimd / rhs.mSimd).Clamp(); } [[nodiscard]] CColor operator+(float val) const { return CColor(mSimd + simd(val)).Clamp(); } [[nodiscard]] CColor operator-(float val) const { return CColor(mSimd - simd(val)).Clamp(); } [[nodiscard]] CColor operator*(float val) const { return CColor(mSimd * simd(val)).Clamp(); } [[nodiscard]] CColor operator/(float val) const { return CColor(mSimd / simd(val)).Clamp(); } const CColor& operator+=(const CColor& rhs) { mSimd += rhs.mSimd; Clamp(); return *this; } const CColor& operator-=(const CColor& rhs) { mSimd -= rhs.mSimd; Clamp(); return *this; } const CColor& operator*=(const CColor& rhs) { mSimd *= rhs.mSimd; Clamp(); return *this; } const CColor& operator/=(const CColor& rhs) { mSimd /= rhs.mSimd; Clamp(); return *this; } const CColor& operator+=(float rhs) { mSimd += simd(rhs); Clamp(); return *this; } const CColor& operator-=(float rhs) { mSimd -= simd(rhs); Clamp(); return *this; } const CColor& operator*=(float rhs) { mSimd *= simd(rhs); Clamp(); return *this; } const CColor& operator/=(float rhs) { mSimd /= simd(rhs); Clamp(); return *this; } void normalize() { float mag = magnitude(); mag = 1.f / mag; *this *= mag; } [[nodiscard]] CColor normalized() const { float mag = magnitude(); mag = 1.f / mag; return *this * mag; } [[nodiscard]] float magSquared() const { return mSimd.dot4(mSimd); } [[nodiscard]] float magnitude() const { return std::sqrt(magSquared()); } [[nodiscard]] static CColor lerp(const CColor& a, const CColor& b, float t) { return zeus::simd(1.f - t) * a.mSimd + b.mSimd * zeus::simd(t); } [[nodiscard]] static CColor nlerp(const CColor& a, const CColor& b, float t) { return lerp(a, b, t).normalized(); } [[nodiscard]] simd::reference operator[](const size_t& idx) { assert(idx < 4); return mSimd[idx]; } [[nodiscard]] float operator[](const size_t& idx) const { assert(idx < 4); return mSimd[idx]; } void splat(float rgb, float a) { mSimd = simd(rgb); mSimd[3] = a; } [[nodiscard]] float rgbDot(const CColor& rhs) const { return mSimd.dot3(rhs.mSimd); } void fromRGBA8(const Comp8 ri, const Comp8 gi, const Comp8 bi, const Comp8 ai) { mSimd = simd(ri * OneOver255, gi * OneOver255, bi * OneOver255, ai * OneOver255); } void fromRGBA32(Comp32 rgba) { RGBA32 tmp; tmp.rgba = COLOR(rgba); fromRGBA8(tmp.r, tmp.g, tmp.b, tmp.a); } /** * @brief Converts a CColor to RGBA8 * @param ro Red component * @param go Green component * @param bo Blue component * @param ao Alpha component */ void toRGBA8(Comp8& ro, Comp8& go, Comp8& bo, Comp8& ao) const { ro = Comp8(r() * 255); go = Comp8(g() * 255); bo = Comp8(b() * 255); ao = Comp8(a() * 255); } Comp32 toRGBA() const { RGBA32 ret; ret.r = r() * 255; ret.g = g() * 255; ret.b = b() * 255; ret.a = a() * 255; return ret.rgba; } [[nodiscard]] unsigned short toRGB5A3() const { Comp8 r; Comp8 g; Comp8 b; Comp8 a; toRGBA8(r, g, b, a); if (a == 255) { return static_cast((r & 0xf8) << 7 | (g & 0xf8) << 2 | b >> 3 | 0x8000); } return static_cast((r & 0xf0) << 4 | (g & 0xf0) | b >> 4 | (a & 0xe0) << 7); } /** * @brief Assigns rgba from hsv * @param h[0-1] The hue percentage of the color. * @param s[0-1] The saturation percentage of the color. * @param v[0-1] The value percentage of the color. * @param _a[0-1] The alpha percentage of the color. */ void fromHSV(float h, float s, float v, float _a = 1.0); /** * @brief Converts rgba to hsv * @param h[0-1] The hue percentage of the color. * @param s[0-1] The saturation percentage of the color. * @param v[0-1] The value percentage of the color. */ void toHSV(float& h, float& s, float& v) const; void fromHSL(float h, float s, float l, float _a = 1.0); void toHSL(float& h, float& s, float& l) const; [[nodiscard]] CColor toGrayscale() const { return {std::sqrt((r() * r() + g() * g() + b() * b()) / 3), a()}; } /** * @brief Clamps to GPU-safe RGBA values [0,1] */ CColor& Clamp() { r() = std::min(1.f, std::max(0.f, float(r()))); g() = std::min(1.f, std::max(0.f, float(g()))); b() = std::min(1.f, std::max(0.f, float(b()))); a() = std::min(1.f, std::max(0.f, float(a()))); return *this; } [[nodiscard]] float r() const { return mSimd[0]; } [[nodiscard]] float g() const { return mSimd[1]; } [[nodiscard]] float b() const { return mSimd[2]; } [[nodiscard]] float a() const { return mSimd[3]; } [[nodiscard]] simd::reference r() { return mSimd[0]; } [[nodiscard]] simd::reference g() { return mSimd[1]; } [[nodiscard]] simd::reference b() { return mSimd[2]; } [[nodiscard]] simd::reference a() { return mSimd[3]; } }; constexpr CVector4f::CVector4f(const zeus::CColor& other) : mSimd(other.mSimd) {} constexpr CVector4f& CVector4f::operator=(const CColor& other) { mSimd = other.mSimd; return *this; } constexpr inline CColor skRed(1.f, 0.f, 0.f, 1.f); constexpr inline CColor skBlack(0.f, 0.f, 0.f, 1.f); constexpr inline CColor skBlue(0.f, 0.f, 1.f, 1.f); constexpr inline CColor skCyan(0.f, 1.f, 1.f, 1.f); constexpr inline CColor skGreen(0.f, 1.f, 0.f, 1.f); constexpr inline CColor skGrey(0.5f, 0.5f, 0.5f, 1.f); constexpr inline CColor skMagenta(1.f, 0.f, 1.f, 1.f); constexpr inline CColor skOrange(1.f, 0.43f, 0.f, 1.f); constexpr inline CColor skPurple(0.63f, 0.f, 1.f, 1.f); constexpr inline CColor skYellow(1.f, 1.f, 0.f, 1.f); constexpr inline CColor skWhite(1.f, 1.f, 1.f, 1.f); constexpr inline CColor skClear(0.f, 0.f, 0.f, 0.f); [[nodiscard]] inline CColor operator+(float lhs, const CColor& rhs) { return CColor(simd(lhs) + rhs.mSimd).Clamp(); } [[nodiscard]] inline CColor operator-(float lhs, const CColor& rhs) { return CColor(simd(lhs) - rhs.mSimd).Clamp(); } [[nodiscard]] inline CColor operator*(float lhs, const CColor& rhs) { return CColor(simd(lhs) * rhs.mSimd).Clamp(); } [[nodiscard]] inline CColor operator/(float lhs, const CColor& rhs) { return CColor(simd(lhs) / rhs.mSimd).Clamp(); } } // namespace zeus namespace std { template <> struct hash { size_t operator()(const zeus::CColor& color) const noexcept { size_t ret = std::hash()(color.r()); zeus::hash_combine_impl(ret, std::hash()(color.g())); zeus::hash_combine_impl(ret, std::hash()(color.b())); zeus::hash_combine_impl(ret, std::hash()(color.a())); return ret; } }; } // namespace std