• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkColorPriv.h"
9 #include "SkColorSpace_A2B.h"
10 #include "SkColorSpace_Base.h"
11 #include "SkColorSpace_XYZ.h"
12 #include "SkColorSpacePriv.h"
13 #include "SkColorSpaceXform_A2B.h"
14 #include "SkColorSpaceXform_Base.h"
15 #include "SkColorSpaceXformPriv.h"
16 #include "SkHalf.h"
17 #include "SkOpts.h"
18 #include "SkPM4fPriv.h"
19 #include "SkRasterPipeline.h"
20 #include "SkSRGB.h"
21 
22 static constexpr float sk_linear_from_2dot2[256] = {
23         0.000000000000000000f, 0.000005077051900662f, 0.000023328004666099f, 0.000056921765712193f,
24         0.000107187362341244f, 0.000175123977503027f, 0.000261543754548491f, 0.000367136269815943f,
25         0.000492503787191433f, 0.000638182842167022f, 0.000804658499513058f, 0.000992374304074325f,
26         0.001201739522438400f, 0.001433134589671860f, 0.001686915316789280f, 0.001963416213396470f,
27         0.002262953160706430f, 0.002585825596234170f, 0.002932318323938360f, 0.003302703032003640f,
28         0.003697239578900130f, 0.004116177093282750f, 0.004559754922526020f, 0.005028203456855540f,
29         0.005521744850239660f, 0.006040593654849810f, 0.006584957382581690f, 0.007155037004573030f,
30         0.007751027397660610f, 0.008373117745148580f, 0.009021491898012130f, 0.009696328701658230f,
31         0.010397802292555300f, 0.011126082368383200f, 0.011881334434813700f, 0.012663720031582100f,
32         0.013473396940142600f, 0.014310519374884100f, 0.015175238159625200f, 0.016067700890886900f,
33         0.016988052089250000f, 0.017936433339950200f, 0.018912983423721500f, 0.019917838438785700f,
34         0.020951131914781100f, 0.022012994919336500f, 0.023103556157921400f, 0.024222942067534200f,
35         0.025371276904734600f, 0.026548682828472900f, 0.027755279978126000f, 0.028991186547107800f,
36         0.030256518852388700f, 0.031551391400226400f, 0.032875916948383800f, 0.034230206565082000f,
37         0.035614369684918800f, 0.037028514161960200f, 0.038472746320194600f, 0.039947171001525600f,
38         0.041451891611462500f, 0.042987010162657100f, 0.044552627316421400f, 0.046148842422351000f,
39         0.047775753556170600f, 0.049433457555908000f, 0.051122050056493400f, 0.052841625522879000f,
40         0.054592277281760300f, 0.056374097551979800f, 0.058187177473685400f, 0.060031607136313200f,
41         0.061907475605455800f, 0.063814870948677200f, 0.065753880260330100f, 0.067724589685424300f,
42         0.069727084442598800f, 0.071761448846239100f, 0.073827766327784600f, 0.075926119456264800f,
43         0.078056589958101900f, 0.080219258736215100f, 0.082414205888459200f, 0.084641510725429500f,
44         0.086901251787660300f, 0.089193506862247800f, 0.091518352998919500f, 0.093875866525577800f,
45         0.096266123063339700f, 0.098689197541094500f, 0.101145164209600000f, 0.103634096655137000f,
46         0.106156067812744000f, 0.108711149979039000f, 0.111299414824660000f, 0.113920933406333000f,
47         0.116575776178572000f, 0.119264013005047000f, 0.121985713169619000f, 0.124740945387051000f,
48         0.127529777813422000f, 0.130352278056244000f, 0.133208513184300000f, 0.136098549737202000f,
49         0.139022453734703000f, 0.141980290685736000f, 0.144972125597231000f, 0.147998022982685000f,
50         0.151058046870511000f, 0.154152260812165000f, 0.157280727890073000f, 0.160443510725344000f,
51         0.163640671485290000f, 0.166872271890766000f, 0.170138373223312000f, 0.173439036332135000f,
52         0.176774321640903000f, 0.180144289154390000f, 0.183548998464951000f, 0.186988508758844000f,
53         0.190462878822409000f, 0.193972167048093000f, 0.197516431440340000f, 0.201095729621346000f,
54         0.204710118836677000f, 0.208359655960767000f, 0.212044397502288000f, 0.215764399609395000f,
55         0.219519718074868000f, 0.223310408341127000f, 0.227136525505149000f, 0.230998124323267000f,
56         0.234895259215880000f, 0.238827984272048000f, 0.242796353254002000f, 0.246800419601550000f,
57         0.250840236436400000f, 0.254915856566385000f, 0.259027332489606000f, 0.263174716398492000f,
58         0.267358060183772000f, 0.271577415438375000f, 0.275832833461245000f, 0.280124365261085000f,
59         0.284452061560024000f, 0.288815972797219000f, 0.293216149132375000f, 0.297652640449211000f,
60         0.302125496358853000f, 0.306634766203158000f, 0.311180499057984000f, 0.315762743736397000f,
61         0.320381548791810000f, 0.325036962521076000f, 0.329729032967515000f, 0.334457807923889000f,
62         0.339223334935327000f, 0.344025661302187000f, 0.348864834082879000f, 0.353740900096629000f,
63         0.358653905926199000f, 0.363603897920553000f, 0.368590922197487000f, 0.373615024646202000f,
64         0.378676250929840000f, 0.383774646487975000f, 0.388910256539059000f, 0.394083126082829000f,
65         0.399293299902674000f, 0.404540822567962000f, 0.409825738436323000f, 0.415148091655907000f,
66         0.420507926167587000f, 0.425905285707146000f, 0.431340213807410000f, 0.436812753800359000f,
67         0.442322948819202000f, 0.447870841800410000f, 0.453456475485731000f, 0.459079892424160000f,
68         0.464741134973889000f, 0.470440245304218000f, 0.476177265397440000f, 0.481952237050698000f,
69         0.487765201877811000f, 0.493616201311074000f, 0.499505276603030000f, 0.505432468828216000f,
70         0.511397818884880000f, 0.517401367496673000f, 0.523443155214325000f, 0.529523222417277000f,
71         0.535641609315311000f, 0.541798355950137000f, 0.547993502196972000f, 0.554227087766085000f,
72         0.560499152204328000f, 0.566809734896638000f, 0.573158875067523000f, 0.579546611782525000f,
73         0.585972983949661000f, 0.592438030320847000f, 0.598941789493296000f, 0.605484299910907000f,
74         0.612065599865624000f, 0.618685727498780000f, 0.625344720802427000f, 0.632042617620641000f,
75         0.638779455650817000f, 0.645555272444935000f, 0.652370105410821000f, 0.659223991813387000f,
76         0.666116968775851000f, 0.673049073280942000f, 0.680020342172095000f, 0.687030812154625000f,
77         0.694080519796882000f, 0.701169501531402000f, 0.708297793656032000f, 0.715465432335048000f,
78         0.722672453600255000f, 0.729918893352071000f, 0.737204787360605000f, 0.744530171266715000f,
79         0.751895080583051000f, 0.759299550695091000f, 0.766743616862161000f, 0.774227314218442000f,
80         0.781750677773962000f, 0.789313742415586000f, 0.796916542907978000f, 0.804559113894567000f,
81         0.812241489898490000f, 0.819963705323528000f, 0.827725794455034000f, 0.835527791460841000f,
82         0.843369730392169000f, 0.851251645184515000f, 0.859173569658532000f, 0.867135537520905000f,
83         0.875137582365205000f, 0.883179737672745000f, 0.891262036813419000f, 0.899384513046529000f,
84         0.907547199521614000f, 0.915750129279253000f, 0.923993335251873000f, 0.932276850264543000f,
85         0.940600707035753000f, 0.948964938178195000f, 0.957369576199527000f, 0.965814653503130000f,
86         0.974300202388861000f, 0.982826255053791000f, 0.991392843592940000f, 1.000000000000000000f,
87 };
88 
89 ///////////////////////////////////////////////////////////////////////////////////////////////////
90 
build_table_linear_from_gamma(float * outTable,float exponent)91 static void build_table_linear_from_gamma(float* outTable, float exponent) {
92     for (float x = 0.0f; x <= 1.0f; x += (1.0f/255.0f)) {
93         *outTable++ = powf(x, exponent);
94     }
95 }
96 
97 // outTable is always 256 entries, inTable may be larger or smaller.
build_table_linear_from_gamma(float * outTable,const float * inTable,int inTableSize)98 static void build_table_linear_from_gamma(float* outTable, const float* inTable,
99                                           int inTableSize) {
100     if (256 == inTableSize) {
101         memcpy(outTable, inTable, sizeof(float) * 256);
102         return;
103     }
104 
105     for (float x = 0.0f; x <= 1.0f; x += (1.0f/255.0f)) {
106         *outTable++ = interp_lut(x, inTable, inTableSize);
107     }
108 }
109 
110 
build_table_linear_from_gamma(float * outTable,float g,float a,float b,float c,float d,float e,float f)111 static void build_table_linear_from_gamma(float* outTable, float g, float a, float b, float c,
112                                           float d, float e, float f) {
113     // Y = (aX + b)^g + e  for X >= d
114     // Y = cX + f          otherwise
115     for (float x = 0.0f; x <= 1.0f; x += (1.0f/255.0f)) {
116         if (x >= d) {
117             *outTable++ = clamp_0_1(powf(a * x + b, g) + e);
118         } else {
119             *outTable++ = clamp_0_1(c * x + f);
120         }
121     }
122 }
123 
124 ///////////////////////////////////////////////////////////////////////////////////////////////////
125 
126 static const int kDstGammaTableSize = SkColorSpaceXform_Base::kDstGammaTableSize;
127 
build_table_linear_to_gamma(uint8_t * outTable,float exponent)128 static void build_table_linear_to_gamma(uint8_t* outTable, float exponent) {
129     float toGammaExp = 1.0f / exponent;
130 
131     for (int i = 0; i < kDstGammaTableSize; i++) {
132         float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1)));
133         outTable[i] = clamp_normalized_float_to_byte(powf(x, toGammaExp));
134     }
135 }
136 
build_table_linear_to_gamma(uint8_t * outTable,const float * inTable,int inTableSize)137 static void build_table_linear_to_gamma(uint8_t* outTable, const float* inTable,
138                                         int inTableSize) {
139     invert_table_gamma(nullptr, outTable, kDstGammaTableSize, inTable, inTableSize);
140 }
141 
inverse_parametric(float x,float g,float a,float b,float c,float d,float e,float f)142 static float inverse_parametric(float x, float g, float a, float b, float c, float d, float e,
143                                 float f) {
144     // We need to take the inverse of the following piecewise function.
145     // Y = (aX + b)^g + c  for X >= d
146     // Y = eX + f          otherwise
147 
148     // Assume that the gamma function is continuous, or this won't make much sense anyway.
149     // Plug in |d| to the first equation to calculate the new piecewise interval.
150     // Then simply use the inverse of the original functions.
151     float interval = c * d + f;
152     if (x < interval) {
153         // X = (Y - F) / C
154         if (0.0f == c) {
155             // The gamma curve for this segment is constant, so the inverse is undefined.
156             // Since this is the lower segment, guess zero.
157             return 0.0f;
158         }
159 
160         return (x - f) / c;
161     }
162 
163     // X = ((Y - E)^(1 / G) - B) / A
164     if (0.0f == a || 0.0f == g) {
165         // The gamma curve for this segment is constant, so the inverse is undefined.
166         // Since this is the upper segment, guess one.
167         return 1.0f;
168     }
169 
170     return (powf(x - e, 1.0f / g) - b) / a;
171 }
172 
build_table_linear_to_gamma(uint8_t * outTable,float g,float a,float b,float c,float d,float e,float f)173 static void build_table_linear_to_gamma(uint8_t* outTable, float g, float a,
174                                         float b, float c, float d, float e, float f) {
175     for (int i = 0; i < kDstGammaTableSize; i++) {
176         float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1)));
177         float y = inverse_parametric(x, g, a, b, c, d, e, f);
178         outTable[i] = clamp_normalized_float_to_byte(y);
179     }
180 }
181 
182 ///////////////////////////////////////////////////////////////////////////////////////////////////
183 
184 template <typename T>
185 struct GammaFns {
186     const T* fSRGBTable;
187     const T* f2Dot2Table;
188     void (*fBuildFromValue)(T*, float);
189     void (*fBuildFromTable)(T*, const float*, int);
190     void (*fBuildFromParam)(T*, float, float, float, float, float, float, float);
191 };
192 
193 static const GammaFns<float> kToLinear {
194     sk_linear_from_srgb,
195     sk_linear_from_2dot2,
196     &build_table_linear_from_gamma,
197     &build_table_linear_from_gamma,
198     &build_table_linear_from_gamma,
199 };
200 
201 static const GammaFns<uint8_t> kFromLinear {
202     nullptr,
203     nullptr,
204     &build_table_linear_to_gamma,
205     &build_table_linear_to_gamma,
206     &build_table_linear_to_gamma,
207 };
208 
209 // Build tables to transform src gamma to linear.
210 template <typename T>
build_gamma_tables(const T * outGammaTables[3],T * gammaTableStorage,int gammaTableSize,const SkColorSpace_XYZ * space,const GammaFns<T> & fns,bool gammasAreMatching)211 static void build_gamma_tables(const T* outGammaTables[3], T* gammaTableStorage, int gammaTableSize,
212                                const SkColorSpace_XYZ* space, const GammaFns<T>& fns,
213                                bool gammasAreMatching)
214 {
215     switch (space->gammaNamed()) {
216         case kSRGB_SkGammaNamed:
217             outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.fSRGBTable;
218             break;
219         case k2Dot2Curve_SkGammaNamed:
220             outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.f2Dot2Table;
221             break;
222         case kLinear_SkGammaNamed:
223             outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = nullptr;
224             break;
225         default: {
226             const SkGammas* gammas = space->gammas();
227             SkASSERT(gammas);
228 
229             auto build_table = [=](int i) {
230                 if (gammas->isNamed(i)) {
231                     switch (gammas->data(i).fNamed) {
232                         case kSRGB_SkGammaNamed:
233                             (*fns.fBuildFromParam)(&gammaTableStorage[i * gammaTableSize],
234                                                    gSRGB_TransferFn.fG,
235                                                    gSRGB_TransferFn.fA,
236                                                    gSRGB_TransferFn.fB,
237                                                    gSRGB_TransferFn.fC,
238                                                    gSRGB_TransferFn.fD,
239                                                    gSRGB_TransferFn.fE,
240                                                    gSRGB_TransferFn.fF);
241                             outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
242                             break;
243                         case k2Dot2Curve_SkGammaNamed:
244                             (*fns.fBuildFromValue)(&gammaTableStorage[i * gammaTableSize], 2.2f);
245                             outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
246                             break;
247                         case kLinear_SkGammaNamed:
248                             (*fns.fBuildFromValue)(&gammaTableStorage[i * gammaTableSize], 1.0f);
249                             outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
250                             break;
251                         default:
252                             SkASSERT(false);
253                             break;
254                     }
255                 } else if (gammas->isValue(i)) {
256                     (*fns.fBuildFromValue)(&gammaTableStorage[i * gammaTableSize],
257                                            gammas->data(i).fValue);
258                     outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
259                 } else if (gammas->isTable(i)) {
260                     (*fns.fBuildFromTable)(&gammaTableStorage[i * gammaTableSize], gammas->table(i),
261                                            gammas->data(i).fTable.fSize);
262                     outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
263                 } else {
264                     SkASSERT(gammas->isParametric(i));
265                     const SkColorSpaceTransferFn& params = gammas->params(i);
266                     (*fns.fBuildFromParam)(&gammaTableStorage[i * gammaTableSize], params.fG,
267                                            params.fA, params.fB, params.fC, params.fD, params.fE,
268                                            params.fF);
269                     outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
270                 }
271             };
272 
273             if (gammasAreMatching) {
274                 build_table(0);
275                 outGammaTables[1] = outGammaTables[0];
276                 outGammaTables[2] = outGammaTables[0];
277             } else {
278                 build_table(0);
279                 build_table(1);
280                 build_table(2);
281             }
282 
283             break;
284         }
285     }
286 }
287 
BuildDstGammaTables(const uint8_t * dstGammaTables[3],uint8_t * dstStorage,const SkColorSpace_XYZ * space,bool gammasAreMatching)288 void SkColorSpaceXform_Base::BuildDstGammaTables(const uint8_t* dstGammaTables[3],
289                                                  uint8_t* dstStorage,
290                                                  const SkColorSpace_XYZ* space,
291                                                  bool gammasAreMatching) {
292     build_gamma_tables(dstGammaTables, dstStorage, kDstGammaTableSize, space, kFromLinear,
293                        gammasAreMatching);
294 }
295 
296 ///////////////////////////////////////////////////////////////////////////////////////////////////
297 
New(SkColorSpace * srcSpace,SkColorSpace * dstSpace)298 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(SkColorSpace* srcSpace,
299                                                           SkColorSpace* dstSpace) {
300     return SkColorSpaceXform_Base::New(srcSpace, dstSpace, SkTransferFunctionBehavior::kRespect);
301 }
302 
New(SkColorSpace * srcSpace,SkColorSpace * dstSpace,SkTransferFunctionBehavior premulBehavior)303 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform_Base::New(SkColorSpace* srcSpace,
304         SkColorSpace* dstSpace, SkTransferFunctionBehavior premulBehavior) {
305 
306     if (!srcSpace || !dstSpace) {
307         // Invalid input
308         return nullptr;
309     }
310 
311     if (SkColorSpace_Base::Type::kA2B == as_CSB(dstSpace)->type()) {
312         SkCSXformPrintf("A2B destinations not supported\n");
313         return nullptr;
314     }
315 
316     if (SkColorSpace_Base::Type::kA2B == as_CSB(srcSpace)->type()) {
317         SkColorSpace_A2B* src = static_cast<SkColorSpace_A2B*>(srcSpace);
318         SkColorSpace_XYZ* dst = static_cast<SkColorSpace_XYZ*>(dstSpace);
319         return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_A2B(src, dst));
320     }
321     SkColorSpace_XYZ* srcSpaceXYZ = static_cast<SkColorSpace_XYZ*>(srcSpace);
322     SkColorSpace_XYZ* dstSpaceXYZ = static_cast<SkColorSpace_XYZ*>(dstSpace);
323 
324     ColorSpaceMatch csm = kNone_ColorSpaceMatch;
325     SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor);
326     if (SkColorSpace::Equals(srcSpace, dstSpace)) {
327         srcToDst.setIdentity();
328         csm = kFull_ColorSpaceMatch;
329     } else {
330         if (srcSpaceXYZ->toXYZD50Hash() == dstSpaceXYZ->toXYZD50Hash()) {
331             SkASSERT(*srcSpaceXYZ->toXYZD50() == *dstSpaceXYZ->toXYZD50() && "Hash collision");
332             srcToDst.setIdentity();
333             csm = kGamut_ColorSpaceMatch;
334         } else {
335             srcToDst.setConcat(*dstSpaceXYZ->fromXYZD50(), *srcSpaceXYZ->toXYZD50());
336         }
337     }
338 
339     switch (csm) {
340         case kNone_ColorSpaceMatch:
341             return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_XYZ
342                     <kNone_ColorSpaceMatch>(srcSpaceXYZ, srcToDst, dstSpaceXYZ, premulBehavior));
343         case kGamut_ColorSpaceMatch:
344             return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_XYZ
345                     <kGamut_ColorSpaceMatch>(srcSpaceXYZ, srcToDst, dstSpaceXYZ, premulBehavior));
346         case kFull_ColorSpaceMatch:
347             return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_XYZ
348                     <kFull_ColorSpaceMatch>(srcSpaceXYZ, srcToDst, dstSpaceXYZ, premulBehavior));
349         default:
350             SkASSERT(false);
351             return nullptr;
352     }
353 }
354 
355 ///////////////////////////////////////////////////////////////////////////////////////////////////
356 
357 #define AI SK_ALWAYS_INLINE
358 
load_matrix(const float matrix[13],Sk4f & rXgXbX,Sk4f & rYgYbY,Sk4f & rZgZbZ,Sk4f & rTgTbT)359 static AI void load_matrix(const float matrix[13],
360                            Sk4f& rXgXbX, Sk4f& rYgYbY, Sk4f& rZgZbZ, Sk4f& rTgTbT) {
361     rXgXbX = Sk4f::Load(matrix + 0);
362     rYgYbY = Sk4f::Load(matrix + 3);
363     rZgZbZ = Sk4f::Load(matrix + 6);
364     rTgTbT = Sk4f::Load(matrix + 9);
365 }
366 
367 enum Order {
368     kRGBA_Order,
369     kBGRA_Order,
370 };
371 
set_rb_shifts(Order kOrder,int * kRShift,int * kBShift)372 static AI void set_rb_shifts(Order kOrder, int* kRShift, int* kBShift) {
373     if (kRGBA_Order == kOrder) {
374         *kRShift = 0;
375         *kBShift = 16;
376     } else {
377         *kRShift = 16;
378         *kBShift = 0;
379     }
380 }
381 
382 template <Order kOrder>
load_rgb_from_tables(const uint32_t * src,Sk4f & r,Sk4f & g,Sk4f & b,Sk4f & a,const float * const srcTables[3])383 static AI void load_rgb_from_tables(const uint32_t* src,
384                                     Sk4f& r, Sk4f& g, Sk4f& b, Sk4f& a,
385                                     const float* const srcTables[3]) {
386     int kRShift, kGShift = 8, kBShift;
387     set_rb_shifts(kOrder, &kRShift, &kBShift);
388     r = { srcTables[0][(src[0] >> kRShift) & 0xFF],
389           srcTables[0][(src[1] >> kRShift) & 0xFF],
390           srcTables[0][(src[2] >> kRShift) & 0xFF],
391           srcTables[0][(src[3] >> kRShift) & 0xFF], };
392     g = { srcTables[1][(src[0] >> kGShift) & 0xFF],
393           srcTables[1][(src[1] >> kGShift) & 0xFF],
394           srcTables[1][(src[2] >> kGShift) & 0xFF],
395           srcTables[1][(src[3] >> kGShift) & 0xFF], };
396     b = { srcTables[2][(src[0] >> kBShift) & 0xFF],
397           srcTables[2][(src[1] >> kBShift) & 0xFF],
398           srcTables[2][(src[2] >> kBShift) & 0xFF],
399           srcTables[2][(src[3] >> kBShift) & 0xFF], };
400     a = 0.0f; // Don't let the compiler complain that |a| is uninitialized.
401 }
402 
403 template <Order kOrder>
load_rgba_from_tables(const uint32_t * src,Sk4f & r,Sk4f & g,Sk4f & b,Sk4f & a,const float * const srcTables[3])404 static AI void load_rgba_from_tables(const uint32_t* src,
405                                      Sk4f& r, Sk4f& g, Sk4f& b, Sk4f& a,
406                                      const float* const srcTables[3]) {
407     int kRShift, kGShift = 8, kBShift;
408     set_rb_shifts(kOrder, &kRShift, &kBShift);
409     r = { srcTables[0][(src[0] >> kRShift) & 0xFF],
410           srcTables[0][(src[1] >> kRShift) & 0xFF],
411           srcTables[0][(src[2] >> kRShift) & 0xFF],
412           srcTables[0][(src[3] >> kRShift) & 0xFF], };
413     g = { srcTables[1][(src[0] >> kGShift) & 0xFF],
414           srcTables[1][(src[1] >> kGShift) & 0xFF],
415           srcTables[1][(src[2] >> kGShift) & 0xFF],
416           srcTables[1][(src[3] >> kGShift) & 0xFF], };
417     b = { srcTables[2][(src[0] >> kBShift) & 0xFF],
418           srcTables[2][(src[1] >> kBShift) & 0xFF],
419           srcTables[2][(src[2] >> kBShift) & 0xFF],
420           srcTables[2][(src[3] >> kBShift) & 0xFF], };
421     a = (1.0f / 255.0f) * SkNx_cast<float>(Sk4u::Load(src) >> 24);
422 }
423 
424 template <Order kOrder>
load_rgb_linear(const uint32_t * src,Sk4f & r,Sk4f & g,Sk4f & b,Sk4f & a,const float * const[3])425 static AI void load_rgb_linear(const uint32_t* src, Sk4f& r, Sk4f& g, Sk4f& b, Sk4f& a,
426                                const float* const[3]) {
427     int kRShift, kGShift = 8, kBShift;
428     set_rb_shifts(kOrder, &kRShift, &kBShift);
429     r = (1.0f / 255.0f) * SkNx_cast<float>((Sk4u::Load(src) >> kRShift) & 0xFF);
430     g = (1.0f / 255.0f) * SkNx_cast<float>((Sk4u::Load(src) >> kGShift) & 0xFF);
431     b = (1.0f / 255.0f) * SkNx_cast<float>((Sk4u::Load(src) >> kBShift) & 0xFF);
432     a = 0.0f; // Don't let the compiler complain that |a| is uninitialized.
433 }
434 
435 template <Order kOrder>
load_rgba_linear(const uint32_t * src,Sk4f & r,Sk4f & g,Sk4f & b,Sk4f & a,const float * const[3])436 static AI void load_rgba_linear(const uint32_t* src, Sk4f& r, Sk4f& g, Sk4f& b, Sk4f& a,
437                                 const float* const[3]) {
438     int kRShift, kGShift = 8, kBShift;
439     set_rb_shifts(kOrder, &kRShift, &kBShift);
440     r = (1.0f / 255.0f) * SkNx_cast<float>((Sk4u::Load(src) >> kRShift) & 0xFF);
441     g = (1.0f / 255.0f) * SkNx_cast<float>((Sk4u::Load(src) >> kGShift) & 0xFF);
442     b = (1.0f / 255.0f) * SkNx_cast<float>((Sk4u::Load(src) >> kBShift) & 0xFF);
443     a = (1.0f / 255.0f) * SkNx_cast<float>((Sk4u::Load(src) >> 24));
444 }
445 
446 template <Order kOrder>
load_rgb_from_tables_1(const uint32_t * src,Sk4f & r,Sk4f & g,Sk4f & b,Sk4f & a,const float * const srcTables[3])447 static AI void load_rgb_from_tables_1(const uint32_t* src,
448                                       Sk4f& r, Sk4f& g, Sk4f& b, Sk4f& a,
449                                       const float* const srcTables[3]) {
450     int kRShift, kGShift = 8, kBShift;
451     set_rb_shifts(kOrder, &kRShift, &kBShift);
452     r = Sk4f(srcTables[0][(*src >> kRShift) & 0xFF]);
453     g = Sk4f(srcTables[1][(*src >> kGShift) & 0xFF]);
454     b = Sk4f(srcTables[2][(*src >> kBShift) & 0xFF]);
455     a = 0.0f; // Don't let MSAN complain that |a| is uninitialized.
456 }
457 
458 template <Order kOrder>
load_rgba_from_tables_1(const uint32_t * src,Sk4f & r,Sk4f & g,Sk4f & b,Sk4f & a,const float * const srcTables[3])459 static AI void load_rgba_from_tables_1(const uint32_t* src,
460                                        Sk4f& r, Sk4f& g, Sk4f& b, Sk4f& a,
461                                        const float* const srcTables[3]) {
462     int kRShift, kGShift = 8, kBShift;
463     set_rb_shifts(kOrder, &kRShift, &kBShift);
464     r = Sk4f(srcTables[0][(*src >> kRShift) & 0xFF]);
465     g = Sk4f(srcTables[1][(*src >> kGShift) & 0xFF]);
466     b = Sk4f(srcTables[2][(*src >> kBShift) & 0xFF]);
467     a = (1.0f / 255.0f) * Sk4f(*src >> 24);
468 }
469 
470 template <Order kOrder>
load_rgb_linear_1(const uint32_t * src,Sk4f & r,Sk4f & g,Sk4f & b,Sk4f & a,const float * const srcTables[3])471 static AI void load_rgb_linear_1(const uint32_t* src,
472                                  Sk4f& r, Sk4f& g, Sk4f& b, Sk4f& a,
473                                  const float* const srcTables[3]) {
474     int kRShift, kGShift = 8, kBShift;
475     set_rb_shifts(kOrder, &kRShift, &kBShift);
476     r = Sk4f((1.0f / 255.0f) * ((*src >> kRShift) & 0xFF));
477     g = Sk4f((1.0f / 255.0f) * ((*src >> kGShift) & 0xFF));
478     b = Sk4f((1.0f / 255.0f) * ((*src >> kBShift) & 0xFF));
479     a = 0.0f; // Don't let MSAN complain that |a| is uninitialized.
480 }
481 
482 template <Order kOrder>
load_rgba_linear_1(const uint32_t * src,Sk4f & r,Sk4f & g,Sk4f & b,Sk4f & a,const float * const srcTables[3])483 static AI void load_rgba_linear_1(const uint32_t* src,
484                                   Sk4f& r, Sk4f& g, Sk4f& b, Sk4f& a,
485                                   const float* const srcTables[3]) {
486     int kRShift, kGShift = 8, kBShift;
487     set_rb_shifts(kOrder, &kRShift, &kBShift);
488     r = Sk4f((1.0f / 255.0f) * ((*src >> kRShift) & 0xFF));
489     g = Sk4f((1.0f / 255.0f) * ((*src >> kGShift) & 0xFF));
490     b = Sk4f((1.0f / 255.0f) * ((*src >> kBShift) & 0xFF));
491     a = Sk4f((1.0f / 255.0f) * ((*src >> 24)));
492 }
493 
transform_gamut(const Sk4f & r,const Sk4f & g,const Sk4f & b,const Sk4f & a,const Sk4f & rXgXbX,const Sk4f & rYgYbY,const Sk4f & rZgZbZ,Sk4f & dr,Sk4f & dg,Sk4f & db,Sk4f & da)494 static AI void transform_gamut(const Sk4f& r, const Sk4f& g, const Sk4f& b, const Sk4f& a,
495                                const Sk4f& rXgXbX, const Sk4f& rYgYbY, const Sk4f& rZgZbZ,
496                                Sk4f& dr, Sk4f& dg, Sk4f& db, Sk4f& da) {
497     dr = rXgXbX[0]*r + rYgYbY[0]*g + rZgZbZ[0]*b;
498     dg = rXgXbX[1]*r + rYgYbY[1]*g + rZgZbZ[1]*b;
499     db = rXgXbX[2]*r + rYgYbY[2]*g + rZgZbZ[2]*b;
500     da = a;
501 }
502 
transform_gamut_1(const Sk4f & r,const Sk4f & g,const Sk4f & b,const Sk4f & rXgXbX,const Sk4f & rYgYbY,const Sk4f & rZgZbZ,Sk4f & rgba)503 static AI void transform_gamut_1(const Sk4f& r, const Sk4f& g, const Sk4f& b,
504                                  const Sk4f& rXgXbX, const Sk4f& rYgYbY, const Sk4f& rZgZbZ,
505                                  Sk4f& rgba) {
506     rgba = rXgXbX*r + rYgYbY*g + rZgZbZ*b;
507 }
508 
translate_gamut(const Sk4f & rTgTbT,Sk4f & dr,Sk4f & dg,Sk4f & db)509 static AI void translate_gamut(const Sk4f& rTgTbT, Sk4f& dr, Sk4f& dg, Sk4f& db) {
510     dr = dr + rTgTbT[0];
511     dg = dg + rTgTbT[1];
512     db = db + rTgTbT[2];
513 }
514 
translate_gamut_1(const Sk4f & rTgTbT,Sk4f & rgba)515 static AI void translate_gamut_1(const Sk4f& rTgTbT, Sk4f& rgba) {
516     rgba = rgba + rTgTbT;
517 }
518 
519 template <Order kOrder>
store_srgb(void * dst,const uint32_t * src,Sk4f & dr,Sk4f & dg,Sk4f & db,Sk4f &,const uint8_t * const[3])520 static AI void store_srgb(void* dst, const uint32_t* src, Sk4f& dr, Sk4f& dg, Sk4f& db, Sk4f&,
521                           const uint8_t* const[3]) {
522     int kRShift, kGShift = 8, kBShift;
523     set_rb_shifts(kOrder, &kRShift, &kBShift);
524     dr = sk_linear_to_srgb_needs_trunc(dr);
525     dg = sk_linear_to_srgb_needs_trunc(dg);
526     db = sk_linear_to_srgb_needs_trunc(db);
527 
528     dr = sk_clamp_0_255(dr);
529     dg = sk_clamp_0_255(dg);
530     db = sk_clamp_0_255(db);
531 
532     Sk4i da = Sk4i::Load(src) & 0xFF000000;
533 
534     Sk4i rgba = (SkNx_cast<int>(dr) << kRShift)
535               | (SkNx_cast<int>(dg) << kGShift)
536               | (SkNx_cast<int>(db) << kBShift)
537               | (da                           );
538     rgba.store(dst);
539 }
540 
541 template <Order kOrder>
store_srgb_1(void * dst,const uint32_t * src,Sk4f & rgba,const Sk4f &,const uint8_t * const[3])542 static AI void store_srgb_1(void* dst, const uint32_t* src,
543                             Sk4f& rgba, const Sk4f&,
544                             const uint8_t* const[3]) {
545     rgba = sk_clamp_0_255(sk_linear_to_srgb_needs_trunc(rgba));
546 
547     uint32_t tmp;
548     SkNx_cast<uint8_t>(SkNx_cast<int32_t>(rgba)).store(&tmp);
549     tmp = (*src & 0xFF000000) | (tmp & 0x00FFFFFF);
550     if (kBGRA_Order == kOrder) {
551         tmp = SkSwizzle_RB(tmp);
552     }
553 
554     *(uint32_t*)dst = tmp;
555 }
556 
linear_to_2dot2(const Sk4f & x)557 static AI Sk4f linear_to_2dot2(const Sk4f& x) {
558     // x^(29/64) is a very good approximation of the true value, x^(1/2.2).
559     auto x2  = x.rsqrt(),                            // x^(-1/2)
560          x32 = x2.rsqrt().rsqrt().rsqrt().rsqrt(),   // x^(-1/32)
561          x64 = x32.rsqrt();                          // x^(+1/64)
562 
563     // 29 = 32 - 2 - 1
564     return 255.0f * x2.invert() * x32 * x64.invert();
565 }
566 
567 template <Order kOrder>
store_2dot2(void * dst,const uint32_t * src,Sk4f & dr,Sk4f & dg,Sk4f & db,Sk4f &,const uint8_t * const[3])568 static AI void store_2dot2(void* dst, const uint32_t* src, Sk4f& dr, Sk4f& dg, Sk4f& db, Sk4f&,
569                            const uint8_t* const[3]) {
570     int kRShift, kGShift = 8, kBShift;
571     set_rb_shifts(kOrder, &kRShift, &kBShift);
572     dr = linear_to_2dot2(dr);
573     dg = linear_to_2dot2(dg);
574     db = linear_to_2dot2(db);
575 
576     dr = sk_clamp_0_255(dr);
577     dg = sk_clamp_0_255(dg);
578     db = sk_clamp_0_255(db);
579 
580     Sk4i da = Sk4i::Load(src) & 0xFF000000;
581 
582     Sk4i rgba = (Sk4f_round(dr) << kRShift)
583               | (Sk4f_round(dg) << kGShift)
584               | (Sk4f_round(db) << kBShift)
585               | (da                       );
586     rgba.store(dst);
587 }
588 
589 template <Order kOrder>
store_2dot2_1(void * dst,const uint32_t * src,Sk4f & rgba,const Sk4f &,const uint8_t * const[3])590 static AI void store_2dot2_1(void* dst, const uint32_t* src,
591                              Sk4f& rgba, const Sk4f&,
592                              const uint8_t* const[3]) {
593     rgba = sk_clamp_0_255(linear_to_2dot2(rgba));
594 
595     uint32_t tmp;
596     SkNx_cast<uint8_t>(Sk4f_round(rgba)).store(&tmp);
597     tmp = (*src & 0xFF000000) | (tmp & 0x00FFFFFF);
598     if (kBGRA_Order == kOrder) {
599         tmp = SkSwizzle_RB(tmp);
600     }
601 
602     *(uint32_t*)dst = tmp;
603 }
604 
605 template <Order kOrder>
store_linear(void * dst,const uint32_t * src,Sk4f & dr,Sk4f & dg,Sk4f & db,Sk4f &,const uint8_t * const[3])606 static AI void store_linear(void* dst, const uint32_t* src, Sk4f& dr, Sk4f& dg, Sk4f& db, Sk4f&,
607                             const uint8_t* const[3]) {
608     int kRShift, kGShift = 8, kBShift;
609     set_rb_shifts(kOrder, &kRShift, &kBShift);
610     dr = sk_clamp_0_255(255.0f * dr);
611     dg = sk_clamp_0_255(255.0f * dg);
612     db = sk_clamp_0_255(255.0f * db);
613 
614     Sk4i da = Sk4i::Load(src) & 0xFF000000;
615 
616     Sk4i rgba = (Sk4f_round(dr) << kRShift)
617               | (Sk4f_round(dg) << kGShift)
618               | (Sk4f_round(db) << kBShift)
619               | (da                       );
620     rgba.store(dst);
621 }
622 
623 template <Order kOrder>
store_linear_1(void * dst,const uint32_t * src,Sk4f & rgba,const Sk4f &,const uint8_t * const[3])624 static AI void store_linear_1(void* dst, const uint32_t* src,
625                               Sk4f& rgba, const Sk4f&,
626                               const uint8_t* const[3]) {
627     rgba = sk_clamp_0_255(255.0f * rgba);
628 
629     uint32_t tmp;
630     SkNx_cast<uint8_t>(Sk4f_round(rgba)).store(&tmp);
631     tmp = (*src & 0xFF000000) | (tmp & 0x00FFFFFF);
632     if (kBGRA_Order == kOrder) {
633         tmp = SkSwizzle_RB(tmp);
634     }
635 
636     *(uint32_t*)dst = tmp;
637 }
638 
639 template <Order kOrder>
store_f16(void * dst,const uint32_t * src,Sk4f & dr,Sk4f & dg,Sk4f & db,Sk4f & da,const uint8_t * const[3])640 static AI void store_f16(void* dst, const uint32_t* src, Sk4f& dr, Sk4f& dg, Sk4f& db, Sk4f& da,
641                          const uint8_t* const[3]) {
642     Sk4h::Store4(dst, SkFloatToHalf_finite_ftz(dr),
643                       SkFloatToHalf_finite_ftz(dg),
644                       SkFloatToHalf_finite_ftz(db),
645                       SkFloatToHalf_finite_ftz(da));
646 }
647 
648 template <Order kOrder>
store_f16_1(void * dst,const uint32_t * src,Sk4f & rgba,const Sk4f & a,const uint8_t * const[3])649 static AI void store_f16_1(void* dst, const uint32_t* src,
650                            Sk4f& rgba, const Sk4f& a,
651                            const uint8_t* const[3]) {
652     rgba = Sk4f(rgba[0], rgba[1], rgba[2], a[3]);
653     SkFloatToHalf_finite_ftz(rgba).store((uint64_t*) dst);
654 }
655 
656 template <Order kOrder>
store_f16_opaque(void * dst,const uint32_t * src,Sk4f & dr,Sk4f & dg,Sk4f & db,Sk4f &,const uint8_t * const[3])657 static AI void store_f16_opaque(void* dst, const uint32_t* src, Sk4f& dr, Sk4f& dg, Sk4f& db,
658                                 Sk4f&, const uint8_t* const[3]) {
659     Sk4h::Store4(dst, SkFloatToHalf_finite_ftz(dr),
660                       SkFloatToHalf_finite_ftz(dg),
661                       SkFloatToHalf_finite_ftz(db),
662                       SK_Half1);
663 }
664 
665 template <Order kOrder>
store_f16_1_opaque(void * dst,const uint32_t * src,Sk4f & rgba,const Sk4f &,const uint8_t * const[3])666 static AI void store_f16_1_opaque(void* dst, const uint32_t* src,
667                                   Sk4f& rgba, const Sk4f&,
668                                   const uint8_t* const[3]) {
669     uint64_t tmp;
670     SkFloatToHalf_finite_ftz(rgba).store(&tmp);
671     tmp &= 0x0000FFFFFFFFFFFF;
672     tmp |= static_cast<uint64_t>(SK_Half1) << 48;
673     *((uint64_t*) dst) = tmp;
674 }
675 
676 template <Order kOrder>
store_generic(void * dst,const uint32_t * src,Sk4f & dr,Sk4f & dg,Sk4f & db,Sk4f &,const uint8_t * const dstTables[3])677 static AI void store_generic(void* dst, const uint32_t* src, Sk4f& dr, Sk4f& dg, Sk4f& db, Sk4f&,
678                              const uint8_t* const dstTables[3]) {
679     int kRShift, kGShift = 8, kBShift;
680     set_rb_shifts(kOrder, &kRShift, &kBShift);
681     dr = Sk4f::Min(Sk4f::Max(1023.0f * dr, 0.0f), 1023.0f);
682     dg = Sk4f::Min(Sk4f::Max(1023.0f * dg, 0.0f), 1023.0f);
683     db = Sk4f::Min(Sk4f::Max(1023.0f * db, 0.0f), 1023.0f);
684 
685     Sk4i ir = Sk4f_round(dr);
686     Sk4i ig = Sk4f_round(dg);
687     Sk4i ib = Sk4f_round(db);
688 
689     Sk4i da = Sk4i::Load(src) & 0xFF000000;
690 
691     uint32_t* dst32 = (uint32_t*) dst;
692     dst32[0] = dstTables[0][ir[0]] << kRShift
693              | dstTables[1][ig[0]] << kGShift
694              | dstTables[2][ib[0]] << kBShift
695              | da[0];
696     dst32[1] = dstTables[0][ir[1]] << kRShift
697              | dstTables[1][ig[1]] << kGShift
698              | dstTables[2][ib[1]] << kBShift
699              | da[1];
700     dst32[2] = dstTables[0][ir[2]] << kRShift
701              | dstTables[1][ig[2]] << kGShift
702              | dstTables[2][ib[2]] << kBShift
703              | da[2];
704     dst32[3] = dstTables[0][ir[3]] << kRShift
705              | dstTables[1][ig[3]] << kGShift
706              | dstTables[2][ib[3]] << kBShift
707              | da[3];
708 }
709 
710 template <Order kOrder>
store_generic_1(void * dst,const uint32_t * src,Sk4f & rgba,const Sk4f &,const uint8_t * const dstTables[3])711 static AI void store_generic_1(void* dst, const uint32_t* src,
712                                Sk4f& rgba, const Sk4f&,
713                                const uint8_t* const dstTables[3]) {
714     int kRShift, kGShift = 8, kBShift;
715     set_rb_shifts(kOrder, &kRShift, &kBShift);
716     rgba = Sk4f::Min(Sk4f::Max(1023.0f * rgba, 0.0f), 1023.0f);
717 
718     Sk4i indices = Sk4f_round(rgba);
719 
720     *((uint32_t*) dst) = dstTables[0][indices[0]] << kRShift
721                        | dstTables[1][indices[1]] << kGShift
722                        | dstTables[2][indices[2]] << kBShift
723                        | (*src & 0xFF000000);
724 }
725 
726 typedef decltype(load_rgb_from_tables<kRGBA_Order>  )* LoadFn;
727 typedef decltype(load_rgb_from_tables_1<kRGBA_Order>)* Load1Fn;
728 typedef decltype(store_generic<kRGBA_Order>         )* StoreFn;
729 typedef decltype(store_generic_1<kRGBA_Order>       )* Store1Fn;
730 
731 enum SrcFormat {
732     kRGBA_8888_Linear_SrcFormat,
733     kRGBA_8888_Table_SrcFormat,
734     kBGRA_8888_Linear_SrcFormat,
735     kBGRA_8888_Table_SrcFormat,
736 };
737 
738 enum DstFormat {
739     kRGBA_8888_Linear_DstFormat,
740     kRGBA_8888_SRGB_DstFormat,
741     kRGBA_8888_2Dot2_DstFormat,
742     kRGBA_8888_Table_DstFormat,
743     kBGRA_8888_Linear_DstFormat,
744     kBGRA_8888_SRGB_DstFormat,
745     kBGRA_8888_2Dot2_DstFormat,
746     kBGRA_8888_Table_DstFormat,
747     kF16_Linear_DstFormat,
748 };
749 
750 template <SrcFormat kSrc,
751           DstFormat kDst,
752           SkAlphaType kAlphaType,
753           ColorSpaceMatch kCSM>
color_xform_RGBA(void * dst,const void * vsrc,int len,const float * const srcTables[3],const float matrix[13],const uint8_t * const dstTables[3])754 static void color_xform_RGBA(void* dst, const void* vsrc, int len,
755                              const float* const srcTables[3], const float matrix[13],
756                              const uint8_t* const dstTables[3]) {
757     LoadFn load;
758     Load1Fn load_1;
759     const bool kLoadAlpha = kF16_Linear_DstFormat == kDst && kOpaque_SkAlphaType != kAlphaType;
760     switch (kSrc) {
761         case kRGBA_8888_Linear_SrcFormat:
762             if (kLoadAlpha) {
763                 load = load_rgba_linear<kRGBA_Order>;
764                 load_1 = load_rgba_linear_1<kRGBA_Order>;
765             } else {
766                 load = load_rgb_linear<kRGBA_Order>;
767                 load_1 = load_rgb_linear_1<kRGBA_Order>;
768             }
769             break;
770         case kRGBA_8888_Table_SrcFormat:
771             if (kLoadAlpha) {
772                 load = load_rgba_from_tables<kRGBA_Order>;
773                 load_1 = load_rgba_from_tables_1<kRGBA_Order>;
774             } else {
775                 load = load_rgb_from_tables<kRGBA_Order>;
776                 load_1 = load_rgb_from_tables_1<kRGBA_Order>;
777             }
778             break;
779         case kBGRA_8888_Linear_SrcFormat:
780             if (kLoadAlpha) {
781                 load = load_rgba_linear<kBGRA_Order>;
782                 load_1 = load_rgba_linear_1<kBGRA_Order>;
783             } else {
784                 load = load_rgb_linear<kBGRA_Order>;
785                 load_1 = load_rgb_linear_1<kBGRA_Order>;
786             }
787             break;
788         case kBGRA_8888_Table_SrcFormat:
789             if (kLoadAlpha) {
790                 load = load_rgba_from_tables<kBGRA_Order>;
791                 load_1 = load_rgba_from_tables_1<kBGRA_Order>;
792             } else {
793                 load = load_rgb_from_tables<kBGRA_Order>;
794                 load_1 = load_rgb_from_tables_1<kBGRA_Order>;
795             }
796             break;
797     }
798 
799     StoreFn store;
800     Store1Fn store_1;
801     size_t sizeOfDstPixel;
802     switch (kDst) {
803         case kRGBA_8888_Linear_DstFormat:
804             store   = store_linear<kRGBA_Order>;
805             store_1 = store_linear_1<kRGBA_Order>;
806             sizeOfDstPixel = 4;
807             break;
808         case kRGBA_8888_SRGB_DstFormat:
809             store   = store_srgb<kRGBA_Order>;
810             store_1 = store_srgb_1<kRGBA_Order>;
811             sizeOfDstPixel = 4;
812             break;
813         case kRGBA_8888_2Dot2_DstFormat:
814             store   = store_2dot2<kRGBA_Order>;
815             store_1 = store_2dot2_1<kRGBA_Order>;
816             sizeOfDstPixel = 4;
817             break;
818         case kRGBA_8888_Table_DstFormat:
819             store   = store_generic<kRGBA_Order>;
820             store_1 = store_generic_1<kRGBA_Order>;
821             sizeOfDstPixel = 4;
822             break;
823         case kBGRA_8888_Linear_DstFormat:
824             store   = store_linear<kBGRA_Order>;
825             store_1 = store_linear_1<kBGRA_Order>;
826             sizeOfDstPixel = 4;
827             break;
828         case kBGRA_8888_SRGB_DstFormat:
829             store   = store_srgb<kBGRA_Order>;
830             store_1 = store_srgb_1<kBGRA_Order>;
831             sizeOfDstPixel = 4;
832             break;
833         case kBGRA_8888_2Dot2_DstFormat:
834             store   = store_2dot2<kBGRA_Order>;
835             store_1 = store_2dot2_1<kBGRA_Order>;
836             sizeOfDstPixel = 4;
837             break;
838         case kBGRA_8888_Table_DstFormat:
839             store   = store_generic<kBGRA_Order>;
840             store_1 = store_generic_1<kBGRA_Order>;
841             sizeOfDstPixel = 4;
842             break;
843         case kF16_Linear_DstFormat:
844             store   = (kOpaque_SkAlphaType == kAlphaType) ? store_f16_opaque<kRGBA_Order> :
845                                                             store_f16<kRGBA_Order>;
846             store_1 = (kOpaque_SkAlphaType == kAlphaType) ? store_f16_1_opaque<kRGBA_Order> :
847                                                             store_f16_1<kRGBA_Order>;
848             sizeOfDstPixel = 8;
849             break;
850     }
851 
852     const uint32_t* src = (const uint32_t*) vsrc;
853     Sk4f rXgXbX, rYgYbY, rZgZbZ, rTgTbT;
854     load_matrix(matrix, rXgXbX, rYgYbY, rZgZbZ, rTgTbT);
855 
856     if (len >= 4) {
857         // Naively this would be a loop of load-transform-store, but we found it faster to
858         // move the N+1th load ahead of the Nth store.  We don't bother doing this for N<4.
859         Sk4f r, g, b, a;
860         load(src, r, g, b, a, srcTables);
861         src += 4;
862         len -= 4;
863 
864         Sk4f dr, dg, db, da;
865         while (len >= 4) {
866             if (kNone_ColorSpaceMatch == kCSM) {
867                 transform_gamut(r, g, b, a, rXgXbX, rYgYbY, rZgZbZ, dr, dg, db, da);
868                 translate_gamut(rTgTbT, dr, dg, db);
869             } else {
870                 dr = r;
871                 dg = g;
872                 db = b;
873                 da = a;
874             }
875 
876             load(src, r, g, b, a, srcTables);
877 
878             store(dst, src - 4, dr, dg, db, da, dstTables);
879             dst = SkTAddOffset<void>(dst, 4 * sizeOfDstPixel);
880             src += 4;
881             len -= 4;
882         }
883 
884         if (kNone_ColorSpaceMatch == kCSM) {
885             transform_gamut(r, g, b, a, rXgXbX, rYgYbY, rZgZbZ, dr, dg, db, da);
886             translate_gamut(rTgTbT, dr, dg, db);
887         } else {
888             dr = r;
889             dg = g;
890             db = b;
891             da = a;
892         }
893 
894         store(dst, src - 4, dr, dg, db, da, dstTables);
895         dst = SkTAddOffset<void>(dst, 4 * sizeOfDstPixel);
896     }
897 
898     while (len > 0) {
899         Sk4f r, g, b, a;
900         load_1(src, r, g, b, a, srcTables);
901 
902         Sk4f rgba;
903         if (kNone_ColorSpaceMatch == kCSM) {
904             transform_gamut_1(r, g, b, rXgXbX, rYgYbY, rZgZbZ, rgba);
905             translate_gamut_1(rTgTbT, rgba);
906         } else {
907             rgba = Sk4f(r[0], g[0], b[0], a[0]);
908         }
909 
910         store_1(dst, src, rgba, a, dstTables);
911 
912         src += 1;
913         len -= 1;
914         dst = SkTAddOffset<void>(dst, sizeOfDstPixel);
915     }
916 }
917 
918 ///////////////////////////////////////////////////////////////////////////////////////////////////
919 
num_tables(SkColorSpace_XYZ * space)920 static AI int num_tables(SkColorSpace_XYZ* space) {
921     switch (space->gammaNamed()) {
922         case kSRGB_SkGammaNamed:
923         case k2Dot2Curve_SkGammaNamed:
924         case kLinear_SkGammaNamed:
925             return 0;
926         default: {
927             const SkGammas* gammas = space->gammas();
928             SkASSERT(gammas);
929 
930             bool gammasAreMatching = (gammas->type(0) == gammas->type(1)) &&
931                                      (gammas->data(0) == gammas->data(1)) &&
932                                      (gammas->type(0) == gammas->type(2)) &&
933                                      (gammas->data(0) == gammas->data(2));
934 
935             // It's likely that each component will have the same gamma.  In this case,
936             // we only need to build one table.
937             return gammasAreMatching ? 1 : 3;
938         }
939     }
940 }
941 
942 template <ColorSpaceMatch kCSM>
943 SkColorSpaceXform_XYZ<kCSM>
SkColorSpaceXform_XYZ(SkColorSpace_XYZ * srcSpace,const SkMatrix44 & srcToDst,SkColorSpace_XYZ * dstSpace,SkTransferFunctionBehavior premulBehavior)944 ::SkColorSpaceXform_XYZ(SkColorSpace_XYZ* srcSpace, const SkMatrix44& srcToDst,
945                         SkColorSpace_XYZ* dstSpace, SkTransferFunctionBehavior premulBehavior)
946     : fPremulBehavior(premulBehavior)
947 {
948     fSrcToDst[ 0] = srcToDst.get(0, 0);
949     fSrcToDst[ 1] = srcToDst.get(1, 0);
950     fSrcToDst[ 2] = srcToDst.get(2, 0);
951     fSrcToDst[ 3] = srcToDst.get(0, 1);
952     fSrcToDst[ 4] = srcToDst.get(1, 1);
953     fSrcToDst[ 5] = srcToDst.get(2, 1);
954     fSrcToDst[ 6] = srcToDst.get(0, 2);
955     fSrcToDst[ 7] = srcToDst.get(1, 2);
956     fSrcToDst[ 8] = srcToDst.get(2, 2);
957     fSrcToDst[ 9] = srcToDst.get(0, 3);
958     fSrcToDst[10] = srcToDst.get(1, 3);
959     fSrcToDst[11] = srcToDst.get(2, 3);
960     fSrcToDst[12] = 0.0f;
961 
962     const int numSrcTables = num_tables(srcSpace);
963     const size_t srcEntries = numSrcTables * 256;
964     const bool srcGammasAreMatching = (1 >= numSrcTables);
965     fSrcStorage.reset(srcEntries);
966     build_gamma_tables(fSrcGammaTables, fSrcStorage.get(), 256, srcSpace, kToLinear,
967                        srcGammasAreMatching);
968 
969     const int numDstTables = num_tables(dstSpace);
970     dstSpace->toDstGammaTables(fDstGammaTables, &fDstStorage, numDstTables);
971 
972     if (srcSpace->gammaIsLinear()) {
973         fSrcGamma = kLinear_SrcGamma;
974     } else if (kSRGB_SkGammaNamed == srcSpace->gammaNamed()) {
975         fSrcGamma = kSRGB_SrcGamma;
976     } else {
977         fSrcGamma = kTable_SrcGamma;
978     }
979 
980     switch (dstSpace->gammaNamed()) {
981         case kSRGB_SkGammaNamed:
982             fDstGamma = kSRGB_DstGamma;
983             break;
984         case k2Dot2Curve_SkGammaNamed:
985             fDstGamma = k2Dot2_DstGamma;
986             break;
987         case kLinear_SkGammaNamed:
988             fDstGamma = kLinear_DstGamma;
989             break;
990         default:
991             fDstGamma = kTable_DstGamma;
992             break;
993     }
994 }
995 
996 ///////////////////////////////////////////////////////////////////////////////////////////////////
997 
998 template <SrcFormat kSrc, DstFormat kDst, ColorSpaceMatch kCSM>
apply_set_alpha(void * dst,const void * src,int len,SkAlphaType alphaType,const float * const srcTables[3],const float matrix[13],const uint8_t * const dstTables[3])999 static AI bool apply_set_alpha(void* dst, const void* src, int len, SkAlphaType alphaType,
1000                                const float* const srcTables[3], const float matrix[13],
1001                                const uint8_t* const dstTables[3]) {
1002     switch (alphaType) {
1003         case kOpaque_SkAlphaType:
1004             color_xform_RGBA<kSrc, kDst, kOpaque_SkAlphaType, kCSM>
1005                     (dst, src, len, srcTables, matrix, dstTables);
1006             return true;
1007         case kUnpremul_SkAlphaType:
1008             color_xform_RGBA<kSrc, kDst, kUnpremul_SkAlphaType, kCSM>
1009                     (dst, src, len, srcTables, matrix, dstTables);
1010             return true;
1011         default:
1012             return false;
1013     }
1014 }
1015 
1016 template <DstFormat kDst, ColorSpaceMatch kCSM>
apply_set_src(void * dst,const void * src,int len,SkAlphaType alphaType,const float * const srcTables[3],const float matrix[13],const uint8_t * const dstTables[3],SkColorSpaceXform::ColorFormat srcColorFormat,SrcGamma srcGamma)1017 static AI bool apply_set_src(void* dst, const void* src, int len, SkAlphaType alphaType,
1018                              const float* const srcTables[3], const float matrix[13],
1019                              const uint8_t* const dstTables[3],
1020                              SkColorSpaceXform::ColorFormat srcColorFormat,
1021                              SrcGamma srcGamma) {
1022     switch (srcColorFormat) {
1023         case SkColorSpaceXform::kRGBA_8888_ColorFormat:
1024             switch (srcGamma) {
1025                 case kLinear_SrcGamma:
1026                     return apply_set_alpha<kRGBA_8888_Linear_SrcFormat, kDst, kCSM>
1027                             (dst, src, len, alphaType, nullptr, matrix, dstTables);
1028                 default:
1029                     return apply_set_alpha<kRGBA_8888_Table_SrcFormat, kDst, kCSM>
1030                             (dst, src, len, alphaType, srcTables, matrix, dstTables);
1031             }
1032         case SkColorSpaceXform::kBGRA_8888_ColorFormat:
1033             switch (srcGamma) {
1034                 case kLinear_SrcGamma:
1035                     return apply_set_alpha<kBGRA_8888_Linear_SrcFormat, kDst, kCSM>
1036                             (dst, src, len, alphaType, nullptr, matrix, dstTables);
1037                 default:
1038                     return apply_set_alpha<kBGRA_8888_Table_SrcFormat, kDst, kCSM>
1039                             (dst, src, len, alphaType, srcTables, matrix, dstTables);
1040             }
1041         default:
1042             return false;
1043     }
1044 }
1045 
1046 #undef AI
1047 
1048 template <ColorSpaceMatch kCSM>
1049 bool SkColorSpaceXform_XYZ<kCSM>
onApply(ColorFormat dstColorFormat,void * dst,ColorFormat srcColorFormat,const void * src,int len,SkAlphaType alphaType) const1050 ::onApply(ColorFormat dstColorFormat, void* dst, ColorFormat srcColorFormat, const void* src,
1051           int len, SkAlphaType alphaType) const
1052 {
1053     if (kFull_ColorSpaceMatch == kCSM) {
1054         if (kPremul_SkAlphaType != alphaType) {
1055             if ((kRGBA_8888_ColorFormat == dstColorFormat &&
1056                  kRGBA_8888_ColorFormat == srcColorFormat) ||
1057                 (kBGRA_8888_ColorFormat == dstColorFormat &&
1058                  kBGRA_8888_ColorFormat == srcColorFormat))
1059             {
1060                 memcpy(dst, src, len * sizeof(uint32_t));
1061                 return true;
1062             }
1063             if ((kRGBA_8888_ColorFormat == dstColorFormat &&
1064                  kBGRA_8888_ColorFormat == srcColorFormat) ||
1065                 (kBGRA_8888_ColorFormat == dstColorFormat &&
1066                  kRGBA_8888_ColorFormat == srcColorFormat))
1067             {
1068                 SkOpts::RGBA_to_BGRA((uint32_t*) dst, src, len);
1069                 return true;
1070             }
1071         }
1072     }
1073 
1074     if (kRGBA_F32_ColorFormat == dstColorFormat ||
1075         kBGR_565_ColorFormat == dstColorFormat ||
1076         kRGBA_F32_ColorFormat == srcColorFormat ||
1077         kRGBA_F16_ColorFormat == srcColorFormat ||
1078         kRGBA_U16_BE_ColorFormat == srcColorFormat ||
1079         kRGB_U16_BE_ColorFormat == srcColorFormat ||
1080         kPremul_SkAlphaType == alphaType)
1081     {
1082         return this->applyPipeline(dstColorFormat, dst, srcColorFormat, src, len, alphaType);
1083     }
1084 
1085     switch (dstColorFormat) {
1086         case kRGBA_8888_ColorFormat:
1087             switch (fDstGamma) {
1088                 case kLinear_DstGamma:
1089                     return apply_set_src<kRGBA_8888_Linear_DstFormat, kCSM>
1090                             (dst, src, len, alphaType, fSrcGammaTables, fSrcToDst, nullptr,
1091                              srcColorFormat, fSrcGamma);
1092                 case kSRGB_DstGamma:
1093                     return apply_set_src<kRGBA_8888_SRGB_DstFormat, kCSM>
1094                             (dst, src, len, alphaType, fSrcGammaTables, fSrcToDst, nullptr,
1095                              srcColorFormat, fSrcGamma);
1096                 case k2Dot2_DstGamma:
1097                     return apply_set_src<kRGBA_8888_2Dot2_DstFormat, kCSM>
1098                             (dst, src, len, alphaType, fSrcGammaTables, fSrcToDst, nullptr,
1099                              srcColorFormat, fSrcGamma);
1100                 case kTable_DstGamma:
1101                     return apply_set_src<kRGBA_8888_Table_DstFormat, kCSM>
1102                             (dst, src, len, alphaType, fSrcGammaTables, fSrcToDst, fDstGammaTables,
1103                              srcColorFormat, fSrcGamma);
1104             }
1105         case kBGRA_8888_ColorFormat:
1106             switch (fDstGamma) {
1107                 case kLinear_DstGamma:
1108                     return apply_set_src<kBGRA_8888_Linear_DstFormat, kCSM>
1109                             (dst, src, len, alphaType, fSrcGammaTables, fSrcToDst, nullptr,
1110                              srcColorFormat, fSrcGamma);
1111                 case kSRGB_DstGamma:
1112                     return apply_set_src<kBGRA_8888_SRGB_DstFormat, kCSM>
1113                             (dst, src, len, alphaType, fSrcGammaTables, fSrcToDst, nullptr,
1114                              srcColorFormat, fSrcGamma);
1115                 case k2Dot2_DstGamma:
1116                     return apply_set_src<kBGRA_8888_2Dot2_DstFormat, kCSM>
1117                             (dst, src, len, alphaType, fSrcGammaTables, fSrcToDst, nullptr,
1118                              srcColorFormat, fSrcGamma);
1119                 case kTable_DstGamma:
1120                     return apply_set_src<kBGRA_8888_Table_DstFormat, kCSM>
1121                             (dst, src, len, alphaType, fSrcGammaTables, fSrcToDst, fDstGammaTables,
1122                              srcColorFormat, fSrcGamma);
1123             }
1124         case kRGBA_F16_ColorFormat:
1125             switch (fDstGamma) {
1126                 case kLinear_DstGamma:
1127                     return apply_set_src<kF16_Linear_DstFormat, kCSM>
1128                             (dst, src, len, alphaType, fSrcGammaTables, fSrcToDst, nullptr,
1129                              srcColorFormat, fSrcGamma);
1130                 default:
1131                     return false;
1132             }
1133         default:
1134             SkASSERT(false);
1135             return false;
1136     }
1137 }
1138 
apply(ColorFormat dstColorFormat,void * dst,ColorFormat srcColorFormat,const void * src,int len,SkAlphaType alphaType) const1139 bool SkColorSpaceXform::apply(ColorFormat dstColorFormat, void* dst, ColorFormat srcColorFormat,
1140                               const void* src, int len, SkAlphaType alphaType) const {
1141     return ((SkColorSpaceXform_Base*) this)->onApply(dstColorFormat, dst, srcColorFormat, src, len,
1142                                                      alphaType);
1143 }
1144 
Apply(SkColorSpace * dstCS,ColorFormat dstFormat,void * dst,SkColorSpace * srcCS,ColorFormat srcFormat,const void * src,int count,AlphaOp op)1145 bool SkColorSpaceXform::Apply(SkColorSpace* dstCS, ColorFormat dstFormat, void* dst,
1146                               SkColorSpace* srcCS, ColorFormat srcFormat, const void* src,
1147                               int count, AlphaOp op) {
1148     SkAlphaType at;
1149     switch (op) {
1150         case kPreserve_AlphaOp:    at = kUnpremul_SkAlphaType; break;
1151         case kPremul_AlphaOp:      at = kPremul_SkAlphaType;   break;
1152         case kSrcIsOpaque_AlphaOp: at = kOpaque_SkAlphaType;   break;
1153     }
1154     return New(srcCS, dstCS)->apply(dstFormat, dst, srcFormat, src, count, at);
1155 }
1156 
1157 ///////////////////////////////////////////////////////////////////////////////////////////////////
1158 
1159 template <ColorSpaceMatch kCSM>
1160 bool SkColorSpaceXform_XYZ<kCSM>
applyPipeline(ColorFormat dstColorFormat,void * dst,ColorFormat srcColorFormat,const void * src,int len,SkAlphaType alphaType) const1161 ::applyPipeline(ColorFormat dstColorFormat, void* dst, ColorFormat srcColorFormat,
1162                 const void* src, int len, SkAlphaType alphaType) const {
1163     SkRasterPipeline_<256> pipeline;
1164 
1165     LoadTablesContext loadTables;
1166     switch (srcColorFormat) {
1167         case kRGBA_8888_ColorFormat:
1168             if (kLinear_SrcGamma == fSrcGamma) {
1169                 pipeline.append(SkRasterPipeline::load_8888, &src);
1170             } else {
1171                 loadTables.fSrc = src;
1172                 loadTables.fR = fSrcGammaTables[0];
1173                 loadTables.fG = fSrcGammaTables[1];
1174                 loadTables.fB = fSrcGammaTables[2];
1175                 pipeline.append(SkRasterPipeline::load_tables, &loadTables);
1176             }
1177 
1178             break;
1179         case kBGRA_8888_ColorFormat:
1180             if (kLinear_SrcGamma == fSrcGamma) {
1181                 pipeline.append(SkRasterPipeline::load_bgra, &src);
1182             } else {
1183                 loadTables.fSrc = src;
1184                 loadTables.fR = fSrcGammaTables[2];
1185                 loadTables.fG = fSrcGammaTables[1];
1186                 loadTables.fB = fSrcGammaTables[0];
1187                 pipeline.append(SkRasterPipeline::load_tables, &loadTables);
1188                 pipeline.append(SkRasterPipeline::swap_rb);
1189             }
1190 
1191             break;
1192         case kRGBA_F16_ColorFormat:
1193             if (kLinear_SrcGamma != fSrcGamma) {
1194                 return false;
1195             }
1196             pipeline.append(SkRasterPipeline::load_f16, &src);
1197             break;
1198         case kRGBA_F32_ColorFormat:
1199             if (kLinear_SrcGamma != fSrcGamma) {
1200                 return false;
1201             }
1202             pipeline.append(SkRasterPipeline::load_f32, &src);
1203             break;
1204         case kRGBA_U16_BE_ColorFormat:
1205             switch (fSrcGamma) {
1206                 case kLinear_SrcGamma:
1207                     pipeline.append(SkRasterPipeline::load_u16_be, &src);
1208                     break;
1209                 case kSRGB_SrcGamma:
1210                     pipeline.append(SkRasterPipeline::load_u16_be, &src);
1211                     pipeline.append_from_srgb(kUnpremul_SkAlphaType);
1212                     break;
1213                 case kTable_SrcGamma:
1214                     loadTables.fSrc = src;
1215                     loadTables.fR = fSrcGammaTables[0];
1216                     loadTables.fG = fSrcGammaTables[1];
1217                     loadTables.fB = fSrcGammaTables[2];
1218                     pipeline.append(SkRasterPipeline::load_tables_u16_be, &loadTables);
1219                     break;
1220             }
1221             break;
1222         case kRGB_U16_BE_ColorFormat:
1223             switch (fSrcGamma) {
1224                 case kLinear_SrcGamma:
1225                     pipeline.append(SkRasterPipeline::load_rgb_u16_be, &src);
1226                     break;
1227                 case kSRGB_SrcGamma:
1228                     pipeline.append(SkRasterPipeline::load_rgb_u16_be, &src);
1229                     pipeline.append_from_srgb(kUnpremul_SkAlphaType);
1230                     break;
1231                 case kTable_SrcGamma:
1232                     loadTables.fSrc = src;
1233                     loadTables.fR = fSrcGammaTables[0];
1234                     loadTables.fG = fSrcGammaTables[1];
1235                     loadTables.fB = fSrcGammaTables[2];
1236                     pipeline.append(SkRasterPipeline::load_tables_rgb_u16_be, &loadTables);
1237                     break;
1238             }
1239             break;
1240         default:
1241             return false;
1242     }
1243 
1244     if (kNone_ColorSpaceMatch == kCSM) {
1245         pipeline.append(SkRasterPipeline::matrix_3x4, fSrcToDst);
1246 
1247         if (kRGBA_F16_ColorFormat != dstColorFormat &&
1248             kRGBA_F32_ColorFormat != dstColorFormat)
1249         {
1250             bool need_clamp_0, need_clamp_1;
1251             analyze_3x4_matrix(fSrcToDst, &need_clamp_0, &need_clamp_1);
1252 
1253             if (need_clamp_0) { pipeline.append(SkRasterPipeline::clamp_0); }
1254             if (need_clamp_1) { pipeline.append(SkRasterPipeline::clamp_1); }
1255         }
1256     }
1257 
1258     if (kPremul_SkAlphaType == alphaType && SkTransferFunctionBehavior::kRespect == fPremulBehavior)
1259     {
1260         pipeline.append(SkRasterPipeline::premul);
1261     }
1262 
1263     TablesContext tables;
1264     SkColorSpaceTransferFn to_2dot2 = {0,0,0,0,0,0,0};
1265     to_2dot2.fG = 1/2.2f;
1266     to_2dot2.fA = 1;
1267     switch (fDstGamma) {
1268         case kSRGB_DstGamma:
1269             pipeline.append(SkRasterPipeline::to_srgb);
1270             break;
1271         case k2Dot2_DstGamma:
1272             pipeline.append(SkRasterPipeline::parametric_r, &to_2dot2);
1273             pipeline.append(SkRasterPipeline::parametric_g, &to_2dot2);
1274             pipeline.append(SkRasterPipeline::parametric_b, &to_2dot2);
1275             break;
1276         case kTable_DstGamma:
1277             tables.fR = fDstGammaTables[0];
1278             tables.fG = fDstGammaTables[1];
1279             tables.fB = fDstGammaTables[2];
1280             tables.fCount = SkColorSpaceXform_Base::kDstGammaTableSize;
1281             pipeline.append(SkRasterPipeline::byte_tables_rgb, &tables);
1282         default:
1283             break;
1284     }
1285 
1286     if (kPremul_SkAlphaType == alphaType && SkTransferFunctionBehavior::kIgnore == fPremulBehavior)
1287     {
1288         pipeline.append(SkRasterPipeline::premul);
1289     }
1290 
1291     switch (dstColorFormat) {
1292         case kRGBA_8888_ColorFormat:
1293              pipeline.append(SkRasterPipeline::store_8888, &dst);
1294             break;
1295         case kBGRA_8888_ColorFormat:
1296             pipeline.append(SkRasterPipeline::store_bgra, &dst);
1297             break;
1298         case kRGBA_F16_ColorFormat:
1299             if (kLinear_DstGamma != fDstGamma) {
1300                 return false;
1301             }
1302             pipeline.append(SkRasterPipeline::store_f16, &dst);
1303             break;
1304         case kRGBA_F32_ColorFormat:
1305             if (kLinear_DstGamma != fDstGamma) {
1306                 return false;
1307             }
1308             pipeline.append(SkRasterPipeline::store_f32, &dst);
1309             break;
1310         case kBGR_565_ColorFormat:
1311             if (kOpaque_SkAlphaType != alphaType) {
1312                 return false;
1313             }
1314             pipeline.append(SkRasterPipeline::store_565, &dst);
1315             break;
1316         default:
1317             return false;
1318     }
1319 
1320     pipeline.run(0,0, len);
1321     return true;
1322 }
1323 
1324 ///////////////////////////////////////////////////////////////////////////////////////////////////
1325 
SlowIdentityXform(SkColorSpace_XYZ * space)1326 std::unique_ptr<SkColorSpaceXform> SlowIdentityXform(SkColorSpace_XYZ* space) {
1327     return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_XYZ<kNone_ColorSpaceMatch>
1328             (space, SkMatrix::I(), space, SkTransferFunctionBehavior::kRespect));
1329 }
1330