OLD | NEW |
---|---|
(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 class SkColorSpaceXform_A2B::ProcessingElement { | |
21 public: | |
22 virtual ~ProcessingElement() {} | |
23 virtual void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) = 0; | |
24 virtual bool merge(ProcessingElement* other) = 0; | |
25 | |
26 enum class Type { | |
27 kGamma, | |
28 kGammaTable, | |
29 kMatrix, | |
30 kCLUT, | |
31 kLabToXYZ | |
32 }; | |
33 virtual Type type() const = 0; | |
34 }; | |
35 | |
36 class SkColorSpaceXform_A2B::ApplyCLUT : public SkColorSpaceXform_A2B::Processin gElement { | |
37 public: | |
38 ApplyCLUT(const SkColorLookUpTable* clut) | |
39 : fCLUT(sk_ref_sp(clut)) | |
40 {} | |
41 | |
42 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
msarett
2016/10/26 20:43:29
This feels like a really good place to use SkRaste
raftias
2016/10/27 18:39:39
Acknowledged.
| |
43 while (len > 0) { | |
44 // unpack into float[3] = {r, g, b} arrays like interp_3d_clut() nee ds | |
msarett
2016/10/26 20:43:29
If it's convenient, you are welcome to change the
raftias
2016/10/27 18:39:38
It involved look-up tables so without constructing
| |
45 float rgb[4][3] = { | |
46 {(*rSrc)[0], (*gSrc)[0], (*bSrc)[0]}, | |
47 {(*rSrc)[1], (*gSrc)[1], (*bSrc)[1]}, | |
48 {(*rSrc)[2], (*gSrc)[2], (*bSrc)[2]}, | |
49 {(*rSrc)[3], (*gSrc)[3], (*bSrc)[3]} | |
50 }; | |
51 for (int i = 0; i < 4; ++i) { | |
52 interp_3d_clut(rgb[i], rgb[i], fCLUT.get()); | |
53 } | |
54 *rSrc = Sk4f(rgb[0][0], rgb[1][0], rgb[2][0], rgb[3][0]); | |
55 *gSrc = Sk4f(rgb[0][1], rgb[1][1], rgb[2][1], rgb[3][1]); | |
56 *bSrc = Sk4f(rgb[0][2], rgb[1][2], rgb[2][2], rgb[3][2]); | |
57 ++rSrc; | |
58 ++gSrc; | |
59 ++bSrc; | |
60 --len; | |
61 } | |
62 } | |
63 | |
64 bool merge(ProcessingElement*) override { return false; } | |
65 | |
66 Type type() const override { return Type::kCLUT; } | |
67 | |
68 private: | |
69 sk_sp<const SkColorLookUpTable> fCLUT; | |
70 }; | |
71 | |
72 class SkColorSpaceXform_A2B::LabToXYZ : public SkColorSpaceXform_A2B::Processing Element { | |
73 public: | |
74 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
75 while (len > 0) { | |
76 // convert from Lab to XYZ | |
77 Sk4f Y = *rSrc*(100.f/116.f) + (16.f/116.f); | |
78 Sk4f X = *gSrc * (255.f/500.f) - (128.f/500.f) + Y; | |
79 Sk4f Z = Y - *bSrc * (255.f/200.f) - (128.f/200.f); | |
80 | |
81 /*const Sk4f l = *rSrc * 100.f; | |
82 const Sk4f a = *gSrc * 255.f - 128.f; | |
83 const Sk4f b = *bSrc * 255.f - 128.f; | |
84 Sk4f Y = (l + 16.f) * (1.f/116.f); | |
85 Sk4f X = a * (1.f/500.f) + Y; | |
86 Sk4f Z = Y - (b * (1.f/200.f));*/ | |
87 | |
88 | |
89 Sk4f cubed; | |
90 cubed = X*X*X; | |
91 X = (cubed > 0.008856f).thenElse(cubed, (X - (16.f/116.f)) * (1.f/7. 787f)); | |
92 cubed = Y*Y*Y; | |
93 Y = (cubed > 0.008856f).thenElse(cubed, (Y - (16.f/116.f)) * (1.f/7. 787f)); | |
94 cubed = Z*Z*Z; | |
95 Z = (cubed > 0.008856f).thenElse(cubed, (Z - (16.f/116.f)) * (1.f/7. 787f)); | |
96 | |
97 // adjust to D50 illuminant | |
98 X *= 0.96422f; | |
99 Y *= 1.00000f; | |
100 Z *= 0.82521f; | |
101 | |
102 *rSrc = X; | |
103 *gSrc = Y; | |
104 *bSrc = Z; | |
105 ++rSrc; | |
106 ++gSrc; | |
107 ++bSrc; | |
108 --len; | |
109 } | |
110 } | |
111 | |
112 bool merge(ProcessingElement*) override { return false; } | |
113 | |
114 Type type() const override { return Type::kLabToXYZ; } | |
115 }; | |
116 | |
117 class SkColorSpaceXform_A2B::ApplyMatrix : public SkColorSpaceXform_A2B::Process ingElement { | |
118 public: | |
119 ApplyMatrix(const SkMatrix44& matrix) | |
120 : fMatrix(matrix) | |
121 {} | |
122 | |
123 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
124 Sk4f rXgXbX, rYgYbY, rZgZbZ, rTgTbT; | |
125 float colMajor[16]; | |
126 fMatrix.asColMajorf(colMajor); | |
127 load_matrix(colMajor, rXgXbX, rYgYbY, rZgZbZ, rTgTbT); | |
128 while (len > 0) { | |
129 Sk4f dr, dg, db, a; // a is ignored | |
130 transform_gamut(*rSrc, *gSrc, *bSrc, a, rXgXbX, rYgYbY, rZgZbZ, dr, dg, db, a); | |
131 translate_gamut(rTgTbT, dr, dg, db); | |
132 *rSrc = dr; | |
133 *gSrc = dg; | |
134 *bSrc = db; | |
135 ++rSrc; | |
136 ++gSrc; | |
137 ++bSrc; | |
138 --len; | |
139 } | |
140 } | |
141 bool merge(ProcessingElement* other) override { | |
142 if (other->type() == Type::kMatrix) { | |
143 fMatrix.preConcat(static_cast<ApplyMatrix*>(other)->fMatrix); | |
144 return true; | |
145 } | |
146 return false; | |
147 } | |
148 Type type() const override { | |
149 return Type::kMatrix; | |
150 } | |
151 private: | |
152 SkMatrix44 fMatrix; | |
153 }; | |
154 | |
155 | |
156 | |
157 // adapted from the integer-input version in SkSRGB.h | |
158 template <int N> | |
159 static inline SkNx<N,float> sk_linear_from_float_srgb_math(const SkNx<N,float>& x) { | |
msarett
2016/10/26 20:43:29
Let's move this to SkSRGB.h. I think the int->flo
raftias
2016/10/27 18:39:38
Removed function entirely. Will consider this if/w
| |
160 // Non-linear segment of sRGB curve approximated by | |
161 // l = 0.0025 + 0.6975x^2 + 0.3x^3 | |
162 const SkNx<N,float> k0 = 0.0025f, | |
163 k2 = 0.6975f, | |
164 k3 = 0.3000f; | |
165 auto hi = x*x*(x*k3 + k2) + k0; | |
166 | |
167 // Linear segment of sRGB curve: the normal slope, extended a little further than normal. | |
168 auto lo = x * (1.f/12.92f); | |
169 | |
170 return (x < (14.025f / 255.f)).thenElse(lo, hi); | |
171 } | |
172 | |
173 | |
174 class SkColorSpaceXform_A2B::ApplyGammaSRGB : public SkColorSpaceXform_A2B::Proc essingElement { | |
175 public: | |
176 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
177 while (len > 0) { | |
178 *rSrc = sk_linear_from_float_srgb_math(*rSrc); | |
179 *gSrc = sk_linear_from_float_srgb_math(*gSrc); | |
180 *bSrc = sk_linear_from_float_srgb_math(*bSrc); | |
181 ++rSrc; | |
182 ++gSrc; | |
183 ++bSrc; | |
184 --len; | |
185 } | |
186 } | |
187 bool merge(ProcessingElement* other) override { return false; } | |
188 Type type() const override { return Type::kGamma; } | |
189 }; | |
190 | |
191 static inline Sk4f linear_from_2dot2(const Sk4f& x) { | |
192 | |
193 // x^(141/64) = x^(2.20312) is a great approximation of the true value, x^(2 .2). | |
194 auto x16 = x.rsqrt().rsqrt().rsqrt().rsqrt(); // x^(1/16) = x^(4/64); | |
195 auto x64 = x16.rsqrt().rsqrt(); // x^(1/64) | |
196 | |
197 // x^(35/16) = x^(2.1875) is an okay one as well and would be quicker: | |
msarett
2016/10/26 20:43:29
Please remove this comment. It's not a bad point,
raftias
2016/10/27 18:39:39
Removed function entirely. Will consider this if/w
| |
198 // (both pass ColorSpaceXformTests's +/-1 difference vs x^2.2) | |
199 // return x*x * x16*x16*x16; | |
200 | |
201 // x^(141/64) = x^(128/64) * x^(12/64) * x^(1/64) | |
202 return x*x * x16*x16*x16 * x64; | |
203 } | |
204 | |
205 class SkColorSpaceXform_A2B::ApplyGamma2Dot2 : public SkColorSpaceXform_A2B::Pro cessingElement { | |
206 public: | |
207 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
208 while (len > 0) { | |
209 *rSrc = linear_from_2dot2(*rSrc); | |
210 *gSrc = linear_from_2dot2(*gSrc); | |
211 *bSrc = linear_from_2dot2(*bSrc); | |
212 ++rSrc; | |
213 ++gSrc; | |
214 ++bSrc; | |
215 --len; | |
216 } | |
217 } | |
218 bool merge(ProcessingElement* other) override { return false; } | |
219 Type type() const override { return Type::kGamma; } | |
220 }; | |
221 | |
222 // TODO(raftias): See if there's some actually-vectorized implementation somewhe re? | |
msarett
2016/10/26 20:43:29
As far I know, there's not. I don't even think th
| |
223 // or remove once all powf() calling transforms (value, param) are converted int o tables | |
224 static inline Sk4f SkNx_powf(const Sk4f& x, float exp) { | |
225 return Sk4f{::powf(x[0], exp), ::powf(x[1], exp), ::powf(x[2], exp), ::powf( x[3], exp)}; | |
226 } | |
227 | |
228 // TODO(raftias): remove and store in tables | |
229 class SkColorSpaceXform_A2B::ApplyGammaValue : public SkColorSpaceXform_A2B::Pro cessingElement { | |
230 public: | |
231 ApplyGammaValue(float rExp, float gExp, float bExp) | |
232 : fRExp(rExp) | |
233 , fGExp(gExp) | |
234 , fBExp(bExp) | |
235 {} | |
236 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
237 while (len > 0) { | |
238 *rSrc = SkNx_powf(*rSrc, fRExp); | |
239 *gSrc = SkNx_powf(*gSrc, fGExp); | |
240 *bSrc = SkNx_powf(*bSrc, fBExp); | |
241 ++rSrc; | |
242 ++gSrc; | |
243 ++bSrc; | |
244 --len; | |
245 } | |
246 } | |
247 bool merge(ProcessingElement* other) override { return false; } | |
248 Type type() const override { return Type::kGamma; } | |
249 private: | |
250 float fRExp; | |
251 float fGExp; | |
252 float fBExp; | |
253 }; | |
254 | |
255 // TODO(raftias): remove and store in tables? | |
256 class SkColorSpaceXform_A2B::ApplyGammaParams : public SkColorSpaceXform_A2B::Pr ocessingElement { | |
257 public: | |
258 ApplyGammaParams(const SkColorSpaceTransferFn& rParams, const SkColorSpaceTr ansferFn& gParams, | |
259 const SkColorSpaceTransferFn& bParams) | |
260 : fRP(rParams) | |
261 , fGP(gParams) | |
262 , fBP(bParams) | |
263 {} | |
264 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
265 // TODO(raftias): profile against a non-vectorized implementation, since if the (short) | |
266 // linear segment is hit a lot then there's a lot of unnecessary powf() calls. | |
267 // Or just remove these and cache into tables. | |
268 while (len > 0) { | |
269 *rSrc = (*rSrc <= fRP.fD).thenElse(fRP.fE * (*rSrc) + fRP.fF, | |
270 SkNx_powf(*rSrc*fRP.fA + fRP.fB, fRP.fG) + fRP.fC); | |
271 *gSrc = (*gSrc <= fGP.fD).thenElse(fGP.fE * (*gSrc) + fGP.fF, | |
272 SkNx_powf(*gSrc*fGP.fA + fGP.fB, fGP.fG) + fGP.fC); | |
273 *bSrc = (*bSrc <= fBP.fD).thenElse(fBP.fE * (*bSrc) + fBP.fF, | |
274 SkNx_powf(*bSrc*fBP.fA + fBP.fB, fBP.fG) + fBP.fC); | |
275 ++rSrc; | |
276 ++gSrc; | |
277 ++bSrc; | |
278 --len; | |
279 } | |
280 } | |
281 bool merge(ProcessingElement* other) override { return false; } | |
282 Type type() const override { return Type::kGamma; } | |
283 private: | |
284 const SkColorSpaceTransferFn fRP; | |
285 const SkColorSpaceTransferFn fGP; | |
286 const SkColorSpaceTransferFn fBP; | |
287 }; | |
288 | |
289 class SkColorSpaceXform_A2B::ApplyGammaTable : public SkColorSpaceXform_A2B::Pro cessingElement { | |
290 public: | |
291 ApplyGammaTable(const SkGammas* gammas) { | |
292 // TODO(raftias): use 1 table if all channels point to the same table | |
293 const int numTables = 3; | |
294 const size_t tableBytes = numTables * kDstGammaTableSize * sizeof(float) ; | |
295 const float tableStep = 1.f / (float)(kDstGammaTableSize - 1); | |
296 fStorage.reset(tableBytes); | |
297 float* outTable = fStorage.get(); | |
298 for (int i = 0; i < numTables; ++i) { | |
299 SkASSERT(gammas->isTable(i)); | |
300 const float* inTable = gammas->table(i); | |
301 const int inTableSize = gammas->data(i).fTable.fSize; | |
302 fGammaTables[i] = outTable; | |
303 if (kDstGammaTableSize == inTableSize) { | |
304 memcpy(outTable, inTable, sizeof(float) * kDstGammaTableSize); | |
305 return; | |
306 } | |
307 | |
308 for (float x = 0.0f; x <= 1.0f; x += tableStep) { | |
309 *outTable++ = interp_lut(x, inTable, inTableSize); | |
310 } | |
311 } | |
312 } | |
313 | |
314 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
315 const float maxIndex = kDstGammaTableSize - 1; | |
316 while (len > 0) { | |
317 *rSrc = Sk4f::Min(Sk4f::Max(maxIndex * (*rSrc), 0.f), maxIndex); | |
318 *gSrc = Sk4f::Min(Sk4f::Max(maxIndex * (*gSrc), 0.f), maxIndex); | |
319 *bSrc = Sk4f::Min(Sk4f::Max(maxIndex * (*bSrc), 0.f), maxIndex); | |
320 const Sk4i ir = Sk4f_round(*rSrc); | |
321 const Sk4i ig = Sk4f_round(*gSrc); | |
322 const Sk4i ib = Sk4f_round(*bSrc); | |
323 *rSrc = Sk4f((float) fGammaTables[0][ir[0]], | |
324 (float) fGammaTables[0][ir[1]], | |
325 (float) fGammaTables[0][ir[2]], | |
326 (float) fGammaTables[0][ir[3]]); | |
327 *gSrc = Sk4f((float) fGammaTables[1][ig[0]], | |
328 (float) fGammaTables[1][ig[1]], | |
329 (float) fGammaTables[1][ig[2]], | |
330 (float) fGammaTables[1][ig[3]]); | |
331 *bSrc = Sk4f((float) fGammaTables[2][ib[0]], | |
332 (float) fGammaTables[2][ib[1]], | |
333 (float) fGammaTables[2][ib[2]], | |
334 (float) fGammaTables[2][ib[3]]); | |
335 ++rSrc; | |
336 ++gSrc; | |
337 ++bSrc; | |
338 --len; | |
339 } | |
340 } | |
341 | |
342 bool merge(ProcessingElement* other) override { | |
343 if (Type::kGammaTable == other->type()) { | |
344 auto otherGamma = static_cast<const SkColorSpaceXform_A2B::ApplyGamm aTable*>(other); | |
345 const int numTables = 3; // set to 1 if matching | |
346 for (int table = 0; table < numTables; ++table) { | |
347 for (int i = 0; i < kDstGammaTableSize; ++i) { | |
348 const int indexIntoOther = (int)(fGammaTables[table][i] + 0. 5f); | |
349 fGammaTables[table][i] = otherGamma->fGammaTables[table][ind exIntoOther]; | |
350 } | |
351 } | |
352 return true; | |
353 } | |
354 return false; | |
355 } | |
356 | |
357 Type type() const override { return Type::kGammaTable; } | |
358 | |
359 private: | |
360 SkAutoTMalloc<float> fStorage; | |
361 float* fGammaTables[3]; | |
362 }; | |
363 | |
364 //////////////////////////////////////////////////////////////////////////////// /////////////////// | |
365 | |
366 | |
367 bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorForma t srcFormat, | |
368 const void* src, int count, SkAlphaType alph aType) const { | |
369 LoadFn load; | |
370 const bool loadAlpha = (kPremul_SkAlphaType == alphaType) || | |
371 (kRGBA_F16_ColorFormat == dstFormat) || | |
372 (kRGBA_F32_ColorFormat == dstFormat); | |
373 | |
374 switch (srcFormat) { | |
375 case kRGBA_8888_ColorFormat: | |
376 load = loadAlpha ? load_rgba_linear<kRGBA_Order> | |
377 : load_rgb_linear<kRGBA_Order>; | |
378 break; | |
379 case kBGRA_8888_ColorFormat: | |
380 load = loadAlpha ? load_rgba_linear<kBGRA_Order> | |
381 : load_rgb_linear<kBGRA_Order>; | |
382 break; | |
383 default: | |
384 SkCSXformPrintf("F16/F32 source color format not supported\n"); | |
385 return false; | |
386 } | |
387 | |
388 StoreFn store = store_linear<kRGBA_Order>; | |
389 size_t sizeOfDstPixel = 0; | |
390 switch (dstFormat) { | |
391 case kRGBA_8888_ColorFormat: | |
392 switch (fDstGamma) { | |
393 case kLinear_SkGammaNamed: | |
394 store = store_linear<kRGBA_Order>; | |
395 break; | |
396 case kSRGB_SkGammaNamed: | |
397 store = store_srgb<kRGBA_Order>; | |
398 break; | |
399 case k2Dot2Curve_SkGammaNamed: | |
400 store = store_2dot2<kRGBA_Order>; | |
401 break; | |
402 default: | |
403 store = store_generic<kRGBA_Order>; | |
404 break; | |
405 } | |
406 sizeOfDstPixel = 4; | |
407 break; | |
408 case kBGRA_8888_ColorFormat: | |
409 switch (fDstGamma) { | |
410 case kLinear_SkGammaNamed: | |
411 store = store_linear<kBGRA_Order>; | |
412 break; | |
413 case kSRGB_SkGammaNamed: | |
414 store = store_srgb<kBGRA_Order>; | |
415 break; | |
416 case k2Dot2Curve_SkGammaNamed: | |
417 store = store_2dot2<kBGRA_Order>; | |
418 break; | |
419 default: | |
420 store = store_generic<kBGRA_Order>; | |
421 break; | |
422 } | |
423 sizeOfDstPixel = 4; | |
424 break; | |
425 case kRGBA_F16_ColorFormat: | |
426 if (fDstGamma != kLinear_SkGammaNamed) { | |
427 return false; | |
428 } | |
429 store = (kOpaque_SkAlphaType == alphaType) ? store_f16_opaque<kRGBA_ Order> | |
430 : store_f16<kRGBA_Order>; | |
431 sizeOfDstPixel = 8; | |
432 break; | |
433 case kRGBA_F32_ColorFormat: | |
434 if (fDstGamma != kLinear_SkGammaNamed) { | |
435 return false; | |
436 } | |
437 store = store_f32<kRGBA_Order>; | |
438 sizeOfDstPixel = 16; | |
439 break; | |
440 } | |
441 SkASSERT(sizeOfDstPixel > 0); | |
442 | |
443 const int allocLen = (count + 3) / 4; | |
444 SkAutoTMalloc<Sk4f> rSrc(allocLen); | |
445 SkAutoTMalloc<Sk4f> gSrc(allocLen); | |
446 SkAutoTMalloc<Sk4f> bSrc(allocLen); | |
447 Sk4f ignore; | |
448 const uint32_t* loadSrc = (const uint32_t*)src; | |
449 int loadLen = count; | |
450 Sk4f* loadRSrc = rSrc.get(); | |
451 Sk4f* loadGSrc = gSrc.get(); | |
452 Sk4f* loadBSrc = bSrc.get(); | |
453 while (loadLen >= 4) { | |
454 load(loadSrc, *loadRSrc, *loadGSrc, *loadBSrc, ignore, nullptr); | |
455 loadSrc += 4; | |
456 loadLen -= 4; | |
457 ++loadRSrc; | |
458 ++loadGSrc; | |
459 ++loadBSrc; | |
460 } | |
461 if (loadLen > 0) { | |
462 // read the last 1-3 RGBA values into a buffer and load all 4 RGBA | |
463 // values out of the buffer, letting the last 1-3 be arbitrary values | |
464 // (all byte combos are valid RGBA values) | |
465 uint32_t readOverrun[16]; | |
466 memcpy(readOverrun, loadSrc, loadLen * sizeof(uint32_t)); | |
467 load(readOverrun, *loadRSrc, *loadGSrc, *loadBSrc, ignore, nullptr); | |
468 } | |
469 | |
470 for (const std::unique_ptr<ProcessingElement>& element : fProcessingElements ) { | |
471 element->apply(rSrc.get(), gSrc.get(), bSrc.get(), allocLen); | |
472 } | |
473 | |
474 Sk4f* storeRSrc = rSrc.get(); | |
475 Sk4f* storeGSrc = gSrc.get(); | |
476 Sk4f* storeBSrc = bSrc.get(); | |
477 loadSrc = (const uint32_t*)src; | |
478 Sk4f alpha; | |
479 while (count >= 4) { | |
480 // load the alpha from the source image since our transformations only a ffected R/G/B | |
481 load(loadSrc, ignore, ignore, ignore, alpha, nullptr); | |
482 | |
483 if (kPremul_SkAlphaType == alphaType) { | |
484 premultiply(*storeRSrc, *storeGSrc, *storeBSrc, alpha); | |
485 } | |
486 | |
487 store(dst, (const uint32_t*)src, *storeRSrc, *storeGSrc, *storeBSrc, alp ha, | |
488 fDstGammaTables); | |
489 dst = SkTAddOffset<void>(dst, 4 * sizeOfDstPixel); | |
490 count -= 4; | |
491 loadSrc += 4; // to load alpha | |
492 ++storeRSrc; | |
493 ++storeGSrc; | |
494 ++storeBSrc; | |
495 } | |
496 if (count > 0) { | |
497 const int maxSizeOfDstPixel = 16; | |
498 SkASSERT(sizeOfDstPixel <= maxSizeOfDstPixel); | |
499 uint32_t writeOverrun[4*maxSizeOfDstPixel]; | |
500 | |
501 load(loadSrc, ignore, ignore, ignore, alpha, nullptr); | |
502 | |
503 if (kPremul_SkAlphaType == alphaType) { | |
504 premultiply(*storeRSrc, *storeGSrc, *storeBSrc, alpha); | |
505 } | |
506 | |
507 store(writeOverrun, (const uint32_t*)src, *storeRSrc, *storeGSrc, *store BSrc, alpha, | |
508 fDstGammaTables); | |
509 memcpy(dst, writeOverrun, count * sizeOfDstPixel); | |
510 } | |
511 return true; | |
512 } | |
513 | |
514 SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace, | |
515 SkColorSpace_XYZ* dstSpace) { | |
516 #if (SkCSXformPrintfDefined) | |
517 static const char* debugGammaNamed[4] = { | |
518 "Linear", "SRGB", "2.2", "NonStandard" | |
519 }; | |
520 static const char* debugGammas[5] = { | |
521 "None", "Named", "Value", "Table", "Param" | |
522 }; | |
523 #endif | |
524 // add in all device -> PCS xforms | |
525 for (size_t i = 0; i < srcSpace->count(); ++i) { | |
526 const SkColorSpace_A2B::Element& e = srcSpace->element(i); | |
527 switch (e.type()) { | |
528 case SkColorSpace_A2B::Element::Type::kGammaNamed: | |
529 SkCSXformPrintf("Gamma element added: %s\n", | |
530 debugGammaNamed[(int)e.gammaNamed()]); | |
531 switch (e.gammaNamed()) { | |
532 case kLinear_SkGammaNamed: | |
533 // there is no point in adding a transform here | |
534 break; | |
535 case kSRGB_SkGammaNamed: | |
536 fProcessingElements.push_back(skstd::make_unique<ApplyGa mmaSRGB>()); | |
537 break; | |
538 case k2Dot2Curve_SkGammaNamed: | |
539 fProcessingElements.push_back(skstd::make_unique<ApplyGa mma2Dot2>()); | |
540 break; | |
541 case kNonStandard_SkGammaNamed: | |
542 SkASSERT(false); | |
543 break; | |
544 } | |
545 break; | |
546 case SkColorSpace_A2B::Element::Type::kGammas: { | |
547 const SkGammas* gammas = &e.gammas(); | |
548 SkCSXformPrintf("Adding gamma element:"); | |
549 for (int channel = 0; channel < 3; ++channel) { | |
550 SkCSXformPrintf(" %s", debugGammas[(int)gammas->type(ch annel)]); | |
551 } | |
552 SkCSXformPrintf("\n"); | |
553 if (gammas->type(0) != gammas->type(1) || gammas->type(0) != gammas->type(2)) { | |
554 SkCSXformPrintf("Mixed gamma types are not supported rig ht now."); | |
555 SkCSXformPrintf(" Using only 1st gamma channel.\n"); | |
556 } | |
557 switch (gammas->type(0)) | |
558 { | |
559 case SkGammas::Type::kNone_Type: | |
560 SkCSXformPrintf("Invalid gamma type\n"); | |
561 break; | |
562 case SkGammas::Type::kNamed_Type: | |
563 switch (gammas->data(0).fNamed) { | |
564 case kLinear_SkGammaNamed: | |
565 // there is no point in adding a transform h ere | |
566 break; | |
567 case kSRGB_SkGammaNamed: | |
568 fProcessingElements.push_back( | |
569 skstd::make_unique<ApplyGammaSRGB>() ); | |
570 break; | |
571 case k2Dot2Curve_SkGammaNamed: | |
572 fProcessingElements.push_back( | |
573 skstd::make_unique<ApplyGamma2Dot2>( )); | |
574 break; | |
575 case kNonStandard_SkGammaNamed: | |
576 SkASSERT(false); | |
577 break; | |
578 } | |
579 break; | |
580 case SkGammas::Type::kValue_Type: | |
581 // TODO(raftias): pre-compute and cache these into a table | |
582 fProcessingElements.push_back(skstd::make_unique<App lyGammaValue>( | |
583 gammas->data(0).fValue, gammas->data(1).fVal ue, | |
584 gammas->data(2).fValue)); | |
585 break; | |
586 case SkGammas::Type::kTable_Type: | |
587 fProcessingElements.push_back( | |
588 skstd::make_unique<ApplyGammaTable>(gammas)); | |
589 break; | |
590 case SkGammas::Type::kParam_Type: { | |
591 // TODO(raftias): pre-compute and cache these into a table? | |
592 const SkColorSpaceTransferFn& pr = gammas->params(0) ; | |
593 const SkColorSpaceTransferFn& pg = gammas->params(1) ; | |
594 const SkColorSpaceTransferFn& pb = gammas->params(2) ; | |
595 fProcessingElements.push_back(skstd::make_unique<App lyGammaParams>( | |
596 pr, pg, pb)); | |
597 } | |
598 break; | |
599 } | |
600 } | |
601 break; | |
602 case SkColorSpace_A2B::Element::Type::kCLUT: | |
603 fProcessingElements.push_back(skstd::make_unique<ApplyCLUT>(&e.c olorLUT())); | |
604 break; | |
605 case SkColorSpace_A2B::Element::Type::kMatrix: | |
606 if (!e.matrix().isIdentity()) { | |
607 fProcessingElements.push_back(skstd::make_unique<ApplyMatrix >(e.matrix())); | |
608 } | |
609 break; | |
610 } | |
611 } | |
612 | |
613 // Lab PCS -> XYZ PCS | |
614 if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) { | |
615 SkCSXformPrintf("Lab -> XYZ element added\n"); | |
616 fProcessingElements.push_back(skstd::make_unique<LabToXYZ>()); | |
617 } | |
618 | |
619 // and XYZ PCS -> device xforms | |
620 if (!dstSpace->fromXYZD50()->isIdentity()) { | |
621 fProcessingElements.push_back(skstd::make_unique<ApplyMatrix>(*dstSpace- >fromXYZD50())); | |
622 } | |
623 | |
624 //concatTransforms(); | |
msarett
2016/10/26 20:43:29
Let's drop this for now if it's not being used yet
raftias
2016/10/27 18:39:39
Done.
| |
625 | |
626 const int numDstTables = num_tables(dstSpace); | |
627 dstSpace->toDstGammaTables(fDstGammaTables, &fDstStorage, numDstTables); | |
628 fDstGamma = dstSpace->gammaNamed(); | |
629 } | |
630 | |
631 void SkColorSpaceXform_A2B::concatTransforms() { | |
632 uint32_t i = 0; | |
633 while (i + 1 < fProcessingElements.size()) { | |
634 if (fProcessingElements[i]->merge(fProcessingElements[i + 1].get())) { | |
635 // We could just reset the (i+1)th element then do 1 pass at the end | |
636 // to remove null elements to get O(n) instead of O(n^2), but n shou ld | |
637 // be very small so this should not matter | |
638 fProcessingElements.erase(fProcessingElements.begin() + i + 1); | |
639 } else { | |
640 ++i; | |
641 } | |
642 } | |
643 } | |
OLD | NEW |