| Index: src/core/SkColorSpaceXform.cpp
|
| diff --git a/src/core/SkColorSpaceXform.cpp b/src/core/SkColorSpaceXform.cpp
|
| index ce9d52ac3ffca223fb4f3e2d21c2436280235d21..cb95c2999d027993521319f9c7945c1a8074c6e1 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);
|
|
|
|
|