#pragma once
#include "ColorSpace.h"
#include "ColorUtilities.h"
#include "ExtendedColor.h"
#include <wtf/Forward.h>
#include <wtf/HashFunctions.h>
#include <wtf/Hasher.h>
#include <wtf/Optional.h>
#if USE(CG)
typedef struct CGColor* CGColorRef;
#endif
#if PLATFORM(WIN)
struct _D3DCOLORVALUE;
typedef _D3DCOLORVALUE D3DCOLORVALUE;
typedef D3DCOLORVALUE D2D_COLOR_F;
typedef D2D_COLOR_F D2D1_COLOR_F;
struct D2D_VECTOR_4F;
typedef D2D_VECTOR_4F D2D1_VECTOR_4F;
#endif
#if PLATFORM(GTK)
typedef struct _GdkRGBA GdkRGBA;
#endif
namespace WebCore {
class Color {
WTF_MAKE_FAST_ALLOCATED;
public:
Color() = default;
Color(SRGBA<uint8_t>);
Color(Optional<SRGBA<uint8_t>>);
enum SemanticTag { Semantic };
Color(SRGBA<uint8_t>, SemanticTag);
Color(Optional<SRGBA<uint8_t>>, SemanticTag);
Color(ColorComponents<float>, ColorSpace);
Color(const SRGBA<float>&);
Color(const LinearSRGBA<float>&);
Color(const DisplayP3<float>&);
explicit Color(WTF::HashTableEmptyValueType);
explicit Color(WTF::HashTableDeletedValueType);
bool isHashTableDeletedValue() const { return m_colorData.inlineColorAndFlags == deletedHashValue; }
WEBCORE_EXPORT Color(const Color&);
WEBCORE_EXPORT Color(Color&&);
WEBCORE_EXPORT Color& operator=(const Color&);
WEBCORE_EXPORT Color& operator=(Color&&);
~Color();
unsigned hash() const;
bool isValid() const { return isExtended() || (m_colorData.inlineColorAndFlags & validInlineColorBit); }
bool isSemantic() const { return isInline() && (m_colorData.inlineColorAndFlags & isSemanticInlineColorBit); }
bool isOpaque() const { return isExtended() ? asExtended().alpha() == 1.0 : asInline().alpha == 255; }
bool isVisible() const { return isExtended() ? asExtended().alpha() > 0.0 : asInline().alpha > 0; }
uint8_t alphaByte() const { return isExtended() ? convertToComponentByte(asExtended().alpha()) : asInline().alpha; }
float alphaAsFloat() const { return isExtended() ? asExtended().alpha() : convertToComponentFloat(asInline().alpha); }
WEBCORE_EXPORT float luminance() const;
WEBCORE_EXPORT float lightness() const;
template<typename Functor> decltype(auto) callOnUnderlyingType(Functor&&) const;
template<typename T> SRGBA<T> toSRGBALossy() const;
WEBCORE_EXPORT std::pair<ColorSpace, ColorComponents<float>> colorSpaceAndComponents() const;
WEBCORE_EXPORT Color lightened() const;
WEBCORE_EXPORT Color darkened() const;
Color invertedColorWithAlpha(Optional<float> alpha) const;
Color invertedColorWithAlpha(float alpha) const;
Color colorWithAlphaMultipliedBy(Optional<float>) const;
Color colorWithAlphaMultipliedBy(float) const;
Color colorWithAlpha(Optional<float>) const;
WEBCORE_EXPORT Color colorWithAlpha(float) const;
Color opaqueColor() const { return colorWithAlpha(1.0f); }
Color semanticColor() const;
#if PLATFORM(GTK)
Color(const GdkRGBA&);
operator GdkRGBA() const;
#endif
#if USE(CG)
WEBCORE_EXPORT Color(CGColorRef);
WEBCORE_EXPORT Color(CGColorRef, SemanticTag);
#endif
#if PLATFORM(WIN)
WEBCORE_EXPORT Color(D2D1_COLOR_F);
WEBCORE_EXPORT operator D2D1_COLOR_F() const;
WEBCORE_EXPORT operator D2D1_VECTOR_4F() const;
#endif
static constexpr auto transparentBlack = SRGBA<uint8_t> { };
static constexpr auto black = SRGBA<uint8_t> { 0, 0, 0 };
static constexpr auto white = SRGBA<uint8_t> { 255, 255, 255 };
static constexpr auto darkGray = SRGBA<uint8_t> { 128, 128, 128 };
static constexpr auto gray = SRGBA<uint8_t> { 160, 160, 160 };
static constexpr auto lightGray = SRGBA<uint8_t> { 192, 192, 192 };
static constexpr auto cyan = SRGBA<uint8_t> { 0, 255, 255 };
static constexpr auto yellow = SRGBA<uint8_t> { 255, 255, 0 };
static constexpr auto red = SRGBA<uint8_t> { 255, 0, 0 };
static constexpr auto magenta = SRGBA<uint8_t> { 255, 0, 255 };
static constexpr auto blue = SRGBA<uint8_t> { 0, 0, 255 };
static constexpr auto green = SRGBA<uint8_t> { 0, 255, 0 };
static constexpr auto darkGreen = SRGBA<uint8_t> { 0, 128, 0 };
static constexpr auto orange = SRGBA<uint8_t> { 255, 128, 0 };
bool isExtended() const { return !(m_colorData.inlineColorAndFlags & invalidInlineColor); }
bool isInline() const { return !isExtended(); }
const ExtendedColor& asExtended() const;
SRGBA<uint8_t> asInline() const;
friend bool operator==(const Color& a, const Color& b);
friend bool equalIgnoringSemanticColor(const Color& a, const Color& b);
friend bool extendedColorsEqual(const Color&, const Color&);
static bool isBlackColor(const Color&);
static bool isWhiteColor(const Color&);
template<class Encoder> void encode(Encoder&) const;
template<class Decoder> static Optional<Color> decode(Decoder&);
private:
Color(Ref<ExtendedColor>&&);
void setColor(SRGBA<uint8_t>);
void setColor(const SRGBA<float>&);
void setColor(const LinearSRGBA<float>&);
void setColor(const DisplayP3<float>&);
void setExtendedColor(Ref<ExtendedColor>&&);
void tagAsSemantic() { m_colorData.inlineColorAndFlags |= isSemanticInlineColorBit; }
void tagAsValid() { m_colorData.inlineColorAndFlags |= validInlineColor; }
static const uint64_t extendedColor = 0x0;
static const uint64_t invalidInlineColor = 0x1;
static const uint64_t validInlineColorBit = 0x2;
static const uint64_t validInlineColor = 0x3;
static const uint64_t isSemanticInlineColorBit = 0x4;
static const uint64_t deletedHashValue = 0xFFFFFFFFFFFFFFFD;
static const uint64_t emptyHashValue = 0xFFFFFFFFFFFFFFFB;
union {
uint64_t inlineColorAndFlags { invalidInlineColor };
ExtendedColor* extendedColor;
} m_colorData;
};
bool operator==(const Color&, const Color&);
bool operator!=(const Color&, const Color&);
bool extendedColorsEqual(const Color&, const Color&);
#if USE(CG)
WEBCORE_EXPORT CGColorRef cachedCGColor(const Color&);
#endif
WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, const Color&);
WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, ColorSpace);
inline bool operator==(const Color& a, const Color& b)
{
if (a.isExtended() || b.isExtended())
return extendedColorsEqual(a, b);
return a.m_colorData.inlineColorAndFlags == b.m_colorData.inlineColorAndFlags;
}
inline bool operator!=(const Color& a, const Color& b)
{
return !(a == b);
}
inline bool extendedColorsEqual(const Color& a, const Color& b)
{
if (a.isExtended() && b.isExtended())
return a.asExtended() == b.asExtended();
ASSERT(a.isExtended() || b.isExtended());
return false;
}
inline bool equalIgnoringSemanticColor(const Color& a, const Color& b)
{
if (a.isExtended() || b.isExtended())
return extendedColorsEqual(a, b);
return (a.m_colorData.inlineColorAndFlags & ~Color::isSemanticInlineColorBit) == (b.m_colorData.inlineColorAndFlags & ~Color::isSemanticInlineColorBit);
}
inline Color::Color(SRGBA<uint8_t> color)
{
setColor(color);
}
inline Color::Color(Optional<SRGBA<uint8_t>> color)
{
if (color)
setColor(*color);
}
inline Color::Color(SRGBA<uint8_t> color, SemanticTag)
{
setColor(color);
tagAsSemantic();
}
inline Color::Color(Optional<SRGBA<uint8_t>> color, SemanticTag)
{
if (color) {
setColor(*color);
tagAsSemantic();
}
}
inline Color::Color(ColorComponents<float> components, ColorSpace colorSpace)
{
switch (colorSpace) {
case ColorSpace::SRGB:
setColor(asSRGBA(components));
return;
case ColorSpace::LinearRGB:
setColor(asLinearSRGBA(components));
return;
case ColorSpace::DisplayP3:
setColor(asDisplayP3(components));
return;
}
}
inline Color::Color(const SRGBA<float>& color)
{
setColor(color);
}
inline Color::Color(const LinearSRGBA<float>& color)
{
setColor(color);
}
inline Color::Color(const DisplayP3<float>& color)
{
setColor(color);
}
inline Color::Color(Ref<ExtendedColor>&& extendedColor)
{
setExtendedColor(WTFMove(extendedColor));
}
inline Color::Color(WTF::HashTableDeletedValueType)
{
static_assert(deletedHashValue & invalidInlineColor, "Color's deleted hash value must not look like an ExtendedColor");
static_assert(!(deletedHashValue & validInlineColorBit), "Color's deleted hash value must not look like a valid InlineColor");
static_assert(deletedHashValue & (1 << 4), "Color's deleted hash value must have some bits set that an InlineColor wouldn't have");
m_colorData.inlineColorAndFlags = deletedHashValue;
ASSERT(!isExtended());
}
inline Color::Color(WTF::HashTableEmptyValueType)
{
static_assert(emptyHashValue & invalidInlineColor, "Color's empty hash value must not look like an ExtendedColor");
static_assert(emptyHashValue & (1 << 4), "Color's deleted hash value must have some bits set that an InlineColor wouldn't have");
m_colorData.inlineColorAndFlags = emptyHashValue;
ASSERT(!isExtended());
}
inline Color::~Color()
{
if (isExtended())
m_colorData.extendedColor->deref();
}
inline unsigned Color::hash() const
{
if (isExtended())
return computeHash(asExtended().components(), asExtended().colorSpace());
return WTF::intHash(m_colorData.inlineColorAndFlags);
}
template<typename Functor> decltype(auto) Color::callOnUnderlyingType(Functor&& functor) const
{
if (isExtended())
return asExtended().callOnUnderlyingType(std::forward<Functor>(functor));
return std::invoke(std::forward<Functor>(functor), asInline());
}
template<typename T> SRGBA<T> Color::toSRGBALossy() const
{
return callOnUnderlyingType(WTF::makeVisitor(
[] (const SRGBA<uint8_t>& color) {
if constexpr (std::is_same_v<T, uint8_t>)
return color;
if constexpr (std::is_same_v<T, float>)
return convertToComponentFloats(color);
},
[] (const auto& color) {
if constexpr (std::is_same_v<T, uint8_t>)
return convertToComponentBytes(toSRGBA(color));
if constexpr (std::is_same_v<T, float>)
return toSRGBA(color);
}
));
}
inline Color Color::invertedColorWithAlpha(Optional<float> alpha) const
{
return alpha ? invertedColorWithAlpha(alpha.value()) : *this;
}
inline Color Color::colorWithAlphaMultipliedBy(float amount) const
{
return colorWithAlpha(amount * alphaAsFloat());
}
inline Color Color::colorWithAlphaMultipliedBy(Optional<float> alpha) const
{
return alpha ? colorWithAlphaMultipliedBy(alpha.value()) : *this;
}
inline Color Color::colorWithAlpha(Optional<float> alpha) const
{
return alpha ? colorWithAlpha(alpha.value()) : *this;
}
inline const ExtendedColor& Color::asExtended() const
{
ASSERT(isExtended());
return *m_colorData.extendedColor;
}
inline SRGBA<uint8_t> Color::asInline() const
{
ASSERT(isInline());
return asSRGBA(Packed::RGBA { static_cast<uint32_t>(m_colorData.inlineColorAndFlags >> 32) });
}
inline void Color::setColor(SRGBA<uint8_t> color)
{
m_colorData.inlineColorAndFlags = static_cast<uint64_t>(Packed::RGBA { color }.value) << 32;
tagAsValid();
}
inline void Color::setColor(const SRGBA<float>& color)
{
setExtendedColor(ExtendedColor::create(color));
}
inline void Color::setColor(const LinearSRGBA<float>& color)
{
setExtendedColor(ExtendedColor::create(color));
}
inline void Color::setColor(const DisplayP3<float>& color)
{
setExtendedColor(ExtendedColor::create(color));
}
inline void Color::setExtendedColor(Ref<ExtendedColor>&& extendedColor)
{
m_colorData.inlineColorAndFlags = 0;
m_colorData.extendedColor = &extendedColor.leakRef();
ASSERT(isExtended());
}
inline bool Color::isBlackColor(const Color& color)
{
return color.callOnUnderlyingType([] (const auto& color) {
return WebCore::isBlack(color);
});
}
inline bool Color::isWhiteColor(const Color& color)
{
return color.callOnUnderlyingType([] (const auto& color) {
return WebCore::isWhite(color);
});
}
template<class Encoder> void Color::encode(Encoder& encoder) const
{
if (isExtended()) {
encoder << true;
auto& extendedColor = asExtended();
auto [c1, c2, c3, alpha] = extendedColor.components();
encoder << c1;
encoder << c2;
encoder << c3;
encoder << alpha;
encoder << extendedColor.colorSpace();
return;
}
encoder << false;
if (!isValid()) {
encoder << false;
return;
}
encoder << true;
encoder << Packed::RGBA { asInline() }.value;
}
template<class Decoder> Optional<Color> Color::decode(Decoder& decoder)
{
bool isExtended;
if (!decoder.decode(isExtended))
return WTF::nullopt;
if (isExtended) {
float c1;
float c2;
float c3;
float alpha;
ColorSpace colorSpace;
if (!decoder.decode(c1))
return WTF::nullopt;
if (!decoder.decode(c2))
return WTF::nullopt;
if (!decoder.decode(c3))
return WTF::nullopt;
if (!decoder.decode(alpha))
return WTF::nullopt;
if (!decoder.decode(colorSpace))
return WTF::nullopt;
return Color { ExtendedColor::create({ c1, c2, c3, alpha }, colorSpace) };
}
bool isValid;
if (!decoder.decode(isValid))
return WTF::nullopt;
if (!isValid)
return Color { };
uint32_t value;
if (!decoder.decode(value))
return WTF::nullopt;
return Color { asSRGBA(Packed::RGBA { value }) };
}
}
namespace WTF {
template<> struct DefaultHash<WebCore::Color>;
template<> struct HashTraits<WebCore::Color>;
}