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