Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(112)

Side by Side Diff: src/core/SkColorSpaceXform_A2B.cpp

Issue 2449243003: Initial implementation of a SkColorSpace_A2B xform (Closed)
Patch Set: fixed compile error on certain trybots related to RVO move-elision Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/core/SkColorSpaceXform_A2B.h ('k') | src/core/SkColorSpace_A2B.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 #include "SkRasterPipeline_opts.h"
21
22 #define AI SK_ALWAYS_INLINE
23
24 namespace {
25
26 class ApplyParametric {
27 public:
28 ApplyParametric(const SkColorSpaceTransferFn& fn)
29 : fFn(fn)
30 {}
31
32 float operator()(float x) const {
33 float y;
34 if (x >= fFn.fD) {
35 y = ::powf(fFn.fA * x + fFn.fB, fFn.fG) + fFn.fC;
36 } else {
37 y = fFn.fE * x + fFn.fF;
38 }
39 if (y >= 1.f) {
40 return 1.f;
41 } else if (y >= 0.f) {
42 return y;
43 }
44 return 0.f;
45 }
46
47 private:
48 SkColorSpaceTransferFn fFn;
49 };
50
51 class ApplyTable {
52 public:
53 ApplyTable(const float* table, int size)
54 : fTable(table)
55 , fSize(size)
56 {}
57
58 float operator()(float x) const {
59 return interp_lut(x, fTable, fSize);
60 }
61
62 private:
63 const float* fTable;
64 int fSize;
65 };
66
67 }
68
69 //////////////////////////////////////////////////////////////////////////////// ///////////////////
70 bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorForma t srcFormat,
71 const void* src, int count, SkAlphaType alph aType) const {
72 SkRasterPipeline pipeline;
73 switch (srcFormat) {
74 case kBGRA_8888_ColorFormat:
75 pipeline.append(SkRasterPipeline::load_s_8888, &src);
76 pipeline.append(SkRasterPipeline::swap_rb);
77 break;
78 case kRGBA_8888_ColorFormat:
79 pipeline.append(SkRasterPipeline::load_s_8888, &src);
80 break;
81 default:
82 SkCSXformPrintf("F16/F32 source color format not supported\n");
83 return false;
84 }
85
86 pipeline.extend(fElementsPipeline);
87
88 if (kPremul_SkAlphaType == alphaType) {
89 pipeline.append(SkRasterPipeline::premul);
90 }
91
92 switch (dstFormat) {
93 case kBGRA_8888_ColorFormat:
94 pipeline.append(SkRasterPipeline::swap_rb);
95 pipeline.append(SkRasterPipeline::store_8888, &dst);
96 break;
97 case kRGBA_8888_ColorFormat:
98 pipeline.append(SkRasterPipeline::store_8888, &dst);
99 break;
100 case kRGBA_F16_ColorFormat:
101 if (!fLinearDstGamma) {
102 return false;
103 }
104 pipeline.append(SkRasterPipeline::store_f16, &dst);
105 break;
106 case kRGBA_F32_ColorFormat:
107 if (!fLinearDstGamma) {
108 return false;
109 }
110 pipeline.append(SkRasterPipeline::store_f32, &dst);
111 break;
112 }
113
114 auto p = pipeline.compile();
115
116 p(0, count);
117
118 return true;
119 }
120
121 static inline SkColorSpaceTransferFn value_to_parametric(float exp) {
122 return {exp, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
123 }
124
125 static inline SkColorSpaceTransferFn gammanamed_to_parametric(SkGammaNamed gamma Named) {
126 switch (gammaNamed) {
127 case kLinear_SkGammaNamed:
128 return value_to_parametric(1.f);
129 case kSRGB_SkGammaNamed:
130 return {2.4f, (1.f / 1.055f), (0.055f / 1.055f), 0.f, 0.04045f, (1.f / 12.92f), 0.f};
131 case k2Dot2Curve_SkGammaNamed:
132 return value_to_parametric(2.2f);
133 default:
134 SkASSERT(false);
135 return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f};
136 }
137 }
138
139 static inline SkColorSpaceTransferFn gamma_to_parametric(const SkGammas& gammas, int channel) {
140 switch (gammas.type(channel)) {
141 case SkGammas::Type::kNamed_Type:
142 return gammanamed_to_parametric(gammas.data(channel).fNamed);
143 case SkGammas::Type::kValue_Type:
144 return value_to_parametric(gammas.data(channel).fValue);
145 case SkGammas::Type::kParam_Type:
146 return gammas.params(channel);
147 default:
148 SkASSERT(false);
149 return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f};
150 }
151 }
152 static inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransfe rFn& fn) {
153 // Original equation is: y = (ax + b)^g + c for x >= d
154 // y = ex + f otherwise
155 //
156 // so 1st inverse is: (y - c)^(1/g) = ax + b
157 // x = ((y - c)^(1/g) - b) / a
158 //
159 // which can be re-written as: x = (1/a)(y - c)^(1/g) - b/a
160 // x = ((1/a)^g)^(1/g) * (y - c)^(1/g) - b/a
161 // x = ([(1/a)^g]y + [-((1/a)^g)c]) ^ [1/g] + [- b/a]
162 //
163 // and 2nd inverse is: x = (y - f) / e
164 // which can be re-written as: x = [1/e]y + [-f/e]
165 //
166 // and now both can be expressed in terms of the same parametric form as the
167 // original - parameters are enclosed in square barckets.
168
169 // find inverse for linear segment (if possible)
170 float e, f;
171 if (0.f == fn.fE) {
172 // otherwise assume it should be 0 as it is the lower segment
173 // as y = f is a constant function
174 e = 0.f;
175 f = 0.f;
176 } else {
177 e = 1.f / fn.fE;
178 f = -fn.fF / fn.fE;
179 }
180 // find inverse for the other segment (if possible)
181 float g, a, b, c;
182 if (0.f == fn.fA || 0.f == fn.fG) {
183 // otherwise assume it should be 1 as it is the top segment
184 // as you can't invert the constant functions y = b^g + c, or y = 1 + c
185 g = 1.f;
186 a = 0.f;
187 b = 0.f;
188 c = 1.f;
189 } else {
190 g = 1.f / fn.fG;
191 a = powf(1.f / fn.fA, fn.fG);
192 b = -a * fn.fC;
193 c = -fn.fB / fn.fA;
194 }
195 const float d = fn.fE * fn.fD + fn.fF;
196 return {g, a, b, c, d, e, f};
197 }
198
199 static std::vector<float> build_inverse_table(const float* inTable, int inTableS ize) {
200 static constexpr int kInvTableSize = 256;
201 std::vector<float> outTable(kInvTableSize);
202 for (int i = 0; i < kInvTableSize; ++i) {
203 const float x = ((float) i) * (1.f / ((float) (kInvTableSize - 1)));
204 const float y = inverse_interp_lut(x, inTable, inTableSize);
205 outTable[i] = y;
206 }
207 return outTable;
208 }
209
210 SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
211 SkColorSpace_XYZ* dstSpace)
212 : fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) {
213 #if (SkCSXformPrintfDefined)
214 static const char* debugGammaNamed[4] = {
215 "Linear", "SRGB", "2.2", "NonStandard"
216 };
217 static const char* debugGammas[5] = {
218 "None", "Named", "Value", "Table", "Param"
219 };
220 #endif
221 // add in all input color space -> PCS xforms
222 for (int i = 0; i < srcSpace->count(); ++i) {
223 const SkColorSpace_A2B::Element& e = srcSpace->element(i);
224 switch (e.type()) {
225 case SkColorSpace_A2B::Element::Type::kGammaNamed:
226 if (kLinear_SkGammaNamed != e.gammaNamed()) {
227 SkCSXformPrintf("Gamma stage added: %s\n",
228 debugGammaNamed[(int)e.gammaNamed()]);
229 addGamma(ApplyParametric(gammanamed_to_parametric(e.gammaNam ed())),
230 kRGB_Channels);
231 }
232 break;
233 case SkColorSpace_A2B::Element::Type::kGammas: {
234 const SkGammas& gammas = e.gammas();
235 SkCSXformPrintf("Gamma stage added:");
236 for (int channel = 0; channel < 3; ++channel) {
237 SkCSXformPrintf(" %s", debugGammas[(int)gammas.type(cha nnel)]);
238 }
239 SkCSXformPrintf("\n");
240 bool gammaNeedsRef = false;
241 for (int channel = 0; channel < 3; ++channel) {
242 if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
243 addGamma(ApplyTable(gammas.table(channel),
244 gammas.data(channel).fTable.fSiz e),
245 static_cast<Channels>(channel));
246 gammaNeedsRef = true;
247 } else {
248 addGamma(ApplyParametric(gamma_to_parametric(gammas, channel)),
249 static_cast<Channels>(channel));
250 }
251 }
252 if (gammaNeedsRef) {
253 fGammaRefs.push_back(sk_ref_sp(&gammas));
254 }
255 }
256 break;
257 case SkColorSpace_A2B::Element::Type::kCLUT:
258 SkCSXformPrintf("CLUT stage added [%d][%d][%d]\n", e.colorLUT(). fGridPoints[0],
259 e.colorLUT().fGridPoints[1], e.colorLUT().fGridP oints[2]);
260 fCLUTs.push_back(sk_ref_sp(&e.colorLUT()));
261 fElementsPipeline.append(SkRasterPipeline::color_lookup_table,
262 fCLUTs.back().get());
263 break;
264 case SkColorSpace_A2B::Element::Type::kMatrix:
265 if (!e.matrix().isIdentity()) {
266 SkCSXformPrintf("Matrix stage added\n");
267 addMatrix(e.matrix());
268 }
269 break;
270 }
271 }
272
273 // Lab PCS -> XYZ PCS
274 if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) {
275 SkCSXformPrintf("Lab -> XYZ element added\n");
276 fElementsPipeline.append(SkRasterPipeline::lab_to_xyz);
277 }
278
279 // and XYZ PCS -> output color space xforms
280 if (!dstSpace->fromXYZD50()->isIdentity()) {
281 addMatrix(*dstSpace->fromXYZD50());
282 }
283
284 if (kNonStandard_SkGammaNamed != dstSpace->gammaNamed()) {
285 if (!fLinearDstGamma) {
286 addGamma(ApplyParametric(
287 invert_parametric(gammanamed_to_parametric(dstSpace- >gammaNamed()))),
288 kRGB_Channels);
289 }
290 } else {
291 for (int channel = 0; channel < 3; ++channel) {
292 const SkGammas& gammas = *dstSpace->gammas();
293 if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
294 fGammaTables.push_front(build_inverse_table(gammas.table(channel ),
295 gammas.data(channel) .fTable.fSize));
296 addGamma(ApplyTable(fGammaTables.front().data(), fGammaTables.fr ont().size()),
297 static_cast<Channels>(channel));
298 } else {
299 addGamma(ApplyParametric(invert_parametric(gamma_to_parametric(g ammas, channel))),
300 static_cast<Channels>(channel));
301 }
302 }
303 }
304 }
305
306 void SkColorSpaceXform_A2B::addGamma(std::function<float(float)> fn, Channels ch annels) {
307 fGammaFunctions.push_front(std::move(fn));
308 switch (channels) {
309 case kRGB_Channels:
310 fElementsPipeline.append(SkRasterPipeline::fn_1_r, &fGammaFunctions. front());
311 fElementsPipeline.append(SkRasterPipeline::fn_1_g, &fGammaFunctions. front());
312 fElementsPipeline.append(SkRasterPipeline::fn_1_b, &fGammaFunctions. front());
313 break;
314 case kR_Channels:
315 fElementsPipeline.append(SkRasterPipeline::fn_1_r, &fGammaFunctions. front());
316 break;
317 case kG_Channels:
318 fElementsPipeline.append(SkRasterPipeline::fn_1_g, &fGammaFunctions. front());
319 break;
320 case kB_Channels:
321 fElementsPipeline.append(SkRasterPipeline::fn_1_b, &fGammaFunctions. front());
322 break;
323 default:
324 SkASSERT(false);
325 }
326 }
327
328 void SkColorSpaceXform_A2B::addMatrix(const SkMatrix44& matrix) {
329 fMatrices.push_front(std::vector<float>(12));
330 auto& m = fMatrices.front();
331 m[ 0] = matrix.get(0, 0);
332 m[ 1] = matrix.get(1, 0);
333 m[ 2] = matrix.get(2, 0);
334 m[ 3] = matrix.get(0, 1);
335 m[ 4] = matrix.get(1, 1);
336 m[ 5] = matrix.get(2, 1);
337 m[ 6] = matrix.get(0, 2);
338 m[ 7] = matrix.get(1, 2);
339 m[ 8] = matrix.get(2, 2);
340 m[ 9] = matrix.get(0, 3);
341 m[10] = matrix.get(1, 3);
342 m[11] = matrix.get(2, 3);
343 SkASSERT(matrix.get(3, 0) == 0.f);
344 SkASSERT(matrix.get(3, 1) == 0.f);
345 SkASSERT(matrix.get(3, 2) == 0.f);
346 SkASSERT(matrix.get(3, 3) == 1.f);
347 fElementsPipeline.append(SkRasterPipeline::matrix_3x4, m.data());
348 fElementsPipeline.append(SkRasterPipeline::clamp_0);
349 fElementsPipeline.append(SkRasterPipeline::clamp_a);
350 }
351
352
OLDNEW
« no previous file with comments | « src/core/SkColorSpaceXform_A2B.h ('k') | src/core/SkColorSpace_A2B.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698