diff options
author | Mike Klein <mtklein@chromium.org> | 2016-12-01 16:38:16 +0000 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2016-12-01 16:38:30 +0000 |
commit | 62458a6778bc39eea5360301a67d192b3a263df1 (patch) | |
tree | 46e3969ee23b12b9a10cc69790e358b6db0c0cb0 /src/core/SkColorSpace_ICC.cpp | |
parent | 51c3fcd376c5c9972d9476b5532f6164375a38d1 (diff) |
Revert "Added CMYK support for ICC profiles."
This reverts commit 51c3fcd376c5c9972d9476b5532f6164375a38d1.
Reason for revert: ASAN, MSAN both take issue with parse_and_load_gamma()
Original change's description:
> Added CMYK support for ICC profiles.
>
> Changed ICC parsing/SkGammas/SkColorLookUpTable to handle non-3-channel
> inputs. Parsed CMYK A2B ICC profiles. Integrated this with SkJpegCodec
> (the only file that supports CMYK) and SkColorSpaceXform_A2B to allow
> parsing and color xforming of ICC CMYK images.
>
> BUG=skia:
>
> GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=5197
> CQ_INCLUDE_TRYBOTS=skia.primary:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD
>
>
> Change-Id: Id6619f63f04071f79cd2d84321857dfa269ad3aa
> Reviewed-on: https://skia-review.googlesource.com/5197
> Commit-Queue: Mike Klein <mtklein@chromium.org>
> Reviewed-by: Matt Sarett <msarett@google.com>
> Reviewed-by: Mike Klein <mtklein@chromium.org>
> Reviewed-by: Leon Scroggins <scroggo@google.com>
>
TBR=mtklein@chromium.org,mtklein@google.com,msarett@google.com,scroggo@google.com,brianosman@google.com,raftias@google.com,reviews@skia.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
Change-Id: Ib43fef00bc233c0b4fa47ed29040d69601def267
Reviewed-on: https://skia-review.googlesource.com/5423
Commit-Queue: Mike Klein <mtklein@chromium.org>
Reviewed-by: Mike Klein <mtklein@chromium.org>
Diffstat (limited to 'src/core/SkColorSpace_ICC.cpp')
-rw-r--r-- | src/core/SkColorSpace_ICC.cpp | 604 |
1 files changed, 247 insertions, 357 deletions
diff --git a/src/core/SkColorSpace_ICC.cpp b/src/core/SkColorSpace_ICC.cpp index ed65f816e1..5fe066ac11 100644 --- a/src/core/SkColorSpace_ICC.cpp +++ b/src/core/SkColorSpace_ICC.cpp @@ -49,7 +49,6 @@ static constexpr size_t kICCHeaderSize = 132; static constexpr size_t kICCTagTableEntrySize = 12; static constexpr uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); -static constexpr uint32_t kCMYK_ColorSpace = SkSetFourByteTag('C', 'M', 'Y', 'K'); static constexpr uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); static constexpr uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); static constexpr uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); @@ -128,19 +127,9 @@ struct ICCProfileHeader { fProfileClass == kColorSpace_Profile, "Unsupported profile"); - switch (fInputColorSpace) { - case kRGB_ColorSpace: - SkColorSpacePrintf("RGB Input Color Space"); - break; - case kCMYK_ColorSpace: - SkColorSpacePrintf("CMYK Input Color Space\n"); - break; - default: - SkColorSpacePrintf("Unsupported Input Color Space: %c%c%c%c\n", - (fInputColorSpace>>24)&0xFF, (fInputColorSpace>>16)&0xFF, - (fInputColorSpace>> 8)&0xFF, (fInputColorSpace>> 0)&0xFF); - return false; - } + // TODO (msarett): + // All the profiles we've tested so far use RGB as the input color space. + return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space"); switch (fPCS) { case kXYZ_PCSSpace: @@ -151,9 +140,7 @@ struct ICCProfileHeader { break; default: // ICC currently (V4.3) only specifices XYZ and Lab PCS spaces - SkColorSpacePrintf("Unsupported PCS space: %c%c%c%c\n", - (fPCS>>24)&0xFF, (fPCS>>16)&0xFF, - (fPCS>> 8)&0xFF, (fPCS>> 0)&0xFF); + SkColorSpacePrintf("Unsupported PCS space\n"); return false; } @@ -629,6 +616,7 @@ static bool load_color_lut(sk_sp<SkColorLookUpTable>* colorLUT, uint32_t inputCh return false; } + SkASSERT(3 == inputChannels); uint32_t numEntries = SkColorLookUpTable::kOutputChannels; for (uint32_t i = 0; i < inputChannels; i++) { if (0 == gridPoints[i]) { @@ -747,12 +735,14 @@ static bool load_matrix(SkMatrix44* matrix, const uint8_t* src, size_t len, bool } static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) { - for (uint8_t i = 0; i < gammas->channels(); ++i) { - if (!gammas->isNamed(i) || gammas->data(i).fNamed != gammas->data(0).fNamed) { - return kNonStandard_SkGammaNamed; - } + if (gammas->isNamed(0) && gammas->isNamed(1) && gammas->isNamed(2) && + gammas->fRedData.fNamed == gammas->fGreenData.fNamed && + gammas->fRedData.fNamed == gammas->fBlueData.fNamed) + { + return gammas->fRedData.fNamed; } - return gammas->data(0).fNamed; + + return kNonStandard_SkGammaNamed; } /** @@ -762,87 +752,93 @@ static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) { * read the table into heap memory. And for parametric gammas, we need to copy over the * parameter values. * - * @param gammaNamed Out-variable. The named gamma curve. - * @param gammas Out-variable. The stored gamma curve information. Can be null if - * gammaNamed is a named curve - * @param inputChannels The number of gamma input channels - * @param rTagPtr Pointer to start of the gamma tag. - * @param taglen The size in bytes of the tag + * @param gammaNamed Out-variable. The named gamma curve. + * @param gammas Out-variable. The stored gamma curve information. Can be null if + * gammaNamed is a named curve + * @param rTagPtr Pointer to start of the gamma tag. + * @param taglen The size in bytes of the tag * - * @return false on failure, true on success + * @return false on failure, true on success */ static bool parse_and_load_gamma(SkGammaNamed* gammaNamed, sk_sp<SkGammas>* gammas, - uint8_t inputChannels, const uint8_t* tagSrc, size_t tagLen) { - SkGammas::Data data[kMaxColorChannels]; - SkColorSpaceTransferFn params[kMaxColorChannels]; - SkGammas::Type type[kMaxColorChannels]; - const uint8_t* tagPtr[kMaxColorChannels]; - - tagPtr[0] = tagSrc; + const uint8_t* rTagPtr, size_t tagLen) +{ + SkGammas::Data rData; + SkColorSpaceTransferFn rParams; *gammaNamed = kNonStandard_SkGammaNamed; // On an invalid first gamma, tagBytes remains set as zero. This causes the two // subsequent to be treated as identical (which is what we want). size_t tagBytes = 0; - type[0] = parse_gamma(&data[0], ¶ms[0], &tagBytes, tagPtr[0], tagLen); - handle_invalid_gamma(&type[0], &data[0]); + SkGammas::Type rType = parse_gamma(&rData, &rParams, &tagBytes, rTagPtr, tagLen); + handle_invalid_gamma(&rType, &rData); size_t alignedTagBytes = SkAlign4(tagBytes); - bool allChannelsSame = false; - if (inputChannels * alignedTagBytes <= tagLen) { - allChannelsSame = true; - for (uint8_t i = 1; i < inputChannels; ++i) { - if (0 != memcmp(tagPtr, tagPtr + i * alignedTagBytes, tagBytes)) { - allChannelsSame = false; - break; - } - } - } - if (allChannelsSame) { - if (SkGammas::Type::kNamed_Type == type[0]) { - *gammaNamed = data[0].fNamed; + if ((3 * alignedTagBytes <= tagLen) && + !memcmp(rTagPtr, rTagPtr + 1 * alignedTagBytes, tagBytes) && + !memcmp(rTagPtr, rTagPtr + 2 * alignedTagBytes, tagBytes)) + { + if (SkGammas::Type::kNamed_Type == rType) { + *gammaNamed = rData.fNamed; } else { size_t allocSize = sizeof(SkGammas); - return_if_false(safe_add(allocSize, gamma_alloc_size(type[0], data[0]), &allocSize), + return_if_false(safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize), "SkGammas struct is too large to allocate"); void* memory = sk_malloc_throw(allocSize); - *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels)); - load_gammas(memory, 0, type[0], &data[0], params[0], tagPtr[0]); + *gammas = sk_sp<SkGammas>(new (memory) SkGammas()); + load_gammas(memory, 0, rType, &rData, rParams, rTagPtr); - for (uint8_t channel = 0; channel < inputChannels; ++channel) { - (*gammas)->fType[channel] = type[0]; - (*gammas)->fData[channel] = data[0]; - } + (*gammas)->fRedType = rType; + (*gammas)->fGreenType = rType; + (*gammas)->fBlueType = rType; + + (*gammas)->fRedData = rData; + (*gammas)->fGreenData = rData; + (*gammas)->fBlueData = rData; } } else { - for (uint8_t channel = 1; channel < inputChannels; ++channel) { - tagPtr[channel] = tagPtr[channel - 1] + alignedTagBytes; - tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0; - tagBytes = 0; - type[channel] = parse_gamma(&data[channel], ¶ms[channel], &tagBytes, - tagPtr[channel], tagLen); - handle_invalid_gamma(&type[channel], &data[channel]); - alignedTagBytes = SkAlign4(tagBytes); - } + const uint8_t* gTagPtr = rTagPtr + alignedTagBytes; + tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0; + SkGammas::Data gData; + SkColorSpaceTransferFn gParams; + tagBytes = 0; + SkGammas::Type gType = parse_gamma(&gData, &gParams, &tagBytes, gTagPtr, + tagLen); + handle_invalid_gamma(&gType, &gData); + + alignedTagBytes = SkAlign4(tagBytes); + const uint8_t* bTagPtr = gTagPtr + alignedTagBytes; + tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0; + SkGammas::Data bData; + SkColorSpaceTransferFn bParams; + SkGammas::Type bType = parse_gamma(&bData, &bParams, &tagBytes, bTagPtr, + tagLen); + handle_invalid_gamma(&bType, &bData); size_t allocSize = sizeof(SkGammas); - for (uint8_t channel = 0; channel < inputChannels; ++channel) { - return_if_false(safe_add(allocSize, gamma_alloc_size(type[channel], data[channel]), - &allocSize), - "SkGammas struct is too large to allocate"); - } + return_if_false(safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize), + "SkGammas struct is too large to allocate"); + return_if_false(safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize), + "SkGammas struct is too large to allocate"); + return_if_false(safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize), + "SkGammas struct is too large to allocate"); void* memory = sk_malloc_throw(allocSize); - *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels)); + *gammas = sk_sp<SkGammas>(new (memory) SkGammas()); uint32_t offset = 0; - for (uint8_t channel = 0; channel < inputChannels; ++channel) { - (*gammas)->fType[channel] = type[channel]; - offset += load_gammas(memory,offset, type[channel], &data[channel], params[channel], - tagPtr[channel]); - (*gammas)->fData[channel] = data[channel]; + (*gammas)->fRedType = rType; + offset += load_gammas(memory, offset, rType, &rData, rParams, rTagPtr); - } + (*gammas)->fGreenType = gType; + offset += load_gammas(memory, offset, gType, &gData, gParams, gTagPtr); + + (*gammas)->fBlueType = bType; + load_gammas(memory, offset, bType, &bData, bParams, bTagPtr); + + (*gammas)->fRedData = rData; + (*gammas)->fGreenData = gData; + (*gammas)->fBlueData = bData; } if (kNonStandard_SkGammaNamed == *gammaNamed) { @@ -893,7 +889,7 @@ static bool load_lut_gammas(sk_sp<SkGammas>* gammas, size_t numTables, size_t en "SkGammas struct is too large to allocate"); void* memory = sk_malloc_throw(allocSize); - *gammas = sk_sp<SkGammas>(new (memory) SkGammas(numTables)); + *gammas = sk_sp<SkGammas>(new (memory) SkGammas()); for (size_t tableIndex = 0; tableIndex < numTablesToUse; ++tableIndex) { const uint8_t* ptr = src + readBytesPerChannel * tableIndex; @@ -910,18 +906,24 @@ static bool load_lut_gammas(sk_sp<SkGammas>* gammas, size_t numTables, size_t en } } - SkASSERT(1 == numTablesToUse|| numTables == numTablesToUse); + (*gammas)->fRedType = SkGammas::Type::kTable_Type; + (*gammas)->fGreenType = SkGammas::Type::kTable_Type; + (*gammas)->fBlueType = SkGammas::Type::kTable_Type; - size_t tableOffset = 0; - for (size_t tableIndex = 0; tableIndex < numTables; ++tableIndex) { - (*gammas)->fType[tableIndex] = SkGammas::Type::kTable_Type; - (*gammas)->fData[tableIndex].fTable.fOffset = tableOffset; - (*gammas)->fData[tableIndex].fTable.fSize = entriesPerTable; - if (numTablesToUse > 1) { - tableOffset += writeBytesPerChannel; - } + if (1 == numTablesToUse) { + (*gammas)->fRedData.fTable.fOffset = 0; + (*gammas)->fGreenData.fTable.fOffset = 0; + (*gammas)->fBlueData.fTable.fOffset = 0; + } else { + (*gammas)->fRedData.fTable.fOffset = 0; + (*gammas)->fGreenData.fTable.fOffset = writeBytesPerChannel; + (*gammas)->fBlueData.fTable.fOffset = writeBytesPerChannel * 2; } + (*gammas)->fRedData.fTable.fSize = entriesPerTable; + (*gammas)->fGreenData.fTable.fSize = entriesPerTable; + (*gammas)->fBlueData.fTable.fSize = entriesPerTable; + return true; } @@ -932,23 +934,15 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con // must be zero. const uint8_t inputChannels = src[8]; const uint8_t outputChannels = src[9]; - if (SkColorLookUpTable::kOutputChannels != outputChannels) { - // We only handle RGB outputs. The number of output channels must be 3. - SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels); - return false; - } - if (inputChannels == 0 || inputChannels > 4) { - // And we only support 4 input channels. - // ICC says up to 16 but our decode can only handle 4. - // It could easily be extended to support up to 8, but we only allow CMYK/RGB - // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8. - // We can always change this check when we support bigger input spaces. - SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n", - inputChannels); + if (3 != inputChannels || SkColorLookUpTable::kOutputChannels != outputChannels) { + // We only handle (supposedly) RGB inputs and RGB outputs. The numbers of input + // channels and output channels both must be 3. + // TODO (msarett): + // Support different numbers of input channels. Ex: CMYK (4). + SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag.\n"); return false; } - // It is important that these are loaded in the order of application, as the // order you construct an A2B color space's elements is the order it is applied @@ -958,14 +952,13 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con const size_t tagLen = len - offsetToACurves; SkGammaNamed gammaNamed; sk_sp<SkGammas> gammas; - if (!parse_and_load_gamma(&gammaNamed, &gammas, inputChannels, src + offsetToACurves, - tagLen)) { + if (!parse_and_load_gamma(&gammaNamed, &gammas, src + offsetToACurves, tagLen)) { return false; } if (gammas) { elements->push_back(SkColorSpace_A2B::Element(std::move(gammas))); - } else if (kLinear_SkGammaNamed != gammaNamed) { - elements->push_back(SkColorSpace_A2B::Element(gammaNamed, inputChannels)); + } else { + elements->push_back(SkColorSpace_A2B::Element(gammaNamed)); } } @@ -982,8 +975,8 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con return false; } - SkASSERT(inputChannels <= kMaxColorChannels); - uint8_t gridPoints[kMaxColorChannels]; + SkASSERT(3 == inputChannels); + uint8_t gridPoints[3]; for (uint32_t i = 0; i < inputChannels; ++i) { gridPoints[i] = clutSrc[i]; } @@ -1003,14 +996,13 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con const size_t tagLen = len - offsetToMCurves; SkGammaNamed gammaNamed; sk_sp<SkGammas> gammas; - if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToMCurves, - tagLen)) { + if (!parse_and_load_gamma(&gammaNamed, &gammas, src + offsetToMCurves, tagLen)) { return false; } if (gammas) { elements->push_back(SkColorSpace_A2B::Element(std::move(gammas))); - } else if (kLinear_SkGammaNamed != gammaNamed) { - elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels)); + } else { + elements->push_back(SkColorSpace_A2B::Element(gammaNamed)); } } @@ -1019,7 +1011,7 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor); if (!load_matrix(&matrix, src + offsetToMatrix, len - offsetToMatrix, true, pcs)) { SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); - } else if (!matrix.isIdentity()) { + } else { elements->push_back(SkColorSpace_A2B::Element(matrix)); } } @@ -1029,14 +1021,13 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con const size_t tagLen = len - offsetToBCurves; SkGammaNamed gammaNamed; sk_sp<SkGammas> gammas; - if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToBCurves, - tagLen)) { + if (!parse_and_load_gamma(&gammaNamed, &gammas, src + offsetToBCurves, tagLen)) { return false; } if (gammas) { elements->push_back(SkColorSpace_A2B::Element(std::move(gammas))); - } else if (kLinear_SkGammaNamed != gammaNamed) { - elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels)); + } else { + elements->push_back(SkColorSpace_A2B::Element(gammaNamed)); } } @@ -1061,19 +1052,12 @@ bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const // The four bytes (4-7) that we skipped are reserved and must be zero. const uint8_t inputChannels = src[8]; const uint8_t outputChannels = src[9]; - if (SkColorLookUpTable::kOutputChannels != outputChannels) { - // We only handle RGB outputs. The number of output channels must be 3. - SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels); - return false; - } - if (inputChannels == 0 || inputChannels > 4) { - // And we only support 4 input channels. - // ICC says up to 16 but our decode can only handle 4. - // It could easily be extended to support up to 8, but we only allow CMYK/RGB - // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8. - // We can always change this check when we support bigger input spaces. - SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n", - inputChannels); + if (3 != inputChannels || SkColorLookUpTable::kOutputChannels != outputChannels) { + // We only handle (supposedly) RGB inputs and RGB outputs. The numbers of input + // channels and output channels both must be 3. + // TODO (msarett): + // Support different numbers of input channels. Ex: CMYK (4). + SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag.\n"); return false; } @@ -1082,13 +1066,7 @@ bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor); load_matrix(&matrix, &src[12], len - 12, false, pcs); - if (!matrix.isIdentity()) { - // ICC specs (10.8/10.9) say lut8/16Type profiles must have identity matrices - // if the input color space is not PCSXYZ, and we do not support PCSXYZ input color spaces - // so we should never encounter a non-identity matrix here. - SkColorSpacePrintf("non-Identity matrix found in non-XYZ input color space lut profile"); - return false; - } + elements->push_back(SkColorSpace_A2B::Element(matrix)); size_t dataOffset = 48; // # of input table entries @@ -1133,21 +1111,12 @@ bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const return false; } SkASSERT(inputGammas); - const SkGammaNamed inputGammaNamed = is_named(inputGammas); - if (kLinear_SkGammaNamed != inputGammaNamed) { - if (kNonStandard_SkGammaNamed != inputGammaNamed) { - elements->push_back(SkColorSpace_A2B::Element(inputGammaNamed, inputChannels)); - } else { - elements->push_back(SkColorSpace_A2B::Element(std::move(inputGammas))); - } - } + elements->push_back(SkColorSpace_A2B::Element(std::move(inputGammas))); const size_t clutOffset = inputOffset + precision*inTableEntries*inputChannels; return_if_false(len >= clutOffset, "A2B0 lutnType tag too small for CLUT"); sk_sp<SkColorLookUpTable> colorLUT; - const uint8_t gridPoints[kMaxColorChannels] = { - clutGridPoints, clutGridPoints, clutGridPoints, clutGridPoints - }; + const uint8_t gridPoints[3] = {clutGridPoints, clutGridPoints, clutGridPoints}; if (!load_color_lut(&colorLUT, inputChannels, precision, gridPoints, src + clutOffset, len - clutOffset)) { SkColorSpacePrintf("Failed to read color LUT from lutnType tag.\n"); @@ -1169,33 +1138,13 @@ bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const return false; } SkASSERT(outputGammas); - const SkGammaNamed outputGammaNamed = is_named(outputGammas); - if (kLinear_SkGammaNamed != outputGammaNamed) { - if (kNonStandard_SkGammaNamed != outputGammaNamed) { - elements->push_back(SkColorSpace_A2B::Element(outputGammaNamed, outputChannels)); - } else { - elements->push_back(SkColorSpace_A2B::Element(std::move(outputGammas))); - } - } + elements->push_back(SkColorSpace_A2B::Element(std::move(outputGammas))); return true; } -static inline int icf_channels(SkColorSpace_Base::InputColorFormat inputColorFormat) { - switch (inputColorFormat) { - case SkColorSpace_Base::InputColorFormat::kRGB: - return 3; - case SkColorSpace_Base::InputColorFormat::kCMYK: - return 4; - default: - SkASSERT(false); - return -1; - } -} - static bool load_a2b0(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src, - size_t len, SkColorSpace_A2B::PCS pcs, - SkColorSpace_Base::InputColorFormat inputColorFormat) { + size_t len, SkColorSpace_A2B::PCS pcs) { const uint32_t type = read_big_endian_u32(src); switch (type) { case kTAG_AtoBType: @@ -1204,54 +1153,26 @@ static bool load_a2b0(std::vector<SkColorSpace_A2B::Element>* elements, const ui return false; } SkColorSpacePrintf("A2B0 tag is of type lutAtoBType\n"); - if (!load_a2b0_a_to_b_type(elements, src, len, pcs)) { - return false; - } - break; + return load_a2b0_a_to_b_type(elements, src, len, pcs); case kTAG_lut8Type: if (len < 48) { SkColorSpacePrintf("lut8 tag is too small (%d bytes).", len); return false; } SkColorSpacePrintf("A2B0 tag of type lut8Type\n"); - if (!load_a2b0_lutn_type(elements, src, len, pcs)) { - return false; - } - break; + return load_a2b0_lutn_type(elements, src, len, pcs); case kTAG_lut16Type: if (len < 52) { SkColorSpacePrintf("lut16 tag is too small (%d bytes).", len); return false; } SkColorSpacePrintf("A2B0 tag of type lut16Type\n"); - if (!load_a2b0_lutn_type(elements, src, len, pcs)) { - return false; - } - break; + return load_a2b0_lutn_type(elements, src, len, pcs); default: SkColorSpacePrintf("Unsupported A to B tag type: %c%c%c%c\n", (type>>24)&0xFF, (type>>16)&0xFF, (type>>8)&0xFF, type&0xFF); - return false; - } - // now let's verify that the input/output channels of each A2B element actually match up - SkASSERT(!elements->empty()); - if (icf_channels(inputColorFormat) != elements->front().inputChannels()) { - SkColorSpacePrintf("Input channel count does not match first A2B element's input count"); - return false; - } - for (size_t i = 1; i < elements->size(); ++i) { - if ((*elements)[i - 1].outputChannels() != (*elements)[i].inputChannels()) { - SkColorSpacePrintf("A2B elements don't agree in input/output channel counts"); - return false; - } } - SkASSERT(SkColorSpace_A2B::PCS::kLAB == pcs || SkColorSpace_A2B::PCS::kXYZ == pcs); - static constexpr int kPCSChannels = 3; // must be PCSLAB or PCSXYZ - if (kPCSChannels != elements->back().outputChannels()) { - SkColorSpacePrintf("PCS channel count doesn't match last A2B element's output count"); - return false; - } - return true; + return false; } static bool tag_equals(const ICCTag* a, const ICCTag* b, const uint8_t* base) { @@ -1289,11 +1210,6 @@ static inline bool is_close_to_d50(const SkMatrix44& matrix) { } sk_sp<SkColorSpace> SkColorSpace::MakeICC(const void* input, size_t len) { - return SkColorSpace_Base::MakeICC(input, len, SkColorSpace_Base::InputColorFormat::kRGB); -} - -sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len, - InputColorFormat inputColorFormat) { if (!input || len < kICCHeaderSize) { return_null("Data is null or not large enough to contain an ICC profile"); } @@ -1312,22 +1228,6 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len, return nullptr; } - switch (inputColorFormat) { - case InputColorFormat::kRGB: - if (header.fInputColorSpace != kRGB_ColorSpace) { - return_null("Provided input color format (RGB) does not match profile.\n"); - } - break; - case InputColorFormat::kCMYK: - if (header.fInputColorSpace != kCMYK_ColorSpace) { - return_null("Provided input color format (CMYK) does not match profile.\n"); - return nullptr; - } - break; - default: - return_null("Provided input color format not supported"); - } - // Adjust ptr and len before reading the tags. if (len < header.fSize) { SkColorSpacePrintf("ICC profile might be truncated.\n"); @@ -1357,84 +1257,102 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len, } } - // Recognize color profile specified by A2B0 tag. - // this must be done before XYZ profile checking, as a profile can have both - // in which case we should use the A2B case to be accurate - // (XYZ is there as a fallback / quick preview) - const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); - if (a2b0) { - const SkColorSpace_A2B::PCS pcs = kXYZ_PCSSpace == header.fPCS - ? SkColorSpace_A2B::PCS::kXYZ - : SkColorSpace_A2B::PCS::kLAB; - std::vector<SkColorSpace_A2B::Element> elements; - if (load_a2b0(&elements, a2b0->addr(base), a2b0->fLength, pcs, inputColorFormat)) { - return sk_sp<SkColorSpace>(new SkColorSpace_A2B(inputColorFormat, std::move(elements), - pcs, std::move(data))); - } - SkColorSpacePrintf("Ignoring malformed A2B0 tag.\n"); - } - - if (kRGB_ColorSpace == header.fInputColorSpace) { - // Recognize the rXYZ, gXYZ, and bXYZ tags. - const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); - const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); - const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); - // Lab PCS means the profile is required to be an n-component LUT-based - // profile, so 3-component matrix-based profiles can only have an XYZ PCS - if (r && g && b && kXYZ_PCSSpace == header.fPCS) { - float toXYZ[9]; - if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) || - !load_xyz(&toXYZ[3], g->addr(base), g->fLength) || - !load_xyz(&toXYZ[6], b->addr(base), b->fLength)) - { - return_null("Need valid rgb tags for XYZ space"); - } - SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); - mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2], - toXYZ[3], toXYZ[4], toXYZ[5], - toXYZ[6], toXYZ[7], toXYZ[8]); - if (!is_close_to_d50(mat)) { - // QCMS treats these profiles as "bogus". I'm not sure if that's - // correct, but we certainly do not handle non-D50 matrices - // correctly. So I'll disable this for now. - SkColorSpacePrintf("Matrix is not close to D50"); - return nullptr; + switch (header.fInputColorSpace) { + case kRGB_ColorSpace: { + // Recognize color profile specified by A2B0 tag. + // this must be done before XYZ profile checking, as a profile can have both + // in which case we should use the A2B case to be accurate + // (XYZ is there as a fallback / quick preview) + const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); + if (a2b0) { + const SkColorSpace_A2B::PCS pcs = kXYZ_PCSSpace == header.fPCS + ? SkColorSpace_A2B::PCS::kXYZ + : SkColorSpace_A2B::PCS::kLAB; + std::vector<SkColorSpace_A2B::Element> elements; + if (load_a2b0(&elements, a2b0->addr(base), a2b0->fLength, pcs)) { + return sk_sp<SkColorSpace>(new SkColorSpace_A2B(pcs, std::move(data), + std::move(elements))); + } + SkColorSpacePrintf("Ignoring malformed A2B0 tag.\n"); } - r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); - g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); - b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); - - // If some, but not all, of the gamma tags are missing, assume that all - // gammas are meant to be the same. This behavior is an arbitrary guess, - // but it simplifies the code below. - if ((!r || !g || !b) && (r || g || b)) { - if (!r) { - r = g ? g : b; + // Recognize the rXYZ, gXYZ, and bXYZ tags. + const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); + const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); + const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); + // Lab PCS means the profile is required to be an n-component LUT-based + // profile, so 3-component matrix-based profiles can only have an XYZ PCS + if (r && g && b && kXYZ_PCSSpace == header.fPCS) { + float toXYZ[9]; + if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) || + !load_xyz(&toXYZ[3], g->addr(base), g->fLength) || + !load_xyz(&toXYZ[6], b->addr(base), b->fLength)) + { + return_null("Need valid rgb tags for XYZ space"); } - - if (!g) { - g = r ? r : b; + SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); + mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2], + toXYZ[3], toXYZ[4], toXYZ[5], + toXYZ[6], toXYZ[7], toXYZ[8]); + if (!is_close_to_d50(mat)) { + // QCMS treats these profiles as "bogus". I'm not sure if that's + // correct, but we certainly do not handle non-D50 matrices + // correctly. So I'll disable this for now. + SkColorSpacePrintf("Matrix is not close to D50"); + return nullptr; } - if (!b) { - b = r ? r : g; + r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); + g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); + b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); + + // If some, but not all, of the gamma tags are missing, assume that all + // gammas are meant to be the same. This behavior is an arbitrary guess, + // but it simplifies the code below. + if ((!r || !g || !b) && (r || g || b)) { + if (!r) { + r = g ? g : b; + } + + if (!g) { + g = r ? r : b; + } + + if (!b) { + b = r ? r : g; + } } - } - SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed; - sk_sp<SkGammas> gammas = nullptr; - size_t tagBytes; - if (r && g && b) { - if (tag_equals(r, g, base) && tag_equals(g, b, base)) { - SkGammas::Data data; - SkColorSpaceTransferFn params; - SkGammas::Type type = - parse_gamma(&data, ¶ms, &tagBytes, r->addr(base), r->fLength); - handle_invalid_gamma(&type, &data); - - if (SkGammas::Type::kNamed_Type == type) { - gammaNamed = data.fNamed; + SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed; + sk_sp<SkGammas> gammas = nullptr; + size_t tagBytes; + if (r && g && b) { + if (tag_equals(r, g, base) && tag_equals(g, b, base)) { + SkGammas::Data data; + SkColorSpaceTransferFn params; + SkGammas::Type type = + parse_gamma(&data, ¶ms, &tagBytes, r->addr(base), r->fLength); + handle_invalid_gamma(&type, &data); + + if (SkGammas::Type::kNamed_Type == type) { + gammaNamed = data.fNamed; + } else { + size_t allocSize = sizeof(SkGammas); + if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) { + return_null("SkGammas struct is too large to allocate"); + } + void* memory = sk_malloc_throw(allocSize); + gammas = sk_sp<SkGammas>(new (memory) SkGammas()); + load_gammas(memory, 0, type, &data, params, r->addr(base)); + + gammas->fRedType = type; + gammas->fGreenType = type; + gammas->fBlueType = type; + + gammas->fRedData = data; + gammas->fGreenData = data; + gammas->fBlueData = data; + } } else { SkGammas::Data rData; SkColorSpaceTransferFn rParams; @@ -1455,81 +1373,53 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len, handle_invalid_gamma(&bType, &bData); size_t allocSize = sizeof(SkGammas); - if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) { + if (!safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize) || + !safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize) || + !safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize)) + { return_null("SkGammas struct is too large to allocate"); } void* memory = sk_malloc_throw(allocSize); - gammas = sk_sp<SkGammas>(new (memory) SkGammas(3)); - load_gammas(memory, 0, type, &data, params, r->addr(base)); + gammas = sk_sp<SkGammas>(new (memory) SkGammas()); - for (int i = 0; i < 3; ++i) { - gammas->fType[i] = type; - gammas->fData[i] = data; - } - } - } else { - SkGammas::Data rData; - SkColorSpaceTransferFn rParams; - SkGammas::Type rType = - parse_gamma(&rData, &rParams, &tagBytes, r->addr(base), r->fLength); - handle_invalid_gamma(&rType, &rData); - - SkGammas::Data gData; - SkColorSpaceTransferFn gParams; - SkGammas::Type gType = - parse_gamma(&gData, &gParams, &tagBytes, g->addr(base), g->fLength); - handle_invalid_gamma(&gType, &gData); - - SkGammas::Data bData; - SkColorSpaceTransferFn bParams; - SkGammas::Type bType = - parse_gamma(&bData, &bParams, &tagBytes, b->addr(base), b->fLength); - handle_invalid_gamma(&bType, &bData); - - size_t allocSize = sizeof(SkGammas); - if (!safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize) || - !safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize) || - !safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize)) { - return_null("SkGammas struct is too large to allocate"); - } - void* memory = sk_malloc_throw(allocSize); - gammas = sk_sp<SkGammas>(new (memory) SkGammas(3)); + uint32_t offset = 0; + gammas->fRedType = rType; + offset += load_gammas(memory, offset, rType, &rData, rParams, + r->addr(base)); - uint32_t offset = 0; - gammas->fType[0] = rType; - offset += load_gammas(memory, offset, rType, &rData, rParams, - r->addr(base)); + gammas->fGreenType = gType; + offset += load_gammas(memory, offset, gType, &gData, gParams, + g->addr(base)); - gammas->fType[1] = gType; - offset += load_gammas(memory, offset, gType, &gData, gParams, - g->addr(base)); + gammas->fBlueType = bType; + load_gammas(memory, offset, bType, &bData, bParams, b->addr(base)); - gammas->fType[2] = bType; - load_gammas(memory, offset, bType, &bData, bParams, b->addr(base)); + gammas->fRedData = rData; + gammas->fGreenData = gData; + gammas->fBlueData = bData; + } + } else { + // Guess sRGB if the profile is missing transfer functions. + gammaNamed = kSRGB_SkGammaNamed; + } - gammas->fData[0] = rData; - gammas->fData[1] = gData; - gammas->fData[2] = bData; + if (kNonStandard_SkGammaNamed == gammaNamed) { + // It's possible that we'll initially detect non-matching gammas, only for + // them to evaluate to the same named gamma curve. + gammaNamed = is_named(gammas); } - } else { - // Guess sRGB if the profile is missing transfer functions. - gammaNamed = kSRGB_SkGammaNamed; - } - if (kNonStandard_SkGammaNamed == gammaNamed) { - // It's possible that we'll initially detect non-matching gammas, only for - // them to evaluate to the same named gamma curve. - gammaNamed = is_named(gammas); - } + if (kNonStandard_SkGammaNamed == gammaNamed) { + return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed, + std::move(gammas), + mat, std::move(data))); + } - if (kNonStandard_SkGammaNamed == gammaNamed) { - return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed, - std::move(gammas), - mat, std::move(data))); + return SkColorSpace_Base::MakeRGB(gammaNamed, mat); } - - return SkColorSpace_Base::MakeRGB(gammaNamed, mat); } + default: + break; } return_null("ICC profile contains unsupported colorspace"); |