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