diff options
author | msarett <msarett@google.com> | 2016-06-23 15:12:52 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-06-23 15:12:52 -0700 |
commit | 0f83e0151f757ecd8d55d55ffefef58ecb11a97b (patch) | |
tree | 119e9da478b4c517224b1aaa68ee35984219e337 | |
parent | 3fe0aabac1a9ffa926c1de83642371028669831d (diff) | |
download | platform_external_skqp-0f83e0151f757ecd8d55d55ffefef58ecb11a97b.tar.gz platform_external_skqp-0f83e0151f757ecd8d55d55ffefef58ecb11a97b.tar.bz2 platform_external_skqp-0f83e0151f757ecd8d55d55ffefef58ecb11a97b.zip |
Add support for 3D colorLUTs to SkColorXform
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2097553002
Review-Url: https://codereview.chromium.org/2097553002
-rw-r--r-- | dm/DMSrcSink.cpp | 5 | ||||
-rw-r--r-- | src/core/SkColorSpace.cpp | 24 | ||||
-rw-r--r-- | src/core/SkColorSpaceXform.cpp | 141 | ||||
-rw-r--r-- | src/core/SkColorSpaceXform.h | 14 | ||||
-rw-r--r-- | src/core/SkColorSpace_Base.h | 24 |
5 files changed, 163 insertions, 45 deletions
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 8afbfb9f2f..f0b10103f7 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -896,10 +896,7 @@ Error ColorCodecSrc::draw(SkCanvas* canvas) const { std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(srcSpace, dstSpace); if (!xform) { - // FIXME (msarett): - // I haven't implemented conversions for all of the images that I've uploaded for - // testing. Once we support all of them, this should be a fatal error. - return Error::Nonfatal("Unimplemented color conversion."); + return "Unimplemented color conversion."; } uint32_t* row = (uint32_t*) bitmap.getPixels(); diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp index 3f93f16233..f988c7d323 100644 --- a/src/core/SkColorSpace.cpp +++ b/src/core/SkColorSpace.cpp @@ -32,10 +32,10 @@ SkColorSpace_Base::SkColorSpace_Base(GammaNamed gammaNamed, const SkMatrix44& to , fProfileData(nullptr) {} -SkColorSpace_Base::SkColorSpace_Base(SkColorLookUpTable* colorLUT, sk_sp<SkGammas> gammas, +SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkColorLookUpTable> colorLUT, sk_sp<SkGammas> gammas, const SkMatrix44& toXYZD50, sk_sp<SkData> profileData) : INHERITED(kNonStandard_GammaNamed, toXYZD50, kUnknown_Named) - , fColorLUT(colorLUT) + , fColorLUT(std::move(colorLUT)) , fGammas(std::move(gammas)) , fProfileData(std::move(profileData)) {} @@ -677,7 +677,7 @@ bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32 } size_t dataLen = len - kColorLUTHeaderSize; - SkASSERT(inputChannels <= SkColorLookUpTable::kMaxChannels && 3 == outputChannels); + SkASSERT(3 == inputChannels && 3 == outputChannels); colorLUT->fInputChannels = inputChannels; colorLUT->fOutputChannels = outputChannels; uint32_t numEntries = 1; @@ -783,11 +783,10 @@ bool load_a2b0(SkColorLookUpTable* colorLUT, SkGammaCurve* gammas, SkMatrix44* t // must be zero. uint8_t inputChannels = src[8]; uint8_t outputChannels = src[9]; - if (0 == inputChannels || inputChannels > SkColorLookUpTable::kMaxChannels || - 3 != outputChannels) { - // The color LUT assumes that there are at most 16 input channels. For RGB - // profiles, output channels should be 3. - SkColorSpacePrintf("Too many input or output channels in A to B tag.\n"); + if (3 != inputChannels || 3 != outputChannels) { + // We only handle (supposedly) RGB inputs and RGB outputs. The numbers of input + // channels and output channels both must be 3. + SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag.\n"); return false; } @@ -936,21 +935,22 @@ sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* input, size_t len) { // Recognize color profile specified by A2B0 tag. const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); if (a2b0) { - SkAutoTDelete<SkColorLookUpTable> colorLUT(new SkColorLookUpTable()); + sk_sp<SkColorLookUpTable> colorLUT = sk_make_sp<SkColorLookUpTable>(); SkGammaCurve curves[3]; SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); - if (!load_a2b0(colorLUT, curves, &toXYZ, a2b0->addr((const uint8_t*) base), + if (!load_a2b0(colorLUT.get(), curves, &toXYZ, a2b0->addr((const uint8_t*) base), a2b0->fLength)) { return_null("Failed to parse A2B0 tag"); } GammaNamed gammaNamed = SkGammas::Named(curves); - if (colorLUT->fTable || kNonStandard_GammaNamed == gammaNamed) { + colorLUT = colorLUT->fTable ? colorLUT : nullptr; + if (colorLUT || kNonStandard_GammaNamed == gammaNamed) { sk_sp<SkGammas> gammas = sk_make_sp<SkGammas>(std::move(curves[0]), std::move(curves[1]), std::move(curves[2])); - return sk_sp<SkColorSpace>(new SkColorSpace_Base(colorLUT.release(), + return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(colorLUT), std::move(gammas), toXYZ, std::move(data))); } else { diff --git a/src/core/SkColorSpaceXform.cpp b/src/core/SkColorSpaceXform.cpp index ce9d52ac3f..cb95c2999d 100644 --- a/src/core/SkColorSpaceXform.cpp +++ b/src/core/SkColorSpaceXform.cpp @@ -27,8 +27,9 @@ std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpa return nullptr; } - if (as_CSB(srcSpace)->colorLUT() || as_CSB(dstSpace)->colorLUT()) { - // Unimplemented + if (as_CSB(dstSpace)->colorLUT()) { + // It would be really weird for a dst profile to have a color LUT. I don't think + // we need to support this. return nullptr; } @@ -39,7 +40,8 @@ std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpa if (0.0f == srcToDst.getFloat(3, 0) && 0.0f == srcToDst.getFloat(3, 1) && - 0.0f == srcToDst.getFloat(3, 2)) + 0.0f == srcToDst.getFloat(3, 2) && + !as_CSB(srcSpace)->colorLUT()) { switch (srcSpace->gammaNamed()) { case SkColorSpace::kSRGB_GammaNamed: @@ -69,8 +71,7 @@ std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpa } } - return std::unique_ptr<SkColorSpaceXform>( - new SkDefaultXform(srcSpace, srcToDst, dstSpace)); + return std::unique_ptr<SkColorSpaceXform>(new SkDefaultXform(srcSpace, srcToDst, dstSpace)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -540,7 +541,8 @@ static void build_table_linear_to_gamma(uint8_t* outTable, int outTableSize, flo SkDefaultXform::SkDefaultXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatrix44& srcToDst, const sk_sp<SkColorSpace>& dstSpace) - : fSrcToDst(srcToDst) + : fColorLUT(sk_ref_sp((SkColorLookUpTable*) as_CSB(srcSpace)->colorLUT())) + , fSrcToDst(srcToDst) { // Build tables to transform src gamma to linear. switch (srcSpace->gammaNamed()) { @@ -687,6 +689,10 @@ SkDefaultXform::SkDefaultXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatr } } +static float byte_to_float(uint8_t byte) { + return ((float) byte) * (1.0f / 255.0f); +} + // Clamp to the 0-1 range. static float clamp_normalized_float(float v) { if (v > 1.0f) { @@ -698,13 +704,124 @@ static float clamp_normalized_float(float v) { } } +static void interp_3d_clut(float dst[3], float src[3], const SkColorLookUpTable* colorLUT) { + // Call the src components x, y, and z. + uint8_t maxX = colorLUT->fGridPoints[0] - 1; + uint8_t maxY = colorLUT->fGridPoints[1] - 1; + uint8_t maxZ = colorLUT->fGridPoints[2] - 1; + + // An approximate index into each of the three dimensions of the table. + float x = src[0] * maxX; + float y = src[1] * maxY; + float z = src[2] * maxZ; + + // This gives us the low index for our interpolation. + int ix = sk_float_floor2int(x); + int iy = sk_float_floor2int(y); + int iz = sk_float_floor2int(z); + + // Make sure the low index is not also the max index. + ix = (maxX == ix) ? ix - 1 : ix; + iy = (maxY == iy) ? iy - 1 : iy; + iz = (maxZ == iz) ? iz - 1 : iz; + + // Weighting factors for the interpolation. + float diffX = x - ix; + float diffY = y - iy; + float diffZ = z - iz; + + // Constants to help us navigate the 3D table. + // Ex: Assume x = a, y = b, z = c. + // table[a * n001 + b * n010 + c * n100] logically equals table[a][b][c]. + const int n000 = 0; + const int n001 = 3 * colorLUT->fGridPoints[1] * colorLUT->fGridPoints[2]; + const int n010 = 3 * colorLUT->fGridPoints[2]; + const int n011 = n001 + n010; + const int n100 = 3; + const int n101 = n100 + n001; + const int n110 = n100 + n010; + const int n111 = n110 + n001; + + // Base ptr into the table. + float* ptr = &colorLUT->fTable[ix*n001 + iy*n010 + iz*n100]; + + // The code below performs a tetrahedral interpolation for each of the three + // dst components. Once the tetrahedron containing the interpolation point is + // identified, the interpolation is a weighted sum of grid values at the + // vertices of the tetrahedron. The claim is that tetrahedral interpolation + // provides a more accurate color conversion. + // blogs.mathworks.com/steve/2006/11/24/tetrahedral-interpolation-for-colorspace-conversion/ + // + // I have one test image, and visually I can't tell the difference between + // tetrahedral and trilinear interpolation. In terms of computation, the + // tetrahedral code requires more branches but less computation. The + // SampleICC library provides an option for the client to choose either + // tetrahedral or trilinear. + for (int i = 0; i < 3; i++) { + if (diffZ < diffY) { + if (diffZ < diffX) { + dst[i] = (ptr[n000] + diffZ * (ptr[n110] - ptr[n010]) + + diffY * (ptr[n010] - ptr[n000]) + + diffX * (ptr[n111] - ptr[n110])); + } else if (diffY < diffX) { + dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) + + diffY * (ptr[n011] - ptr[n001]) + + diffX * (ptr[n001] - ptr[n000])); + } else { + dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) + + diffY * (ptr[n010] - ptr[n000]) + + diffX * (ptr[n011] - ptr[n010])); + } + } else { + if (diffZ < diffX) { + dst[i] = (ptr[n000] + diffZ * (ptr[n101] - ptr[n001]) + + diffY * (ptr[n111] - ptr[n101]) + + diffX * (ptr[n001] - ptr[n000])); + } else if (diffY < diffX) { + dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) + + diffY * (ptr[n111] - ptr[n101]) + + diffX * (ptr[n101] - ptr[n100])); + } else { + dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) + + diffY * (ptr[n110] - ptr[n100]) + + diffX * (ptr[n111] - ptr[n110])); + } + } + + // Increment the table ptr in order to handle the next component. + // Note that this is the how table is designed: all of nXXX + // variables are multiples of 3 because there are 3 output + // components. + ptr++; + } +} + void SkDefaultXform::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const { while (len-- > 0) { + uint8_t r = (*src >> 0) & 0xFF, + g = (*src >> 8) & 0xFF, + b = (*src >> 16) & 0xFF; + + if (fColorLUT) { + float in[3]; + float out[3]; + + in[0] = byte_to_float(r); + in[1] = byte_to_float(g); + in[2] = byte_to_float(b); + + interp_3d_clut(out, in, fColorLUT.get()); + + r = sk_float_round2int(255.0f * clamp_normalized_float(out[0])); + g = sk_float_round2int(255.0f * clamp_normalized_float(out[1])); + b = sk_float_round2int(255.0f * clamp_normalized_float(out[2])); + } + // Convert to linear. float srcFloats[3]; - srcFloats[0] = fSrcGammaTables[0][(*src >> 0) & 0xFF]; - srcFloats[1] = fSrcGammaTables[1][(*src >> 8) & 0xFF]; - srcFloats[2] = fSrcGammaTables[2][(*src >> 16) & 0xFF]; + srcFloats[0] = fSrcGammaTables[0][r]; + srcFloats[1] = fSrcGammaTables[1][g]; + srcFloats[2] = fSrcGammaTables[2][b]; // Convert to dst gamut. float dstFloats[3]; @@ -724,9 +841,9 @@ void SkDefaultXform::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_ dstFloats[2] = clamp_normalized_float(dstFloats[2]); // Convert to dst gamma. - uint8_t r = fDstGammaTables[0][sk_float_round2int((kDstGammaTableSize - 1) * dstFloats[0])]; - uint8_t g = fDstGammaTables[1][sk_float_round2int((kDstGammaTableSize - 1) * dstFloats[1])]; - uint8_t b = fDstGammaTables[2][sk_float_round2int((kDstGammaTableSize - 1) * dstFloats[2])]; + r = fDstGammaTables[0][sk_float_round2int((kDstGammaTableSize - 1) * dstFloats[0])]; + g = fDstGammaTables[1][sk_float_round2int((kDstGammaTableSize - 1) * dstFloats[1])]; + b = fDstGammaTables[2][sk_float_round2int((kDstGammaTableSize - 1) * dstFloats[2])]; *dst = SkPackARGB32NoCheck(0xFF, r, g, b); diff --git a/src/core/SkColorSpaceXform.h b/src/core/SkColorSpaceXform.h index 583386a8a2..723dea4335 100644 --- a/src/core/SkColorSpaceXform.h +++ b/src/core/SkColorSpaceXform.h @@ -60,17 +60,19 @@ private: SkDefaultXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatrix44& srcToDst, const sk_sp<SkColorSpace>& dstSpace); - static constexpr int kDstGammaTableSize = 1024; + static constexpr int kDstGammaTableSize = 1024; + + sk_sp<SkColorLookUpTable> fColorLUT; // May contain pointers into storage or pointers into precomputed tables. - const float* fSrcGammaTables[3]; - float fSrcGammaTableStorage[3 * 256]; + const float* fSrcGammaTables[3]; + float fSrcGammaTableStorage[3 * 256]; - const SkMatrix44 fSrcToDst; + const SkMatrix44 fSrcToDst; // May contain pointers into storage or pointers into precomputed tables. - const uint8_t* fDstGammaTables[3]; - uint8_t fDstGammaTableStorage[3 * kDstGammaTableSize]; + const uint8_t* fDstGammaTables[3]; + uint8_t fDstGammaTableStorage[3 * kDstGammaTableSize]; friend class SkColorSpaceXform; }; diff --git a/src/core/SkColorSpace_Base.h b/src/core/SkColorSpace_Base.h index 17e5938100..6289b09f50 100644 --- a/src/core/SkColorSpace_Base.h +++ b/src/core/SkColorSpace_Base.h @@ -138,16 +138,18 @@ public: friend class SkColorSpace; }; -struct SkColorLookUpTable { - static const uint8_t kMaxChannels = 16; - +struct SkColorLookUpTable : public SkRefCnt { uint8_t fInputChannels; uint8_t fOutputChannels; - uint8_t fGridPoints[kMaxChannels]; + uint8_t fGridPoints[3]; std::unique_ptr<float[]> fTable; - SkColorLookUpTable() { - memset(this, 0, sizeof(struct SkColorLookUpTable)); + SkColorLookUpTable() + : fInputChannels(0) + , fOutputChannels(0) + , fTable(nullptr) + { + fGridPoints[0] = fGridPoints[1] = fGridPoints[2] = 0; } }; @@ -171,12 +173,12 @@ private: SkColorSpace_Base(GammaNamed gammaNamed, const SkMatrix44& toXYZ, Named named); - SkColorSpace_Base(SkColorLookUpTable* colorLUT, sk_sp<SkGammas> gammas, const SkMatrix44& toXYZ, - sk_sp<SkData> profileData); + SkColorSpace_Base(sk_sp<SkColorLookUpTable> colorLUT, sk_sp<SkGammas> gammas, + const SkMatrix44& toXYZ, sk_sp<SkData> profileData); - SkAutoTDelete<SkColorLookUpTable> fColorLUT; - sk_sp<SkGammas> fGammas; - sk_sp<SkData> fProfileData; + sk_sp<SkColorLookUpTable> fColorLUT; + sk_sp<SkGammas> fGammas; + sk_sp<SkData> fProfileData; friend class SkColorSpace; friend class ColorSpaceXformTest; |