SkColorSpace.cpp revision 959a2937d55279c6d020f2511007bb39ed322ace
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 "SkAtomics.h" 9#include "SkColorSpace.h" 10#include "SkOncePtr.h" 11 12static bool color_space_almost_equal(float a, float b) { 13 return SkTAbs(a - b) < 0.01f; 14} 15 16void SkFloat3::dump() const { 17 SkDebugf("[%7.4f %7.4f %7.4f]\n", fVec[0], fVec[1], fVec[2]); 18} 19 20////////////////////////////////////////////////////////////////////////////////////////////////// 21 22static int32_t gUniqueColorSpaceID; 23 24SkColorSpace::SkColorSpace(SkGammas gammas, const SkMatrix44& toXYZD50, Named named) 25 : fGammas(std::move(gammas)) 26 , fToXYZD50(toXYZD50) 27 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) 28 , fNamed(named) 29{} 30 31SkColorSpace::SkColorSpace(SkColorLookUpTable colorLUT, SkGammas gammas, 32 const SkMatrix44& toXYZD50) 33 : fColorLUT(std::move(colorLUT)) 34 , fGammas(std::move(gammas)) 35 , fToXYZD50(toXYZD50) 36 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) 37 , fNamed(kUnknown_Named) 38{} 39 40const float gSRGB_toXYZD50[] { 41 0.4358f, 0.2224f, 0.0139f, // * R 42 0.3853f, 0.7170f, 0.0971f, // * G 43 0.1430f, 0.0606f, 0.7139f, // * B 44}; 45 46SK_DECLARE_STATIC_ONCE_PTR(SkColorSpace, sRGB); 47 48sk_sp<SkColorSpace> SkColorSpace::NewRGB(SkGammas gammas, const SkMatrix44& toXYZD50) { 49 // Check if we really have sRGB 50 if (color_space_almost_equal(2.2f, gammas.fRed.fValue) && 51 color_space_almost_equal(2.2f, gammas.fGreen.fValue) && 52 color_space_almost_equal(2.2f, gammas.fBlue.fValue) && 53 color_space_almost_equal(toXYZD50.getFloat(0, 0), gSRGB_toXYZD50[0]) && 54 color_space_almost_equal(toXYZD50.getFloat(0, 1), gSRGB_toXYZD50[1]) && 55 color_space_almost_equal(toXYZD50.getFloat(0, 2), gSRGB_toXYZD50[2]) && 56 color_space_almost_equal(toXYZD50.getFloat(1, 0), gSRGB_toXYZD50[3]) && 57 color_space_almost_equal(toXYZD50.getFloat(1, 1), gSRGB_toXYZD50[4]) && 58 color_space_almost_equal(toXYZD50.getFloat(1, 2), gSRGB_toXYZD50[5]) && 59 color_space_almost_equal(toXYZD50.getFloat(2, 0), gSRGB_toXYZD50[6]) && 60 color_space_almost_equal(toXYZD50.getFloat(2, 1), gSRGB_toXYZD50[7]) && 61 color_space_almost_equal(toXYZD50.getFloat(2, 2), gSRGB_toXYZD50[8]) && 62 color_space_almost_equal(toXYZD50.getFloat(0, 3), 0.0f) && 63 color_space_almost_equal(toXYZD50.getFloat(1, 3), 0.0f) && 64 color_space_almost_equal(toXYZD50.getFloat(2, 3), 0.0f) && 65 color_space_almost_equal(toXYZD50.getFloat(3, 0), 0.0f) && 66 color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) && 67 color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) && 68 color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f)) 69 { 70 return SkColorSpace::NewNamed(kSRGB_Named); 71 } 72 73 return sk_sp<SkColorSpace>(new SkColorSpace(std::move(gammas), toXYZD50, kUnknown_Named)); 74} 75 76sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { 77 switch (named) { 78 case kSRGB_Named: { 79 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); 80 srgbToxyzD50.set3x3ColMajorf(gSRGB_toXYZD50); 81 return sk_ref_sp(sRGB.get([=]{ 82 return new SkColorSpace(SkGammas(2.2f, 2.2f, 2.2f), srgbToxyzD50, kSRGB_Named); 83 })); 84 } 85 default: 86 break; 87 } 88 return nullptr; 89} 90 91/////////////////////////////////////////////////////////////////////////////////////////////////// 92 93#include "SkFixed.h" 94#include "SkTemplates.h" 95 96#define SkColorSpacePrintf(...) 97 98#define return_if_false(pred, msg) \ 99 do { \ 100 if (!(pred)) { \ 101 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ 102 return false; \ 103 } \ 104 } while (0) 105 106#define return_null(msg) \ 107 do { \ 108 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ 109 return nullptr; \ 110 } while (0) 111 112static uint16_t read_big_endian_short(const uint8_t* ptr) { 113 return ptr[0] << 8 | ptr[1]; 114} 115 116static uint32_t read_big_endian_uint(const uint8_t* ptr) { 117 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; 118} 119 120static int32_t read_big_endian_int(const uint8_t* ptr) { 121 return (int32_t) read_big_endian_uint(ptr); 122} 123 124// This is equal to the header size according to the ICC specification (128) 125// plus the size of the tag count (4). We include the tag count since we 126// always require it to be present anyway. 127static const size_t kICCHeaderSize = 132; 128 129// Contains a signature (4), offset (4), and size (4). 130static const size_t kICCTagTableEntrySize = 12; 131 132static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); 133 134struct ICCProfileHeader { 135 uint32_t fSize; 136 137 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.). 138 // We're always going to use this one. 139 uint32_t fCMMType_ignored; 140 141 uint32_t fVersion; 142 uint32_t fProfileClass; 143 uint32_t fInputColorSpace; 144 uint32_t fPCS; 145 uint32_t fDateTime_ignored[3]; 146 uint32_t fSignature; 147 148 // Indicates the platform that this profile was created for (ex: Apple, Microsoft). This 149 // doesn't really matter to us. 150 uint32_t fPlatformTarget_ignored; 151 152 // Flags can indicate: 153 // (1) Whether this profile was embedded in a file. This flag is consistently wrong. 154 // Ex: The profile came from a file but indicates that it did not. 155 // (2) Whether we are allowed to use the profile independently of the color data. If set, 156 // this may allow us to use the embedded profile for testing separate from the original 157 // image. 158 uint32_t fFlags_ignored; 159 160 // We support many output devices. It doesn't make sense to think about the attributes of 161 // the device in the context of the image profile. 162 uint32_t fDeviceManufacturer_ignored; 163 uint32_t fDeviceModel_ignored; 164 uint32_t fDeviceAttributes_ignored[2]; 165 166 uint32_t fRenderingIntent; 167 int32_t fIlluminantXYZ[3]; 168 169 // We don't care who created the profile. 170 uint32_t fCreator_ignored; 171 172 // This is an MD5 checksum. Could be useful for checking if profiles are equal. 173 uint32_t fProfileId_ignored[4]; 174 175 // Reserved for future use. 176 uint32_t fReserved_ignored[7]; 177 178 uint32_t fTagCount; 179 180 void init(const uint8_t* src, size_t len) { 181 SkASSERT(kICCHeaderSize == sizeof(*this)); 182 183 uint32_t* dst = (uint32_t*) this; 184 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) { 185 dst[i] = read_big_endian_uint(src); 186 } 187 } 188 189 bool valid() const { 190 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); 191 192 uint8_t majorVersion = fVersion >> 24; 193 return_if_false(majorVersion <= 4, "Unsupported version"); 194 195 // These are the three basic classes of profiles that we might expect to see embedded 196 // in images. Four additional classes exist, but they generally are used as a convenient 197 // way for CMMs to store calculated transforms. 198 const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); 199 const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); 200 const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); 201 return_if_false(fProfileClass == kDisplay_Profile || 202 fProfileClass == kInput_Profile || 203 fProfileClass == kOutput_Profile, 204 "Unsupported profile"); 205 206 // TODO (msarett): 207 // All the profiles we've tested so far use RGB as the input color space. 208 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space"); 209 210 // TODO (msarett): 211 // All the profiles we've tested so far use XYZ as the profile connection space. 212 const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); 213 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); 214 215 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature"); 216 217 // TODO (msarett): 218 // Should we treat different rendering intents differently? 219 // Valid rendering intents include kPerceptual (0), kRelative (1), 220 // kSaturation (2), and kAbsolute (3). 221 return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); 222 223 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0]), 0.96420f) && 224 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1]), 1.00000f) && 225 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2]), 0.82491f), 226 "Illuminant must be D50"); 227 228 return_if_false(fTagCount <= 100, "Too many tags"); 229 230 return true; 231 } 232}; 233 234struct ICCTag { 235 uint32_t fSignature; 236 uint32_t fOffset; 237 uint32_t fLength; 238 239 const uint8_t* init(const uint8_t* src) { 240 fSignature = read_big_endian_uint(src); 241 fOffset = read_big_endian_uint(src + 4); 242 fLength = read_big_endian_uint(src + 8); 243 return src + 12; 244 } 245 246 bool valid(size_t len) { 247 return_if_false(fOffset + fLength <= len, "Tag too large for ICC profile"); 248 return true; 249 } 250 251 const uint8_t* addr(const uint8_t* src) const { 252 return src + fOffset; 253 } 254 255 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) { 256 for (int i = 0; i < count; ++i) { 257 if (tags[i].fSignature == signature) { 258 return &tags[i]; 259 } 260 } 261 return nullptr; 262 } 263}; 264 265static const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z'); 266static const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z'); 267static const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z'); 268static const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C'); 269static const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); 270static const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); 271static const uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0'); 272 273bool load_xyz(float dst[3], const uint8_t* src, size_t len) { 274 if (len < 20) { 275 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len); 276 return false; 277 } 278 279 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); 280 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); 281 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); 282 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); 283 return true; 284} 285 286static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v'); 287static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a'); 288 289bool SkColorSpace::LoadGammas(SkGammaCurve* gammas, uint32_t numGammas, const uint8_t* src, 290 size_t len) { 291 for (uint32_t i = 0; i < numGammas; i++) { 292 if (len < 12) { 293 // FIXME (msarett): 294 // We could potentially return false here after correctly parsing *some* of the 295 // gammas correctly. Should we somehow try to indicate a partial success? 296 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); 297 return false; 298 } 299 300 // We need to count the number of bytes in the tag, so we are able to move to the 301 // next tag on the next loop iteration. 302 size_t tagBytes; 303 304 uint32_t type = read_big_endian_uint(src); 305 switch (type) { 306 case kTAG_CurveType: { 307 uint32_t count = read_big_endian_uint(src + 8); 308 tagBytes = 12 + count * 2; 309 if (0 == count) { 310 // Some tags require a gamma curve, but the author doesn't actually want 311 // to transform the data. In this case, it is common to see a curve with 312 // a count of 0. 313 gammas[i].fValue = 1.0f; 314 break; 315 } else if (len < 12 + 2 * count) { 316 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); 317 return false; 318 } 319 320 const uint16_t* table = (const uint16_t*) (src + 12); 321 if (1 == count) { 322 // The table entry is the gamma (with a bias of 256). 323 uint16_t value = read_big_endian_short((const uint8_t*) table); 324 gammas[i].fValue = value / 256.0f; 325 SkColorSpacePrintf("gamma %d %g\n", value, *gamma); 326 break; 327 } 328 329 // Fill in the interpolation table. 330 // FIXME (msarett): 331 // We should recognize commonly occurring tables and just set gamma to 2.2f. 332 gammas[i].fTableSize = count; 333 gammas[i].fTable = std::unique_ptr<float[]>(new float[count]); 334 for (uint32_t j = 0; j < count; j++) { 335 gammas[i].fTable[j] = 336 (read_big_endian_short((const uint8_t*) &table[j])) / 65535.0f; 337 } 338 break; 339 } 340 case kTAG_ParaCurveType: 341 // Guess 2.2f. 342 // FIXME (msarett): Handle parametric curves. 343 SkColorSpacePrintf("parametric curve\n"); 344 gammas[i].fValue = 2.2f; 345 346 // Determine the size of the parametric curve tag. 347 switch(read_big_endian_short(src + 8)) { 348 case 0: 349 tagBytes = 12 + 4; 350 break; 351 case 1: 352 tagBytes = 12 + 12; 353 break; 354 case 2: 355 tagBytes = 12 + 16; 356 break; 357 case 3: 358 tagBytes = 12 + 20; 359 break; 360 case 4: 361 tagBytes = 12 + 28; 362 break; 363 default: 364 SkColorSpacePrintf("Invalid parametric curve type\n"); 365 return false; 366 } 367 break; 368 default: 369 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); 370 return false; 371 } 372 373 // Adjust src and len if there is another gamma curve to load. 374 if (0 != numGammas) { 375 // Each curve is padded to 4-byte alignment. 376 tagBytes = SkAlign4(tagBytes); 377 if (len < tagBytes) { 378 return false; 379 } 380 381 src += tagBytes; 382 len -= tagBytes; 383 } 384 } 385 386 return true; 387} 388 389static const uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); 390 391bool SkColorSpace::LoadColorLUT(SkColorLookUpTable* colorLUT, uint32_t inputChannels, 392 uint32_t outputChannels, const uint8_t* src, size_t len) { 393 if (len < 20) { 394 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); 395 return false; 396 } 397 398 SkASSERT(inputChannels <= SkColorLookUpTable::kMaxChannels && 3 == outputChannels); 399 colorLUT->fInputChannels = inputChannels; 400 colorLUT->fOutputChannels = outputChannels; 401 uint32_t numEntries = 1; 402 for (uint32_t i = 0; i < inputChannels; i++) { 403 colorLUT->fGridPoints[i] = src[i]; 404 numEntries *= src[i]; 405 } 406 numEntries *= outputChannels; 407 408 // Space is provided for a maximum of the 16 input channels. Now we determine the precision 409 // of the table values. 410 uint8_t precision = src[16]; 411 switch (precision) { 412 case 1: // 8-bit data 413 case 2: // 16-bit data 414 break; 415 default: 416 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n", len); 417 return false; 418 } 419 420 if (len < 20 + numEntries * precision) { 421 SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len); 422 return false; 423 } 424 425 // Movable struct colorLUT has ownership of fTable. 426 colorLUT->fTable = std::unique_ptr<float[]>(new float[numEntries]); 427 const uint8_t* ptr = src + 20; 428 for (uint32_t i = 0; i < numEntries; i++, ptr += precision) { 429 if (1 == precision) { 430 colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f; 431 } else { 432 colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0f; 433 } 434 } 435 436 return true; 437} 438 439bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) { 440 if (len < 48) { 441 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); 442 return false; 443 } 444 445 float array[16]; 446 array[ 0] = SkFixedToFloat(read_big_endian_int(src)); 447 array[ 1] = SkFixedToFloat(read_big_endian_int(src + 4)); 448 array[ 2] = SkFixedToFloat(read_big_endian_int(src + 8)); 449 array[ 3] = 0; 450 array[ 4] = SkFixedToFloat(read_big_endian_int(src + 12)); 451 array[ 5] = SkFixedToFloat(read_big_endian_int(src + 16)); 452 array[ 6] = SkFixedToFloat(read_big_endian_int(src + 20)); 453 array[ 7] = 0; 454 array[ 8] = SkFixedToFloat(read_big_endian_int(src + 24)); 455 array[ 9] = SkFixedToFloat(read_big_endian_int(src + 28)); 456 array[10] = SkFixedToFloat(read_big_endian_int(src + 32)); 457 array[11] = 0; 458 array[12] = SkFixedToFloat(read_big_endian_int(src + 36)); // translate R 459 array[13] = SkFixedToFloat(read_big_endian_int(src + 40)); // translate G 460 array[14] = SkFixedToFloat(read_big_endian_int(src + 44)); 461 array[15] = 1; 462 toXYZ->setColMajorf(array); 463 return true; 464} 465 466bool SkColorSpace::LoadA2B0(SkColorLookUpTable* colorLUT, SkGammas* gammas, SkMatrix44* toXYZ, 467 const uint8_t* src, size_t len) { 468 if (len < 32) { 469 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); 470 return false; 471 } 472 473 uint32_t type = read_big_endian_uint(src); 474 if (kTAG_AtoBType != type) { 475 // FIXME (msarett): Need to support lut8Type and lut16Type. 476 SkColorSpacePrintf("Unsupported A to B tag type.\n"); 477 return false; 478 } 479 480 // Read the number of channels. The four bytes that we skipped are reserved and 481 // must be zero. 482 uint8_t inputChannels = src[8]; 483 uint8_t outputChannels = src[9]; 484 if (0 == inputChannels || inputChannels > SkColorLookUpTable::kMaxChannels || 485 3 != outputChannels) { 486 // The color LUT assumes that there are at most 16 input channels. For RGB 487 // profiles, output channels should be 3. 488 SkColorSpacePrintf("Too many input or output channels in A to B tag.\n"); 489 return false; 490 } 491 492 // Read the offsets of each element in the A to B tag. With the exception of A curves and 493 // B curves (which we do not yet support), we will handle these elements in the order in 494 // which they should be applied (rather than the order in which they occur in the tag). 495 // If the offset is non-zero it indicates that the element is present. 496 uint32_t offsetToACurves = read_big_endian_int(src + 28); 497 uint32_t offsetToBCurves = read_big_endian_int(src + 12); 498 if ((0 != offsetToACurves) || (0 != offsetToBCurves)) { 499 // FIXME (msarett): Handle A and B curves. 500 // Note that the A curve is technically required in order to have a color LUT. 501 // However, all the A curves I have seen so far have are just placeholders that 502 // don't actually transform the data. 503 SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n"); 504 } 505 506 uint32_t offsetToColorLUT = read_big_endian_int(src + 24); 507 if (0 != offsetToColorLUT && offsetToColorLUT < len) { 508 if (!SkColorSpace::LoadColorLUT(colorLUT, inputChannels, outputChannels, 509 src + offsetToColorLUT, len - offsetToColorLUT)) { 510 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); 511 } 512 } 513 514 uint32_t offsetToMCurves = read_big_endian_int(src + 20); 515 if (0 != offsetToMCurves && offsetToMCurves < len) { 516 if (!SkColorSpace::LoadGammas(&gammas->fRed, outputChannels, src + offsetToMCurves, 517 len - offsetToMCurves)) { 518 SkColorSpacePrintf("Failed to read M curves from A to B tag.\n"); 519 } 520 } 521 522 uint32_t offsetToMatrix = read_big_endian_int(src + 16); 523 if (0 != offsetToMatrix && offsetToMatrix < len) { 524 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { 525 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); 526 } 527 } 528 529 return true; 530} 531 532sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) { 533 const uint8_t* ptr = (const uint8_t*) base; 534 535 if (len < kICCHeaderSize) { 536 return_null("Data is not large enough to contain an ICC profile"); 537 } 538 539 // Read the ICC profile header and check to make sure that it is valid. 540 ICCProfileHeader header; 541 header.init(ptr, len); 542 if (!header.valid()) { 543 return nullptr; 544 } 545 546 // Adjust ptr and len before reading the tags. 547 if (len < header.fSize) { 548 SkColorSpacePrintf("ICC profile might be truncated.\n"); 549 } else if (len > header.fSize) { 550 SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n"); 551 len = header.fSize; 552 } 553 ptr += kICCHeaderSize; 554 len -= kICCHeaderSize; 555 556 // Parse tag headers. 557 uint32_t tagCount = header.fTagCount; 558 SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount); 559 if (len < kICCTagTableEntrySize * tagCount) { 560 return_null("Not enough input data to read tag table entries"); 561 } 562 563 SkAutoTArray<ICCTag> tags(tagCount); 564 for (uint32_t i = 0; i < tagCount; i++) { 565 ptr = tags[i].init(ptr); 566 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24) & 0xFF, 567 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF, 568 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLength); 569 570 if (!tags[i].valid(kICCHeaderSize + len)) { 571 return_null("Tag is too large to fit in ICC profile"); 572 } 573 } 574 575 switch (header.fInputColorSpace) { 576 case kRGB_ColorSpace: { 577 // Recognize the rXYZ, gXYZ, and bXYZ tags. 578 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); 579 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); 580 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); 581 if (r && g && b) { 582 float toXYZ[9]; 583 if (!load_xyz(&toXYZ[0], r->addr((const uint8_t*) base), r->fLength) || 584 !load_xyz(&toXYZ[3], g->addr((const uint8_t*) base), g->fLength) || 585 !load_xyz(&toXYZ[6], b->addr((const uint8_t*) base), b->fLength)) 586 { 587 return_null("Need valid rgb tags for XYZ space"); 588 } 589 590 // It is not uncommon to see missing or empty gamma tags. This indicates 591 // that we should use unit gamma. 592 SkGammas gammas; 593 r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); 594 g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); 595 b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); 596 if (!r || !SkColorSpace::LoadGammas(&gammas.fRed, 1, 597 r->addr((const uint8_t*) base), r->fLength)) { 598 SkColorSpacePrintf("Failed to read R gamma tag.\n"); 599 } 600 if (!g || !SkColorSpace::LoadGammas(&gammas.fGreen, 1, 601 g->addr((const uint8_t*) base), g->fLength)) { 602 SkColorSpacePrintf("Failed to read G gamma tag.\n"); 603 } 604 if (!b || !SkColorSpace::LoadGammas(&gammas.fBlue, 1, 605 b->addr((const uint8_t*) base), b->fLength)) { 606 SkColorSpacePrintf("Failed to read B gamma tag.\n"); 607 } 608 609 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); 610 mat.set3x3ColMajorf(toXYZ); 611 return SkColorSpace::NewRGB(std::move(gammas), mat); 612 } 613 614 // Recognize color profile specified by A2B0 tag. 615 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); 616 if (a2b0) { 617 SkColorLookUpTable colorLUT; 618 SkGammas gammas; 619 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); 620 if (!SkColorSpace::LoadA2B0(&colorLUT, &gammas, &toXYZ, 621 a2b0->addr((const uint8_t*) base), a2b0->fLength)) { 622 return_null("Failed to parse A2B0 tag"); 623 } 624 625 // If there is no colorLUT, use NewRGB. This allows us to check if the 626 // profile is sRGB. 627 if (!colorLUT.fTable) { 628 return SkColorSpace::NewRGB(std::move(gammas), toXYZ); 629 } 630 631 return sk_sp<SkColorSpace>(new SkColorSpace(std::move(colorLUT), std::move(gammas), 632 toXYZ)); 633 } 634 635 } 636 default: 637 break; 638 } 639 640 return_null("ICC profile contains unsupported colorspace"); 641} 642