SkColorSpace.cpp revision 85def2e0673f3b75c4500440b95ab3dac7435702
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 11static inline bool SkFloatIsFinite(float x) { return 0 == x * 0; } 12 13// 14// SkFloat3x3 15// 16// In memory order, values are a, b, c, d, e, f, g, h, i 17// 18// When applied to a color component vector (e.g. [ r, r, r ] or [ g, g, g ] we do 19// 20// [ r r r ] * [ a b c ] + [ g g g ] * [ d e f ] + [ b b b ] * [ g h i ] 21// 22// Thus in our point-on-the-right notation, the matrix looks like 23// 24// [ a d g ] [ r ] 25// [ b e h ] * [ g ] 26// [ c f i ] [ b ] 27// 28static SkFloat3x3 concat(const SkFloat3x3& left, const SkFloat3x3& rite) { 29 SkFloat3x3 result; 30 for (int row = 0; row < 3; ++row) { 31 for (int col = 0; col < 3; ++col) { 32 double tmp = 0; 33 for (int i = 0; i < 3; ++i) { 34 tmp += (double)left.fMat[row + i * 3] * rite.fMat[i + col * 3]; 35 } 36 result.fMat[row + col * 3] = (double)tmp; 37 } 38 } 39 return result; 40} 41 42static double det(const SkFloat3x3& m) { 43 return (double)m.fMat[0] * m.fMat[4] * m.fMat[8] + 44 (double)m.fMat[3] * m.fMat[7] * m.fMat[2] + 45 (double)m.fMat[6] * m.fMat[1] * m.fMat[5] - 46 (double)m.fMat[0] * m.fMat[7] * m.fMat[5] - 47 (double)m.fMat[3] * m.fMat[1] * m.fMat[8] - 48 (double)m.fMat[6] * m.fMat[4] * m.fMat[2]; 49} 50 51static double det2x2(const SkFloat3x3& m, int a, int b, int c, int d) { 52 return (double)m.fMat[a] * m.fMat[b] - (double)m.fMat[c] * m.fMat[d]; 53} 54 55static SkFloat3x3 invert(const SkFloat3x3& m) { 56 double d = det(m); 57 SkASSERT(SkFloatIsFinite((float)d)); 58 double scale = 1 / d; 59 SkASSERT(SkFloatIsFinite((float)scale)); 60 61 return {{ 62 (float)(scale * det2x2(m, 4, 8, 5, 7)), 63 (float)(scale * det2x2(m, 7, 2, 8, 1)), 64 (float)(scale * det2x2(m, 1, 5, 2, 4)), 65 66 (float)(scale * det2x2(m, 6, 5, 8, 3)), 67 (float)(scale * det2x2(m, 0, 8, 2, 6)), 68 (float)(scale * det2x2(m, 3, 2, 5, 0)), 69 70 (float)(scale * det2x2(m, 3, 7, 4, 6)), 71 (float)(scale * det2x2(m, 6, 1, 7, 0)), 72 (float)(scale * det2x2(m, 0, 4, 1, 3)), 73 }}; 74} 75 76void SkFloat3::dump() const { 77 SkDebugf("[%7.4f %7.4f %7.4f]\n", fVec[0], fVec[1], fVec[2]); 78} 79 80void SkFloat3x3::dump() const { 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], 83 fMat[3], fMat[4], fMat[5], 84 fMat[6], fMat[7], fMat[8]); 85} 86 87////////////////////////////////////////////////////////////////////////////////////////////////// 88 89static int32_t gUniqueColorSpaceID; 90 91SkColorSpace::SkColorSpace(const SkFloat3& gamma, const SkFloat3x3& toXYZD50, Named named) 92 : fGamma(gamma) 93 , fToXYZD50(toXYZD50) 94 , fToXYZOffset({{ 0.0f, 0.0f, 0.0f }}) 95 , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) 96 , fNamed(named) 97{ 98 for (int i = 0; i < 3; ++i) { 99 SkASSERT(SkFloatIsFinite(gamma.fVec[i])); 100 for (int j = 0; j < 3; ++j) { 101 SkASSERT(SkFloatIsFinite(toXYZD50.fMat[3*i + j])); 102 } 103 } 104} 105 106SkColorSpace::SkColorSpace(SkColorLookUpTable colorLUT, const SkFloat3& gamma, 107 const SkFloat3x3& toXYZD50, 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 116sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFloat3& gamma) { 117 for (int i = 0; i < 3; ++i) { 118 if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) { 119 return nullptr; 120 } 121 for (int j = 0; j < 3; ++j) { 122 if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) { 123 return nullptr; 124 } 125 } 126 } 127 128 // check the matrix for invertibility 129 float d = det(toXYZD50); 130 if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) { 131 return nullptr; 132 } 133 134 return sk_sp<SkColorSpace>(new SkColorSpace(gamma, toXYZD50, kUnknown_Named)); 135} 136 137void SkColorSpace::dump() const { 138 fToXYZD50.dump(); 139 fGamma.dump(); 140} 141 142////////////////////////////////////////////////////////////////////////////////////////////////// 143 144const SkFloat3 gDevice_gamma {{ 0, 0, 0 }}; 145const SkFloat3x3 gDevice_toXYZD50 {{ 146 1, 0, 0, 147 0, 1, 0, 148 0, 0, 1 149}}; 150 151const SkFloat3 gSRGB_gamma {{ 2.2f, 2.2f, 2.2f }}; 152const SkFloat3x3 gSRGB_toXYZD50 {{ 153 0.4358f, 0.2224f, 0.0139f, // * R 154 0.3853f, 0.7170f, 0.0971f, // * G 155 0.1430f, 0.0606f, 0.7139f, // * B 156}}; 157 158sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) { 159 switch (named) { 160 case kDevice_Named: 161 return sk_sp<SkColorSpace>(new SkColorSpace(gDevice_gamma, gDevice_toXYZD50, 162 kDevice_Named)); 163 case kSRGB_Named: 164 return sk_sp<SkColorSpace>(new SkColorSpace(gSRGB_gamma, gSRGB_toXYZD50, kSRGB_Named)); 165 default: 166 break; 167 } 168 return nullptr; 169} 170 171/////////////////////////////////////////////////////////////////////////////////////////////////// 172 173#include "SkFixed.h" 174#include "SkTemplates.h" 175 176#define SkColorSpacePrintf(...) 177 178#define return_if_false(pred, msg) \ 179 do { \ 180 if (!(pred)) { \ 181 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ 182 return false; \ 183 } \ 184 } while (0) 185 186#define return_null(msg) \ 187 do { \ 188 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ 189 return nullptr; \ 190 } while (0) 191 192static uint16_t read_big_endian_short(const uint8_t* ptr) { 193 return ptr[0] << 8 | ptr[1]; 194} 195 196static uint32_t read_big_endian_uint(const uint8_t* ptr) { 197 return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; 198} 199 200static int32_t read_big_endian_int(const uint8_t* ptr) { 201 return (int32_t) read_big_endian_uint(ptr); 202} 203 204static bool color_space_almost_equal(float a, float b) { 205 return SkTAbs(a - b) < 0.01f; 206} 207 208// This is equal to the header size according to the ICC specification (128) 209// plus the size of the tag count (4). We include the tag count since we 210// always require it to be present anyway. 211static const size_t kICCHeaderSize = 132; 212 213// Contains a signature (4), offset (4), and size (4). 214static const size_t kICCTagTableEntrySize = 12; 215 216static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); 217 218struct ICCProfileHeader { 219 uint32_t fSize; 220 221 // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.). 222 // We're always going to use this one. 223 uint32_t fCMMType_ignored; 224 225 uint32_t fVersion; 226 uint32_t fProfileClass; 227 uint32_t fInputColorSpace; 228 uint32_t fPCS; 229 uint32_t fDateTime_ignored[3]; 230 uint32_t fSignature; 231 232 // Indicates the platform that this profile was created for (ex: Apple, Microsoft). This 233 // doesn't really matter to us. 234 uint32_t fPlatformTarget_ignored; 235 236 // Flags can indicate: 237 // (1) Whether this profile was embedded in a file. This flag is consistently wrong. 238 // Ex: The profile came from a file but indicates that it did not. 239 // (2) Whether we are allowed to use the profile independently of the color data. If set, 240 // this may allow us to use the embedded profile for testing separate from the original 241 // image. 242 uint32_t fFlags_ignored; 243 244 // We support many output devices. It doesn't make sense to think about the attributes of 245 // the device in the context of the image profile. 246 uint32_t fDeviceManufacturer_ignored; 247 uint32_t fDeviceModel_ignored; 248 uint32_t fDeviceAttributes_ignored[2]; 249 250 uint32_t fRenderingIntent; 251 int32_t fIlluminantXYZ[3]; 252 253 // We don't care who created the profile. 254 uint32_t fCreator_ignored; 255 256 // This is an MD5 checksum. Could be useful for checking if profiles are equal. 257 uint32_t fProfileId_ignored[4]; 258 259 // Reserved for future use. 260 uint32_t fReserved_ignored[7]; 261 262 uint32_t fTagCount; 263 264 void init(const uint8_t* src, size_t len) { 265 SkASSERT(kICCHeaderSize == sizeof(*this)); 266 267 uint32_t* dst = (uint32_t*) this; 268 for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) { 269 dst[i] = read_big_endian_uint(src); 270 } 271 } 272 273 bool valid() const { 274 return_if_false(fSize >= kICCHeaderSize, "Size is too small"); 275 276 uint8_t majorVersion = fVersion >> 24; 277 return_if_false(majorVersion <= 4, "Unsupported version"); 278 279 // These are the three basic classes of profiles that we might expect to see embedded 280 // in images. Four additional classes exist, but they generally are used as a convenient 281 // way for CMMs to store calculated transforms. 282 const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); 283 const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); 284 const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); 285 return_if_false(fProfileClass == kDisplay_Profile || 286 fProfileClass == kInput_Profile || 287 fProfileClass == kOutput_Profile, 288 "Unsupported profile"); 289 290 // TODO (msarett): 291 // All the profiles we've tested so far use RGB as the input color space. 292 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space"); 293 294 // TODO (msarett): 295 // All the profiles we've tested so far use XYZ as the profile connection space. 296 const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); 297 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); 298 299 return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature"); 300 301 // TODO (msarett): 302 // Should we treat different rendering intents differently? 303 // Valid rendering intents include kPerceptual (0), kRelative (1), 304 // kSaturation (2), and kAbsolute (3). 305 return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); 306 307 return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0]), 0.96420f) && 308 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1]), 1.00000f) && 309 color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2]), 0.82491f), 310 "Illuminant must be D50"); 311 312 return_if_false(fTagCount <= 100, "Too many tags"); 313 314 return true; 315 } 316}; 317 318struct ICCTag { 319 uint32_t fSignature; 320 uint32_t fOffset; 321 uint32_t fLength; 322 323 const uint8_t* init(const uint8_t* src) { 324 fSignature = read_big_endian_uint(src); 325 fOffset = read_big_endian_uint(src + 4); 326 fLength = read_big_endian_uint(src + 8); 327 return src + 12; 328 } 329 330 bool valid(size_t len) { 331 return_if_false(fOffset + fLength <= len, "Tag too large for ICC profile"); 332 return true; 333 } 334 335 const uint8_t* addr(const uint8_t* src) const { 336 return src + fOffset; 337 } 338 339 static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) { 340 for (int i = 0; i < count; ++i) { 341 if (tags[i].fSignature == signature) { 342 return &tags[i]; 343 } 344 } 345 return nullptr; 346 } 347}; 348 349static const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z'); 350static const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z'); 351static const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z'); 352static const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C'); 353static const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); 354static const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); 355static const uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0'); 356 357bool load_xyz(float dst[3], const uint8_t* src, size_t len) { 358 if (len < 20) { 359 SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len); 360 return false; 361 } 362 363 dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); 364 dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); 365 dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); 366 SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); 367 return true; 368} 369 370static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v'); 371static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a'); 372 373// FIXME (msarett): 374// We need to handle the possibility that the gamma curve does not correspond to 2.2f. 375static bool load_gammas(float* gammas, uint32_t numGammas, const uint8_t* src, size_t len) { 376 for (uint32_t i = 0; i < numGammas; i++) { 377 if (len < 12) { 378 // FIXME (msarett): 379 // We could potentially return false here after correctly parsing *some* of the 380 // gammas correctly. Should we somehow try to indicate a partial success? 381 SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); 382 return false; 383 } 384 385 // We need to count the number of bytes in the tag, so we are able to move 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 actually 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*) table); 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 guess 2.2f. 415 for (uint32_t j = 0; j < count; j++) { 416 SkColorSpacePrintf("curve[%d] %d\n", j, 417 read_big_endian_short((const uint8_t*) &table[j])); 418 } 419 420 gammas[i] = 2.2f; 421 break; 422 } 423 case kTAG_ParaCurveType: 424 // Guess 2.2f. 425 SkColorSpacePrintf("parametric curve\n"); 426 gammas[i] = 2.2f; 427 428 switch(read_big_endian_short(src + 8)) { 429 case 0: 430 tagBytes = 12 + 4; 431 break; 432 case 1: 433 tagBytes = 12 + 12; 434 break; 435 case 2: 436 tagBytes = 12 + 16; 437 break; 438 case 3: 439 tagBytes = 12 + 20; 440 break; 441 case 4: 442 tagBytes = 12 + 28; 443 break; 444 default: 445 SkColorSpacePrintf("Invalid parametric curve type\n"); 446 return false; 447 } 448 break; 449 default: 450 SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); 451 return false; 452 } 453 454 // Adjust src and len if there is another gamma curve to load. 455 if (0 != numGammas) { 456 // Each curve is padded to 4-byte alignment. 457 tagBytes = SkAlign4(tagBytes); 458 if (len < tagBytes) { 459 return false; 460 } 461 462 src += tagBytes; 463 len -= tagBytes; 464 } 465 } 466 467 // If all of the gammas we encounter are 1.0f, indicate that we failed to load gammas. 468 // There is no need to apply a gamma of 1.0f. 469 for (uint32_t i = 0; i < numGammas; i++) { 470 if (1.0f != gammas[i]) { 471 return true; 472 } 473 } 474 475 return false; 476} 477 478static const uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' '); 479 480bool 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 determine 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; 505 default: 506 SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n", len); 507 return false; 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.0f; 523 } 524 } 525 526 return true; 527} 528 529bool load_matrix(SkFloat3x3* toXYZ, SkFloat3* toXYZOffset, const uint8_t* src, size_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 550bool load_a2b0(SkColorLookUpTable* colorLUT, SkFloat3* gamma, SkFloat3x3* toXYZ, 551 SkFloat3* toXYZOffset, const uint8_t* src, size_t len) { 552 if (len < 32) { 553 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); 554 return false; 555 } 556 557 uint32_t type = read_big_endian_uint(src); 558 if (kTAG_AtoBType != type) { 559 // FIXME (msarett): Need to support lut8Type and lut16Type. 560 SkColorSpacePrintf("Unsupported A to B tag type.\n"); 561 return false; 562 } 563 564 // Read the number of channels. The four bytes that we skipped are reserved and 565 // must be zero. 566 uint8_t inputChannels = src[8]; 567 uint8_t outputChannels = src[9]; 568 if (0 == inputChannels || inputChannels > SkColorLookUpTable::kMaxChannels || 569 0 < outputChannels || outputChannels > SkColorLookUpTable::kMaxChannels) { 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 of 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 in 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 color LUT. 585 // However, all the A curves I have seen so far have are just placeholders 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 + offsetToColorLUT, 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 - offsetToMatrix)) { 609 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); 610 } 611 } 612 613 return true; 614} 615 616sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) { 617 const uint8_t* ptr = (const uint8_t*) base; 618 619 if (len < kICCHeaderSize) { 620 return_null("Data is not large enough to contain an ICC profile"); 621 } 622 623 // Read the ICC profile header and check to make sure that it is valid. 624 ICCProfileHeader header; 625 header.init(ptr, len); 626 if (!header.valid()) { 627 return nullptr; 628 } 629 630 // Adjust ptr and len before reading the tags. 631 if (len < header.fSize) { 632 SkColorSpacePrintf("ICC profile might be truncated.\n"); 633 } else if (len > header.fSize) { 634 SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n"); 635 len = header.fSize; 636 } 637 ptr += kICCHeaderSize; 638 len -= kICCHeaderSize; 639 640 // Parse tag headers. 641 uint32_t tagCount = header.fTagCount; 642 SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount); 643 if (len < kICCTagTableEntrySize * tagCount) { 644 return_null("Not enough input data to read tag table entries"); 645 } 646 647 SkAutoTArray<ICCTag> tags(tagCount); 648 for (uint32_t i = 0; i < tagCount; i++) { 649 ptr = tags[i].init(ptr); 650 SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24) & 0xFF, 651 (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF, 652 (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLength); 653 654 if (!tags[i].valid(kICCHeaderSize + len)) { 655 return_null("Tag is too large to fit in ICC profile"); 656 } 657 } 658 659 switch (header.fInputColorSpace) { 660 case kRGB_ColorSpace: { 661 // Recognize the rXYZ, gXYZ, and bXYZ tags. 662 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); 663 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); 664 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); 665 if (r && g && b) { 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*) base), 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*) base), 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*) base), b->fLength)) 692 { 693 SkColorSpacePrintf("Failed to read B gamma tag.\n"); 694 } 695 return SkColorSpace::NewRGB(toXYZ, gamma); 696 } 697 698 // Recognize color profile specified by A2B0 tag. 699 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); 700 if (a2b0) { 701 SkColorLookUpTable colorLUT; 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)); 712 } 713 714 } 715 default: 716 break; 717 } 718 719 return_null("ICC profile contains unsupported colorspace"); 720} 721 722/////////////////////////////////////////////////////////////////////////////////////////////////// 723 724SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColorSpace* dst, 725 SkFloat3x3* result) { 726 if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst->named())) { 727 if (result) { 728 *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }}; 729 } 730 return kIdentity_Result; 731 } 732 if (result) { 733 *result = concat(src->fToXYZD50, invert(dst->fToXYZD50)); 734 } 735 return kNormal_Result; 736} 737 738#include "SkColor.h" 739#include "SkNx.h" 740#include "SkPM4f.h" 741 742void SkApply3x3ToPM4f(const SkFloat3x3& m, const SkPM4f src[], SkPM4f dst[], int count) { 743 SkASSERT(1 == SkPM4f::G); 744 SkASSERT(3 == SkPM4f::A); 745 746 Sk4f cr, cg, cb; 747 cg = Sk4f::Load(m.fMat + 3); 748 if (0 == SkPM4f::R) { 749 SkASSERT(2 == SkPM4f::B); 750 cr = Sk4f::Load(m.fMat + 0); 751 cb = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); 752 } else { 753 SkASSERT(0 == SkPM4f::B); 754 SkASSERT(2 == SkPM4f::R); 755 cb = Sk4f::Load(m.fMat + 0); 756 cr = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); 757 } 758 cr = cr * Sk4f(1, 1, 1, 0); 759 cg = cg * Sk4f(1, 1, 1, 0); 760 cb = cb * Sk4f(1, 1, 1, 0); 761 762 for (int i = 0; i < count; ++i) { 763 Sk4f r = Sk4f(src[i].fVec[SkPM4f::R]); 764 Sk4f g = Sk4f(src[i].fVec[SkPM4f::G]); 765 Sk4f b = Sk4f(src[i].fVec[SkPM4f::B]); 766 Sk4f a = Sk4f(0, 0, 0, src[i].fVec[SkPM4f::A]); 767 (cr * r + cg * g + cb * b + a).store(&dst[i]); 768 } 769} 770 771/////////////////////////////////////////////////////////////////////////////////////////////////// 772 773void SkColorSpace::Test() { 774 SkFloat3x3 mat {{ 2, 0, 0, 0, 3, 0, 0, 0, 4 }}; 775 SkFloat3x3 inv = invert(mat); 776 mat.dump(); 777 inv.dump(); 778 concat(mat, inv).dump(); 779 concat(inv, mat).dump(); 780 SkDebugf("\n"); 781 782 mat = gSRGB_toXYZD50; 783 inv = invert(mat); 784 mat.dump(); 785 inv.dump(); 786 concat(mat, inv).dump(); 787 concat(inv, mat).dump(); 788 SkDebugf("\n"); 789 790 sk_sp<SkColorSpace> cs0(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); 791 sk_sp<SkColorSpace> cs1(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); 792 793 cs0->dump(); 794 cs1->dump(); 795 SkFloat3x3 xform; 796 (void)SkColorSpace::Concat(cs0.get(), cs1.get(), &xform); 797 xform.dump(); 798 SkDebugf("\n"); 799} 800 801// D65 white point of Rec. 709 [8] are: 802// 803// D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 804// 805// R G B white 806// x 0.640 0.300 0.150 0.3127 807// y 0.330 0.600 0.060 0.3290 808// z 0.030 0.100 0.790 0.3582 809