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