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, c onst SkFloat3x3& toXYZD50, | |
scroggo
2016/04/28 12:16:02
nit: over 100 chars
msarett
2016/04/28 13:32:08
Done.
| |
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" |
(...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 i = 0; i < count; i++) { | |
416 SkColorSpacePrintf("curve[%d] %d\n", i, | |
417 read_big_endian_short((const uint8_t*) &table[i])); | |
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, SkFloat3* toXYZOffset, | |
551 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 |