OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkAtomics.h" | 8 #include "SkAtomics.h" |
9 #include "SkColorSpace.h" | 9 #include "SkColorSpace.h" |
10 | 10 |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
81 SkDebugf("[%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f]\n", | 81 SkDebugf("[%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f]\n", |
82 fMat[0], fMat[1], fMat[2], | 82 fMat[0], fMat[1], fMat[2], |
83 fMat[3], fMat[4], fMat[5], | 83 fMat[3], fMat[4], fMat[5], |
84 fMat[6], fMat[7], fMat[8]); | 84 fMat[6], fMat[7], fMat[8]); |
85 } | 85 } |
86 | 86 |
87 //////////////////////////////////////////////////////////////////////////////// ////////////////// | 87 //////////////////////////////////////////////////////////////////////////////// ////////////////// |
88 | 88 |
89 static int32_t gUniqueColorSpaceID; | 89 static int32_t gUniqueColorSpaceID; |
90 | 90 |
91 SkColorSpace::SkColorSpace(const SkFloat3x3& toXYZD50, const SkFloat3& gamma, Na med named) | 91 SkColorSpace::SkColorSpace(const SkFloat3& gamma, const SkFloat3x3& toXYZD50, Na med named) |
92 : fToXYZD50(toXYZD50) | 92 : fGamma(gamma) |
93 , fGamma(gamma) | 93 , fToXYZD50(toXYZD50) |
94 , fToXYZOffset({ 0.0f, 0.0f, 0.0f }) | |
94 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) | 95 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) |
95 , fNamed(named) | 96 , fNamed(named) |
96 { | 97 { |
97 for (int i = 0; i < 3; ++i) { | 98 for (int i = 0; i < 3; ++i) { |
98 SkASSERT(SkFloatIsFinite(gamma.fVec[i])); | 99 SkASSERT(SkFloatIsFinite(gamma.fVec[i])); |
99 for (int j = 0; j < 3; ++j) { | 100 for (int j = 0; j < 3; ++j) { |
100 SkASSERT(SkFloatIsFinite(toXYZD50.fMat[3*i + j])); | 101 SkASSERT(SkFloatIsFinite(toXYZD50.fMat[3*i + j])); |
101 } | 102 } |
102 } | 103 } |
103 } | 104 } |
104 | 105 |
106 SkColorSpace::SkColorSpace(SkColorLUT colorLUT, const SkFloat3& gamma, const SkF loat3x3& toXYZD50, | |
107 const SkFloat3& toXYZOffset) | |
108 : fColorLUT(std::move(colorLUT)) | |
109 , fGamma(gamma) | |
110 , fToXYZD50(toXYZD50) | |
111 , fToXYZOffset(toXYZOffset) | |
112 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) | |
113 , fNamed(kUnknown_Named) | |
114 {} | |
115 | |
105 sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFlo at3& gamma) { | 116 sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFlo at3& gamma) { |
106 for (int i = 0; i < 3; ++i) { | 117 for (int i = 0; i < 3; ++i) { |
107 if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) { | 118 if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) { |
108 return nullptr; | 119 return nullptr; |
109 } | 120 } |
110 for (int j = 0; j < 3; ++j) { | 121 for (int j = 0; j < 3; ++j) { |
111 if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) { | 122 if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) { |
112 return nullptr; | 123 return nullptr; |
113 } | 124 } |
114 } | 125 } |
115 } | 126 } |
116 | 127 |
117 // check the matrix for invertibility | 128 // check the matrix for invertibility |
118 float d = det(toXYZD50); | 129 float d = det(toXYZD50); |
119 if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) { | 130 if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) { |
120 return nullptr; | 131 return nullptr; |
121 } | 132 } |
122 | 133 |
123 return sk_sp<SkColorSpace>(new SkColorSpace(toXYZD50, gamma, kUnknown_Named) ); | 134 return sk_sp<SkColorSpace>(new SkColorSpace(gamma, toXYZD50, kUnknown_Named) ); |
124 } | 135 } |
125 | 136 |
126 void SkColorSpace::dump() const { | 137 void SkColorSpace::dump() const { |
127 fToXYZD50.dump(); | 138 fToXYZD50.dump(); |
128 fGamma.dump(); | 139 fGamma.dump(); |
129 } | 140 } |
130 | 141 |
131 //////////////////////////////////////////////////////////////////////////////// ////////////////// | 142 //////////////////////////////////////////////////////////////////////////////// ////////////////// |
132 | 143 |
133 const SkFloat3 gDevice_gamma {{ 0, 0, 0 }}; | 144 const SkFloat3 gDevice_gamma {{ 0, 0, 0 }}; |
134 const SkFloat3x3 gDevice_toXYZD50 {{ | 145 const SkFloat3x3 gDevice_toXYZD50 {{ |
135 1, 0, 0, | 146 1, 0, 0, |
136 0, 1, 0, | 147 0, 1, 0, |
137 0, 0, 1 | 148 0, 0, 1 |
138 }}; | 149 }}; |
139 | 150 |
140 const SkFloat3 gSRGB_gamma {{ 2.2f, 2.2f, 2.2f }}; | 151 const SkFloat3 gSRGB_gamma {{ 2.2f, 2.2f, 2.2f }}; |
141 const SkFloat3x3 gSRGB_toXYZD50 {{ | 152 const SkFloat3x3 gSRGB_toXYZD50 {{ |
142 0.4358f, 0.2224f, 0.0139f, // * R | 153 0.4358f, 0.2224f, 0.0139f, // * R |
143 0.3853f, 0.7170f, 0.0971f, // * G | 154 0.3853f, 0.7170f, 0.0971f, // * G |
144 0.1430f, 0.0606f, 0.7139f, // * B | 155 0.1430f, 0.0606f, 0.7139f, // * B |
145 }}; | 156 }}; |
146 | 157 |
147 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { | 158 sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { |
148 switch (named) { | 159 switch (named) { |
149 case kDevice_Named: | 160 case kDevice_Named: |
150 return sk_sp<SkColorSpace>(new SkColorSpace(gDevice_toXYZD50, gDevic e_gamma, | 161 return sk_sp<SkColorSpace>(new SkColorSpace(gDevice_gamma, gDevice_t oXYZD50, |
151 kDevice_Named)); | 162 kDevice_Named)); |
152 case kSRGB_Named: | 163 case kSRGB_Named: |
153 return sk_sp<SkColorSpace>(new SkColorSpace(gSRGB_toXYZD50, gSRGB_ga mma, kSRGB_Named)); | 164 return sk_sp<SkColorSpace>(new SkColorSpace(gSRGB_gamma, gSRGB_toXYZ D50, kSRGB_Named)); |
154 default: | 165 default: |
155 break; | 166 break; |
156 } | 167 } |
157 return nullptr; | 168 return nullptr; |
158 } | 169 } |
159 | 170 |
160 //////////////////////////////////////////////////////////////////////////////// /////////////////// | 171 //////////////////////////////////////////////////////////////////////////////// /////////////////// |
161 | 172 |
162 #include "SkFixed.h" | 173 #include "SkFixed.h" |
163 #include "SkTemplates.h" | 174 #include "SkTemplates.h" |
164 | 175 |
165 #define SkColorSpacePrintf(...) | 176 #define SkColorSpacePrintf(...) |
166 | 177 |
167 #define return_if_false(pred, msg) \ | 178 #define return_if_false(pred, msg) \ |
msarett
2016/04/27 18:56:44
I think maybe in a follow-up change I'll create a
scroggo
2016/04/27 20:18:46
Do you think this stuff will be used in other file
msarett
2016/04/27 21:17:58
sgtm
| |
168 do { \ | 179 do { \ |
169 if (!(pred)) { \ | 180 if (!(pred)) { \ |
170 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ | 181 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ |
171 return false; \ | 182 return false; \ |
172 } \ | 183 } \ |
173 } while (0) | 184 } while (0) |
174 | 185 |
175 #define return_null(msg) \ | 186 #define return_null(msg) \ |
176 do { \ | 187 do { \ |
177 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ | 188 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
328 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature ) { | 339 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature ) { |
329 for (int i = 0; i < count; ++i) { | 340 for (int i = 0; i < count; ++i) { |
330 if (tags[i].fSignature == signature) { | 341 if (tags[i].fSignature == signature) { |
331 return &tags[i]; | 342 return &tags[i]; |
332 } | 343 } |
333 } | 344 } |
334 return nullptr; | 345 return nullptr; |
335 } | 346 } |
336 }; | 347 }; |
337 | 348 |
338 // TODO (msarett): | |
339 // Should we recognize more tags? | |
340 static const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z'); | 349 static const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z'); |
341 static const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z'); | 350 static const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z'); |
342 static const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z'); | 351 static const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z'); |
343 static const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C'); | 352 static const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C'); |
344 static const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); | 353 static const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); |
345 static const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); | 354 static const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); |
355 static const uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0'); | |
346 | 356 |
347 bool load_xyz(float dst[3], const uint8_t* src, size_t len) { | 357 bool load_xyz(float dst[3], const uint8_t* src, size_t len) { |
348 if (len < 20) { | 358 if (len < 20) { |
349 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len); | 359 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len); |
350 return false; | 360 return false; |
351 } | 361 } |
352 | 362 |
353 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); | 363 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); |
354 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); | 364 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); |
355 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); | 365 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); |
356 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); | 366 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); |
357 return true; | 367 return true; |
358 } | 368 } |
359 | 369 |
360 static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v'); | 370 static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v'); |
361 static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a'); | 371 static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a'); |
362 | 372 |
363 static bool load_gamma(float* gamma, const uint8_t* src, size_t len) { | 373 // FIXME (msarett): |
364 if (len < 14) { | 374 // We need to handle the possibility that the gamma curve does not correspond to 2.2f. |
msarett
2016/04/27 18:56:44
Next up, I will probably create a gamma struct, wh
| |
365 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); | 375 static bool load_gammas(float* gammas, uint32_t numGammas, const uint8_t* src, s ize_t len) { |
msarett
2016/04/27 18:56:45
This function is mostly the same minus a few chang
| |
366 return false; | 376 for (uint32_t i = 0; i < numGammas; i++) { |
msarett
2016/04/27 18:56:44
Added a loop so we can handle multiple gamma curve
| |
367 } | 377 if (len < 12) { |
368 | 378 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
369 uint32_t type = read_big_endian_uint(src); | 379 return false; |
scroggo
2016/04/27 20:18:46
If the first gamma is valid, but the second is not
msarett
2016/04/27 21:17:58
I have no idea if we'd come across this error. I'
| |
370 switch (type) { | 380 } |
371 case kTAG_CurveType: { | 381 |
372 uint32_t count = read_big_endian_int(src + 8); | 382 uint32_t type = read_big_endian_uint(src); |
373 if (0 == count) { | 383 size_t tagBytes; |
msarett
2016/04/27 18:56:45
Now we need to count the number of bytes in the ta
scroggo
2016/04/27 20:18:46
Maybe these comments would be useful to have in th
msarett
2016/04/27 21:17:57
Done.
| |
384 switch (type) { | |
385 case kTAG_CurveType: { | |
386 uint32_t count = read_big_endian_uint(src + 8); | |
387 tagBytes = 12 + count * 2; | |
388 if (0 == count) { | |
msarett
2016/04/27 18:56:44
count == 0 now means unit gamma.
| |
389 // Some tags require a gamma curve, but the author doesn't a ctually want | |
390 // to transform the data. In this case, it is common to see a curve with | |
391 // a count of 0. | |
392 gammas[i] = 1.0f; | |
393 break; | |
394 } else if (len < 12 + 2 * count) { | |
395 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len) ; | |
396 return false; | |
397 } | |
398 | |
399 const uint16_t* table = (const uint16_t*) (src + 12); | |
400 if (1 == count) { | |
401 // Table entry is the exponent (bias 256). | |
402 uint16_t value = read_big_endian_short((const uint8_t*) tabl e); | |
403 gammas[i] = value / 256.0f; | |
404 SkColorSpacePrintf("gamma %d %g\n", value, *gamma); | |
405 break; | |
406 } | |
407 | |
408 // Print the interpolation table. For now, we ignore this and g uess 2.2f. | |
409 for (uint32_t i = 0; i < count; i++) { | |
410 SkColorSpacePrintf("curve[%d] %d\n", i, | |
411 read_big_endian_short((const uint8_t*) &table[i])); | |
412 } | |
413 | |
414 gammas[i] = 2.2f; | |
415 break; | |
416 } | |
417 case kTAG_ParaCurveType: | |
418 // Guess 2.2f. | |
419 SkColorSpacePrintf("parametric curve\n"); | |
420 gammas[i] = 2.2f; | |
421 | |
422 switch(read_big_endian_short(src + 8)) { | |
msarett
2016/04/27 18:56:44
Added switch statement to determine how many bytes
| |
423 case 0: | |
424 tagBytes = 12 + 4; | |
425 break; | |
426 case 1: | |
427 tagBytes = 12 + 12; | |
428 break; | |
429 case 2: | |
430 tagBytes = 12 + 16; | |
431 break; | |
432 case 3: | |
433 tagBytes = 12 + 20; | |
434 break; | |
435 case 4: | |
436 tagBytes = 12 + 28; | |
437 break; | |
438 default: | |
439 SkColorSpacePrintf("Invalid parametric curve type\n"); | |
440 return false; | |
441 } | |
442 break; | |
443 default: | |
444 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); | |
445 return false; | |
446 } | |
447 | |
448 // Adjust src and len if there is another gamma curve to load. | |
msarett
2016/04/27 18:56:45
The rest of this function is also new.
| |
449 if (0 != numGammas) { | |
450 // Each curve will be padded to 4-byte alignment. | |
scroggo
2016/04/27 20:18:46
I found this sentence confusing - I thought you me
msarett
2016/04/27 21:17:57
Done.
| |
451 tagBytes = SkAlign4(tagBytes); | |
452 if (len < tagBytes) { | |
374 return false; | 453 return false; |
375 } | 454 } |
376 | 455 |
377 const uint16_t* table = (const uint16_t*) (src + 12); | 456 src += tagBytes; |
378 if (1 == count) { | 457 len -= tagBytes; |
379 // Table entry is the exponent (bias 256). | 458 } |
380 uint16_t value = read_big_endian_short((const uint8_t*) table); | 459 } |
381 *gamma = value / 256.0f; | 460 |
382 SkColorSpacePrintf("gamma %d %g\n", value, *gamma); | 461 // If all of the gammas we encounter are 1.0f, indicate that we failed to lo ad gammas. |
383 return true; | 462 // There is no need to apply a gamma of 1.0f. |
384 } | 463 for (uint32_t i = 0; i < numGammas; i++) { |
385 | 464 if (1.0f != gammas[i]) { |
386 // Check length again if we have a table. | |
387 if (len < 12 + 2 * count) { | |
388 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); | |
389 return false; | |
390 } | |
391 | |
392 // Print the interpolation table. For now, we ignore this and guess 2.2f. | |
393 for (uint32_t i = 0; i < count; i++) { | |
394 SkColorSpacePrintf("curve[%d] %d\n", i, | |
395 read_big_endian_short((const uint8_t*) &table[i])); | |
396 } | |
397 | |
398 *gamma = 2.2f; | |
399 return true; | 465 return true; |
400 } | 466 } |
401 case kTAG_ParaCurveType: | 467 } |
402 // Guess 2.2f. | 468 |
403 SkColorSpacePrintf("parametric curve\n"); | 469 return false; |
404 *gamma = 2.2f; | 470 } |
405 return true; | 471 |
472 static const uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); | |
473 | |
474 bool load_color_lut(SkColorLUT* colorLUT, uint32_t inputChannels, uint32_t outpu tChannels, | |
475 const uint8_t* src, size_t len) { | |
476 if (len < 20) { | |
477 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); | |
478 return false; | |
479 } | |
480 | |
481 colorLUT->fInputChannels = inputChannels; | |
482 colorLUT->fOutputChannels = outputChannels; | |
scroggo
2016/04/27 20:18:46
Should we SkASSERT(inputChannels <= kMaxChannels &
msarett
2016/04/27 21:17:57
Done.
| |
483 uint32_t numEntries = 1; | |
484 for (uint32_t i = 0; i < inputChannels; i++) { | |
485 colorLUT->fGridPoints[i] = src[i]; | |
486 numEntries *= src[i]; | |
487 } | |
488 numEntries *= outputChannels; | |
489 | |
490 // Space is provided for a maximum of the 16 input channels. Now we determi ne the precision | |
491 // of the table values. | |
492 uint8_t precision = src[16]; | |
493 switch (precision) { | |
494 case 1: // 8-bit data | |
495 case 2: // 16-bit data | |
496 break; | |
406 default: | 497 default: |
407 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); | 498 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n", len); |
408 return false; | 499 return false; |
409 } | 500 } |
501 | |
502 if (len < 20 + numEntries * precision) { | |
503 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); | |
504 return false; | |
505 } | |
506 | |
507 // Movable struct colorLUT has ownership of fTable. | |
508 colorLUT->fTable = std::unique_ptr<float[]>(new float[numEntries]); | |
509 const uint8_t* ptr = src + 20; | |
510 for (uint32_t i = 0; i < numEntries; i++, ptr += precision) { | |
511 if (1 == precision) { | |
512 colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f; | |
513 } else { | |
514 colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0 f; | |
515 } | |
516 } | |
517 | |
518 return true; | |
519 } | |
520 | |
521 bool load_matrix(SkFloat3x3* toXYZ, SkFloat3* toXYZOffset, const uint8_t* src, s ize_t len) { | |
522 if (len < 48) { | |
523 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); | |
524 return false; | |
525 } | |
526 | |
527 toXYZ->fMat[0] = SkFixedToFloat(read_big_endian_int(src)); | |
528 toXYZ->fMat[3] = SkFixedToFloat(read_big_endian_int(src + 4)); | |
529 toXYZ->fMat[6] = SkFixedToFloat(read_big_endian_int(src + 8)); | |
530 toXYZ->fMat[1] = SkFixedToFloat(read_big_endian_int(src + 12)); | |
531 toXYZ->fMat[4] = SkFixedToFloat(read_big_endian_int(src + 16)); | |
532 toXYZ->fMat[7] = SkFixedToFloat(read_big_endian_int(src + 20)); | |
533 toXYZ->fMat[2] = SkFixedToFloat(read_big_endian_int(src + 24)); | |
534 toXYZ->fMat[5] = SkFixedToFloat(read_big_endian_int(src + 28)); | |
535 toXYZ->fMat[8] = SkFixedToFloat(read_big_endian_int(src + 32)); | |
536 toXYZOffset->fVec[0] = SkFixedToFloat(read_big_endian_int(src + 36)); | |
537 toXYZOffset->fVec[1] = SkFixedToFloat(read_big_endian_int(src + 40)); | |
538 toXYZOffset->fVec[2] = SkFixedToFloat(read_big_endian_int(src + 44)); | |
539 return true; | |
540 } | |
541 | |
542 bool load_a2b0(SkColorLUT* colorLUT, SkFloat3* gamma, SkFloat3x3* toXYZ, SkFloat 3* toXYZOffset, | |
543 const uint8_t* src, size_t len) { | |
544 if (len < 32) { | |
545 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); | |
546 return false; | |
547 } | |
548 | |
549 uint32_t type = read_big_endian_uint(src); | |
550 if (kTAG_AtoBType != type) { | |
551 // FIXME (msarett): Need to support lut8Type and lut16Type. | |
552 SkColorSpacePrintf("Unsupported A to B tag type.\n"); | |
553 return false; | |
554 } | |
555 | |
556 // Read the number of channels. The four bytes that we skipped are reserved and | |
557 // must be zero. | |
558 uint8_t inputChannels = src[8]; | |
559 uint8_t outputChannels = src[9]; | |
560 if (0 == inputChannels || inputChannels > SkColorLUT::kMaxChannels || | |
561 0 < outputChannels || outputChannels > SkColorLUT::kMaxChannels) { | |
562 // The color LUT assumes that there are at most 16 input channels. For RGB | |
563 // profiles, output channels should be 3. | |
564 SkColorSpacePrintf("Too many input or output channels in A to B tag.\n") ; | |
565 return false; | |
566 } | |
567 | |
568 // Skip two more reserved zero bytes before reading the offsets of each elem ent in | |
569 // the A to B tag. If the offset is non-zero it indicates that the element is present. | |
570 // We read these in the order that the elements should be applied, rather th an the | |
scroggo
2016/04/27 20:18:46
It might be clearer to move these to where they're
msarett
2016/04/27 21:17:57
sgtm. Yes, the B curve will be applied last, if w
| |
571 // order in which the offsets are stored. | |
572 uint32_t offsetToACurves = read_big_endian_int(src + 28); | |
573 uint32_t offsetToColorLUT = read_big_endian_int(src + 24); | |
574 uint32_t offsetToMCurves = read_big_endian_int(src + 20); | |
575 uint32_t offsetToMatrix = read_big_endian_int(src + 16); | |
576 uint32_t offsetToBCurves = read_big_endian_int(src + 12); | |
577 | |
578 bool hasACurves = (0 != offsetToACurves); | |
579 bool hasBCurves = (0 != offsetToBCurves); | |
580 if (hasACurves || hasBCurves) { | |
581 // FIXME (msarett): Handle A and B curves. | |
582 // Note that the A curve is technically required in order to have a colo r LUT. | |
583 // However, all the A curves I have seen so far have are just placeholde rs that | |
584 // don't actually transform the data. | |
585 SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n") ; | |
586 } | |
587 | |
588 bool hasColorLUT = (0 != offsetToColorLUT && offsetToColorLUT < len); | |
589 if (hasColorLUT) { | |
590 if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offse tToColorLUT, | |
591 len - offsetToColorLUT)) { | |
592 hasColorLUT = false; | |
scroggo
2016/04/27 20:18:46
It looks like this is never used again. Do you nee
msarett
2016/04/27 21:17:58
Deleting them...
At some point, we'll need to kno
| |
593 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); | |
594 } | |
595 } | |
596 | |
597 bool hasMCurves = (0 != offsetToMCurves && offsetToMCurves < len); | |
598 if (hasMCurves) { | |
599 if (!load_gammas(gamma->fVec, outputChannels, src + offsetToMCurves, len - offsetToMCurves)) | |
600 { | |
601 hasMCurves = false; | |
602 SkColorSpacePrintf("Failed to read M curves from A to B tag.\n"); | |
603 } | |
604 } | |
605 | |
606 bool hasMatrix = (0 != offsetToMatrix && offsetToMatrix < len); | |
607 if (hasMatrix) { | |
608 if (!load_matrix(toXYZ, toXYZOffset, src + offsetToMatrix, len - offsetT oMatrix)) { | |
609 hasMatrix = false; | |
610 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); | |
611 } | |
612 } | |
613 | |
614 return true; | |
410 } | 615 } |
411 | 616 |
412 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) { | 617 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) { |
413 const uint8_t* ptr = (const uint8_t*) base; | 618 const uint8_t* ptr = (const uint8_t*) base; |
414 | 619 |
415 if (len < kICCHeaderSize) { | 620 if (len < kICCHeaderSize) { |
416 return_null("Data is not large enough to contain an ICC profile"); | 621 return_null("Data is not large enough to contain an ICC profile"); |
417 } | 622 } |
418 | 623 |
419 // Read the ICC profile header and check to make sure that it is valid. | 624 // Read the ICC profile header and check to make sure that it is valid. |
(...skipping 25 matching lines...) Expand all Loading... | |
445 ptr = tags[i].init(ptr); | 650 ptr = tags[i].init(ptr); |
446 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24 ) & 0xFF, | 651 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24 ) & 0xFF, |
447 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF, | 652 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF, |
448 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLen gth); | 653 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLen gth); |
449 | 654 |
450 if (!tags[i].valid(kICCHeaderSize + len)) { | 655 if (!tags[i].valid(kICCHeaderSize + len)) { |
451 return_null("Tag is too large to fit in ICC profile"); | 656 return_null("Tag is too large to fit in ICC profile"); |
452 } | 657 } |
453 } | 658 } |
454 | 659 |
455 // Load our XYZ and gamma matrices. | |
456 SkFloat3x3 toXYZ; | |
457 SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }}; | |
458 switch (header.fInputColorSpace) { | 660 switch (header.fInputColorSpace) { |
459 case kRGB_ColorSpace: { | 661 case kRGB_ColorSpace: { |
662 // Recognize the rXYZ, gXYZ, and bXYZ tags. | |
460 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); | 663 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); |
461 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); | 664 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); |
462 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); | 665 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); |
463 if (!r || !g || !b) { | 666 if (r && g && b) { |
msarett
2016/04/27 18:56:44
Moved a bunch of code into this block, but didn't
| |
464 return_null("Need rgb tags for XYZ space"); | 667 SkFloat3x3 toXYZ; |
668 if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r- >fLength) || | |
669 !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g- >fLength) || | |
670 !load_xyz(&toXYZ.fMat[6], b->addr((const uint8_t*) base), b- >fLength)) | |
671 { | |
672 return_null("Need valid rgb tags for XYZ space"); | |
673 } | |
674 | |
675 // It is not uncommon to see missing or empty gamma tags. This indicates | |
676 // that we should use unit gamma. | |
677 SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }}; | |
678 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); | |
679 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); | |
680 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); | |
681 if (!r || | |
682 !load_gammas(&gamma.fVec[0], 1, r->addr((const uint8_t*) bas e), r->fLength)) | |
scroggo
2016/04/27 20:18:46
Previously we required the length to be at least 1
msarett
2016/04/27 21:17:58
Yes, it appears that a gamma chunk with count == 0
| |
683 { | |
684 SkColorSpacePrintf("Failed to read R gamma tag.\n"); | |
685 } | |
686 if (!g || | |
687 !load_gammas(&gamma.fVec[1], 1, g->addr((const uint8_t*) bas e), g->fLength)) | |
688 { | |
689 SkColorSpacePrintf("Failed to read G gamma tag.\n"); | |
690 } | |
691 if (!b || | |
692 !load_gammas(&gamma.fVec[2], 1, b->addr((const uint8_t*) bas e), b->fLength)) | |
693 { | |
694 SkColorSpacePrintf("Failed to read B gamma tag.\n"); | |
695 } | |
696 return SkColorSpace::NewRGB(toXYZ, gamma); | |
465 } | 697 } |
466 | 698 |
467 if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r->fLe ngth) || | 699 // Recognize color profile specified by A2B0 tag. |
468 !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g->fLe ngth) || | 700 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); |
469 !load_xyz(&toXYZ.fMat[6], b->addr((const uint8_t*) base), b->fLe ngth)) | 701 if (a2b0) { |
470 { | 702 SkColorLUT colorLUT; |
471 return_null("Need valid rgb tags for XYZ space"); | 703 SkFloat3 gamma; |
704 SkFloat3x3 toXYZ; | |
705 SkFloat3 toXYZOffset; | |
706 if (!load_a2b0(&colorLUT, &gamma, &toXYZ, &toXYZOffset, | |
707 a2b0->addr((const uint8_t*) base), a2b0->fLength)) { | |
708 return_null("Failed to parse A2B0 tag"); | |
709 } | |
710 | |
711 return sk_sp<SkColorSpace>(new SkColorSpace(std::move(colorLUT), gamma, toXYZ, | |
712 toXYZOffset)); | |
472 } | 713 } |
473 | 714 |
474 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); | |
475 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); | |
476 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); | |
477 if (!r || !load_gamma(&gamma.fVec[0], r->addr((const uint8_t*) base) , r->fLength)) { | |
478 SkColorSpacePrintf("Failed to read R gamma tag.\n"); | |
479 } | |
480 if (!g || !load_gamma(&gamma.fVec[1], g->addr((const uint8_t*) base) , g->fLength)) { | |
481 SkColorSpacePrintf("Failed to read G gamma tag.\n"); | |
482 } | |
483 if (!b || !load_gamma(&gamma.fVec[2], b->addr((const uint8_t*) base) , b->fLength)) { | |
484 SkColorSpacePrintf("Failed to read B gamma tag.\n"); | |
485 } | |
486 return SkColorSpace::NewRGB(toXYZ, gamma); | |
487 } | 715 } |
488 default: | 716 default: |
489 break; | 717 break; |
490 } | 718 } |
491 | 719 |
492 return_null("ICC profile contains unsupported colorspace"); | 720 return_null("ICC profile contains unsupported colorspace"); |
493 } | 721 } |
494 | 722 |
495 //////////////////////////////////////////////////////////////////////////////// /////////////////// | 723 //////////////////////////////////////////////////////////////////////////////// /////////////////// |
496 | 724 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
572 } | 800 } |
573 | 801 |
574 // D65 white point of Rec. 709 [8] are: | 802 // D65 white point of Rec. 709 [8] are: |
575 // | 803 // |
576 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 | 804 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 |
577 // | 805 // |
578 // R G B white | 806 // R G B white |
579 // x 0.640 0.300 0.150 0.3127 | 807 // x 0.640 0.300 0.150 0.3127 |
580 // y 0.330 0.600 0.060 0.3290 | 808 // y 0.330 0.600 0.060 0.3290 |
581 // z 0.030 0.100 0.790 0.3582 | 809 // z 0.030 0.100 0.790 0.3582 |
OLD | NEW |