Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <ui/ColorSpace.h>
     18 
     19 using namespace std::placeholders;
     20 
     21 namespace android {
     22 
     23 static constexpr float linearResponse(float v) {
     24     return v;
     25 }
     26 
     27 static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) {
     28     return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c;
     29 }
     30 
     31 static constexpr float response(float x, const ColorSpace::TransferParameters& p) {
     32     return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x;
     33 }
     34 
     35 static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) {
     36     return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c;
     37 }
     38 
     39 static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) {
     40     return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f;
     41 }
     42 
     43 static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
     44     float xx = std::abs(x);
     45     return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x);
     46 }
     47 
     48 static float absResponse(float x, float g, float a, float b, float c, float d) {
     49    float xx = std::abs(x);
     50    return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x);
     51 }
     52 
     53 static float safePow(float x, float e) {
     54     return powf(x < 0.0f ? 0.0f : x, e);
     55 }
     56 
     57 static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) {
     58     if (parameters.e == 0.0f && parameters.f == 0.0f) {
     59         return std::bind(rcpResponse, _1, parameters);
     60     }
     61     return std::bind(rcpFullResponse, _1, parameters);
     62 }
     63 
     64 static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) {
     65     if (parameters.e == 0.0f && parameters.f == 0.0f) {
     66         return std::bind(response, _1, parameters);
     67     }
     68     return std::bind(fullResponse, _1, parameters);
     69 }
     70 
     71 static ColorSpace::transfer_function toOETF(float gamma) {
     72     if (gamma == 1.0f) {
     73         return linearResponse;
     74     }
     75     return std::bind(safePow, _1, 1.0f / gamma);
     76 }
     77 
     78 static ColorSpace::transfer_function toEOTF(float gamma) {
     79     if (gamma == 1.0f) {
     80         return linearResponse;
     81     }
     82     return std::bind(safePow, _1, gamma);
     83 }
     84 
     85 static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) {
     86     float3 r(rgbToXYZ * float3{1, 0, 0});
     87     float3 g(rgbToXYZ * float3{0, 1, 0});
     88     float3 b(rgbToXYZ * float3{0, 0, 1});
     89 
     90     return {{r.xy / dot(r, float3{1}),
     91              g.xy / dot(g, float3{1}),
     92              b.xy / dot(b, float3{1})}};
     93 }
     94 
     95 static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) {
     96     float3 w(rgbToXYZ * float3{1});
     97     return w.xy / dot(w, float3{1});
     98 }
     99 
    100 ColorSpace::ColorSpace(
    101         const std::string& name,
    102         const mat3& rgbToXYZ,
    103         transfer_function OETF,
    104         transfer_function EOTF,
    105         clamping_function clamper) noexcept
    106         : mName(name)
    107         , mRGBtoXYZ(rgbToXYZ)
    108         , mXYZtoRGB(inverse(rgbToXYZ))
    109         , mOETF(std::move(OETF))
    110         , mEOTF(std::move(EOTF))
    111         , mClamper(std::move(clamper))
    112         , mPrimaries(computePrimaries(rgbToXYZ))
    113         , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
    114 }
    115 
    116 ColorSpace::ColorSpace(
    117         const std::string& name,
    118         const mat3& rgbToXYZ,
    119         const TransferParameters parameters,
    120         clamping_function clamper) noexcept
    121         : mName(name)
    122         , mRGBtoXYZ(rgbToXYZ)
    123         , mXYZtoRGB(inverse(rgbToXYZ))
    124         , mParameters(parameters)
    125         , mOETF(toOETF(mParameters))
    126         , mEOTF(toEOTF(mParameters))
    127         , mClamper(std::move(clamper))
    128         , mPrimaries(computePrimaries(rgbToXYZ))
    129         , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
    130 }
    131 
    132 ColorSpace::ColorSpace(
    133         const std::string& name,
    134         const mat3& rgbToXYZ,
    135         float gamma,
    136         clamping_function clamper) noexcept
    137         : mName(name)
    138         , mRGBtoXYZ(rgbToXYZ)
    139         , mXYZtoRGB(inverse(rgbToXYZ))
    140         , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
    141         , mOETF(toOETF(gamma))
    142         , mEOTF(toEOTF(gamma))
    143         , mClamper(std::move(clamper))
    144         , mPrimaries(computePrimaries(rgbToXYZ))
    145         , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
    146 }
    147 
    148 ColorSpace::ColorSpace(
    149         const std::string& name,
    150         const std::array<float2, 3>& primaries,
    151         const float2& whitePoint,
    152         transfer_function OETF,
    153         transfer_function EOTF,
    154         clamping_function clamper) noexcept
    155         : mName(name)
    156         , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
    157         , mXYZtoRGB(inverse(mRGBtoXYZ))
    158         , mOETF(std::move(OETF))
    159         , mEOTF(std::move(EOTF))
    160         , mClamper(std::move(clamper))
    161         , mPrimaries(primaries)
    162         , mWhitePoint(whitePoint) {
    163 }
    164 
    165 ColorSpace::ColorSpace(
    166         const std::string& name,
    167         const std::array<float2, 3>& primaries,
    168         const float2& whitePoint,
    169         const TransferParameters parameters,
    170         clamping_function clamper) noexcept
    171         : mName(name)
    172         , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
    173         , mXYZtoRGB(inverse(mRGBtoXYZ))
    174         , mParameters(parameters)
    175         , mOETF(toOETF(mParameters))
    176         , mEOTF(toEOTF(mParameters))
    177         , mClamper(std::move(clamper))
    178         , mPrimaries(primaries)
    179         , mWhitePoint(whitePoint) {
    180 }
    181 
    182 ColorSpace::ColorSpace(
    183         const std::string& name,
    184         const std::array<float2, 3>& primaries,
    185         const float2& whitePoint,
    186         float gamma,
    187         clamping_function clamper) noexcept
    188         : mName(name)
    189         , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
    190         , mXYZtoRGB(inverse(mRGBtoXYZ))
    191         , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
    192         , mOETF(toOETF(gamma))
    193         , mEOTF(toEOTF(gamma))
    194         , mClamper(std::move(clamper))
    195         , mPrimaries(primaries)
    196         , mWhitePoint(whitePoint) {
    197 }
    198 
    199 constexpr mat3 ColorSpace::computeXYZMatrix(
    200         const std::array<float2, 3>& primaries, const float2& whitePoint) {
    201     const float2& R = primaries[0];
    202     const float2& G = primaries[1];
    203     const float2& B = primaries[2];
    204     const float2& W = whitePoint;
    205 
    206     float oneRxRy = (1 - R.x) / R.y;
    207     float oneGxGy = (1 - G.x) / G.y;
    208     float oneBxBy = (1 - B.x) / B.y;
    209     float oneWxWy = (1 - W.x) / W.y;
    210 
    211     float RxRy = R.x / R.y;
    212     float GxGy = G.x / G.y;
    213     float BxBy = B.x / B.y;
    214     float WxWy = W.x / W.y;
    215 
    216     float BY =
    217             ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
    218             ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
    219     float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
    220     float RY = 1 - GY - BY;
    221 
    222     float RYRy = RY / R.y;
    223     float GYGy = GY / G.y;
    224     float BYBy = BY / B.y;
    225 
    226     return {
    227         float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)},
    228         float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)},
    229         float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)}
    230     };
    231 }
    232 
    233 const ColorSpace ColorSpace::sRGB() {
    234     return {
    235         "sRGB IEC61966-2.1",
    236         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
    237         {0.3127f, 0.3290f},
    238         {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f}
    239     };
    240 }
    241 
    242 const ColorSpace ColorSpace::linearSRGB() {
    243     return {
    244         "sRGB IEC61966-2.1 (Linear)",
    245         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
    246         {0.3127f, 0.3290f}
    247     };
    248 }
    249 
    250 const ColorSpace ColorSpace::extendedSRGB() {
    251     return {
    252         "scRGB-nl IEC 61966-2-2:2003",
    253         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
    254         {0.3127f, 0.3290f},
    255         std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
    256         std::bind(absResponse,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
    257         std::bind(clamp<float>, _1, -0.799f, 2.399f)
    258     };
    259 }
    260 
    261 const ColorSpace ColorSpace::linearExtendedSRGB() {
    262     return {
    263         "scRGB IEC 61966-2-2:2003",
    264         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
    265         {0.3127f, 0.3290f},
    266         1.0f,
    267         std::bind(clamp<float>, _1, -0.5f, 7.499f)
    268     };
    269 }
    270 
    271 const ColorSpace ColorSpace::NTSC() {
    272     return {
    273         "NTSC (1953)",
    274         {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}},
    275         {0.310f, 0.316f},
    276         {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
    277     };
    278 }
    279 
    280 const ColorSpace ColorSpace::BT709() {
    281     return {
    282         "Rec. ITU-R BT.709-5",
    283         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
    284         {0.3127f, 0.3290f},
    285         {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
    286     };
    287 }
    288 
    289 const ColorSpace ColorSpace::BT2020() {
    290     return {
    291         "Rec. ITU-R BT.2020-1",
    292         {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}},
    293         {0.3127f, 0.3290f},
    294         {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
    295     };
    296 }
    297 
    298 const ColorSpace ColorSpace::AdobeRGB() {
    299     return {
    300         "Adobe RGB (1998)",
    301         {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}},
    302         {0.3127f, 0.3290f},
    303         2.2f
    304     };
    305 }
    306 
    307 const ColorSpace ColorSpace::ProPhotoRGB() {
    308     return {
    309         "ROMM RGB ISO 22028-2:2013",
    310         {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}},
    311         {0.34567f, 0.35850f},
    312         {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f}
    313     };
    314 }
    315 
    316 const ColorSpace ColorSpace::DisplayP3() {
    317     return {
    318         "Display P3",
    319         {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
    320         {0.3127f, 0.3290f},
    321         {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f}
    322     };
    323 }
    324 
    325 const ColorSpace ColorSpace::DCIP3() {
    326     return {
    327         "SMPTE RP 431-2-2007 DCI (P3)",
    328         {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
    329         {0.314f, 0.351f},
    330         2.6f
    331     };
    332 }
    333 
    334 const ColorSpace ColorSpace::ACES() {
    335     return {
    336         "SMPTE ST 2065-1:2012 ACES",
    337         {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}},
    338         {0.32168f, 0.33767f},
    339         1.0f,
    340         std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
    341     };
    342 }
    343 
    344 const ColorSpace ColorSpace::ACEScg() {
    345     return {
    346         "Academy S-2014-004 ACEScg",
    347         {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}},
    348         {0.32168f, 0.33767f},
    349         1.0f,
    350         std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
    351     };
    352 }
    353 
    354 std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size,
    355         const ColorSpace& src, const ColorSpace& dst) {
    356 
    357     size = clamp(size, 2u, 256u);
    358     float m = 1.0f / float(size - 1);
    359 
    360     std::unique_ptr<float3> lut(new float3[size * size * size]);
    361     float3* data = lut.get();
    362 
    363     ColorSpaceConnector connector(src, dst);
    364 
    365     for (uint32_t z = 0; z < size; z++) {
    366         for (int32_t y = int32_t(size - 1); y >= 0; y--) {
    367             for (uint32_t x = 0; x < size; x++) {
    368                 *data++ = connector.transform({x * m, y * m, z * m});
    369             }
    370         }
    371     }
    372 
    373     return lut;
    374 }
    375 
    376 static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
    377 static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
    378 static const mat3 BRADFORD = mat3{
    379     float3{ 0.8951f, -0.7502f,  0.0389f},
    380     float3{ 0.2664f,  1.7135f, -0.0685f},
    381     float3{-0.1614f,  0.0367f,  1.0296f}
    382 };
    383 
    384 static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
    385     float3 srcLMS = matrix * srcWhitePoint;
    386     float3 dstLMS = matrix * dstWhitePoint;
    387     return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
    388 }
    389 
    390 ColorSpaceConnector::ColorSpaceConnector(
    391         const ColorSpace& src,
    392         const ColorSpace& dst) noexcept
    393         : mSource(src)
    394         , mDestination(dst) {
    395 
    396     if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) {
    397         mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ();
    398     } else {
    399         mat3 rgbToXYZ(src.getRGBtoXYZ());
    400         mat3 xyzToRGB(dst.getXYZtoRGB());
    401 
    402         float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1});
    403         float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1});
    404 
    405         if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
    406             rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ();
    407         }
    408 
    409         if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
    410             xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ());
    411         }
    412 
    413         mTransform = xyzToRGB * rgbToXYZ;
    414     }
    415 }
    416 
    417 }; // namespace android
    418