Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2016 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkAtomics.h"
      9 #include "SkColorSpace.h"
     10 
     11 static inline bool SkFloatIsFinite(float x) { return 0 == x * 0; }
     12 
     13 //
     14 // SkFloat3x3
     15 //
     16 // In memory order, values are a, b, c, d, e, f, g, h, i
     17 //
     18 // When applied to a color component vector (e.g. [ r, r, r ] or [ g, g, g ] we do
     19 //
     20 // [ r r r ] * [ a b c ] + [ g g g ] * [ d e f ] + [ b b b ] * [ g h i ]
     21 //
     22 // Thus in our point-on-the-right notation, the matrix looks like
     23 //
     24 // [ a d g ]   [ r ]
     25 // [ b e h ] * [ g ]
     26 // [ c f i ]   [ b ]
     27 //
     28 static SkFloat3x3 concat(const SkFloat3x3& left, const SkFloat3x3& rite) {
     29     SkFloat3x3 result;
     30     for (int row = 0; row < 3; ++row) {
     31         for (int col = 0; col < 3; ++col) {
     32             double tmp = 0;
     33             for (int i = 0; i < 3; ++i) {
     34                 tmp += (double)left.fMat[row + i * 3] * rite.fMat[i + col * 3];
     35             }
     36             result.fMat[row + col * 3] = (double)tmp;
     37         }
     38     }
     39     return result;
     40 }
     41 
     42 static double det(const SkFloat3x3& m) {
     43     return (double)m.fMat[0] * m.fMat[4] * m.fMat[8] +
     44            (double)m.fMat[3] * m.fMat[7] * m.fMat[2] +
     45            (double)m.fMat[6] * m.fMat[1] * m.fMat[5] -
     46            (double)m.fMat[0] * m.fMat[7] * m.fMat[5] -
     47            (double)m.fMat[3] * m.fMat[1] * m.fMat[8] -
     48            (double)m.fMat[6] * m.fMat[4] * m.fMat[2];
     49 }
     50 
     51 static double det2x2(const SkFloat3x3& m, int a, int b, int c, int d) {
     52     return (double)m.fMat[a] * m.fMat[b] - (double)m.fMat[c] * m.fMat[d];
     53 }
     54 
     55 static SkFloat3x3 invert(const SkFloat3x3& m) {
     56     double d = det(m);
     57     SkASSERT(SkFloatIsFinite((float)d));
     58     double scale = 1 / d;
     59     SkASSERT(SkFloatIsFinite((float)scale));
     60 
     61     return {{
     62         (float)(scale * det2x2(m, 4, 8, 5, 7)),
     63         (float)(scale * det2x2(m, 7, 2, 8, 1)),
     64         (float)(scale * det2x2(m, 1, 5, 2, 4)),
     65 
     66         (float)(scale * det2x2(m, 6, 5, 8, 3)),
     67         (float)(scale * det2x2(m, 0, 8, 2, 6)),
     68         (float)(scale * det2x2(m, 3, 2, 5, 0)),
     69 
     70         (float)(scale * det2x2(m, 3, 7, 4, 6)),
     71         (float)(scale * det2x2(m, 6, 1, 7, 0)),
     72         (float)(scale * det2x2(m, 0, 4, 1, 3)),
     73     }};
     74 }
     75 
     76 void SkFloat3::dump() const {
     77     SkDebugf("[%7.4f %7.4f %7.4f]\n", fVec[0], fVec[1], fVec[2]);
     78 }
     79 
     80 void SkFloat3x3::dump() const {
     81     SkDebugf("[%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f]\n",
     82              fMat[0], fMat[1], fMat[2],
     83              fMat[3], fMat[4], fMat[5],
     84              fMat[6], fMat[7], fMat[8]);
     85 }
     86 
     87 //////////////////////////////////////////////////////////////////////////////////////////////////
     88 
     89 static int32_t gUniqueColorSpaceID;
     90 
     91 SkColorSpace::SkColorSpace(const SkFloat3x3& toXYZD50, const SkFloat3& gamma, Named named)
     92     : fToXYZD50(toXYZD50)
     93     , fGamma(gamma)
     94     , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
     95     , fNamed(named)
     96 {
     97     for (int i = 0; i < 3; ++i) {
     98         SkASSERT(SkFloatIsFinite(gamma.fVec[i]));
     99         for (int j = 0; j < 3; ++j) {
    100             SkASSERT(SkFloatIsFinite(toXYZD50.fMat[3*i + j]));
    101         }
    102     }
    103 }
    104 
    105 SkColorSpace* SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFloat3& gamma) {
    106     for (int i = 0; i < 3; ++i) {
    107         if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) {
    108             return nullptr;
    109         }
    110         for (int j = 0; j < 3; ++j) {
    111             if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) {
    112                 return nullptr;
    113             }
    114         }
    115     }
    116 
    117     // check the matrix for invertibility
    118     float d = det(toXYZD50);
    119     if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) {
    120         return nullptr;
    121     }
    122 
    123     return new SkColorSpace(toXYZD50, gamma, kUnknown_Named);
    124 }
    125 
    126 void SkColorSpace::dump() const {
    127     fToXYZD50.dump();
    128     fGamma.dump();
    129 }
    130 
    131 //////////////////////////////////////////////////////////////////////////////////////////////////
    132 
    133 const SkFloat3   gDevice_gamma {{ 0, 0, 0 }};
    134 const SkFloat3x3 gDevice_toXYZD50 {{
    135     1, 0, 0,
    136     0, 1, 0,
    137     0, 0, 1
    138 }};
    139 
    140 const SkFloat3 gSRGB_gamma {{ 2.2f, 2.2f, 2.2f }};
    141 const SkFloat3x3 gSRGB_toXYZD50 {{
    142     0.4358f, 0.2224f, 0.0139f,    // * R
    143     0.3853f, 0.7170f, 0.0971f,    // * G
    144     0.1430f, 0.0606f, 0.7139f,    // * B
    145 }};
    146 
    147 SkColorSpace* SkColorSpace::NewNamed(Named named) {
    148     switch (named) {
    149         case kDevice_Named:
    150             return new SkColorSpace(gDevice_toXYZD50, gDevice_gamma, kDevice_Named);
    151         case kSRGB_Named:
    152             return new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named);
    153         default:
    154             break;
    155     }
    156     return nullptr;
    157 }
    158 
    159 ///////////////////////////////////////////////////////////////////////////////////////////////////
    160 
    161 SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColorSpace* dst,
    162                                           SkFloat3x3* result) {
    163     if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst->named())) {
    164         if (result) {
    165             *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }};
    166         }
    167         return kIdentity_Result;
    168     }
    169     if (result) {
    170         *result = concat(src->fToXYZD50, invert(dst->fToXYZD50));
    171     }
    172     return kNormal_Result;
    173 }
    174 
    175 #include "SkColor.h"
    176 #include "SkNx.h"
    177 #include "SkPM4f.h"
    178 
    179 void SkApply3x3ToPM4f(const SkFloat3x3& m, const SkPM4f src[], SkPM4f dst[], int count) {
    180     SkASSERT(1 == SkPM4f::G);
    181     SkASSERT(3 == SkPM4f::A);
    182 
    183     Sk4f cr, cg, cb;
    184     cg = Sk4f::Load(m.fMat + 3);
    185     if (0 == SkPM4f::R) {
    186         SkASSERT(2 == SkPM4f::B);
    187         cr = Sk4f::Load(m.fMat + 0);
    188         cb = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0);
    189     } else {
    190         SkASSERT(0 == SkPM4f::B);
    191         SkASSERT(2 == SkPM4f::R);
    192         cb = Sk4f::Load(m.fMat + 0);
    193         cr = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0);
    194     }
    195     cr = cr * Sk4f(1, 1, 1, 0);
    196     cg = cg * Sk4f(1, 1, 1, 0);
    197     cb = cb * Sk4f(1, 1, 1, 0);
    198 
    199     for (int i = 0; i < count; ++i) {
    200         Sk4f r = Sk4f(src[i].fVec[SkPM4f::R]);
    201         Sk4f g = Sk4f(src[i].fVec[SkPM4f::G]);
    202         Sk4f b = Sk4f(src[i].fVec[SkPM4f::B]);
    203         Sk4f a = Sk4f(0, 0, 0, src[i].fVec[SkPM4f::A]);
    204         (cr * r + cg * g + cb * b + a).store(&dst[i]);
    205     }
    206 }
    207 
    208 ///////////////////////////////////////////////////////////////////////////////////////////////////
    209 
    210 void SkColorSpace::Test() {
    211     SkFloat3x3 mat {{ 2, 0, 0, 0, 3, 0, 0, 0, 4 }};
    212     SkFloat3x3 inv = invert(mat);
    213     mat.dump();
    214     inv.dump();
    215     concat(mat, inv).dump();
    216     concat(inv, mat).dump();
    217     SkDebugf("\n");
    218 
    219     mat = gSRGB_toXYZD50;
    220     inv = invert(mat);
    221     mat.dump();
    222     inv.dump();
    223     concat(mat, inv).dump();
    224     concat(inv, mat).dump();
    225     SkDebugf("\n");
    226 
    227     SkAutoTUnref<SkColorSpace> cs0(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named));
    228     SkAutoTUnref<SkColorSpace> cs1(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named));
    229 
    230     cs0->dump();
    231     cs1->dump();
    232     SkFloat3x3 xform;
    233     (void)SkColorSpace::Concat(cs0, cs1, &xform);
    234     xform.dump();
    235     SkDebugf("\n");
    236 }
    237 
    238 // D65 white point of Rec.  709 [8] are:
    239 //
    240 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890
    241 //
    242 //          R           G           B           white
    243 //   x      0.640       0.300       0.150       0.3127
    244 //   y      0.330       0.600       0.060       0.3290
    245 //   z      0.030       0.100       0.790       0.3582
    246