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 "SkColorData.h"
      9 #include "SkColorSpacePriv.h"
     10 #include "SkColorSpaceXformPriv.h"
     11 #include "SkColorSpaceXform_A2B.h"
     12 #include "SkColorSpaceXform_Base.h"
     13 #include "SkColorSpace_A2B.h"
     14 #include "SkColorSpace_XYZ.h"
     15 #include "SkHalf.h"
     16 #include "SkMakeUnique.h"
     17 #include "SkOpts.h"
     18 #include "SkPM4fPriv.h"
     19 #include "SkRasterPipeline.h"
     20 #include "SkSRGB.h"
     21 #include "../jumper/SkJumper.h"
     22 
     23 static constexpr float sk_linear_from_2dot2[256] = {
     24         0.000000000000000000f, 0.000005077051900662f, 0.000023328004666099f, 0.000056921765712193f,
     25         0.000107187362341244f, 0.000175123977503027f, 0.000261543754548491f, 0.000367136269815943f,
     26         0.000492503787191433f, 0.000638182842167022f, 0.000804658499513058f, 0.000992374304074325f,
     27         0.001201739522438400f, 0.001433134589671860f, 0.001686915316789280f, 0.001963416213396470f,
     28         0.002262953160706430f, 0.002585825596234170f, 0.002932318323938360f, 0.003302703032003640f,
     29         0.003697239578900130f, 0.004116177093282750f, 0.004559754922526020f, 0.005028203456855540f,
     30         0.005521744850239660f, 0.006040593654849810f, 0.006584957382581690f, 0.007155037004573030f,
     31         0.007751027397660610f, 0.008373117745148580f, 0.009021491898012130f, 0.009696328701658230f,
     32         0.010397802292555300f, 0.011126082368383200f, 0.011881334434813700f, 0.012663720031582100f,
     33         0.013473396940142600f, 0.014310519374884100f, 0.015175238159625200f, 0.016067700890886900f,
     34         0.016988052089250000f, 0.017936433339950200f, 0.018912983423721500f, 0.019917838438785700f,
     35         0.020951131914781100f, 0.022012994919336500f, 0.023103556157921400f, 0.024222942067534200f,
     36         0.025371276904734600f, 0.026548682828472900f, 0.027755279978126000f, 0.028991186547107800f,
     37         0.030256518852388700f, 0.031551391400226400f, 0.032875916948383800f, 0.034230206565082000f,
     38         0.035614369684918800f, 0.037028514161960200f, 0.038472746320194600f, 0.039947171001525600f,
     39         0.041451891611462500f, 0.042987010162657100f, 0.044552627316421400f, 0.046148842422351000f,
     40         0.047775753556170600f, 0.049433457555908000f, 0.051122050056493400f, 0.052841625522879000f,
     41         0.054592277281760300f, 0.056374097551979800f, 0.058187177473685400f, 0.060031607136313200f,
     42         0.061907475605455800f, 0.063814870948677200f, 0.065753880260330100f, 0.067724589685424300f,
     43         0.069727084442598800f, 0.071761448846239100f, 0.073827766327784600f, 0.075926119456264800f,
     44         0.078056589958101900f, 0.080219258736215100f, 0.082414205888459200f, 0.084641510725429500f,
     45         0.086901251787660300f, 0.089193506862247800f, 0.091518352998919500f, 0.093875866525577800f,
     46         0.096266123063339700f, 0.098689197541094500f, 0.101145164209600000f, 0.103634096655137000f,
     47         0.106156067812744000f, 0.108711149979039000f, 0.111299414824660000f, 0.113920933406333000f,
     48         0.116575776178572000f, 0.119264013005047000f, 0.121985713169619000f, 0.124740945387051000f,
     49         0.127529777813422000f, 0.130352278056244000f, 0.133208513184300000f, 0.136098549737202000f,
     50         0.139022453734703000f, 0.141980290685736000f, 0.144972125597231000f, 0.147998022982685000f,
     51         0.151058046870511000f, 0.154152260812165000f, 0.157280727890073000f, 0.160443510725344000f,
     52         0.163640671485290000f, 0.166872271890766000f, 0.170138373223312000f, 0.173439036332135000f,
     53         0.176774321640903000f, 0.180144289154390000f, 0.183548998464951000f, 0.186988508758844000f,
     54         0.190462878822409000f, 0.193972167048093000f, 0.197516431440340000f, 0.201095729621346000f,
     55         0.204710118836677000f, 0.208359655960767000f, 0.212044397502288000f, 0.215764399609395000f,
     56         0.219519718074868000f, 0.223310408341127000f, 0.227136525505149000f, 0.230998124323267000f,
     57         0.234895259215880000f, 0.238827984272048000f, 0.242796353254002000f, 0.246800419601550000f,
     58         0.250840236436400000f, 0.254915856566385000f, 0.259027332489606000f, 0.263174716398492000f,
     59         0.267358060183772000f, 0.271577415438375000f, 0.275832833461245000f, 0.280124365261085000f,
     60         0.284452061560024000f, 0.288815972797219000f, 0.293216149132375000f, 0.297652640449211000f,
     61         0.302125496358853000f, 0.306634766203158000f, 0.311180499057984000f, 0.315762743736397000f,
     62         0.320381548791810000f, 0.325036962521076000f, 0.329729032967515000f, 0.334457807923889000f,
     63         0.339223334935327000f, 0.344025661302187000f, 0.348864834082879000f, 0.353740900096629000f,
     64         0.358653905926199000f, 0.363603897920553000f, 0.368590922197487000f, 0.373615024646202000f,
     65         0.378676250929840000f, 0.383774646487975000f, 0.388910256539059000f, 0.394083126082829000f,
     66         0.399293299902674000f, 0.404540822567962000f, 0.409825738436323000f, 0.415148091655907000f,
     67         0.420507926167587000f, 0.425905285707146000f, 0.431340213807410000f, 0.436812753800359000f,
     68         0.442322948819202000f, 0.447870841800410000f, 0.453456475485731000f, 0.459079892424160000f,
     69         0.464741134973889000f, 0.470440245304218000f, 0.476177265397440000f, 0.481952237050698000f,
     70         0.487765201877811000f, 0.493616201311074000f, 0.499505276603030000f, 0.505432468828216000f,
     71         0.511397818884880000f, 0.517401367496673000f, 0.523443155214325000f, 0.529523222417277000f,
     72         0.535641609315311000f, 0.541798355950137000f, 0.547993502196972000f, 0.554227087766085000f,
     73         0.560499152204328000f, 0.566809734896638000f, 0.573158875067523000f, 0.579546611782525000f,
     74         0.585972983949661000f, 0.592438030320847000f, 0.598941789493296000f, 0.605484299910907000f,
     75         0.612065599865624000f, 0.618685727498780000f, 0.625344720802427000f, 0.632042617620641000f,
     76         0.638779455650817000f, 0.645555272444935000f, 0.652370105410821000f, 0.659223991813387000f,
     77         0.666116968775851000f, 0.673049073280942000f, 0.680020342172095000f, 0.687030812154625000f,
     78         0.694080519796882000f, 0.701169501531402000f, 0.708297793656032000f, 0.715465432335048000f,
     79         0.722672453600255000f, 0.729918893352071000f, 0.737204787360605000f, 0.744530171266715000f,
     80         0.751895080583051000f, 0.759299550695091000f, 0.766743616862161000f, 0.774227314218442000f,
     81         0.781750677773962000f, 0.789313742415586000f, 0.796916542907978000f, 0.804559113894567000f,
     82         0.812241489898490000f, 0.819963705323528000f, 0.827725794455034000f, 0.835527791460841000f,
     83         0.843369730392169000f, 0.851251645184515000f, 0.859173569658532000f, 0.867135537520905000f,
     84         0.875137582365205000f, 0.883179737672745000f, 0.891262036813419000f, 0.899384513046529000f,
     85         0.907547199521614000f, 0.915750129279253000f, 0.923993335251873000f, 0.932276850264543000f,
     86         0.940600707035753000f, 0.948964938178195000f, 0.957369576199527000f, 0.965814653503130000f,
     87         0.974300202388861000f, 0.982826255053791000f, 0.991392843592940000f, 1.000000000000000000f,
     88 };
     89 
     90 ///////////////////////////////////////////////////////////////////////////////////////////////////
     91 
     92 static void build_table_linear_from_gamma(float* outTable, float exponent) {
     93     for (float x = 0.0f; x <= 1.0f; x += (1.0f/255.0f)) {
     94         *outTable++ = powf(x, exponent);
     95     }
     96 }
     97 
     98 // outTable is always 256 entries, inTable may be larger or smaller.
     99 static void build_table_linear_from_gamma(float* outTable, const float* inTable,
    100                                           int inTableSize) {
    101     if (256 == inTableSize) {
    102         memcpy(outTable, inTable, sizeof(float) * 256);
    103         return;
    104     }
    105 
    106     for (float x = 0.0f; x <= 1.0f; x += (1.0f/255.0f)) {
    107         *outTable++ = interp_lut(x, inTable, inTableSize);
    108     }
    109 }
    110 
    111 
    112 static void build_table_linear_from_gamma(float* outTable, float g, float a, float b, float c,
    113                                           float d, float e, float f) {
    114     // Y = (aX + b)^g + e  for X >= d
    115     // Y = cX + f          otherwise
    116     for (float x = 0.0f; x <= 1.0f; x += (1.0f/255.0f)) {
    117         if (x >= d) {
    118             *outTable++ = clamp_0_1(powf(a * x + b, g) + e);
    119         } else {
    120             *outTable++ = clamp_0_1(c * x + f);
    121         }
    122     }
    123 }
    124 
    125 ///////////////////////////////////////////////////////////////////////////////////////////////////
    126 
    127 static const int kDstGammaTableSize = SkColorSpaceXform_Base::kDstGammaTableSize;
    128 
    129 static void build_table_linear_to_gamma(uint8_t* outTable, float exponent) {
    130     float toGammaExp = 1.0f / exponent;
    131 
    132     for (int i = 0; i < kDstGammaTableSize; i++) {
    133         float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1)));
    134         outTable[i] = clamp_normalized_float_to_byte(powf(x, toGammaExp));
    135     }
    136 }
    137 
    138 static void build_table_linear_to_gamma(uint8_t* outTable, const float* inTable,
    139                                         int inTableSize) {
    140     invert_table_gamma(nullptr, outTable, kDstGammaTableSize, inTable, inTableSize);
    141 }
    142 
    143 static float inverse_parametric(float x, float g, float a, float b, float c, float d, float e,
    144                                 float f) {
    145     // We need to take the inverse of the following piecewise function.
    146     // Y = (aX + b)^g + e  for X >= d
    147     // Y = cX + f          otherwise
    148 
    149     // Assume that the gamma function is continuous, or this won't make much sense anyway.
    150     // Plug in |d| to the second equation to calculate the new piecewise interval.
    151     // Then simply use the inverse of the original functions.
    152     float interval = c * d + f;
    153     if (x < interval) {
    154         // X = (Y - F) / C
    155         if (0.0f == c) {
    156             // The gamma curve for this segment is constant, so the inverse is undefined.
    157             // Since this is the lower segment, guess zero.
    158             return 0.0f;
    159         }
    160 
    161         return (x - f) / c;
    162     }
    163 
    164     // X = ((Y - E)^(1 / G) - B) / A
    165     if (0.0f == a || 0.0f == g) {
    166         // The gamma curve for this segment is constant, so the inverse is undefined.
    167         // Since this is the upper segment, guess one.
    168         return 1.0f;
    169     }
    170 
    171     return (powf(x - e, 1.0f / g) - b) / a;
    172 }
    173 
    174 static void build_table_linear_to_gamma(uint8_t* outTable, float g, float a,
    175                                         float b, float c, float d, float e, float f) {
    176     for (int i = 0; i < kDstGammaTableSize; i++) {
    177         float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1)));
    178         float y = inverse_parametric(x, g, a, b, c, d, e, f);
    179         outTable[i] = clamp_normalized_float_to_byte(y);
    180     }
    181 }
    182 
    183 ///////////////////////////////////////////////////////////////////////////////////////////////////
    184 
    185 template <typename T>
    186 struct GammaFns {
    187     const T* fSRGBTable;
    188     const T* f2Dot2Table;
    189     void (*fBuildFromValue)(T*, float);
    190     void (*fBuildFromTable)(T*, const float*, int);
    191     void (*fBuildFromParam)(T*, float, float, float, float, float, float, float);
    192 };
    193 
    194 static const GammaFns<float> kToLinear {
    195     sk_linear_from_srgb,
    196     sk_linear_from_2dot2,
    197     &build_table_linear_from_gamma,
    198     &build_table_linear_from_gamma,
    199     &build_table_linear_from_gamma,
    200 };
    201 
    202 static const GammaFns<uint8_t> kFromLinear {
    203     nullptr,
    204     nullptr,
    205     &build_table_linear_to_gamma,
    206     &build_table_linear_to_gamma,
    207     &build_table_linear_to_gamma,
    208 };
    209 
    210 // Build tables to transform src gamma to linear.
    211 template <typename T>
    212 static void build_gamma_tables(const T* outGammaTables[3], T* gammaTableStorage, int gammaTableSize,
    213                                const SkColorSpace_XYZ* space, const GammaFns<T>& fns,
    214                                bool gammasAreMatching)
    215 {
    216     switch (space->gammaNamed()) {
    217         case kSRGB_SkGammaNamed:
    218             outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.fSRGBTable;
    219             break;
    220         case k2Dot2Curve_SkGammaNamed:
    221             outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.f2Dot2Table;
    222             break;
    223         case kLinear_SkGammaNamed:
    224             outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = nullptr;
    225             break;
    226         default: {
    227             const SkGammas* gammas = space->gammas();
    228             SkASSERT(gammas);
    229 
    230             auto build_table = [=](int i) {
    231                 if (gammas->isNamed(i)) {
    232                     switch (gammas->data(i).fNamed) {
    233                         case kSRGB_SkGammaNamed:
    234                             (*fns.fBuildFromParam)(&gammaTableStorage[i * gammaTableSize],
    235                                                    gSRGB_TransferFn.fG,
    236                                                    gSRGB_TransferFn.fA,
    237                                                    gSRGB_TransferFn.fB,
    238                                                    gSRGB_TransferFn.fC,
    239                                                    gSRGB_TransferFn.fD,
    240                                                    gSRGB_TransferFn.fE,
    241                                                    gSRGB_TransferFn.fF);
    242                             outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
    243                             break;
    244                         case k2Dot2Curve_SkGammaNamed:
    245                             (*fns.fBuildFromValue)(&gammaTableStorage[i * gammaTableSize], 2.2f);
    246                             outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
    247                             break;
    248                         case kLinear_SkGammaNamed:
    249                             (*fns.fBuildFromValue)(&gammaTableStorage[i * gammaTableSize], 1.0f);
    250                             outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
    251                             break;
    252                         default:
    253                             SkASSERT(false);
    254                             break;
    255                     }
    256                 } else if (gammas->isValue(i)) {
    257                     (*fns.fBuildFromValue)(&gammaTableStorage[i * gammaTableSize],
    258                                            gammas->data(i).fValue);
    259                     outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
    260                 } else if (gammas->isTable(i)) {
    261                     (*fns.fBuildFromTable)(&gammaTableStorage[i * gammaTableSize], gammas->table(i),
    262                                            gammas->data(i).fTable.fSize);
    263                     outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
    264                 } else {
    265                     SkASSERT(gammas->isParametric(i));
    266                     const SkColorSpaceTransferFn& params = gammas->params(i);
    267                     (*fns.fBuildFromParam)(&gammaTableStorage[i * gammaTableSize], params.fG,
    268                                            params.fA, params.fB, params.fC, params.fD, params.fE,
    269                                            params.fF);
    270                     outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
    271                 }
    272             };
    273 
    274             if (gammasAreMatching) {
    275                 build_table(0);
    276                 outGammaTables[1] = outGammaTables[0];
    277                 outGammaTables[2] = outGammaTables[0];
    278             } else {
    279                 build_table(0);
    280                 build_table(1);
    281                 build_table(2);
    282             }
    283 
    284             break;
    285         }
    286     }
    287 }
    288 
    289 void SkColorSpaceXform_Base::BuildDstGammaTables(const uint8_t* dstGammaTables[3],
    290                                                  uint8_t* dstStorage,
    291                                                  const SkColorSpace_XYZ* space,
    292                                                  bool gammasAreMatching) {
    293     build_gamma_tables(dstGammaTables, dstStorage, kDstGammaTableSize, space, kFromLinear,
    294                        gammasAreMatching);
    295 }
    296 
    297 ///////////////////////////////////////////////////////////////////////////////////////////////////
    298 
    299 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(SkColorSpace* src,
    300                                                           SkColorSpace* dst) {
    301     return SkColorSpaceXform_Base::New(src, dst, SkTransferFunctionBehavior::kRespect);
    302 }
    303 
    304 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform_Base::New(
    305         SkColorSpace* src,
    306         SkColorSpace* dst,
    307         SkTransferFunctionBehavior premulBehavior) {
    308 
    309     if (!src || !dst) {
    310         // Invalid input
    311         return nullptr;
    312     }
    313 
    314     if (!dst->toXYZD50()) {
    315         SkCSXformPrintf("only XYZ destinations supported\n");
    316         return nullptr;
    317     }
    318 
    319     if (src->toXYZD50()) {
    320         return skstd::make_unique<SkColorSpaceXform_XYZ>(static_cast<SkColorSpace_XYZ*>(src),
    321                                                          static_cast<SkColorSpace_XYZ*>(dst),
    322                                                          premulBehavior);
    323     }
    324     return skstd::make_unique<SkColorSpaceXform_A2B>(static_cast<SkColorSpace_A2B*>(src),
    325                                                      static_cast<SkColorSpace_XYZ*>(dst));
    326 }
    327 
    328 ///////////////////////////////////////////////////////////////////////////////////////////////////
    329 
    330 static inline int num_tables(SkColorSpace_XYZ* space) {
    331     switch (space->gammaNamed()) {
    332         case kSRGB_SkGammaNamed:
    333         case k2Dot2Curve_SkGammaNamed:
    334         case kLinear_SkGammaNamed:
    335             return 0;
    336         default: {
    337             const SkGammas* gammas = space->gammas();
    338             SkASSERT(gammas);
    339 
    340             // It's likely that each component will have the same gamma.  In this case,
    341             // we only need to build one table.
    342             return gammas->allChannelsSame() ? 1 : 3;
    343         }
    344     }
    345 }
    346 
    347 SkColorSpaceXform_XYZ::SkColorSpaceXform_XYZ(SkColorSpace_XYZ* src,
    348                                              SkColorSpace_XYZ* dst,
    349                                              SkTransferFunctionBehavior premulBehavior)
    350     : fPremulBehavior(premulBehavior)
    351 {
    352     fColorSpacesAreIdentical = SkColorSpace::Equals(src, dst);
    353 
    354     SkMatrix44 srcToDst(SkMatrix44::kIdentity_Constructor);
    355     if (!fColorSpacesAreIdentical && *src->toXYZD50() != *dst->toXYZD50()) {
    356         srcToDst.setConcat(*dst->fromXYZD50(), *src->toXYZD50());
    357     }
    358 
    359     fSrcToDst[ 0] = srcToDst.get(0, 0);
    360     fSrcToDst[ 1] = srcToDst.get(1, 0);
    361     fSrcToDst[ 2] = srcToDst.get(2, 0);
    362     fSrcToDst[ 3] = srcToDst.get(0, 1);
    363     fSrcToDst[ 4] = srcToDst.get(1, 1);
    364     fSrcToDst[ 5] = srcToDst.get(2, 1);
    365     fSrcToDst[ 6] = srcToDst.get(0, 2);
    366     fSrcToDst[ 7] = srcToDst.get(1, 2);
    367     fSrcToDst[ 8] = srcToDst.get(2, 2);
    368     fSrcToDst[ 9] = srcToDst.get(0, 3);
    369     fSrcToDst[10] = srcToDst.get(1, 3);
    370     fSrcToDst[11] = srcToDst.get(2, 3);
    371     fSrcToDstIsIdentity = srcToDst.isIdentity();
    372 
    373     const int numSrcTables = num_tables(src);
    374     const size_t srcEntries = numSrcTables * 256;
    375     const bool srcGammasAreMatching = (1 >= numSrcTables);
    376     fSrcStorage.reset(srcEntries);
    377     build_gamma_tables(fSrcGammaTables, fSrcStorage.get(), 256, src, kToLinear,
    378                        srcGammasAreMatching);
    379 
    380     const int numDstTables = num_tables(dst);
    381     dst->toDstGammaTables(fDstGammaTables, &fDstStorage, numDstTables);
    382 
    383     if (src->gammaIsLinear()) {
    384         fSrcGamma = kLinear_SrcGamma;
    385     } else if (kSRGB_SkGammaNamed == src->gammaNamed()) {
    386         fSrcGamma = kSRGB_SrcGamma;
    387     } else {
    388         fSrcGamma = kTable_SrcGamma;
    389     }
    390 
    391     switch (dst->gammaNamed()) {
    392         case kSRGB_SkGammaNamed:
    393             fDstGamma = kSRGB_DstGamma;
    394             break;
    395         case k2Dot2Curve_SkGammaNamed:
    396             fDstGamma = k2Dot2_DstGamma;
    397             break;
    398         case kLinear_SkGammaNamed:
    399             fDstGamma = kLinear_DstGamma;
    400             break;
    401         default:
    402             fDstGamma = kTable_DstGamma;
    403             break;
    404     }
    405 }
    406 
    407 
    408 bool SkColorSpaceXform_XYZ::onApply(ColorFormat dstColorFormat, void* dst,
    409                                     ColorFormat srcColorFormat, const void* src,
    410                                     int len, SkAlphaType alphaType) const {
    411     if (fColorSpacesAreIdentical && kPremul_SkAlphaType != alphaType) {
    412         if ((kRGBA_8888_ColorFormat == dstColorFormat &&
    413              kRGBA_8888_ColorFormat == srcColorFormat) ||
    414             (kBGRA_8888_ColorFormat == dstColorFormat &&
    415              kBGRA_8888_ColorFormat == srcColorFormat))
    416         {
    417             memcpy(dst, src, len * sizeof(uint32_t));
    418             return true;
    419         }
    420 
    421         if ((kRGBA_8888_ColorFormat == dstColorFormat &&
    422              kBGRA_8888_ColorFormat == srcColorFormat) ||
    423             (kBGRA_8888_ColorFormat == dstColorFormat &&
    424              kRGBA_8888_ColorFormat == srcColorFormat))
    425         {
    426             SkOpts::RGBA_to_BGRA((uint32_t*)dst, src, len);
    427             return true;
    428         }
    429     }
    430 
    431     SkRasterPipeline_<256> pipeline;
    432 
    433     SkJumper_MemoryCtx src_ctx = { (void*)src, 0 },
    434                        dst_ctx = { (void*)dst, 0 };
    435 
    436     LoadTablesContext loadTables;
    437     switch (srcColorFormat) {
    438         case kRGBA_8888_ColorFormat:
    439             if (kLinear_SrcGamma == fSrcGamma) {
    440                 pipeline.append(SkRasterPipeline::load_8888, &src_ctx);
    441             } else {
    442                 loadTables.fSrc = src;
    443                 loadTables.fR = fSrcGammaTables[0];
    444                 loadTables.fG = fSrcGammaTables[1];
    445                 loadTables.fB = fSrcGammaTables[2];
    446                 pipeline.append(SkRasterPipeline::load_tables, &loadTables);
    447             }
    448 
    449             break;
    450         case kBGRA_8888_ColorFormat:
    451             if (kLinear_SrcGamma == fSrcGamma) {
    452                 pipeline.append(SkRasterPipeline::load_bgra, &src_ctx);
    453             } else {
    454                 loadTables.fSrc = src;
    455                 loadTables.fR = fSrcGammaTables[2];
    456                 loadTables.fG = fSrcGammaTables[1];
    457                 loadTables.fB = fSrcGammaTables[0];
    458                 pipeline.append(SkRasterPipeline::load_tables, &loadTables);
    459                 pipeline.append(SkRasterPipeline::swap_rb);
    460             }
    461 
    462             break;
    463         case kRGBA_F16_ColorFormat:
    464             if (kLinear_SrcGamma != fSrcGamma) {
    465                 return false;
    466             }
    467             pipeline.append(SkRasterPipeline::load_f16, &src_ctx);
    468             break;
    469         case kRGBA_F32_ColorFormat:
    470             if (kLinear_SrcGamma != fSrcGamma) {
    471                 return false;
    472             }
    473             pipeline.append(SkRasterPipeline::load_f32, &src_ctx);
    474             break;
    475         case kRGBA_U16_BE_ColorFormat:
    476             switch (fSrcGamma) {
    477                 case kLinear_SrcGamma:
    478                     pipeline.append(SkRasterPipeline::load_u16_be, &src_ctx);
    479                     break;
    480                 case kSRGB_SrcGamma:
    481                     pipeline.append(SkRasterPipeline::load_u16_be, &src_ctx);
    482                     pipeline.append(SkRasterPipeline::from_srgb);
    483                     break;
    484                 case kTable_SrcGamma:
    485                     loadTables.fSrc = src;
    486                     loadTables.fR = fSrcGammaTables[0];
    487                     loadTables.fG = fSrcGammaTables[1];
    488                     loadTables.fB = fSrcGammaTables[2];
    489                     pipeline.append(SkRasterPipeline::load_tables_u16_be, &loadTables);
    490                     break;
    491             }
    492             break;
    493         case kRGB_U16_BE_ColorFormat:
    494             switch (fSrcGamma) {
    495                 case kLinear_SrcGamma:
    496                     pipeline.append(SkRasterPipeline::load_rgb_u16_be, &src_ctx);
    497                     break;
    498                 case kSRGB_SrcGamma:
    499                     pipeline.append(SkRasterPipeline::load_rgb_u16_be, &src_ctx);
    500                     pipeline.append(SkRasterPipeline::from_srgb);
    501                     break;
    502                 case kTable_SrcGamma:
    503                     loadTables.fSrc = src;
    504                     loadTables.fR = fSrcGammaTables[0];
    505                     loadTables.fG = fSrcGammaTables[1];
    506                     loadTables.fB = fSrcGammaTables[2];
    507                     pipeline.append(SkRasterPipeline::load_tables_rgb_u16_be, &loadTables);
    508                     break;
    509             }
    510             break;
    511         default:
    512             return false;
    513     }
    514 
    515     if (!fSrcToDstIsIdentity) {
    516         pipeline.append(SkRasterPipeline::matrix_3x4, fSrcToDst);
    517 
    518         if (kRGBA_F16_ColorFormat != dstColorFormat &&
    519             kRGBA_F32_ColorFormat != dstColorFormat)
    520         {
    521             bool need_clamp_0, need_clamp_1;
    522             analyze_3x4_matrix(fSrcToDst, &need_clamp_0, &need_clamp_1);
    523 
    524             if (need_clamp_0) { pipeline.append(SkRasterPipeline::clamp_0); }
    525             if (need_clamp_1) { pipeline.append(SkRasterPipeline::clamp_1); }
    526         }
    527     }
    528 
    529     if (kPremul_SkAlphaType == alphaType && SkTransferFunctionBehavior::kRespect == fPremulBehavior)
    530     {
    531         pipeline.append(SkRasterPipeline::premul);
    532     }
    533 
    534     TablesContext tables;
    535     float to_2dot2 = 1/2.2f;
    536     switch (fDstGamma) {
    537         case kSRGB_DstGamma:
    538             pipeline.append(SkRasterPipeline::to_srgb);
    539             break;
    540         case k2Dot2_DstGamma:
    541             pipeline.append(SkRasterPipeline::gamma, &to_2dot2);
    542             break;
    543         case kTable_DstGamma:
    544             tables.fR = fDstGammaTables[0];
    545             tables.fG = fDstGammaTables[1];
    546             tables.fB = fDstGammaTables[2];
    547             tables.fCount = SkColorSpaceXform_Base::kDstGammaTableSize;
    548             pipeline.append(SkRasterPipeline::byte_tables_rgb, &tables);
    549         default:
    550             break;
    551     }
    552 
    553     if (kPremul_SkAlphaType == alphaType && SkTransferFunctionBehavior::kIgnore == fPremulBehavior)
    554     {
    555         pipeline.append(SkRasterPipeline::premul);
    556     }
    557 
    558     switch (dstColorFormat) {
    559         case kRGBA_8888_ColorFormat:
    560              pipeline.append(SkRasterPipeline::store_8888, &dst_ctx);
    561             break;
    562         case kBGRA_8888_ColorFormat:
    563             pipeline.append(SkRasterPipeline::store_bgra, &dst_ctx);
    564             break;
    565         case kRGBA_F16_ColorFormat:
    566             if (kLinear_DstGamma != fDstGamma) {
    567                 return false;
    568             }
    569             pipeline.append(SkRasterPipeline::store_f16, &dst_ctx);
    570             break;
    571         case kRGBA_F32_ColorFormat:
    572             if (kLinear_DstGamma != fDstGamma) {
    573                 return false;
    574             }
    575             pipeline.append(SkRasterPipeline::store_f32, &dst_ctx);
    576             break;
    577         case kBGR_565_ColorFormat:
    578             if (kOpaque_SkAlphaType != alphaType) {
    579                 return false;
    580             }
    581             pipeline.append(SkRasterPipeline::store_565, &dst_ctx);
    582             break;
    583         default:
    584             return false;
    585     }
    586     pipeline.run(0,0, len,1);
    587     return true;
    588 }
    589 
    590 std::unique_ptr<SkColorSpaceXform> SlowIdentityXform(SkColorSpace_XYZ* space) {
    591     auto xform = skstd::make_unique<SkColorSpaceXform_XYZ>(space, space,
    592                                                            SkTransferFunctionBehavior::kRespect);
    593     xform->pretendNotToBeIdentityForTesting();
    594     return std::move(xform);
    595 }
    596 
    597 bool SkColorSpaceXform::apply(ColorFormat dstColorFormat, void* dst,
    598                               ColorFormat srcColorFormat, const void* src,
    599                               int len, SkAlphaType alphaType) const {
    600     return ((SkColorSpaceXform_Base*) this)->onApply(dstColorFormat, dst,
    601                                                      srcColorFormat, src,
    602                                                      len, alphaType);
    603 }
    604 
    605 bool SkColorSpaceXform::Apply(SkColorSpace* dstCS, ColorFormat dstFormat, void* dst,
    606                               SkColorSpace* srcCS, ColorFormat srcFormat, const void* src,
    607                               int len, AlphaOp op) {
    608     SkAlphaType at;
    609     switch (op) {
    610         case kPreserve_AlphaOp:    at = kUnpremul_SkAlphaType; break;
    611         case kPremul_AlphaOp:      at = kPremul_SkAlphaType;   break;
    612         case kSrcIsOpaque_AlphaOp: at = kOpaque_SkAlphaType;   break;
    613     }
    614     return New(srcCS, dstCS)->apply(dstFormat, dst, srcFormat, src, len, at);
    615 }
    616