blob: 27c9faa97155c766c79f8e4792a68f229a1a687c [file] [log] [blame]
raftias25636012016-11-11 15:27:39 -08001/*
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
raftias25636012016-11-11 15:27:39 -080020bool 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:
Mike Klein729b5822016-11-28 18:23:23 -050025 pipeline.append(SkRasterPipeline::load_8888, &src);
raftias25636012016-11-11 15:27:39 -080026 pipeline.append(SkRasterPipeline::swap_rb);
27 break;
28 case kRGBA_8888_ColorFormat:
Mike Klein729b5822016-11-28 18:23:23 -050029 pipeline.append(SkRasterPipeline::load_8888, &src);
raftias25636012016-11-11 15:27:39 -080030 break;
Matt Sarett379938e2017-01-12 18:34:29 -050031 case kRGBA_U16_BE_ColorFormat:
32 pipeline.append(SkRasterPipeline::load_u16_be, &src);
33 break;
Matt Sarett7a090c42017-01-17 12:22:48 -050034 case kRGB_U16_BE_ColorFormat:
35 pipeline.append(SkRasterPipeline::load_rgb_u16_be, &src);
36 break;
raftias25636012016-11-11 15:27:39 -080037 default:
38 SkCSXformPrintf("F16/F32 source color format not supported\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;
Matt Sarett379938e2017-01-12 18:34:29 -050068 default:
69 return false;
raftias25636012016-11-11 15:27:39 -080070 }
Mike Kleinc789b612016-11-30 13:45:06 -050071 pipeline.run(0,0, count);
raftias25636012016-11-11 15:27:39 -080072
73 return true;
74}
75
Ravi Mistry113d05f2016-12-17 01:31:03 +000076static inline bool gamma_to_parametric(SkColorSpaceTransferFn* coeffs, const SkGammas& gammas,
77 int channel) {
raftias25636012016-11-11 15:27:39 -080078 switch (gammas.type(channel)) {
79 case SkGammas::Type::kNamed_Type:
Ravi Mistry113d05f2016-12-17 01:31:03 +000080 return named_to_parametric(coeffs, gammas.data(channel).fNamed);
raftias25636012016-11-11 15:27:39 -080081 case SkGammas::Type::kValue_Type:
Ravi Mistry113d05f2016-12-17 01:31:03 +000082 value_to_parametric(coeffs, gammas.data(channel).fValue);
83 return true;
raftias25636012016-11-11 15:27:39 -080084 case SkGammas::Type::kParam_Type:
Ravi Mistry113d05f2016-12-17 01:31:03 +000085 *coeffs = gammas.params(channel);
86 return true;
raftias25636012016-11-11 15:27:39 -080087 default:
Ravi Mistry113d05f2016-12-17 01:31:03 +000088 return false;
raftias25636012016-11-11 15:27:39 -080089 }
90}
91static inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransferFn& fn) {
Matt Sarett24107172016-12-19 14:33:35 -050092 // Original equation is: y = (ax + b)^g + e for x >= d
93 // y = cx + f otherwise
raftias25636012016-11-11 15:27:39 -080094 //
Matt Sarett24107172016-12-19 14:33:35 -050095 // so 1st inverse is: (y - e)^(1/g) = ax + b
96 // x = ((y - e)^(1/g) - b) / a
raftias25636012016-11-11 15:27:39 -080097 //
Matt Sarett24107172016-12-19 14:33:35 -050098 // which can be re-written as: x = (1/a)(y - e)^(1/g) - b/a
99 // x = ((1/a)^g)^(1/g) * (y - e)^(1/g) - b/a
100 // x = ([(1/a)^g]y + [-((1/a)^g)e]) ^ [1/g] + [-b/a]
raftias25636012016-11-11 15:27:39 -0800101 //
Matt Sarett24107172016-12-19 14:33:35 -0500102 // and 2nd inverse is: x = (y - f) / c
103 // which can be re-written as: x = [1/c]y + [-f/c]
raftias25636012016-11-11 15:27:39 -0800104 //
105 // and now both can be expressed in terms of the same parametric form as the
raftias197e3112016-12-01 15:31:29 -0500106 // original - parameters are enclosed in square brackets.
raftias25636012016-11-11 15:27:39 -0800107
108 // find inverse for linear segment (if possible)
Matt Sarett24107172016-12-19 14:33:35 -0500109 float c, f;
110 if (0.f == fn.fC) {
raftias25636012016-11-11 15:27:39 -0800111 // otherwise assume it should be 0 as it is the lower segment
112 // as y = f is a constant function
Matt Sarett24107172016-12-19 14:33:35 -0500113 c = 0.f;
raftias25636012016-11-11 15:27:39 -0800114 f = 0.f;
115 } else {
Matt Sarett24107172016-12-19 14:33:35 -0500116 c = 1.f / fn.fC;
117 f = -fn.fF / fn.fC;
raftias25636012016-11-11 15:27:39 -0800118 }
119 // find inverse for the other segment (if possible)
Matt Sarett24107172016-12-19 14:33:35 -0500120 float g, a, b, e;
raftias25636012016-11-11 15:27:39 -0800121 if (0.f == fn.fA || 0.f == fn.fG) {
122 // otherwise assume it should be 1 as it is the top segment
123 // as you can't invert the constant functions y = b^g + c, or y = 1 + c
124 g = 1.f;
125 a = 0.f;
126 b = 0.f;
Matt Sarett24107172016-12-19 14:33:35 -0500127 e = 1.f;
raftias25636012016-11-11 15:27:39 -0800128 } else {
129 g = 1.f / fn.fG;
130 a = powf(1.f / fn.fA, fn.fG);
Matt Sarett24107172016-12-19 14:33:35 -0500131 b = -a * fn.fE;
132 e = -fn.fB / fn.fA;
raftias25636012016-11-11 15:27:39 -0800133 }
Matt Sarett24107172016-12-19 14:33:35 -0500134 const float d = fn.fC * fn.fD + fn.fF;
raftias25636012016-11-11 15:27:39 -0800135 return {g, a, b, c, d, e, f};
136}
137
raftias25636012016-11-11 15:27:39 -0800138SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
139 SkColorSpace_XYZ* dstSpace)
140 : fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) {
141#if (SkCSXformPrintfDefined)
142 static const char* debugGammaNamed[4] = {
143 "Linear", "SRGB", "2.2", "NonStandard"
144 };
145 static const char* debugGammas[5] = {
146 "None", "Named", "Value", "Table", "Param"
147 };
148#endif
Matt Sarett523116d2017-01-12 18:36:38 -0500149 int currentChannels;
150 switch (srcSpace->iccType()) {
151 case SkColorSpace_Base::kRGB_ICCTypeFlag:
raftias54761282016-12-01 13:44:07 -0500152 currentChannels = 3;
153 break;
Matt Sarett523116d2017-01-12 18:36:38 -0500154 case SkColorSpace_Base::kCMYK_ICCTypeFlag:
raftias54761282016-12-01 13:44:07 -0500155 currentChannels = 4;
156 // CMYK images from JPEGs (the only format that supports it) are actually
157 // inverted CMYK, so we need to invert every channel.
158 // TransferFn is y = -x + 1 for x < 1.f, otherwise 0x + 0, ie y = 1 - x for x in [0,1]
Matt Sarett24107172016-12-19 14:33:35 -0500159 this->addTransferFns({1.f, 0.f, 0.f, -1.f, 1.f, 0.f, 1.f}, 4);
raftias54761282016-12-01 13:44:07 -0500160 break;
161 default:
Matt Sarett523116d2017-01-12 18:36:38 -0500162 currentChannels = 0;
raftias54761282016-12-01 13:44:07 -0500163 SkASSERT(false);
164 }
raftias25636012016-11-11 15:27:39 -0800165 // add in all input color space -> PCS xforms
166 for (int i = 0; i < srcSpace->count(); ++i) {
167 const SkColorSpace_A2B::Element& e = srcSpace->element(i);
raftias54761282016-12-01 13:44:07 -0500168 SkASSERT(e.inputChannels() == currentChannels);
169 currentChannels = e.outputChannels();
raftias25636012016-11-11 15:27:39 -0800170 switch (e.type()) {
171 case SkColorSpace_A2B::Element::Type::kGammaNamed:
Ravi Mistry113d05f2016-12-17 01:31:03 +0000172 if (kLinear_SkGammaNamed == e.gammaNamed()) {
173 break;
174 }
175
raftias97524542016-12-14 13:15:05 -0500176 // take the fast path for 3-channel named gammas
177 if (3 == currentChannels) {
178 if (k2Dot2Curve_SkGammaNamed == e.gammaNamed()) {
179 SkCSXformPrintf("fast path from 2.2\n");
180 fElementsPipeline.append(SkRasterPipeline::from_2dot2);
181 break;
182 } else if (kSRGB_SkGammaNamed == e.gammaNamed()) {
183 SkCSXformPrintf("fast path from sRGB\n");
184 // Images should always start the pipeline as unpremul
185 fElementsPipeline.append_from_srgb(kUnpremul_SkAlphaType);
186 break;
187 }
188 }
Ravi Mistry113d05f2016-12-17 01:31:03 +0000189
190 SkCSXformPrintf("Gamma stage added: %s\n", debugGammaNamed[(int)e.gammaNamed()]);
191 SkColorSpaceTransferFn fn;
192 SkAssertResult(named_to_parametric(&fn, e.gammaNamed()));
193 this->addTransferFns(fn, currentChannels);
raftias25636012016-11-11 15:27:39 -0800194 break;
195 case SkColorSpace_A2B::Element::Type::kGammas: {
Matt Sarettdb4d4062016-11-16 16:07:15 -0500196 const SkGammas& gammas = e.gammas();
197 SkCSXformPrintf("Gamma stage added:");
raftias54761282016-12-01 13:44:07 -0500198 for (int channel = 0; channel < gammas.channels(); ++channel) {
Matt Sarettdb4d4062016-11-16 16:07:15 -0500199 SkCSXformPrintf(" %s", debugGammas[(int)gammas.type(channel)]);
200 }
201 SkCSXformPrintf("\n");
202 bool gammaNeedsRef = false;
raftias54761282016-12-01 13:44:07 -0500203 for (int channel = 0; channel < gammas.channels(); ++channel) {
Matt Sarettdb4d4062016-11-16 16:07:15 -0500204 if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
205 SkTableTransferFn table = {
206 gammas.table(channel),
207 gammas.data(channel).fTable.fSize,
208 };
209
raftias54761282016-12-01 13:44:07 -0500210 this->addTableFn(table, channel);
Matt Sarettdb4d4062016-11-16 16:07:15 -0500211 gammaNeedsRef = true;
212 } else {
Ravi Mistry113d05f2016-12-17 01:31:03 +0000213 SkColorSpaceTransferFn fn;
214 SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
raftias54761282016-12-01 13:44:07 -0500215 this->addTransferFn(fn, channel);
raftias25636012016-11-11 15:27:39 -0800216 }
217 }
Matt Sarettdb4d4062016-11-16 16:07:15 -0500218 if (gammaNeedsRef) {
219 fGammaRefs.push_back(sk_ref_sp(&gammas));
220 }
raftias25636012016-11-11 15:27:39 -0800221 break;
Matt Sarettdb4d4062016-11-16 16:07:15 -0500222 }
raftias25636012016-11-11 15:27:39 -0800223 case SkColorSpace_A2B::Element::Type::kCLUT:
raftias54761282016-12-01 13:44:07 -0500224 SkCSXformPrintf("CLUT (%d -> %d) stage added\n", e.colorLUT().inputChannels(),
225 e.colorLUT().outputChannels());
raftias25636012016-11-11 15:27:39 -0800226 fCLUTs.push_back(sk_ref_sp(&e.colorLUT()));
227 fElementsPipeline.append(SkRasterPipeline::color_lookup_table,
228 fCLUTs.back().get());
229 break;
230 case SkColorSpace_A2B::Element::Type::kMatrix:
231 if (!e.matrix().isIdentity()) {
232 SkCSXformPrintf("Matrix stage added\n");
233 addMatrix(e.matrix());
234 }
235 break;
236 }
237 }
238
239 // Lab PCS -> XYZ PCS
240 if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) {
241 SkCSXformPrintf("Lab -> XYZ element added\n");
242 fElementsPipeline.append(SkRasterPipeline::lab_to_xyz);
243 }
244
raftias91db12d2016-12-02 11:56:59 -0500245 // we should now be in XYZ PCS
246 SkASSERT(3 == currentChannels);
247
raftias25636012016-11-11 15:27:39 -0800248 // and XYZ PCS -> output color space xforms
249 if (!dstSpace->fromXYZD50()->isIdentity()) {
250 addMatrix(*dstSpace->fromXYZD50());
251 }
252
raftias97524542016-12-14 13:15:05 -0500253 switch (dstSpace->gammaNamed()) {
254 case kLinear_SkGammaNamed:
255 // do nothing
256 break;
257 case k2Dot2Curve_SkGammaNamed:
258 fElementsPipeline.append(SkRasterPipeline::to_2dot2);
259 break;
260 case kSRGB_SkGammaNamed:
261 fElementsPipeline.append(SkRasterPipeline::to_srgb);
262 break;
263 case kNonStandard_SkGammaNamed: {
264 for (int channel = 0; channel < 3; ++channel) {
265 const SkGammas& gammas = *dstSpace->gammas();
266 if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
267 static constexpr int kInvTableSize = 256;
268 std::vector<float> storage(kInvTableSize);
269 invert_table_gamma(storage.data(), nullptr, storage.size(),
270 gammas.table(channel),
271 gammas.data(channel).fTable.fSize);
272 SkTableTransferFn table = {
273 storage.data(),
274 (int) storage.size(),
275 };
276 fTableStorage.push_front(std::move(storage));
Matt Sarettdb4d4062016-11-16 16:07:15 -0500277
raftias97524542016-12-14 13:15:05 -0500278 this->addTableFn(table, channel);
279 } else {
Ravi Mistry113d05f2016-12-17 01:31:03 +0000280 SkColorSpaceTransferFn fn;
281 SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
282 this->addTransferFn(invert_parametric(fn), channel);
raftias97524542016-12-14 13:15:05 -0500283 }
raftias25636012016-11-11 15:27:39 -0800284 }
285 }
raftias97524542016-12-14 13:15:05 -0500286 break;
raftias25636012016-11-11 15:27:39 -0800287 }
288}
289
raftias54761282016-12-01 13:44:07 -0500290void SkColorSpaceXform_A2B::addTransferFns(const SkColorSpaceTransferFn& fn, int channelCount) {
291 for (int i = 0; i < channelCount; ++i) {
292 this->addTransferFn(fn, i);
293 }
294}
295
296void SkColorSpaceXform_A2B::addTransferFn(const SkColorSpaceTransferFn& fn, int channelIndex) {
Matt Sarettdb4d4062016-11-16 16:07:15 -0500297 fTransferFns.push_front(fn);
raftias54761282016-12-01 13:44:07 -0500298 switch (channelIndex) {
299 case 0:
Mike Klein62458a62016-12-01 16:38:16 +0000300 fElementsPipeline.append(SkRasterPipeline::parametric_r, &fTransferFns.front());
301 break;
raftias54761282016-12-01 13:44:07 -0500302 case 1:
Mike Klein62458a62016-12-01 16:38:16 +0000303 fElementsPipeline.append(SkRasterPipeline::parametric_g, &fTransferFns.front());
304 break;
raftias54761282016-12-01 13:44:07 -0500305 case 2:
Mike Klein62458a62016-12-01 16:38:16 +0000306 fElementsPipeline.append(SkRasterPipeline::parametric_b, &fTransferFns.front());
raftias51c3fcd2016-11-30 11:19:22 -0500307 break;
raftias54761282016-12-01 13:44:07 -0500308 case 3:
309 fElementsPipeline.append(SkRasterPipeline::parametric_a, &fTransferFns.front());
310 break;
Matt Sarettdb4d4062016-11-16 16:07:15 -0500311 default:
312 SkASSERT(false);
313 }
314}
315
raftias54761282016-12-01 13:44:07 -0500316void SkColorSpaceXform_A2B::addTableFn(const SkTableTransferFn& fn, int channelIndex) {
Matt Sarettdb4d4062016-11-16 16:07:15 -0500317 fTableTransferFns.push_front(fn);
raftias54761282016-12-01 13:44:07 -0500318 switch (channelIndex) {
319 case 0:
Mike Klein62458a62016-12-01 16:38:16 +0000320 fElementsPipeline.append(SkRasterPipeline::table_r, &fTableTransferFns.front());
321 break;
raftias54761282016-12-01 13:44:07 -0500322 case 1:
Mike Klein62458a62016-12-01 16:38:16 +0000323 fElementsPipeline.append(SkRasterPipeline::table_g, &fTableTransferFns.front());
324 break;
raftias54761282016-12-01 13:44:07 -0500325 case 2:
Mike Klein62458a62016-12-01 16:38:16 +0000326 fElementsPipeline.append(SkRasterPipeline::table_b, &fTableTransferFns.front());
raftias51c3fcd2016-11-30 11:19:22 -0500327 break;
raftias54761282016-12-01 13:44:07 -0500328 case 3:
329 fElementsPipeline.append(SkRasterPipeline::table_a, &fTableTransferFns.front());
330 break;
raftias25636012016-11-11 15:27:39 -0800331 default:
332 SkASSERT(false);
333 }
334}
335
336void SkColorSpaceXform_A2B::addMatrix(const SkMatrix44& matrix) {
337 fMatrices.push_front(std::vector<float>(12));
338 auto& m = fMatrices.front();
339 m[ 0] = matrix.get(0, 0);
340 m[ 1] = matrix.get(1, 0);
341 m[ 2] = matrix.get(2, 0);
342 m[ 3] = matrix.get(0, 1);
343 m[ 4] = matrix.get(1, 1);
344 m[ 5] = matrix.get(2, 1);
345 m[ 6] = matrix.get(0, 2);
346 m[ 7] = matrix.get(1, 2);
347 m[ 8] = matrix.get(2, 2);
348 m[ 9] = matrix.get(0, 3);
349 m[10] = matrix.get(1, 3);
350 m[11] = matrix.get(2, 3);
351 SkASSERT(matrix.get(3, 0) == 0.f);
352 SkASSERT(matrix.get(3, 1) == 0.f);
353 SkASSERT(matrix.get(3, 2) == 0.f);
354 SkASSERT(matrix.get(3, 3) == 1.f);
355 fElementsPipeline.append(SkRasterPipeline::matrix_3x4, m.data());
356 fElementsPipeline.append(SkRasterPipeline::clamp_0);
Matt Sarettdb4d4062016-11-16 16:07:15 -0500357 fElementsPipeline.append(SkRasterPipeline::clamp_1);
raftias25636012016-11-11 15:27:39 -0800358}