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