• 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 "SkColorSpaceXform_A2B.h"
9 
10 #include "SkColorPriv.h"
11 #include "SkColorSpace_A2B.h"
12 #include "SkColorSpace_XYZ.h"
13 #include "SkColorSpacePriv.h"
14 #include "SkColorSpaceXformPriv.h"
15 #include "SkMakeUnique.h"
16 #include "SkNx.h"
17 #include "SkSRGB.h"
18 #include "SkTypes.h"
19 
onApply(ColorFormat dstFormat,void * dst,ColorFormat srcFormat,const void * src,int count,SkAlphaType alphaType) const20 bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorFormat srcFormat,
21                                     const void* src, int count, SkAlphaType alphaType) const {
22     SkRasterPipeline pipeline;
23     switch (srcFormat) {
24         case kBGRA_8888_ColorFormat:
25             pipeline.append(SkRasterPipeline::load_8888, &src);
26             pipeline.append(SkRasterPipeline::swap_rb);
27             break;
28         case kRGBA_8888_ColorFormat:
29             pipeline.append(SkRasterPipeline::load_8888, &src);
30             break;
31         case kRGBA_U16_BE_ColorFormat:
32             pipeline.append(SkRasterPipeline::load_u16_be, &src);
33             break;
34         case kRGB_U16_BE_ColorFormat:
35             pipeline.append(SkRasterPipeline::load_rgb_u16_be, &src);
36             break;
37         default:
38             SkCSXformPrintf("F16/F32 sources must be linear.\n");
39             return false;
40     }
41 
42     pipeline.extend(fElementsPipeline);
43 
44     if (kPremul_SkAlphaType == alphaType) {
45         pipeline.append(SkRasterPipeline::premul);
46     }
47 
48     switch (dstFormat) {
49         case kBGRA_8888_ColorFormat:
50             pipeline.append(SkRasterPipeline::swap_rb);
51             pipeline.append(SkRasterPipeline::store_8888, &dst);
52             break;
53         case kRGBA_8888_ColorFormat:
54             pipeline.append(SkRasterPipeline::store_8888, &dst);
55             break;
56         case kRGBA_F16_ColorFormat:
57             if (!fLinearDstGamma) {
58                 return false;
59             }
60             pipeline.append(SkRasterPipeline::store_f16, &dst);
61             break;
62         case kRGBA_F32_ColorFormat:
63             if (!fLinearDstGamma) {
64                 return false;
65             }
66             pipeline.append(SkRasterPipeline::store_f32, &dst);
67             break;
68         case kBGR_565_ColorFormat:
69             if (kOpaque_SkAlphaType != alphaType) {
70                 return false;
71             }
72             pipeline.append(SkRasterPipeline::store_565, &dst);
73             break;
74         default:
75             return false;
76     }
77     pipeline.run(0,count);
78 
79     return true;
80 }
81 
gamma_to_parametric(SkColorSpaceTransferFn * coeffs,const SkGammas & gammas,int channel)82 static inline bool gamma_to_parametric(SkColorSpaceTransferFn* coeffs, const SkGammas& gammas,
83                                        int channel) {
84     switch (gammas.type(channel)) {
85         case SkGammas::Type::kNamed_Type:
86             return named_to_parametric(coeffs, gammas.data(channel).fNamed);
87         case SkGammas::Type::kValue_Type:
88             value_to_parametric(coeffs, gammas.data(channel).fValue);
89             return true;
90         case SkGammas::Type::kParam_Type:
91             *coeffs = gammas.params(channel);
92             return true;
93         default:
94             return false;
95     }
96 }
97 
SkColorSpaceXform_A2B(SkColorSpace_A2B * srcSpace,SkColorSpace_XYZ * dstSpace)98 SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
99                                              SkColorSpace_XYZ* dstSpace)
100     : fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) {
101 #if (SkCSXformPrintfDefined)
102     static const char* debugGammaNamed[4] = {
103         "Linear", "SRGB", "2.2", "NonStandard"
104     };
105     static const char* debugGammas[5] = {
106         "None", "Named", "Value", "Table", "Param"
107     };
108 #endif
109     int currentChannels;
110     switch (srcSpace->iccType()) {
111         case SkColorSpace_Base::kRGB_ICCTypeFlag:
112             currentChannels = 3;
113             break;
114         case SkColorSpace_Base::kCMYK_ICCTypeFlag:
115             currentChannels = 4;
116             // CMYK images from JPEGs (the only format that supports it) are actually
117             // inverted CMYK, so we need to invert every channel.
118             // TransferFn is y = -x + 1 for x < 1.f, otherwise 0x + 0, ie y = 1 - x for x in [0,1]
119             this->addTransferFns({1.f, 0.f, 0.f, -1.f, 1.f, 0.f, 1.f}, 4);
120             break;
121         default:
122             currentChannels = 0;
123             SkASSERT(false);
124     }
125     // add in all input color space -> PCS xforms
126     for (int i = 0; i < srcSpace->count(); ++i) {
127         const SkColorSpace_A2B::Element& e = srcSpace->element(i);
128         SkASSERT(e.inputChannels() == currentChannels);
129         currentChannels = e.outputChannels();
130         switch (e.type()) {
131             case SkColorSpace_A2B::Element::Type::kGammaNamed:
132                 if (kLinear_SkGammaNamed == e.gammaNamed()) {
133                     break;
134                 }
135 
136                 // take the fast path for 3-channel named gammas
137                 if (3 == currentChannels) {
138                     if (k2Dot2Curve_SkGammaNamed == e.gammaNamed()) {
139                         SkCSXformPrintf("fast path from 2.2\n");
140                         fElementsPipeline.append(SkRasterPipeline::from_2dot2);
141                         break;
142                     } else if (kSRGB_SkGammaNamed == e.gammaNamed()) {
143                         SkCSXformPrintf("fast path from sRGB\n");
144                         // Images should always start the pipeline as unpremul
145                         fElementsPipeline.append_from_srgb(kUnpremul_SkAlphaType);
146                         break;
147                     }
148                 }
149 
150                 SkCSXformPrintf("Gamma stage added: %s\n", debugGammaNamed[(int)e.gammaNamed()]);
151                 SkColorSpaceTransferFn fn;
152                 SkAssertResult(named_to_parametric(&fn, e.gammaNamed()));
153                 this->addTransferFns(fn, currentChannels);
154                 break;
155             case SkColorSpace_A2B::Element::Type::kGammas: {
156                 const SkGammas& gammas = e.gammas();
157                 SkCSXformPrintf("Gamma stage added:");
158                 for (int channel = 0; channel < gammas.channels(); ++channel) {
159                     SkCSXformPrintf("  %s", debugGammas[(int)gammas.type(channel)]);
160                 }
161                 SkCSXformPrintf("\n");
162                 bool gammaNeedsRef = false;
163                 for (int channel = 0; channel < gammas.channels(); ++channel) {
164                     if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
165                         SkTableTransferFn table = {
166                                 gammas.table(channel),
167                                 gammas.data(channel).fTable.fSize,
168                         };
169 
170                         this->addTableFn(table, channel);
171                         gammaNeedsRef = true;
172                     } else {
173                         SkColorSpaceTransferFn fn;
174                         SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
175                         this->addTransferFn(fn, channel);
176                     }
177                 }
178                 if (gammaNeedsRef) {
179                     fGammaRefs.push_back(sk_ref_sp(&gammas));
180                 }
181                 break;
182             }
183             case SkColorSpace_A2B::Element::Type::kCLUT:
184                 SkCSXformPrintf("CLUT (%d -> %d) stage added\n", e.colorLUT().inputChannels(),
185                                                                  e.colorLUT().outputChannels());
186                 fCLUTs.push_back(sk_ref_sp(&e.colorLUT()));
187                 fElementsPipeline.append(SkRasterPipeline::color_lookup_table,
188                                          fCLUTs.back().get());
189                 break;
190             case SkColorSpace_A2B::Element::Type::kMatrix:
191                 if (!e.matrix().isIdentity()) {
192                     SkCSXformPrintf("Matrix stage added\n");
193                     addMatrix(e.matrix());
194                 }
195                 break;
196         }
197     }
198 
199     // Lab PCS -> XYZ PCS
200     if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) {
201         SkCSXformPrintf("Lab -> XYZ element added\n");
202         fElementsPipeline.append(SkRasterPipeline::lab_to_xyz);
203     }
204 
205     // we should now be in XYZ PCS
206     SkASSERT(3 == currentChannels);
207 
208     // and XYZ PCS -> output color space xforms
209     if (!dstSpace->fromXYZD50()->isIdentity()) {
210         addMatrix(*dstSpace->fromXYZD50());
211     }
212 
213     switch (dstSpace->gammaNamed()) {
214         case kLinear_SkGammaNamed:
215             // do nothing
216             break;
217         case k2Dot2Curve_SkGammaNamed:
218             fElementsPipeline.append(SkRasterPipeline::to_2dot2);
219             break;
220         case kSRGB_SkGammaNamed:
221             fElementsPipeline.append(SkRasterPipeline::to_srgb);
222             break;
223         case kNonStandard_SkGammaNamed: {
224             for (int channel = 0; channel < 3; ++channel) {
225                 const SkGammas& gammas = *dstSpace->gammas();
226                 if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
227                     static constexpr int kInvTableSize = 256;
228                     std::vector<float> storage(kInvTableSize);
229                     invert_table_gamma(storage.data(), nullptr, storage.size(),
230                                        gammas.table(channel),
231                                        gammas.data(channel).fTable.fSize);
232                     SkTableTransferFn table = {
233                             storage.data(),
234                             (int) storage.size(),
235                     };
236                     fTableStorage.push_front(std::move(storage));
237 
238                     this->addTableFn(table, channel);
239                 } else {
240                     SkColorSpaceTransferFn fn;
241                     SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
242                     this->addTransferFn(fn.invert(), channel);
243                 }
244             }
245         }
246         break;
247     }
248 }
249 
addTransferFns(const SkColorSpaceTransferFn & fn,int channelCount)250 void SkColorSpaceXform_A2B::addTransferFns(const SkColorSpaceTransferFn& fn, int channelCount) {
251     for (int i = 0; i < channelCount; ++i) {
252         this->addTransferFn(fn, i);
253     }
254 }
255 
addTransferFn(const SkColorSpaceTransferFn & fn,int channelIndex)256 void SkColorSpaceXform_A2B::addTransferFn(const SkColorSpaceTransferFn& fn, int channelIndex) {
257     fTransferFns.push_front(fn);
258     switch (channelIndex) {
259         case 0:
260             fElementsPipeline.append(SkRasterPipeline::parametric_r, &fTransferFns.front());
261             break;
262         case 1:
263             fElementsPipeline.append(SkRasterPipeline::parametric_g, &fTransferFns.front());
264             break;
265         case 2:
266             fElementsPipeline.append(SkRasterPipeline::parametric_b, &fTransferFns.front());
267             break;
268         case 3:
269             fElementsPipeline.append(SkRasterPipeline::parametric_a, &fTransferFns.front());
270             break;
271         default:
272             SkASSERT(false);
273     }
274 }
275 
addTableFn(const SkTableTransferFn & fn,int channelIndex)276 void SkColorSpaceXform_A2B::addTableFn(const SkTableTransferFn& fn, int channelIndex) {
277     fTableTransferFns.push_front(fn);
278     switch (channelIndex) {
279         case 0:
280             fElementsPipeline.append(SkRasterPipeline::table_r, &fTableTransferFns.front());
281             break;
282         case 1:
283             fElementsPipeline.append(SkRasterPipeline::table_g, &fTableTransferFns.front());
284             break;
285         case 2:
286             fElementsPipeline.append(SkRasterPipeline::table_b, &fTableTransferFns.front());
287             break;
288         case 3:
289             fElementsPipeline.append(SkRasterPipeline::table_a, &fTableTransferFns.front());
290             break;
291         default:
292             SkASSERT(false);
293     }
294 }
295 
addMatrix(const SkMatrix44 & matrix)296 void SkColorSpaceXform_A2B::addMatrix(const SkMatrix44& matrix) {
297     fMatrices.push_front(std::vector<float>(12));
298     auto& m = fMatrices.front();
299     m[ 0] = matrix.get(0, 0);
300     m[ 1] = matrix.get(1, 0);
301     m[ 2] = matrix.get(2, 0);
302     m[ 3] = matrix.get(0, 1);
303     m[ 4] = matrix.get(1, 1);
304     m[ 5] = matrix.get(2, 1);
305     m[ 6] = matrix.get(0, 2);
306     m[ 7] = matrix.get(1, 2);
307     m[ 8] = matrix.get(2, 2);
308     m[ 9] = matrix.get(0, 3);
309     m[10] = matrix.get(1, 3);
310     m[11] = matrix.get(2, 3);
311     SkASSERT(matrix.get(3, 0) == 0.f);
312     SkASSERT(matrix.get(3, 1) == 0.f);
313     SkASSERT(matrix.get(3, 2) == 0.f);
314     SkASSERT(matrix.get(3, 3) == 1.f);
315     fElementsPipeline.append(SkRasterPipeline::matrix_3x4, m.data());
316     fElementsPipeline.append(SkRasterPipeline::clamp_0);
317     fElementsPipeline.append(SkRasterPipeline::clamp_1);
318 }
319