blob: b496b155c5d03b01297a0658067dccbced53da32 [file] [log] [blame]
/*
* Copyright (C) 2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <array>
#include <limits>
namespace WebCore {
enum class RGBBoundedness { Bounded, Extended };
template<typename, RGBBoundedness> struct RGBModel;
template<typename> struct AlphaTraits;
template<typename> struct ColorComponentInfo;
template<typename> struct HSLModel;
template<typename> struct HWBModel;
template<typename> struct LCHModel;
template<typename> struct LabModel;
template<typename> struct XYZModel;
// MARK: Resolved/Unresolved Defintitions
template<typename ColorType, typename ColorModel = typename ColorType::Model> struct ExposedColorType;
template<typename ColorType> struct ResolvedColorType;
template<typename ColorType> struct UnresolvedColorType;
template<typename ColorType> constexpr ResolvedColorType<ColorType> resolvedColor(ColorType input)
{
return ResolvedColorType<ColorType> { input };
}
template<typename ColorType> constexpr UnresolvedColorType<ColorType> unresolvedColor(ColorType input)
{
return UnresolvedColorType<ColorType> { input };
}
inline constexpr ColorComponents<uint8_t, 4> resolveColorComponents(const ColorComponents<uint8_t, 4>& colorComponents)
{
return colorComponents;
}
inline constexpr ColorComponents<float, 4> resolveColorComponents(const ColorComponents<float, 4>& colorComponents)
{
return colorComponents.map([] (float component) { return std::isnan(component) ? 0.0f : component; });
}
template<typename ColorType> struct ResolvedColorType : ExposedColorType<ColorType, typename ColorType::Model> {
using CanonicalType = ColorType;
// Calling resolved() or unresolved() on a type that is already resolved is a no-op, so we can
// just return ourselves.
constexpr auto resolved() const { return *this; }
constexpr auto unresolved() const { return *this; }
private:
template<typename C> friend constexpr ResolvedColorType<C> resolvedColor(C);
explicit constexpr ResolvedColorType(ColorType color)
: ExposedColorType<ColorType, typename ColorType::Model> { resolve(color) }
{
}
template<typename C, typename std::enable_if_t<std::is_same_v<typename C::ComponentType, float>>* = nullptr>
static constexpr C resolve(C color)
{
auto [c1, c2, c3, alpha] = resolveColorComponents(asColorComponents(ExposedColorType<C, typename C::Model> { color }));
return ColorType { c1, c2, c3, alpha };
}
template<typename C, typename std::enable_if_t<std::is_same_v<typename C::ComponentType, uint8_t>>* = nullptr>
static constexpr C resolve(C color)
{
return color;
}
};
template<typename ColorType> struct UnresolvedColorType : ExposedColorType<ColorType, typename ColorType::Model> {
using CanonicalType = ColorType;
// Calling unresolved() on a type that is already unresolved is a no-op, so we can
// just return ourselves.
constexpr auto unresolved() const { return *this; }
private:
template<typename C> friend constexpr UnresolvedColorType<C> unresolvedColor(C);
explicit constexpr UnresolvedColorType(ColorType color)
: ExposedColorType<ColorType, typename ColorType::Model> { color }
{
}
};
// MARK: - Color Model support types.
template<> struct AlphaTraits<float> {
static constexpr float transparent = 0.0f;
static constexpr float opaque = 1.0f;
};
template<> struct AlphaTraits<uint8_t> {
static constexpr uint8_t transparent = 0;
static constexpr uint8_t opaque = 255;
};
enum class ColorComponentType {
Angle,
Number,
Percentage
};
enum class ColorSpaceCoordinateSystem {
RectangularOrthogonal,
CylindricalPolar
};
template<typename T> struct ColorComponentInfo {
T min;
T max;
ColorComponentType type;
};
// MARK: - Color Model Definititions
// MARK: HSLModel
template<> struct HSLModel<float> {
static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
{ 0, 360, ColorComponentType::Angle },
{ 0, 100, ColorComponentType::Percentage },
{ 0, 100, ColorComponentType::Percentage }
} };
static constexpr bool isInvertible = false;
static constexpr auto coordinateSystem = ColorSpaceCoordinateSystem::CylindricalPolar;
};
template<typename ColorType> struct ExposedColorType<ColorType, HSLModel<typename ColorType::ComponentType>> : ColorType {
using ColorType::hue;
using ColorType::saturation;
using ColorType::lightness;
using ColorType::alpha;
};
template<typename T, typename ColorType> constexpr ColorComponents<T, 4> asColorComponents(const ExposedColorType<ColorType, HSLModel<T>>& c)
{
return { c.hue, c.saturation, c.lightness, c.alpha };
}
template<typename ColorType> inline constexpr bool UsesHSLModel = std::is_same_v<typename ColorType::Model, HSLModel<typename ColorType::ComponentType>>;
// MARK: HWBModel
template<> struct HWBModel<float> {
static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
{ 0, 360, ColorComponentType::Angle },
{ 0, 100, ColorComponentType::Percentage },
{ 0, 100, ColorComponentType::Percentage }
} };
static constexpr bool isInvertible = false;
static constexpr auto coordinateSystem = ColorSpaceCoordinateSystem::CylindricalPolar;
};
template<typename ColorType> struct ExposedColorType<ColorType, HWBModel<typename ColorType::ComponentType>> : ColorType {
using ColorType::hue;
using ColorType::whiteness;
using ColorType::blackness;
using ColorType::alpha;
};
template<typename T, typename ColorType> constexpr ColorComponents<T, 4> asColorComponents(const ExposedColorType<ColorType, HWBModel<T>>& c)
{
return { c.hue, c.whiteness, c.blackness, c.alpha };
}
template<typename ColorType> inline constexpr bool UsesHWBModel = std::is_same_v<typename ColorType::Model, HWBModel<typename ColorType::ComponentType>>;
// MARK: LabModel
template<> struct LabModel<float> {
static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
{ 0, std::numeric_limits<float>::infinity(), ColorComponentType::Number },
{ -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), ColorComponentType::Number },
{ -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), ColorComponentType::Number }
} };
static constexpr bool isInvertible = false;
static constexpr auto coordinateSystem = ColorSpaceCoordinateSystem::RectangularOrthogonal;
};
template<typename ColorType> struct ExposedColorType<ColorType, LabModel<typename ColorType::ComponentType>> : ColorType {
using ColorType::lightness;
using ColorType::a;
using ColorType::b;
using ColorType::alpha;
};
template<typename T, typename ColorType> constexpr ColorComponents<T, 4> asColorComponents(const ExposedColorType<ColorType, LabModel<T>>& c)
{
return { c.lightness, c.a, c.b, c.alpha };
}
template<typename ColorType> inline constexpr bool UsesLabModel = std::is_same_v<typename ColorType::Model, LabModel<typename ColorType::ComponentType>>;
// MARK: LCHModel
template<> struct LCHModel<float> {
static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
{ 0, std::numeric_limits<float>::infinity(), ColorComponentType::Number },
{ 0, std::numeric_limits<float>::infinity(), ColorComponentType::Number },
{ 0, 360, ColorComponentType::Angle }
} };
static constexpr bool isInvertible = false;
static constexpr auto coordinateSystem = ColorSpaceCoordinateSystem::CylindricalPolar;
};
template<typename ColorType> struct ExposedColorType<ColorType, LCHModel<typename ColorType::ComponentType>> : ColorType {
using ColorType::lightness;
using ColorType::chroma;
using ColorType::hue;
using ColorType::alpha;
};
template<typename T, typename ColorType> constexpr ColorComponents<T, 4> asColorComponents(const ExposedColorType<ColorType, LCHModel<T>>& c)
{
return { c.lightness, c.chroma, c.hue, c.alpha };
}
template<typename ColorType> inline constexpr bool UsesLCHModel = std::is_same_v<typename ColorType::Model, LCHModel<typename ColorType::ComponentType>>;
// MARK: RGBModel
template<> struct RGBModel<float, RGBBoundedness::Bounded> {
static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
{ 0, 1, ColorComponentType::Number },
{ 0, 1, ColorComponentType::Number },
{ 0, 1, ColorComponentType::Number }
} };
static constexpr bool isInvertible = true;
static constexpr auto coordinateSystem = ColorSpaceCoordinateSystem::RectangularOrthogonal;
};
template<> struct RGBModel<uint8_t, RGBBoundedness::Bounded> {
static constexpr std::array<ColorComponentInfo<uint8_t>, 3> componentInfo { {
{ 0, 255, ColorComponentType::Number },
{ 0, 255, ColorComponentType::Number },
{ 0, 255, ColorComponentType::Number }
} };
static constexpr bool isInvertible = true;
static constexpr auto coordinateSystem = ColorSpaceCoordinateSystem::RectangularOrthogonal;
};
template<> struct RGBModel<float, RGBBoundedness::Extended> {
static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
{ -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), ColorComponentType::Number },
{ -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), ColorComponentType::Number },
{ -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), ColorComponentType::Number }
} };
static constexpr bool isInvertible = false;
static constexpr auto coordinateSystem = ColorSpaceCoordinateSystem::RectangularOrthogonal;
};
template<typename ColorType, RGBBoundedness boundedness> struct ExposedColorType<ColorType, RGBModel<typename ColorType::ComponentType, boundedness>> : ColorType {
using ColorType::red;
using ColorType::green;
using ColorType::blue;
using ColorType::alpha;
};
template<typename T, typename ColorType, RGBBoundedness boundedness> constexpr ColorComponents<T, 4> asColorComponents(const ExposedColorType<ColorType, RGBModel<T, boundedness>>& c)
{
return { c.red, c.green, c.blue, c.alpha };
}
template<typename ColorType> inline constexpr bool UsesRGBModel =
std::is_same_v<typename ColorType::Model, RGBModel<typename ColorType::ComponentType, RGBBoundedness::Bounded>>
|| std::is_same_v<typename ColorType::Model, RGBModel<typename ColorType::ComponentType, RGBBoundedness::Extended>>;
// MARK: XYZModel
template<> struct XYZModel<float> {
static constexpr std::array<ColorComponentInfo<float>, 3> componentInfo { {
{ -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), ColorComponentType::Number },
{ -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), ColorComponentType::Number },
{ -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), ColorComponentType::Number }
} };
static constexpr bool isInvertible = false;
static constexpr auto coordinateSystem = ColorSpaceCoordinateSystem::RectangularOrthogonal;
};
template<typename ColorType> struct ExposedColorType<ColorType, XYZModel<typename ColorType::ComponentType>> : ColorType {
using ColorType::x;
using ColorType::y;
using ColorType::z;
using ColorType::alpha;
};
template<typename T, typename ColorType> constexpr ColorComponents<T, 4> asColorComponents(const ExposedColorType<ColorType, XYZModel<T>>& c)
{
return { c.x, c.y, c.z, c.alpha };
}
template<typename ColorType> inline constexpr bool UsesXYZModel = std::is_same_v<typename ColorType::Model, XYZModel<typename ColorType::ComponentType>>;
// get<> overload (along with std::tuple_size and std::tuple_element below) to support destructuring of explicitly resolved and unresolved colors.
template<size_t I, typename ColorType> constexpr typename ColorType::ComponentType get(const ExposedColorType<ColorType>& color)
{
return asColorComponents(color)[I];
}
}
namespace std {
template<typename ColorType>
class tuple_size<WebCore::ResolvedColorType<ColorType>> : public std::integral_constant<size_t, 4> { };
template<size_t I, typename ColorType>
class tuple_element<I, WebCore::ResolvedColorType<ColorType>> {
public:
using type = typename WebCore::ResolvedColorType<ColorType>::ComponentType;
};
template<typename ColorType>
class tuple_size<WebCore::UnresolvedColorType<ColorType>> : public std::integral_constant<size_t, 4> { };
template<size_t I, typename ColorType>
class tuple_element<I, WebCore::UnresolvedColorType<ColorType>> {
public:
using type = typename WebCore::UnresolvedColorType<ColorType>::ComponentType;
};
}