aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormsarett <msarett@google.com>2016-06-23 15:12:52 -0700
committerCommit bot <commit-bot@chromium.org>2016-06-23 15:12:52 -0700
commit0f83e0151f757ecd8d55d55ffefef58ecb11a97b (patch)
tree119e9da478b4c517224b1aaa68ee35984219e337
parent3fe0aabac1a9ffa926c1de83642371028669831d (diff)
downloadplatform_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.cpp5
-rw-r--r--src/core/SkColorSpace.cpp24
-rw-r--r--src/core/SkColorSpaceXform.cpp141
-rw-r--r--src/core/SkColorSpaceXform.h14
-rw-r--r--src/core/SkColorSpace_Base.h24
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;