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(SkColorLookUpTable colorLUT, const SkFloat3& gamma, |
| 107 const SkFloat3x3& toXYZD50, const SkFloat3& toXYZOffs
et) |
| 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" |
(...skipping 164 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. |
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) { |
366 return false; | 376 for (uint32_t i = 0; i < numGammas; i++) { |
367 } | 377 if (len < 12) { |
368 | 378 // FIXME (msarett): |
369 uint32_t type = read_big_endian_uint(src); | 379 // We could potentially return false here after correctly parsing *s
ome* of the |
370 switch (type) { | 380 // gammas correctly. Should we somehow try to indicate a partial su
ccess? |
371 case kTAG_CurveType: { | 381 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); |
372 uint32_t count = read_big_endian_int(src + 8); | 382 return false; |
373 if (0 == count) { | 383 } |
| 384 |
| 385 // We need to count the number of bytes in the tag, so we are able to mo
ve to the |
| 386 // next tag on the next loop iteration. |
| 387 size_t tagBytes; |
| 388 |
| 389 uint32_t type = read_big_endian_uint(src); |
| 390 switch (type) { |
| 391 case kTAG_CurveType: { |
| 392 uint32_t count = read_big_endian_uint(src + 8); |
| 393 tagBytes = 12 + count * 2; |
| 394 if (0 == count) { |
| 395 // Some tags require a gamma curve, but the author doesn't a
ctually want |
| 396 // to transform the data. In this case, it is common to see
a curve with |
| 397 // a count of 0. |
| 398 gammas[i] = 1.0f; |
| 399 break; |
| 400 } else if (len < 12 + 2 * count) { |
| 401 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len)
; |
| 402 return false; |
| 403 } |
| 404 |
| 405 const uint16_t* table = (const uint16_t*) (src + 12); |
| 406 if (1 == count) { |
| 407 // Table entry is the exponent (bias 256). |
| 408 uint16_t value = read_big_endian_short((const uint8_t*) tabl
e); |
| 409 gammas[i] = value / 256.0f; |
| 410 SkColorSpacePrintf("gamma %d %g\n", value, *gamma); |
| 411 break; |
| 412 } |
| 413 |
| 414 // Print the interpolation table. For now, we ignore this and g
uess 2.2f. |
| 415 for (uint32_t j = 0; j < count; j++) { |
| 416 SkColorSpacePrintf("curve[%d] %d\n", j, |
| 417 read_big_endian_short((const uint8_t*) &table[j])); |
| 418 } |
| 419 |
| 420 gammas[i] = 2.2f; |
| 421 break; |
| 422 } |
| 423 case kTAG_ParaCurveType: |
| 424 // Guess 2.2f. |
| 425 SkColorSpacePrintf("parametric curve\n"); |
| 426 gammas[i] = 2.2f; |
| 427 |
| 428 switch(read_big_endian_short(src + 8)) { |
| 429 case 0: |
| 430 tagBytes = 12 + 4; |
| 431 break; |
| 432 case 1: |
| 433 tagBytes = 12 + 12; |
| 434 break; |
| 435 case 2: |
| 436 tagBytes = 12 + 16; |
| 437 break; |
| 438 case 3: |
| 439 tagBytes = 12 + 20; |
| 440 break; |
| 441 case 4: |
| 442 tagBytes = 12 + 28; |
| 443 break; |
| 444 default: |
| 445 SkColorSpacePrintf("Invalid parametric curve type\n"); |
| 446 return false; |
| 447 } |
| 448 break; |
| 449 default: |
| 450 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); |
| 451 return false; |
| 452 } |
| 453 |
| 454 // Adjust src and len if there is another gamma curve to load. |
| 455 if (0 != numGammas) { |
| 456 // Each curve is padded to 4-byte alignment. |
| 457 tagBytes = SkAlign4(tagBytes); |
| 458 if (len < tagBytes) { |
374 return false; | 459 return false; |
375 } | 460 } |
376 | 461 |
377 const uint16_t* table = (const uint16_t*) (src + 12); | 462 src += tagBytes; |
378 if (1 == count) { | 463 len -= tagBytes; |
379 // Table entry is the exponent (bias 256). | 464 } |
380 uint16_t value = read_big_endian_short((const uint8_t*) table); | 465 } |
381 *gamma = value / 256.0f; | 466 |
382 SkColorSpacePrintf("gamma %d %g\n", value, *gamma); | 467 // If all of the gammas we encounter are 1.0f, indicate that we failed to lo
ad gammas. |
383 return true; | 468 // There is no need to apply a gamma of 1.0f. |
384 } | 469 for (uint32_t i = 0; i < numGammas; i++) { |
385 | 470 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; | 471 return true; |
400 } | 472 } |
401 case kTAG_ParaCurveType: | 473 } |
402 // Guess 2.2f. | 474 |
403 SkColorSpacePrintf("parametric curve\n"); | 475 return false; |
404 *gamma = 2.2f; | 476 } |
405 return true; | 477 |
| 478 static const uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); |
| 479 |
| 480 bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32
_t outputChannels, |
| 481 const uint8_t* src, size_t len) { |
| 482 if (len < 20) { |
| 483 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); |
| 484 return false; |
| 485 } |
| 486 |
| 487 SkASSERT(inputChannels <= SkColorLookUpTable::kMaxChannels && |
| 488 outputChannels <= SkColorLookUpTable::kMaxChannels); |
| 489 colorLUT->fInputChannels = inputChannels; |
| 490 colorLUT->fOutputChannels = outputChannels; |
| 491 uint32_t numEntries = 1; |
| 492 for (uint32_t i = 0; i < inputChannels; i++) { |
| 493 colorLUT->fGridPoints[i] = src[i]; |
| 494 numEntries *= src[i]; |
| 495 } |
| 496 numEntries *= outputChannels; |
| 497 |
| 498 // Space is provided for a maximum of the 16 input channels. Now we determi
ne the precision |
| 499 // of the table values. |
| 500 uint8_t precision = src[16]; |
| 501 switch (precision) { |
| 502 case 1: // 8-bit data |
| 503 case 2: // 16-bit data |
| 504 break; |
406 default: | 505 default: |
407 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); | 506 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n",
len); |
408 return false; | 507 return false; |
409 } | 508 } |
| 509 |
| 510 if (len < 20 + numEntries * precision) { |
| 511 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); |
| 512 return false; |
| 513 } |
| 514 |
| 515 // Movable struct colorLUT has ownership of fTable. |
| 516 colorLUT->fTable = std::unique_ptr<float[]>(new float[numEntries]); |
| 517 const uint8_t* ptr = src + 20; |
| 518 for (uint32_t i = 0; i < numEntries; i++, ptr += precision) { |
| 519 if (1 == precision) { |
| 520 colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f; |
| 521 } else { |
| 522 colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0
f; |
| 523 } |
| 524 } |
| 525 |
| 526 return true; |
| 527 } |
| 528 |
| 529 bool load_matrix(SkFloat3x3* toXYZ, SkFloat3* toXYZOffset, const uint8_t* src, s
ize_t len) { |
| 530 if (len < 48) { |
| 531 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); |
| 532 return false; |
| 533 } |
| 534 |
| 535 toXYZ->fMat[0] = SkFixedToFloat(read_big_endian_int(src)); |
| 536 toXYZ->fMat[3] = SkFixedToFloat(read_big_endian_int(src + 4)); |
| 537 toXYZ->fMat[6] = SkFixedToFloat(read_big_endian_int(src + 8)); |
| 538 toXYZ->fMat[1] = SkFixedToFloat(read_big_endian_int(src + 12)); |
| 539 toXYZ->fMat[4] = SkFixedToFloat(read_big_endian_int(src + 16)); |
| 540 toXYZ->fMat[7] = SkFixedToFloat(read_big_endian_int(src + 20)); |
| 541 toXYZ->fMat[2] = SkFixedToFloat(read_big_endian_int(src + 24)); |
| 542 toXYZ->fMat[5] = SkFixedToFloat(read_big_endian_int(src + 28)); |
| 543 toXYZ->fMat[8] = SkFixedToFloat(read_big_endian_int(src + 32)); |
| 544 toXYZOffset->fVec[0] = SkFixedToFloat(read_big_endian_int(src + 36)); |
| 545 toXYZOffset->fVec[1] = SkFixedToFloat(read_big_endian_int(src + 40)); |
| 546 toXYZOffset->fVec[2] = SkFixedToFloat(read_big_endian_int(src + 44)); |
| 547 return true; |
| 548 } |
| 549 |
| 550 bool load_a2b0(SkColorLookUpTable* colorLUT, SkFloat3* gamma, SkFloat3x3* toXYZ, |
| 551 SkFloat3* toXYZOffset, const uint8_t* src, size_t len) { |
| 552 if (len < 32) { |
| 553 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); |
| 554 return false; |
| 555 } |
| 556 |
| 557 uint32_t type = read_big_endian_uint(src); |
| 558 if (kTAG_AtoBType != type) { |
| 559 // FIXME (msarett): Need to support lut8Type and lut16Type. |
| 560 SkColorSpacePrintf("Unsupported A to B tag type.\n"); |
| 561 return false; |
| 562 } |
| 563 |
| 564 // Read the number of channels. The four bytes that we skipped are reserved
and |
| 565 // must be zero. |
| 566 uint8_t inputChannels = src[8]; |
| 567 uint8_t outputChannels = src[9]; |
| 568 if (0 == inputChannels || inputChannels > SkColorLookUpTable::kMaxChannels |
| |
| 569 0 < outputChannels || outputChannels > SkColorLookUpTable::kMaxChann
els) { |
| 570 // The color LUT assumes that there are at most 16 input channels. For
RGB |
| 571 // profiles, output channels should be 3. |
| 572 SkColorSpacePrintf("Too many input or output channels in A to B tag.\n")
; |
| 573 return false; |
| 574 } |
| 575 |
| 576 // Read the offsets of each element in the A to B tag. With the exception o
f A curves and |
| 577 // B curves (which we do not yet support), we will handle these elements in
the order in |
| 578 // which they should be applied (rather than the order in which they occur i
n the tag). |
| 579 // If the offset is non-zero it indicates that the element is present. |
| 580 uint32_t offsetToACurves = read_big_endian_int(src + 28); |
| 581 uint32_t offsetToBCurves = read_big_endian_int(src + 12); |
| 582 if ((0 != offsetToACurves) || (0 != offsetToBCurves)) { |
| 583 // FIXME (msarett): Handle A and B curves. |
| 584 // Note that the A curve is technically required in order to have a colo
r LUT. |
| 585 // However, all the A curves I have seen so far have are just placeholde
rs that |
| 586 // don't actually transform the data. |
| 587 SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n")
; |
| 588 } |
| 589 |
| 590 uint32_t offsetToColorLUT = read_big_endian_int(src + 24); |
| 591 if (0 != offsetToColorLUT && offsetToColorLUT < len) { |
| 592 if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offse
tToColorLUT, |
| 593 len - offsetToColorLUT)) { |
| 594 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); |
| 595 } |
| 596 } |
| 597 |
| 598 uint32_t offsetToMCurves = read_big_endian_int(src + 20); |
| 599 if (0 != offsetToMCurves && offsetToMCurves < len) { |
| 600 if (!load_gammas(gamma->fVec, outputChannels, src + offsetToMCurves, len
- offsetToMCurves)) |
| 601 { |
| 602 SkColorSpacePrintf("Failed to read M curves from A to B tag.\n"); |
| 603 } |
| 604 } |
| 605 |
| 606 uint32_t offsetToMatrix = read_big_endian_int(src + 16); |
| 607 if (0 != offsetToMatrix && offsetToMatrix < len) { |
| 608 if (!load_matrix(toXYZ, toXYZOffset, src + offsetToMatrix, len - offsetT
oMatrix)) { |
| 609 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); |
| 610 } |
| 611 } |
| 612 |
| 613 return true; |
410 } | 614 } |
411 | 615 |
412 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) { | 616 sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) { |
413 const uint8_t* ptr = (const uint8_t*) base; | 617 const uint8_t* ptr = (const uint8_t*) base; |
414 | 618 |
415 if (len < kICCHeaderSize) { | 619 if (len < kICCHeaderSize) { |
416 return_null("Data is not large enough to contain an ICC profile"); | 620 return_null("Data is not large enough to contain an ICC profile"); |
417 } | 621 } |
418 | 622 |
419 // Read the ICC profile header and check to make sure that it is valid. | 623 // 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); | 649 ptr = tags[i].init(ptr); |
446 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24
) & 0xFF, | 650 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, | 651 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) &
0xFF, |
448 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLen
gth); | 652 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLen
gth); |
449 | 653 |
450 if (!tags[i].valid(kICCHeaderSize + len)) { | 654 if (!tags[i].valid(kICCHeaderSize + len)) { |
451 return_null("Tag is too large to fit in ICC profile"); | 655 return_null("Tag is too large to fit in ICC profile"); |
452 } | 656 } |
453 } | 657 } |
454 | 658 |
455 // Load our XYZ and gamma matrices. | |
456 SkFloat3x3 toXYZ; | |
457 SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }}; | |
458 switch (header.fInputColorSpace) { | 659 switch (header.fInputColorSpace) { |
459 case kRGB_ColorSpace: { | 660 case kRGB_ColorSpace: { |
| 661 // Recognize the rXYZ, gXYZ, and bXYZ tags. |
460 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); | 662 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); |
461 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); | 663 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); |
462 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); | 664 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); |
463 if (!r || !g || !b) { | 665 if (r && g && b) { |
464 return_null("Need rgb tags for XYZ space"); | 666 SkFloat3x3 toXYZ; |
| 667 if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r-
>fLength) || |
| 668 !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g-
>fLength) || |
| 669 !load_xyz(&toXYZ.fMat[6], b->addr((const uint8_t*) base), b-
>fLength)) |
| 670 { |
| 671 return_null("Need valid rgb tags for XYZ space"); |
| 672 } |
| 673 |
| 674 // It is not uncommon to see missing or empty gamma tags. This
indicates |
| 675 // that we should use unit gamma. |
| 676 SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }}; |
| 677 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); |
| 678 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); |
| 679 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); |
| 680 if (!r || |
| 681 !load_gammas(&gamma.fVec[0], 1, r->addr((const uint8_t*) bas
e), r->fLength)) |
| 682 { |
| 683 SkColorSpacePrintf("Failed to read R gamma tag.\n"); |
| 684 } |
| 685 if (!g || |
| 686 !load_gammas(&gamma.fVec[1], 1, g->addr((const uint8_t*) bas
e), g->fLength)) |
| 687 { |
| 688 SkColorSpacePrintf("Failed to read G gamma tag.\n"); |
| 689 } |
| 690 if (!b || |
| 691 !load_gammas(&gamma.fVec[2], 1, b->addr((const uint8_t*) bas
e), b->fLength)) |
| 692 { |
| 693 SkColorSpacePrintf("Failed to read B gamma tag.\n"); |
| 694 } |
| 695 return SkColorSpace::NewRGB(toXYZ, gamma); |
465 } | 696 } |
466 | 697 |
467 if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r->fLe
ngth) || | 698 // Recognize color profile specified by A2B0 tag. |
468 !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g->fLe
ngth) || | 699 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)) | 700 if (a2b0) { |
470 { | 701 SkColorLookUpTable colorLUT; |
471 return_null("Need valid rgb tags for XYZ space"); | 702 SkFloat3 gamma; |
| 703 SkFloat3x3 toXYZ; |
| 704 SkFloat3 toXYZOffset; |
| 705 if (!load_a2b0(&colorLUT, &gamma, &toXYZ, &toXYZOffset, |
| 706 a2b0->addr((const uint8_t*) base), a2b0->fLength)) { |
| 707 return_null("Failed to parse A2B0 tag"); |
| 708 } |
| 709 |
| 710 return sk_sp<SkColorSpace>(new SkColorSpace(std::move(colorLUT),
gamma, toXYZ, |
| 711 toXYZOffset)); |
472 } | 712 } |
473 | 713 |
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 } | 714 } |
488 default: | 715 default: |
489 break; | 716 break; |
490 } | 717 } |
491 | 718 |
492 return_null("ICC profile contains unsupported colorspace"); | 719 return_null("ICC profile contains unsupported colorspace"); |
493 } | 720 } |
494 | 721 |
495 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 722 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
496 | 723 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
572 } | 799 } |
573 | 800 |
574 // D65 white point of Rec. 709 [8] are: | 801 // D65 white point of Rec. 709 [8] are: |
575 // | 802 // |
576 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 | 803 // D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 |
577 // | 804 // |
578 // R G B white | 805 // R G B white |
579 // x 0.640 0.300 0.150 0.3127 | 806 // x 0.640 0.300 0.150 0.3127 |
580 // y 0.330 0.600 0.060 0.3290 | 807 // y 0.330 0.600 0.060 0.3290 |
581 // z 0.030 0.100 0.790 0.3582 | 808 // z 0.030 0.100 0.790 0.3582 |
OLD | NEW |