diff options
author | raftias <raftias@google.com> | 2016-12-02 11:56:59 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2016-12-05 16:38:40 +0000 |
commit | 91db12d89c214235e24599f3ec18df2f952e99eb (patch) | |
tree | bcc7ca2428499bbc85173fa39f3f90cdf8babea4 | |
parent | aa2219c69b81b2258862a45cd82cc9fdc06c987a (diff) |
Color-correct Gray JPEG image decoding via ICC profiles.
SkColorSpace::MakeICC now parses Gray ICC profiles and
SkColorSpaceXform_A2B can now render color spaces from Gray ICC
profiles. This is not enabled for SkPngCodec as of right now as we don't
have any Gray PNG test images currently.
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=5214
CQ_INCLUDE_TRYBOTS=skia.primary:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD
Change-Id: Iea7136d1c163cd17cf0818af57f93efdbcb577e7
Reviewed-on: https://skia-review.googlesource.com/5214
Reviewed-by: Matt Sarett <msarett@google.com>
Commit-Queue: Robert Aftias <raftias@google.com>
-rw-r--r-- | src/codec/SkJpegCodec.cpp | 3 | ||||
-rw-r--r-- | src/core/SkColorSpaceXform_A2B.cpp | 23 | ||||
-rw-r--r-- | src/core/SkColorSpace_Base.h | 3 | ||||
-rw-r--r-- | src/core/SkColorSpace_ICC.cpp | 81 |
4 files changed, 81 insertions, 29 deletions
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp index 7a264d5d99..9a08755b64 100644 --- a/src/codec/SkJpegCodec.cpp +++ b/src/codec/SkJpegCodec.cpp @@ -237,6 +237,9 @@ bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, case JCS_YCCK: inputColorFormat = SkColorSpace_Base::InputColorFormat::kCMYK; break; + case JCS_GRAYSCALE: + inputColorFormat = SkColorSpace_Base::InputColorFormat::kGray; + break; default: break; } diff --git a/src/core/SkColorSpaceXform_A2B.cpp b/src/core/SkColorSpaceXform_A2B.cpp index c6fa99a618..9f48aafd81 100644 --- a/src/core/SkColorSpaceXform_A2B.cpp +++ b/src/core/SkColorSpaceXform_A2B.cpp @@ -14,10 +14,11 @@ #include "SkColorSpaceXformPriv.h" #include "SkMakeUnique.h" #include "SkNx.h" +#include "SkRasterPipeline_opts.h" #include "SkSRGB.h" #include "SkTypes.h" -#include "SkRasterPipeline_opts.h" +#include <algorithm> #define AI SK_ALWAYS_INLINE @@ -170,6 +171,9 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace, // TransferFn is y = -x + 1 for x < 1.f, otherwise 0x + 0, ie y = 1 - x for x in [0,1] this->addTransferFns({1.f, 0.f, 0.f, 0.f, 1.f, -1.f, 1.f}, 4); break; + case SkColorSpace_Base::InputColorFormat::kGray: + currentChannels = 1; + break; default: SkASSERT(false); } @@ -236,7 +240,19 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace, } } - SkASSERT(3 == currentChannels); + // take care of monochrome ICC profiles (but not A2B with gray input color space!) + if (1 == currentChannels) { + // Gray color spaces must multiply their channel by the PCS whitepoint to convert to + // the PCS however, PCSLAB profiles must be n-component LUT-based ones, which + // need to have 3 (to match PCS) output channels, not 1 + SkASSERT(SkColorSpace_Base::InputColorFormat::kGray == srcSpace->inputColorFormat()); + SkASSERT(SkColorSpace_A2B::PCS::kXYZ == srcSpace->pcs()); + constexpr float PCSXYZWhitePoint[3] = {0.9642f, 1.f, 0.8249f}; + fMatrices.push_front(std::vector<float>(12, 0.f)); + std::copy_n(PCSXYZWhitePoint, 3, fMatrices.front().begin()); + fElementsPipeline.append(SkRasterPipeline::matrix_3x4, fMatrices.front().data()); + currentChannels = 3; + } // Lab PCS -> XYZ PCS if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) { @@ -244,6 +260,9 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace, fElementsPipeline.append(SkRasterPipeline::lab_to_xyz); } + // we should now be in XYZ PCS + SkASSERT(3 == currentChannels); + // and XYZ PCS -> output color space xforms if (!dstSpace->fromXYZD50()->isIdentity()) { addMatrix(*dstSpace->fromXYZD50()); diff --git a/src/core/SkColorSpace_Base.h b/src/core/SkColorSpace_Base.h index 947f725036..e1c004e5a1 100644 --- a/src/core/SkColorSpace_Base.h +++ b/src/core/SkColorSpace_Base.h @@ -176,7 +176,8 @@ public: enum class InputColorFormat { kRGB, - kCMYK + kCMYK, + kGray }; static sk_sp<SkColorSpace> MakeICC(const void* input, size_t len, diff --git a/src/core/SkColorSpace_ICC.cpp b/src/core/SkColorSpace_ICC.cpp index 84a29f0c6f..d27de96c3f 100644 --- a/src/core/SkColorSpace_ICC.cpp +++ b/src/core/SkColorSpace_ICC.cpp @@ -50,6 +50,7 @@ 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 kGray_ColorSpace = SkSetFourByteTag('G', 'R', 'A', 'Y'); 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'); @@ -135,6 +136,9 @@ struct ICCProfileHeader { case kCMYK_ColorSpace: SkColorSpacePrintf("CMYK Input Color Space\n"); break; + case kGray_ColorSpace: + SkColorSpacePrintf("Gray Input Color Space\n"); + break; default: SkColorSpacePrintf("Unsupported Input Color Space: %c%c%c%c\n", (fInputColorSpace>>24)&0xFF, (fInputColorSpace>>16)&0xFF, @@ -245,6 +249,7 @@ static constexpr uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z'); static constexpr uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C'); static constexpr uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); static constexpr uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); +static constexpr uint32_t kTAG_kTRC = SkSetFourByteTag('k', 'T', 'R', 'C'); static constexpr uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0'); static bool load_xyz(float dst[3], const uint8_t* src, size_t len) { @@ -1187,6 +1192,8 @@ static inline int icf_channels(SkColorSpace_Base::InputColorFormat inputColorFor return 3; case SkColorSpace_Base::InputColorFormat::kCMYK: return 4; + case SkColorSpace_Base::InputColorFormat::kGray: + return 1; default: SkASSERT(false); return -1; @@ -1303,8 +1310,8 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len, // Create our own copy of the input. void* memory = sk_malloc_throw(len); memcpy(memory, input, len); - sk_sp<SkData> data = SkData::MakeFromMalloc(memory, len); - const uint8_t* base = data->bytes(); + sk_sp<SkData> profileData = SkData::MakeFromMalloc(memory, len); + const uint8_t* base = profileData->bytes(); const uint8_t* ptr = base; // Read the ICC profile header and check to make sure that it is valid. @@ -1323,7 +1330,11 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len, case InputColorFormat::kCMYK: if (header.fInputColorSpace != kCMYK_ColorSpace) { return_null("Provided input color format (CMYK) does not match profile.\n"); - return nullptr; + } + break; + case InputColorFormat::kGray: + if (header.fInputColorSpace != kGray_ColorSpace) { + return_null("Provided input color format (Gray) does not match profile.\n"); } break; default: @@ -1371,19 +1382,24 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len, 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))); + pcs, std::move(profileData))); } SkColorSpacePrintf("Ignoring malformed A2B0 tag.\n"); } + // Lab PCS means the profile is required to be an n-component LUT-based + // profile, so monochrome/3-component matrix-based profiles can only have an XYZ PCS + if (kLAB_PCSSpace == header.fPCS) { + return_null("Invalid PCS. PCSLAB only supported on A2B0 profiles."); + } + SkASSERT(kXYZ_PCSSpace == header.fPCS); + 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) { + if (r && g && b) { float toXYZ[9]; if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) || !load_xyz(&toXYZ[3], g->addr(base), g->fLength) || @@ -1438,24 +1454,6 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len, if (SkGammas::Type::kNamed_Type == type) { gammaNamed = data.fNamed; } 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(type, data), &allocSize)) { return_null("SkGammas struct is too large to allocate"); @@ -1527,11 +1525,42 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len, if (kNonStandard_SkGammaNamed == gammaNamed) { return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed, std::move(gammas), - mat, std::move(data))); + mat, std::move(profileData))); } return SkColorSpace_Base::MakeRGB(gammaNamed, mat); } + } else if (kGray_ColorSpace == header.fInputColorSpace) { + const ICCTag* grayTRC = ICCTag::Find(tags.get(), tagCount, kTAG_kTRC); + if (!grayTRC) { + return_null("grayTRC tag required for monochrome profiles."); + } + SkGammas::Data data; + SkColorSpaceTransferFn params; + size_t tagBytes; + SkGammas::Type type = + parse_gamma(&data, ¶ms, &tagBytes, grayTRC->addr(base), grayTRC->fLength); + handle_invalid_gamma(&type, &data); + + std::vector<SkColorSpace_A2B::Element> elements; + if (SkGammas::Type::kNamed_Type == type) { + elements.push_back(SkColorSpace_A2B::Element(data.fNamed, 1)); + } 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); + sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(1)); + load_gammas(memory, 0, type, &data, params, grayTRC->addr(base)); + gammas->fType[0] = type; + gammas->fData[0] = data; + elements.push_back(SkColorSpace_A2B::Element(std::move(gammas))); + } + return sk_sp<SkColorSpace>(new SkColorSpace_A2B(inputColorFormat, + std::move(elements), + SkColorSpace_A2B::PCS::kXYZ, + std::move(profileData))); } return_null("ICC profile contains unsupported colorspace"); |