Loading include/ui/ColorSpace.h +109 −34 Original line number Original line Diff line number Diff line Loading @@ -35,6 +35,16 @@ public: typedef std::function<float(float)> transfer_function; typedef std::function<float(float)> transfer_function; typedef std::function<float(float)> clamping_function; typedef std::function<float(float)> clamping_function; struct TransferParameters { float g = 0.0f; float a = 0.0f; float b = 0.0f; float c = 0.0f; float d = 0.0f; float e = 0.0f; float f = 0.0f; }; /** /** * Creates a named color space with the specified RGB->XYZ * Creates a named color space with the specified RGB->XYZ * conversion matrix. The white point and primaries will be * conversion matrix. The white point and primaries will be Loading @@ -47,8 +57,39 @@ public: ColorSpace( ColorSpace( const std::string& name, const std::string& name, const mat3& rgbToXYZ, const mat3& rgbToXYZ, transfer_function OETF = linearReponse, transfer_function OETF = linearResponse, transfer_function EOTF = linearReponse, transfer_function EOTF = linearResponse, clamping_function clamper = saturate<float> ) noexcept; /** * Creates a named color space with the specified RGB->XYZ * conversion matrix. The white point and primaries will be * computed from the supplied matrix. * * The transfer functions are defined by the set of supplied * transfer parameters. The default clamping function is a * simple saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const mat3& rgbToXYZ, const TransferParameters parameters, clamping_function clamper = saturate<float> ) noexcept; /** * Creates a named color space with the specified RGB->XYZ * conversion matrix. The white point and primaries will be * computed from the supplied matrix. * * The transfer functions are defined by a simple gamma value. * The default clamping function is a saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const mat3& rgbToXYZ, float gamma, clamping_function clamper = saturate<float> clamping_function clamper = saturate<float> ) noexcept; ) noexcept; Loading @@ -65,8 +106,41 @@ public: const std::string& name, const std::string& name, const std::array<float2, 3>& primaries, const std::array<float2, 3>& primaries, const float2& whitePoint, const float2& whitePoint, transfer_function OETF = linearReponse, transfer_function OETF = linearResponse, transfer_function EOTF = linearReponse, transfer_function EOTF = linearResponse, clamping_function clamper = saturate<float> ) noexcept; /** * Creates a named color space with the specified primaries * and white point. The RGB<>XYZ conversion matrices are * computed from the primaries and white point. * * The transfer functions are defined by the set of supplied * transfer parameters. The default clamping function is a * simple saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const std::array<float2, 3>& primaries, const float2& whitePoint, const TransferParameters parameters, clamping_function clamper = saturate<float> ) noexcept; /** * Creates a named color space with the specified primaries * and white point. The RGB<>XYZ conversion matrices are * computed from the primaries and white point. * * The transfer functions are defined by a single gamma value. * The default clamping function is a saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const std::array<float2, 3>& primaries, const float2& whitePoint, float gamma, clamping_function clamper = saturate<float> clamping_function clamper = saturate<float> ) noexcept; ) noexcept; Loading Loading @@ -138,6 +212,10 @@ public: return mWhitePoint; return mWhitePoint; } } constexpr const TransferParameters& getTransferParameters() const noexcept { return mParameters; } /** /** * Converts the supplied XYZ value to xyY. * Converts the supplied XYZ value to xyY. */ */ Loading Loading @@ -166,35 +244,6 @@ public: static const ColorSpace ACES(); static const ColorSpace ACES(); static const ColorSpace ACEScg(); static const ColorSpace ACEScg(); class Connector { public: Connector(const ColorSpace& src, const ColorSpace& dst) noexcept; constexpr const ColorSpace& getSource() const noexcept { return mSource; } constexpr const ColorSpace& getDestination() const noexcept { return mDestination; } constexpr const mat3& getTransform() const noexcept { return mTransform; } constexpr float3 transform(const float3& v) const noexcept { float3 linear = mSource.toLinear(apply(v, mSource.getClamper())); return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper()); } constexpr float3 transformLinear(const float3& v) const noexcept { float3 linear = apply(v, mSource.getClamper()); return apply(mTransform * linear, mDestination.getClamper()); } private: const ColorSpace& mSource; const ColorSpace& mDestination; mat3 mTransform; }; static const Connector connect(const ColorSpace& src, const ColorSpace& dst) { return Connector(src, dst); } // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256) // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256) // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B // The generated 3D LUT is meant to be used as a 3D texture and its Y // The generated 3D LUT is meant to be used as a 3D texture and its Y Loading @@ -208,7 +257,7 @@ private: static constexpr mat3 computeXYZMatrix( static constexpr mat3 computeXYZMatrix( const std::array<float2, 3>& primaries, const float2& whitePoint); const std::array<float2, 3>& primaries, const float2& whitePoint); static constexpr float linearReponse(float v) { static constexpr float linearResponse(float v) { return v; return v; } } Loading @@ -217,6 +266,7 @@ private: mat3 mRGBtoXYZ; mat3 mRGBtoXYZ; mat3 mXYZtoRGB; mat3 mXYZtoRGB; TransferParameters mParameters; transfer_function mOETF; transfer_function mOETF; transfer_function mEOTF; transfer_function mEOTF; clamping_function mClamper; clamping_function mClamper; Loading @@ -225,6 +275,31 @@ private: float2 mWhitePoint; float2 mWhitePoint; }; }; class ColorSpaceConnector { public: ColorSpaceConnector(const ColorSpace& src, const ColorSpace& dst) noexcept; constexpr const ColorSpace& getSource() const noexcept { return mSource; } constexpr const ColorSpace& getDestination() const noexcept { return mDestination; } constexpr const mat3& getTransform() const noexcept { return mTransform; } constexpr float3 transform(const float3& v) const noexcept { float3 linear = mSource.toLinear(apply(v, mSource.getClamper())); return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper()); } constexpr float3 transformLinear(const float3& v) const noexcept { float3 linear = apply(v, mSource.getClamper()); return apply(mTransform * linear, mDestination.getClamper()); } private: ColorSpace mSource; ColorSpace mDestination; mat3 mTransform; }; }; // namespace android }; // namespace android #endif // ANDROID_UI_COLOR_SPACE #endif // ANDROID_UI_COLOR_SPACE libs/ui/ColorSpace.cpp +180 −78 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,83 @@ using namespace std::placeholders; namespace android { namespace android { static constexpr float linearResponse(float v) { return v; } static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) { return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c; } static constexpr float response(float x, const ColorSpace::TransferParameters& p) { return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x; } static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) { return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c; } static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) { return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f; } static float absRcpResponse(float x, float g,float a, float b, float c, float d) { float xx = std::abs(x); return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x); } static float absResponse(float x, float g, float a, float b, float c, float d) { float xx = std::abs(x); return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x); } static float safePow(float x, float e) { return powf(x < 0.0f ? 0.0f : x, e); } static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) { if (parameters.e == 0.0f && parameters.f == 0.0f) { return std::bind(rcpResponse, _1, parameters); } return std::bind(rcpFullResponse, _1, parameters); } static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) { if (parameters.e == 0.0f && parameters.f == 0.0f) { return std::bind(response, _1, parameters); } return std::bind(fullResponse, _1, parameters); } static ColorSpace::transfer_function toOETF(float gamma) { if (gamma == 1.0f) { return linearResponse; } return std::bind(safePow, _1, 1.0f / gamma); } static ColorSpace::transfer_function toEOTF(float gamma) { if (gamma == 1.0f) { return linearResponse; } return std::bind(safePow, _1, gamma); } static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) { float3 r(rgbToXYZ * float3{1, 0, 0}); float3 g(rgbToXYZ * float3{0, 1, 0}); float3 b(rgbToXYZ * float3{0, 0, 1}); return {{r.xy / dot(r, float3{1}), g.xy / dot(g, float3{1}), b.xy / dot(b, float3{1})}}; } static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) { float3 w(rgbToXYZ * float3{1}); return w.xy / dot(w, float3{1}); } ColorSpace::ColorSpace( ColorSpace::ColorSpace( const std::string& name, const std::string& name, const mat3& rgbToXYZ, const mat3& rgbToXYZ, Loading @@ -31,18 +108,41 @@ ColorSpace::ColorSpace( , mXYZtoRGB(inverse(rgbToXYZ)) , mXYZtoRGB(inverse(rgbToXYZ)) , mOETF(std::move(OETF)) , mOETF(std::move(OETF)) , mEOTF(std::move(EOTF)) , mEOTF(std::move(EOTF)) , mClamper(std::move(clamper)) { , mClamper(std::move(clamper)) , mPrimaries(computePrimaries(rgbToXYZ)) float3 r(rgbToXYZ * float3{1, 0, 0}); , mWhitePoint(computeWhitePoint(rgbToXYZ)) { float3 g(rgbToXYZ * float3{0, 1, 0}); } float3 b(rgbToXYZ * float3{0, 0, 1}); mPrimaries[0] = r.xy / dot(r, float3{1}); ColorSpace::ColorSpace( mPrimaries[1] = g.xy / dot(g, float3{1}); const std::string& name, mPrimaries[2] = b.xy / dot(b, float3{1}); const mat3& rgbToXYZ, const TransferParameters parameters, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(rgbToXYZ) , mXYZtoRGB(inverse(rgbToXYZ)) , mParameters(parameters) , mOETF(toOETF(mParameters)) , mEOTF(toEOTF(mParameters)) , mClamper(std::move(clamper)) , mPrimaries(computePrimaries(rgbToXYZ)) , mWhitePoint(computeWhitePoint(rgbToXYZ)) { } float3 w(rgbToXYZ * float3{1}); ColorSpace::ColorSpace( mWhitePoint = w.xy / dot(w, float3{1}); const std::string& name, const mat3& rgbToXYZ, float gamma, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(rgbToXYZ) , mXYZtoRGB(inverse(rgbToXYZ)) , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) , mOETF(toOETF(gamma)) , mEOTF(toEOTF(gamma)) , mClamper(std::move(clamper)) , mPrimaries(computePrimaries(rgbToXYZ)) , mWhitePoint(computeWhitePoint(rgbToXYZ)) { } } ColorSpace::ColorSpace( ColorSpace::ColorSpace( Loading @@ -62,6 +162,40 @@ ColorSpace::ColorSpace( , mWhitePoint(whitePoint) { , mWhitePoint(whitePoint) { } } ColorSpace::ColorSpace( const std::string& name, const std::array<float2, 3>& primaries, const float2& whitePoint, const TransferParameters parameters, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) , mXYZtoRGB(inverse(mRGBtoXYZ)) , mParameters(parameters) , mOETF(toOETF(mParameters)) , mEOTF(toEOTF(mParameters)) , mClamper(std::move(clamper)) , mPrimaries(primaries) , mWhitePoint(whitePoint) { } ColorSpace::ColorSpace( const std::string& name, const std::array<float2, 3>& primaries, const float2& whitePoint, float gamma, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) , mXYZtoRGB(inverse(mRGBtoXYZ)) , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) , mOETF(toOETF(gamma)) , mEOTF(toEOTF(gamma)) , mClamper(std::move(clamper)) , mPrimaries(primaries) , mWhitePoint(whitePoint) { } constexpr mat3 ColorSpace::computeXYZMatrix( constexpr mat3 ColorSpace::computeXYZMatrix( const std::array<float2, 3>& primaries, const float2& whitePoint) { const std::array<float2, 3>& primaries, const float2& whitePoint) { const float2& R = primaries[0]; const float2& R = primaries[0]; Loading Loading @@ -96,33 +230,12 @@ constexpr mat3 ColorSpace::computeXYZMatrix( }; }; } } static constexpr float rcpResponse(float x, float g,float a, float b, float c, float d) { return x >= d * c ? (std::pow(x, 1.0f / g) - b) / a : x / c; } static constexpr float response(float x, float g, float a, float b, float c, float d) { return x >= d ? std::pow(a * x + b, g) : c * x; } static float absRcpResponse(float x, float g,float a, float b, float c, float d) { return std::copysign(rcpResponse(std::abs(x), g, a, b, c, d), x); } static float absResponse(float x, float g, float a, float b, float c, float d) { return std::copysign(response(std::abs(x), g, a, b, c, d), x); } static float safePow(float x, float e) { return powf(x < 0.0f ? 0.0f : x, e); } const ColorSpace ColorSpace::sRGB() { const ColorSpace ColorSpace::sRGB() { return { return { "sRGB IEC61966-2.1", "sRGB IEC61966-2.1", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f} std::bind(response, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f) }; }; } } Loading Loading @@ -150,8 +263,7 @@ const ColorSpace ColorSpace::linearExtendedSRGB() { "scRGB IEC 61966-2-2:2003", "scRGB IEC 61966-2-2:2003", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, linearReponse, 1.0f, linearReponse, std::bind(clamp<float>, _1, -0.5f, 7.499f) std::bind(clamp<float>, _1, -0.5f, 7.499f) }; }; } } Loading @@ -161,8 +273,7 @@ const ColorSpace ColorSpace::NTSC() { "NTSC (1953)", "NTSC (1953)", {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}}, {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}}, {0.310f, 0.316f}, {0.310f, 0.316f}, std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f), {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} std::bind(response, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f) }; }; } } Loading @@ -171,8 +282,7 @@ const ColorSpace ColorSpace::BT709() { "Rec. ITU-R BT.709-5", "Rec. ITU-R BT.709-5", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f), {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} std::bind(response, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f) }; }; } } Loading @@ -181,8 +291,7 @@ const ColorSpace ColorSpace::BT2020() { "Rec. ITU-R BT.2020-1", "Rec. ITU-R BT.2020-1", {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}}, {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f), {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} std::bind(response, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f) }; }; } } Loading @@ -191,8 +300,7 @@ const ColorSpace ColorSpace::AdobeRGB() { "Adobe RGB (1998)", "Adobe RGB (1998)", {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}}, {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(safePow, _1, 1.0f / 2.2f), 2.2f std::bind(safePow, _1, 2.2f) }; }; } } Loading @@ -201,8 +309,7 @@ const ColorSpace ColorSpace::ProPhotoRGB() { "ROMM RGB ISO 22028-2:2013", "ROMM RGB ISO 22028-2:2013", {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}}, {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}}, {0.34567f, 0.35850f}, {0.34567f, 0.35850f}, std::bind(rcpResponse, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f), {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f} std::bind(response, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f) }; }; } } Loading @@ -211,8 +318,7 @@ const ColorSpace ColorSpace::DisplayP3() { "Display P3", "Display P3", {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f), {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f} std::bind(response, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f) }; }; } } Loading @@ -221,8 +327,7 @@ const ColorSpace ColorSpace::DCIP3() { "SMPTE RP 431-2-2007 DCI (P3)", "SMPTE RP 431-2-2007 DCI (P3)", {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {0.314f, 0.351f}, {0.314f, 0.351f}, std::bind(safePow, _1, 1.0f / 2.6f), 2.6f std::bind(safePow, _1, 2.6f) }; }; } } Loading @@ -231,8 +336,7 @@ const ColorSpace ColorSpace::ACES() { "SMPTE ST 2065-1:2012 ACES", "SMPTE ST 2065-1:2012 ACES", {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}}, {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}}, {0.32168f, 0.33767f}, {0.32168f, 0.33767f}, linearReponse, 1.0f, linearReponse, std::bind(clamp<float>, _1, -65504.0f, 65504.0f) std::bind(clamp<float>, _1, -65504.0f, 65504.0f) }; }; } } Loading @@ -242,12 +346,33 @@ const ColorSpace ColorSpace::ACEScg() { "Academy S-2014-004 ACEScg", "Academy S-2014-004 ACEScg", {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}}, {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}}, {0.32168f, 0.33767f}, {0.32168f, 0.33767f}, linearReponse, 1.0f, linearReponse, std::bind(clamp<float>, _1, -65504.0f, 65504.0f) std::bind(clamp<float>, _1, -65504.0f, 65504.0f) }; }; } } std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst) { size = clamp(size, 2u, 256u); float m = 1.0f / float(size - 1); std::unique_ptr<float3> lut(new float3[size * size * size]); float3* data = lut.get(); ColorSpaceConnector connector(src, dst); for (uint32_t z = 0; z < size; z++) { for (int32_t y = int32_t(size - 1); y >= 0; y--) { for (uint32_t x = 0; x < size; x++) { *data++ = connector.transform({x * m, y * m, z * m}); } } } return lut; } static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; static const mat3 BRADFORD = mat3{ static const mat3 BRADFORD = mat3{ Loading @@ -262,7 +387,7 @@ static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const fl return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; } } ColorSpace::Connector::Connector( ColorSpaceConnector::ColorSpaceConnector( const ColorSpace& src, const ColorSpace& src, const ColorSpace& dst) noexcept const ColorSpace& dst) noexcept : mSource(src) : mSource(src) Loading @@ -274,8 +399,8 @@ ColorSpace::Connector::Connector( mat3 rgbToXYZ(src.getRGBtoXYZ()); mat3 rgbToXYZ(src.getRGBtoXYZ()); mat3 xyzToRGB(dst.getXYZtoRGB()); mat3 xyzToRGB(dst.getXYZtoRGB()); float3 srcXYZ = XYZ(float3{src.getWhitePoint(), 1}); float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1}); float3 dstXYZ = XYZ(float3{dst.getWhitePoint(), 1}); float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1}); if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); Loading @@ -289,27 +414,4 @@ ColorSpace::Connector::Connector( } } } } std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst) { size = clamp(size, 2u, 256u); float m = 1.0f / float(size - 1); std::unique_ptr<float3> lut(new float3[size * size * size]); float3* data = lut.get(); Connector connector(src, dst); for (uint32_t z = 0; z < size; z++) { for (int32_t y = int32_t(size - 1); y >= 0; y--) { for (uint32_t x = 0; x < size; x++) { *data++ = connector.transform({x * m, y * m, z * m}); } } } return lut; } }; // namespace android }; // namespace android libs/ui/tests/colorspace_test.cpp +2 −2 Original line number Original line Diff line number Diff line Loading @@ -152,12 +152,12 @@ TEST_F(ColorSpaceTest, Clamping) { TEST_F(ColorSpaceTest, Connect) { TEST_F(ColorSpaceTest, Connect) { // No chromatic adaptation // No chromatic adaptation auto r = ColorSpace::connect(ColorSpace::sRGB(), ColorSpace::AdobeRGB()) auto r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::AdobeRGB()) .transform({1.0f, 0.5f, 0.0f}); .transform({1.0f, 0.5f, 0.0f}); EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); // Test with chromatic adaptation // Test with chromatic adaptation r = ColorSpace::connect(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB()) r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB()) .transform({1.0f, 0.0f, 0.0f}); .transform({1.0f, 0.0f, 0.0f}); EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f}))); EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f}))); } } Loading Loading
include/ui/ColorSpace.h +109 −34 Original line number Original line Diff line number Diff line Loading @@ -35,6 +35,16 @@ public: typedef std::function<float(float)> transfer_function; typedef std::function<float(float)> transfer_function; typedef std::function<float(float)> clamping_function; typedef std::function<float(float)> clamping_function; struct TransferParameters { float g = 0.0f; float a = 0.0f; float b = 0.0f; float c = 0.0f; float d = 0.0f; float e = 0.0f; float f = 0.0f; }; /** /** * Creates a named color space with the specified RGB->XYZ * Creates a named color space with the specified RGB->XYZ * conversion matrix. The white point and primaries will be * conversion matrix. The white point and primaries will be Loading @@ -47,8 +57,39 @@ public: ColorSpace( ColorSpace( const std::string& name, const std::string& name, const mat3& rgbToXYZ, const mat3& rgbToXYZ, transfer_function OETF = linearReponse, transfer_function OETF = linearResponse, transfer_function EOTF = linearReponse, transfer_function EOTF = linearResponse, clamping_function clamper = saturate<float> ) noexcept; /** * Creates a named color space with the specified RGB->XYZ * conversion matrix. The white point and primaries will be * computed from the supplied matrix. * * The transfer functions are defined by the set of supplied * transfer parameters. The default clamping function is a * simple saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const mat3& rgbToXYZ, const TransferParameters parameters, clamping_function clamper = saturate<float> ) noexcept; /** * Creates a named color space with the specified RGB->XYZ * conversion matrix. The white point and primaries will be * computed from the supplied matrix. * * The transfer functions are defined by a simple gamma value. * The default clamping function is a saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const mat3& rgbToXYZ, float gamma, clamping_function clamper = saturate<float> clamping_function clamper = saturate<float> ) noexcept; ) noexcept; Loading @@ -65,8 +106,41 @@ public: const std::string& name, const std::string& name, const std::array<float2, 3>& primaries, const std::array<float2, 3>& primaries, const float2& whitePoint, const float2& whitePoint, transfer_function OETF = linearReponse, transfer_function OETF = linearResponse, transfer_function EOTF = linearReponse, transfer_function EOTF = linearResponse, clamping_function clamper = saturate<float> ) noexcept; /** * Creates a named color space with the specified primaries * and white point. The RGB<>XYZ conversion matrices are * computed from the primaries and white point. * * The transfer functions are defined by the set of supplied * transfer parameters. The default clamping function is a * simple saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const std::array<float2, 3>& primaries, const float2& whitePoint, const TransferParameters parameters, clamping_function clamper = saturate<float> ) noexcept; /** * Creates a named color space with the specified primaries * and white point. The RGB<>XYZ conversion matrices are * computed from the primaries and white point. * * The transfer functions are defined by a single gamma value. * The default clamping function is a saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const std::array<float2, 3>& primaries, const float2& whitePoint, float gamma, clamping_function clamper = saturate<float> clamping_function clamper = saturate<float> ) noexcept; ) noexcept; Loading Loading @@ -138,6 +212,10 @@ public: return mWhitePoint; return mWhitePoint; } } constexpr const TransferParameters& getTransferParameters() const noexcept { return mParameters; } /** /** * Converts the supplied XYZ value to xyY. * Converts the supplied XYZ value to xyY. */ */ Loading Loading @@ -166,35 +244,6 @@ public: static const ColorSpace ACES(); static const ColorSpace ACES(); static const ColorSpace ACEScg(); static const ColorSpace ACEScg(); class Connector { public: Connector(const ColorSpace& src, const ColorSpace& dst) noexcept; constexpr const ColorSpace& getSource() const noexcept { return mSource; } constexpr const ColorSpace& getDestination() const noexcept { return mDestination; } constexpr const mat3& getTransform() const noexcept { return mTransform; } constexpr float3 transform(const float3& v) const noexcept { float3 linear = mSource.toLinear(apply(v, mSource.getClamper())); return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper()); } constexpr float3 transformLinear(const float3& v) const noexcept { float3 linear = apply(v, mSource.getClamper()); return apply(mTransform * linear, mDestination.getClamper()); } private: const ColorSpace& mSource; const ColorSpace& mDestination; mat3 mTransform; }; static const Connector connect(const ColorSpace& src, const ColorSpace& dst) { return Connector(src, dst); } // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256) // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256) // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B // The generated 3D LUT is meant to be used as a 3D texture and its Y // The generated 3D LUT is meant to be used as a 3D texture and its Y Loading @@ -208,7 +257,7 @@ private: static constexpr mat3 computeXYZMatrix( static constexpr mat3 computeXYZMatrix( const std::array<float2, 3>& primaries, const float2& whitePoint); const std::array<float2, 3>& primaries, const float2& whitePoint); static constexpr float linearReponse(float v) { static constexpr float linearResponse(float v) { return v; return v; } } Loading @@ -217,6 +266,7 @@ private: mat3 mRGBtoXYZ; mat3 mRGBtoXYZ; mat3 mXYZtoRGB; mat3 mXYZtoRGB; TransferParameters mParameters; transfer_function mOETF; transfer_function mOETF; transfer_function mEOTF; transfer_function mEOTF; clamping_function mClamper; clamping_function mClamper; Loading @@ -225,6 +275,31 @@ private: float2 mWhitePoint; float2 mWhitePoint; }; }; class ColorSpaceConnector { public: ColorSpaceConnector(const ColorSpace& src, const ColorSpace& dst) noexcept; constexpr const ColorSpace& getSource() const noexcept { return mSource; } constexpr const ColorSpace& getDestination() const noexcept { return mDestination; } constexpr const mat3& getTransform() const noexcept { return mTransform; } constexpr float3 transform(const float3& v) const noexcept { float3 linear = mSource.toLinear(apply(v, mSource.getClamper())); return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper()); } constexpr float3 transformLinear(const float3& v) const noexcept { float3 linear = apply(v, mSource.getClamper()); return apply(mTransform * linear, mDestination.getClamper()); } private: ColorSpace mSource; ColorSpace mDestination; mat3 mTransform; }; }; // namespace android }; // namespace android #endif // ANDROID_UI_COLOR_SPACE #endif // ANDROID_UI_COLOR_SPACE
libs/ui/ColorSpace.cpp +180 −78 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,83 @@ using namespace std::placeholders; namespace android { namespace android { static constexpr float linearResponse(float v) { return v; } static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) { return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c; } static constexpr float response(float x, const ColorSpace::TransferParameters& p) { return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x; } static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) { return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c; } static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) { return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f; } static float absRcpResponse(float x, float g,float a, float b, float c, float d) { float xx = std::abs(x); return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x); } static float absResponse(float x, float g, float a, float b, float c, float d) { float xx = std::abs(x); return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x); } static float safePow(float x, float e) { return powf(x < 0.0f ? 0.0f : x, e); } static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) { if (parameters.e == 0.0f && parameters.f == 0.0f) { return std::bind(rcpResponse, _1, parameters); } return std::bind(rcpFullResponse, _1, parameters); } static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) { if (parameters.e == 0.0f && parameters.f == 0.0f) { return std::bind(response, _1, parameters); } return std::bind(fullResponse, _1, parameters); } static ColorSpace::transfer_function toOETF(float gamma) { if (gamma == 1.0f) { return linearResponse; } return std::bind(safePow, _1, 1.0f / gamma); } static ColorSpace::transfer_function toEOTF(float gamma) { if (gamma == 1.0f) { return linearResponse; } return std::bind(safePow, _1, gamma); } static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) { float3 r(rgbToXYZ * float3{1, 0, 0}); float3 g(rgbToXYZ * float3{0, 1, 0}); float3 b(rgbToXYZ * float3{0, 0, 1}); return {{r.xy / dot(r, float3{1}), g.xy / dot(g, float3{1}), b.xy / dot(b, float3{1})}}; } static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) { float3 w(rgbToXYZ * float3{1}); return w.xy / dot(w, float3{1}); } ColorSpace::ColorSpace( ColorSpace::ColorSpace( const std::string& name, const std::string& name, const mat3& rgbToXYZ, const mat3& rgbToXYZ, Loading @@ -31,18 +108,41 @@ ColorSpace::ColorSpace( , mXYZtoRGB(inverse(rgbToXYZ)) , mXYZtoRGB(inverse(rgbToXYZ)) , mOETF(std::move(OETF)) , mOETF(std::move(OETF)) , mEOTF(std::move(EOTF)) , mEOTF(std::move(EOTF)) , mClamper(std::move(clamper)) { , mClamper(std::move(clamper)) , mPrimaries(computePrimaries(rgbToXYZ)) float3 r(rgbToXYZ * float3{1, 0, 0}); , mWhitePoint(computeWhitePoint(rgbToXYZ)) { float3 g(rgbToXYZ * float3{0, 1, 0}); } float3 b(rgbToXYZ * float3{0, 0, 1}); mPrimaries[0] = r.xy / dot(r, float3{1}); ColorSpace::ColorSpace( mPrimaries[1] = g.xy / dot(g, float3{1}); const std::string& name, mPrimaries[2] = b.xy / dot(b, float3{1}); const mat3& rgbToXYZ, const TransferParameters parameters, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(rgbToXYZ) , mXYZtoRGB(inverse(rgbToXYZ)) , mParameters(parameters) , mOETF(toOETF(mParameters)) , mEOTF(toEOTF(mParameters)) , mClamper(std::move(clamper)) , mPrimaries(computePrimaries(rgbToXYZ)) , mWhitePoint(computeWhitePoint(rgbToXYZ)) { } float3 w(rgbToXYZ * float3{1}); ColorSpace::ColorSpace( mWhitePoint = w.xy / dot(w, float3{1}); const std::string& name, const mat3& rgbToXYZ, float gamma, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(rgbToXYZ) , mXYZtoRGB(inverse(rgbToXYZ)) , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) , mOETF(toOETF(gamma)) , mEOTF(toEOTF(gamma)) , mClamper(std::move(clamper)) , mPrimaries(computePrimaries(rgbToXYZ)) , mWhitePoint(computeWhitePoint(rgbToXYZ)) { } } ColorSpace::ColorSpace( ColorSpace::ColorSpace( Loading @@ -62,6 +162,40 @@ ColorSpace::ColorSpace( , mWhitePoint(whitePoint) { , mWhitePoint(whitePoint) { } } ColorSpace::ColorSpace( const std::string& name, const std::array<float2, 3>& primaries, const float2& whitePoint, const TransferParameters parameters, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) , mXYZtoRGB(inverse(mRGBtoXYZ)) , mParameters(parameters) , mOETF(toOETF(mParameters)) , mEOTF(toEOTF(mParameters)) , mClamper(std::move(clamper)) , mPrimaries(primaries) , mWhitePoint(whitePoint) { } ColorSpace::ColorSpace( const std::string& name, const std::array<float2, 3>& primaries, const float2& whitePoint, float gamma, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) , mXYZtoRGB(inverse(mRGBtoXYZ)) , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) , mOETF(toOETF(gamma)) , mEOTF(toEOTF(gamma)) , mClamper(std::move(clamper)) , mPrimaries(primaries) , mWhitePoint(whitePoint) { } constexpr mat3 ColorSpace::computeXYZMatrix( constexpr mat3 ColorSpace::computeXYZMatrix( const std::array<float2, 3>& primaries, const float2& whitePoint) { const std::array<float2, 3>& primaries, const float2& whitePoint) { const float2& R = primaries[0]; const float2& R = primaries[0]; Loading Loading @@ -96,33 +230,12 @@ constexpr mat3 ColorSpace::computeXYZMatrix( }; }; } } static constexpr float rcpResponse(float x, float g,float a, float b, float c, float d) { return x >= d * c ? (std::pow(x, 1.0f / g) - b) / a : x / c; } static constexpr float response(float x, float g, float a, float b, float c, float d) { return x >= d ? std::pow(a * x + b, g) : c * x; } static float absRcpResponse(float x, float g,float a, float b, float c, float d) { return std::copysign(rcpResponse(std::abs(x), g, a, b, c, d), x); } static float absResponse(float x, float g, float a, float b, float c, float d) { return std::copysign(response(std::abs(x), g, a, b, c, d), x); } static float safePow(float x, float e) { return powf(x < 0.0f ? 0.0f : x, e); } const ColorSpace ColorSpace::sRGB() { const ColorSpace ColorSpace::sRGB() { return { return { "sRGB IEC61966-2.1", "sRGB IEC61966-2.1", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f} std::bind(response, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f) }; }; } } Loading Loading @@ -150,8 +263,7 @@ const ColorSpace ColorSpace::linearExtendedSRGB() { "scRGB IEC 61966-2-2:2003", "scRGB IEC 61966-2-2:2003", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, linearReponse, 1.0f, linearReponse, std::bind(clamp<float>, _1, -0.5f, 7.499f) std::bind(clamp<float>, _1, -0.5f, 7.499f) }; }; } } Loading @@ -161,8 +273,7 @@ const ColorSpace ColorSpace::NTSC() { "NTSC (1953)", "NTSC (1953)", {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}}, {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}}, {0.310f, 0.316f}, {0.310f, 0.316f}, std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f), {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} std::bind(response, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f) }; }; } } Loading @@ -171,8 +282,7 @@ const ColorSpace ColorSpace::BT709() { "Rec. ITU-R BT.709-5", "Rec. ITU-R BT.709-5", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f), {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} std::bind(response, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f) }; }; } } Loading @@ -181,8 +291,7 @@ const ColorSpace ColorSpace::BT2020() { "Rec. ITU-R BT.2020-1", "Rec. ITU-R BT.2020-1", {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}}, {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f), {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} std::bind(response, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f) }; }; } } Loading @@ -191,8 +300,7 @@ const ColorSpace ColorSpace::AdobeRGB() { "Adobe RGB (1998)", "Adobe RGB (1998)", {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}}, {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(safePow, _1, 1.0f / 2.2f), 2.2f std::bind(safePow, _1, 2.2f) }; }; } } Loading @@ -201,8 +309,7 @@ const ColorSpace ColorSpace::ProPhotoRGB() { "ROMM RGB ISO 22028-2:2013", "ROMM RGB ISO 22028-2:2013", {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}}, {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}}, {0.34567f, 0.35850f}, {0.34567f, 0.35850f}, std::bind(rcpResponse, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f), {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f} std::bind(response, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f) }; }; } } Loading @@ -211,8 +318,7 @@ const ColorSpace ColorSpace::DisplayP3() { "Display P3", "Display P3", {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {0.3127f, 0.3290f}, std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f), {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f} std::bind(response, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f) }; }; } } Loading @@ -221,8 +327,7 @@ const ColorSpace ColorSpace::DCIP3() { "SMPTE RP 431-2-2007 DCI (P3)", "SMPTE RP 431-2-2007 DCI (P3)", {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {0.314f, 0.351f}, {0.314f, 0.351f}, std::bind(safePow, _1, 1.0f / 2.6f), 2.6f std::bind(safePow, _1, 2.6f) }; }; } } Loading @@ -231,8 +336,7 @@ const ColorSpace ColorSpace::ACES() { "SMPTE ST 2065-1:2012 ACES", "SMPTE ST 2065-1:2012 ACES", {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}}, {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}}, {0.32168f, 0.33767f}, {0.32168f, 0.33767f}, linearReponse, 1.0f, linearReponse, std::bind(clamp<float>, _1, -65504.0f, 65504.0f) std::bind(clamp<float>, _1, -65504.0f, 65504.0f) }; }; } } Loading @@ -242,12 +346,33 @@ const ColorSpace ColorSpace::ACEScg() { "Academy S-2014-004 ACEScg", "Academy S-2014-004 ACEScg", {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}}, {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}}, {0.32168f, 0.33767f}, {0.32168f, 0.33767f}, linearReponse, 1.0f, linearReponse, std::bind(clamp<float>, _1, -65504.0f, 65504.0f) std::bind(clamp<float>, _1, -65504.0f, 65504.0f) }; }; } } std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst) { size = clamp(size, 2u, 256u); float m = 1.0f / float(size - 1); std::unique_ptr<float3> lut(new float3[size * size * size]); float3* data = lut.get(); ColorSpaceConnector connector(src, dst); for (uint32_t z = 0; z < size; z++) { for (int32_t y = int32_t(size - 1); y >= 0; y--) { for (uint32_t x = 0; x < size; x++) { *data++ = connector.transform({x * m, y * m, z * m}); } } } return lut; } static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; static const mat3 BRADFORD = mat3{ static const mat3 BRADFORD = mat3{ Loading @@ -262,7 +387,7 @@ static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const fl return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; } } ColorSpace::Connector::Connector( ColorSpaceConnector::ColorSpaceConnector( const ColorSpace& src, const ColorSpace& src, const ColorSpace& dst) noexcept const ColorSpace& dst) noexcept : mSource(src) : mSource(src) Loading @@ -274,8 +399,8 @@ ColorSpace::Connector::Connector( mat3 rgbToXYZ(src.getRGBtoXYZ()); mat3 rgbToXYZ(src.getRGBtoXYZ()); mat3 xyzToRGB(dst.getXYZtoRGB()); mat3 xyzToRGB(dst.getXYZtoRGB()); float3 srcXYZ = XYZ(float3{src.getWhitePoint(), 1}); float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1}); float3 dstXYZ = XYZ(float3{dst.getWhitePoint(), 1}); float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1}); if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); Loading @@ -289,27 +414,4 @@ ColorSpace::Connector::Connector( } } } } std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst) { size = clamp(size, 2u, 256u); float m = 1.0f / float(size - 1); std::unique_ptr<float3> lut(new float3[size * size * size]); float3* data = lut.get(); Connector connector(src, dst); for (uint32_t z = 0; z < size; z++) { for (int32_t y = int32_t(size - 1); y >= 0; y--) { for (uint32_t x = 0; x < size; x++) { *data++ = connector.transform({x * m, y * m, z * m}); } } } return lut; } }; // namespace android }; // namespace android
libs/ui/tests/colorspace_test.cpp +2 −2 Original line number Original line Diff line number Diff line Loading @@ -152,12 +152,12 @@ TEST_F(ColorSpaceTest, Clamping) { TEST_F(ColorSpaceTest, Connect) { TEST_F(ColorSpaceTest, Connect) { // No chromatic adaptation // No chromatic adaptation auto r = ColorSpace::connect(ColorSpace::sRGB(), ColorSpace::AdobeRGB()) auto r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::AdobeRGB()) .transform({1.0f, 0.5f, 0.0f}); .transform({1.0f, 0.5f, 0.0f}); EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); // Test with chromatic adaptation // Test with chromatic adaptation r = ColorSpace::connect(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB()) r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB()) .transform({1.0f, 0.0f, 0.0f}); .transform({1.0f, 0.0f, 0.0f}); EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f}))); EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f}))); } } Loading