Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(143)

Unified Diff: src/core/SkColorSpace.cpp

Issue 1925753002: Parse A2B0 tag on ICC profiles (Closed) Base URL: https://skia.googlesource.com/skia.git@sanity-icc-parse
Patch Set: Explicit move constructor for MSVS 2013 Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/core/SkColorSpace.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/core/SkColorSpace.cpp
diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp
index 794d8a04ff4dfa6fa1887582478b00e9736b262d..71fb0de2a03995f685819e7710e41ee3768a9b3d 100644
--- a/src/core/SkColorSpace.cpp
+++ b/src/core/SkColorSpace.cpp
@@ -88,9 +88,10 @@ void SkFloat3x3::dump() const {
static int32_t gUniqueColorSpaceID;
-SkColorSpace::SkColorSpace(const SkFloat3x3& toXYZD50, const SkFloat3& gamma, Named named)
- : fToXYZD50(toXYZD50)
- , fGamma(gamma)
+SkColorSpace::SkColorSpace(const SkFloat3& gamma, const SkFloat3x3& toXYZD50, Named named)
+ : fGamma(gamma)
+ , fToXYZD50(toXYZD50)
+ , fToXYZOffset({{ 0.0f, 0.0f, 0.0f }})
, fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
, fNamed(named)
{
@@ -102,6 +103,16 @@ SkColorSpace::SkColorSpace(const SkFloat3x3& toXYZD50, const SkFloat3& gamma, Na
}
}
+SkColorSpace::SkColorSpace(SkColorLookUpTable colorLUT, const SkFloat3& gamma,
+ const SkFloat3x3& toXYZD50, const SkFloat3& toXYZOffset)
+ : fColorLUT(std::move(colorLUT))
+ , fGamma(gamma)
+ , fToXYZD50(toXYZD50)
+ , fToXYZOffset(toXYZOffset)
+ , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
+ , fNamed(kUnknown_Named)
+{}
+
sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFloat3& gamma) {
for (int i = 0; i < 3; ++i) {
if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) {
@@ -120,7 +131,7 @@ sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFlo
return nullptr;
}
- return sk_sp<SkColorSpace>(new SkColorSpace(toXYZD50, gamma, kUnknown_Named));
+ return sk_sp<SkColorSpace>(new SkColorSpace(gamma, toXYZD50, kUnknown_Named));
}
void SkColorSpace::dump() const {
@@ -147,10 +158,10 @@ const SkFloat3x3 gSRGB_toXYZD50 {{
sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) {
switch (named) {
case kDevice_Named:
- return sk_sp<SkColorSpace>(new SkColorSpace(gDevice_toXYZD50, gDevice_gamma,
+ return sk_sp<SkColorSpace>(new SkColorSpace(gDevice_gamma, gDevice_toXYZD50,
kDevice_Named));
case kSRGB_Named:
- return sk_sp<SkColorSpace>(new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named));
+ return sk_sp<SkColorSpace>(new SkColorSpace(gSRGB_gamma, gSRGB_toXYZD50, kSRGB_Named));
default:
break;
}
@@ -335,14 +346,13 @@ struct ICCTag {
}
};
-// TODO (msarett):
-// Should we recognize more tags?
static const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z');
static const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z');
static const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z');
static const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C');
static const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C');
static const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C');
+static const uint32_t kTAG_A2B0 = SkSetFourByteTag('A', '2', 'B', '0');
bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
if (len < 20) {
@@ -360,53 +370,247 @@ bool load_xyz(float dst[3], const uint8_t* src, size_t len) {
static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v');
static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');
-static bool load_gamma(float* gamma, const uint8_t* src, size_t len) {
- if (len < 14) {
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
- return false;
- }
-
- uint32_t type = read_big_endian_uint(src);
- switch (type) {
- case kTAG_CurveType: {
- uint32_t count = read_big_endian_int(src + 8);
- if (0 == count) {
- return false;
- }
+// FIXME (msarett):
+// We need to handle the possibility that the gamma curve does not correspond to 2.2f.
+static bool load_gammas(float* gammas, uint32_t numGammas, const uint8_t* src, size_t len) {
+ for (uint32_t i = 0; i < numGammas; i++) {
+ if (len < 12) {
+ // FIXME (msarett):
+ // We could potentially return false here after correctly parsing *some* of the
+ // gammas correctly. Should we somehow try to indicate a partial success?
+ SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
+ return false;
+ }
- const uint16_t* table = (const uint16_t*) (src + 12);
- if (1 == count) {
- // Table entry is the exponent (bias 256).
- uint16_t value = read_big_endian_short((const uint8_t*) table);
- *gamma = value / 256.0f;
- SkColorSpacePrintf("gamma %d %g\n", value, *gamma);
- return true;
+ // We need to count the number of bytes in the tag, so we are able to move to the
+ // next tag on the next loop iteration.
+ size_t tagBytes;
+
+ uint32_t type = read_big_endian_uint(src);
+ switch (type) {
+ case kTAG_CurveType: {
+ uint32_t count = read_big_endian_uint(src + 8);
+ tagBytes = 12 + count * 2;
+ if (0 == count) {
+ // Some tags require a gamma curve, but the author doesn't actually want
+ // to transform the data. In this case, it is common to see a curve with
+ // a count of 0.
+ gammas[i] = 1.0f;
+ break;
+ } else if (len < 12 + 2 * count) {
+ SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
+ return false;
+ }
+
+ const uint16_t* table = (const uint16_t*) (src + 12);
+ if (1 == count) {
+ // Table entry is the exponent (bias 256).
+ uint16_t value = read_big_endian_short((const uint8_t*) table);
+ gammas[i] = value / 256.0f;
+ SkColorSpacePrintf("gamma %d %g\n", value, *gamma);
+ break;
+ }
+
+ // Print the interpolation table. For now, we ignore this and guess 2.2f.
+ for (uint32_t j = 0; j < count; j++) {
+ SkColorSpacePrintf("curve[%d] %d\n", j,
+ read_big_endian_short((const uint8_t*) &table[j]));
+ }
+
+ gammas[i] = 2.2f;
+ break;
}
+ case kTAG_ParaCurveType:
+ // Guess 2.2f.
+ SkColorSpacePrintf("parametric curve\n");
+ gammas[i] = 2.2f;
+
+ switch(read_big_endian_short(src + 8)) {
+ case 0:
+ tagBytes = 12 + 4;
+ break;
+ case 1:
+ tagBytes = 12 + 12;
+ break;
+ case 2:
+ tagBytes = 12 + 16;
+ break;
+ case 3:
+ tagBytes = 12 + 20;
+ break;
+ case 4:
+ tagBytes = 12 + 28;
+ break;
+ default:
+ SkColorSpacePrintf("Invalid parametric curve type\n");
+ return false;
+ }
+ break;
+ default:
+ SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
+ return false;
+ }
- // Check length again if we have a table.
- if (len < 12 + 2 * count) {
- SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
+ // Adjust src and len if there is another gamma curve to load.
+ if (0 != numGammas) {
+ // Each curve is padded to 4-byte alignment.
+ tagBytes = SkAlign4(tagBytes);
+ if (len < tagBytes) {
return false;
}
- // Print the interpolation table. For now, we ignore this and guess 2.2f.
- for (uint32_t i = 0; i < count; i++) {
- SkColorSpacePrintf("curve[%d] %d\n", i,
- read_big_endian_short((const uint8_t*) &table[i]));
- }
+ src += tagBytes;
+ len -= tagBytes;
+ }
+ }
- *gamma = 2.2f;
+ // If all of the gammas we encounter are 1.0f, indicate that we failed to load gammas.
+ // There is no need to apply a gamma of 1.0f.
+ for (uint32_t i = 0; i < numGammas; i++) {
+ if (1.0f != gammas[i]) {
return true;
}
- case kTAG_ParaCurveType:
- // Guess 2.2f.
- SkColorSpacePrintf("parametric curve\n");
- *gamma = 2.2f;
- return true;
+ }
+
+ return false;
+}
+
+static const uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
+
+bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32_t outputChannels,
+ const uint8_t* src, size_t len) {
+ if (len < 20) {
+ SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
+ return false;
+ }
+
+ SkASSERT(inputChannels <= SkColorLookUpTable::kMaxChannels &&
+ outputChannels <= SkColorLookUpTable::kMaxChannels);
+ colorLUT->fInputChannels = inputChannels;
+ colorLUT->fOutputChannels = outputChannels;
+ uint32_t numEntries = 1;
+ for (uint32_t i = 0; i < inputChannels; i++) {
+ colorLUT->fGridPoints[i] = src[i];
+ numEntries *= src[i];
+ }
+ numEntries *= outputChannels;
+
+ // Space is provided for a maximum of the 16 input channels. Now we determine the precision
+ // of the table values.
+ uint8_t precision = src[16];
+ switch (precision) {
+ case 1: // 8-bit data
+ case 2: // 16-bit data
+ break;
default:
- SkColorSpacePrintf("Unsupported gamma tag type %d\n", type);
+ SkColorSpacePrintf("Color LUT precision must be 8-bit or 16-bit.\n", len);
return false;
}
+
+ if (len < 20 + numEntries * precision) {
+ SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
+ return false;
+ }
+
+ // Movable struct colorLUT has ownership of fTable.
+ colorLUT->fTable = std::unique_ptr<float[]>(new float[numEntries]);
+ const uint8_t* ptr = src + 20;
+ for (uint32_t i = 0; i < numEntries; i++, ptr += precision) {
+ if (1 == precision) {
+ colorLUT->fTable[i] = ((float) ptr[i]) / 255.0f;
+ } else {
+ colorLUT->fTable[i] = ((float) read_big_endian_short(ptr)) / 65535.0f;
+ }
+ }
+
+ return true;
+}
+
+bool load_matrix(SkFloat3x3* toXYZ, SkFloat3* toXYZOffset, const uint8_t* src, size_t len) {
+ if (len < 48) {
+ SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len);
+ return false;
+ }
+
+ toXYZ->fMat[0] = SkFixedToFloat(read_big_endian_int(src));
+ toXYZ->fMat[3] = SkFixedToFloat(read_big_endian_int(src + 4));
+ toXYZ->fMat[6] = SkFixedToFloat(read_big_endian_int(src + 8));
+ toXYZ->fMat[1] = SkFixedToFloat(read_big_endian_int(src + 12));
+ toXYZ->fMat[4] = SkFixedToFloat(read_big_endian_int(src + 16));
+ toXYZ->fMat[7] = SkFixedToFloat(read_big_endian_int(src + 20));
+ toXYZ->fMat[2] = SkFixedToFloat(read_big_endian_int(src + 24));
+ toXYZ->fMat[5] = SkFixedToFloat(read_big_endian_int(src + 28));
+ toXYZ->fMat[8] = SkFixedToFloat(read_big_endian_int(src + 32));
+ toXYZOffset->fVec[0] = SkFixedToFloat(read_big_endian_int(src + 36));
+ toXYZOffset->fVec[1] = SkFixedToFloat(read_big_endian_int(src + 40));
+ toXYZOffset->fVec[2] = SkFixedToFloat(read_big_endian_int(src + 44));
+ return true;
+}
+
+bool load_a2b0(SkColorLookUpTable* colorLUT, SkFloat3* gamma, SkFloat3x3* toXYZ,
+ SkFloat3* toXYZOffset, const uint8_t* src, size_t len) {
+ if (len < 32) {
+ SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
+ return false;
+ }
+
+ uint32_t type = read_big_endian_uint(src);
+ if (kTAG_AtoBType != type) {
+ // FIXME (msarett): Need to support lut8Type and lut16Type.
+ SkColorSpacePrintf("Unsupported A to B tag type.\n");
+ return false;
+ }
+
+ // Read the number of channels. The four bytes that we skipped are reserved and
+ // must be zero.
+ uint8_t inputChannels = src[8];
+ uint8_t outputChannels = src[9];
+ if (0 == inputChannels || inputChannels > SkColorLookUpTable::kMaxChannels ||
+ 0 < outputChannels || outputChannels > SkColorLookUpTable::kMaxChannels) {
+ // 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");
+ return false;
+ }
+
+ // Read the offsets of each element in the A to B tag. With the exception of A curves and
+ // B curves (which we do not yet support), we will handle these elements in the order in
+ // which they should be applied (rather than the order in which they occur in the tag).
+ // If the offset is non-zero it indicates that the element is present.
+ uint32_t offsetToACurves = read_big_endian_int(src + 28);
+ uint32_t offsetToBCurves = read_big_endian_int(src + 12);
+ if ((0 != offsetToACurves) || (0 != offsetToBCurves)) {
+ // FIXME (msarett): Handle A and B curves.
+ // Note that the A curve is technically required in order to have a color LUT.
+ // However, all the A curves I have seen so far have are just placeholders that
+ // don't actually transform the data.
+ SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n");
+ }
+
+ uint32_t offsetToColorLUT = read_big_endian_int(src + 24);
+ if (0 != offsetToColorLUT && offsetToColorLUT < len) {
+ if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offsetToColorLUT,
+ len - offsetToColorLUT)) {
+ SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n");
+ }
+ }
+
+ uint32_t offsetToMCurves = read_big_endian_int(src + 20);
+ if (0 != offsetToMCurves && offsetToMCurves < len) {
+ if (!load_gammas(gamma->fVec, outputChannels, src + offsetToMCurves, len - offsetToMCurves))
+ {
+ SkColorSpacePrintf("Failed to read M curves from A to B tag.\n");
+ }
+ }
+
+ uint32_t offsetToMatrix = read_big_endian_int(src + 16);
+ if (0 != offsetToMatrix && offsetToMatrix < len) {
+ if (!load_matrix(toXYZ, toXYZOffset, src + offsetToMatrix, len - offsetToMatrix)) {
+ SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
+ }
+ }
+
+ return true;
}
sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) {
@@ -452,38 +656,61 @@ sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) {
}
}
- // Load our XYZ and gamma matrices.
- SkFloat3x3 toXYZ;
- SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }};
switch (header.fInputColorSpace) {
case kRGB_ColorSpace: {
+ // 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);
- if (!r || !g || !b) {
- return_null("Need rgb tags for XYZ space");
+ if (r && g && b) {
+ SkFloat3x3 toXYZ;
+ if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r->fLength) ||
+ !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g->fLength) ||
+ !load_xyz(&toXYZ.fMat[6], b->addr((const uint8_t*) base), b->fLength))
+ {
+ return_null("Need valid rgb tags for XYZ space");
+ }
+
+ // It is not uncommon to see missing or empty gamma tags. This indicates
+ // that we should use unit gamma.
+ SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }};
+ 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 (!r ||
+ !load_gammas(&gamma.fVec[0], 1, r->addr((const uint8_t*) base), r->fLength))
+ {
+ SkColorSpacePrintf("Failed to read R gamma tag.\n");
+ }
+ if (!g ||
+ !load_gammas(&gamma.fVec[1], 1, g->addr((const uint8_t*) base), g->fLength))
+ {
+ SkColorSpacePrintf("Failed to read G gamma tag.\n");
+ }
+ if (!b ||
+ !load_gammas(&gamma.fVec[2], 1, b->addr((const uint8_t*) base), b->fLength))
+ {
+ SkColorSpacePrintf("Failed to read B gamma tag.\n");
+ }
+ return SkColorSpace::NewRGB(toXYZ, gamma);
}
- if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r->fLength) ||
- !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g->fLength) ||
- !load_xyz(&toXYZ.fMat[6], b->addr((const uint8_t*) base), b->fLength))
- {
- return_null("Need valid rgb tags for XYZ space");
+ // Recognize color profile specified by A2B0 tag.
+ const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
+ if (a2b0) {
+ SkColorLookUpTable colorLUT;
+ SkFloat3 gamma;
+ SkFloat3x3 toXYZ;
+ SkFloat3 toXYZOffset;
+ if (!load_a2b0(&colorLUT, &gamma, &toXYZ, &toXYZOffset,
+ a2b0->addr((const uint8_t*) base), a2b0->fLength)) {
+ return_null("Failed to parse A2B0 tag");
+ }
+
+ return sk_sp<SkColorSpace>(new SkColorSpace(std::move(colorLUT), gamma, toXYZ,
+ toXYZOffset));
}
- 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 (!r || !load_gamma(&gamma.fVec[0], r->addr((const uint8_t*) base), r->fLength)) {
- SkColorSpacePrintf("Failed to read R gamma tag.\n");
- }
- if (!g || !load_gamma(&gamma.fVec[1], g->addr((const uint8_t*) base), g->fLength)) {
- SkColorSpacePrintf("Failed to read G gamma tag.\n");
- }
- if (!b || !load_gamma(&gamma.fVec[2], b->addr((const uint8_t*) base), b->fLength)) {
- SkColorSpacePrintf("Failed to read B gamma tag.\n");
- }
- return SkColorSpace::NewRGB(toXYZ, gamma);
}
default:
break;
« no previous file with comments | « src/core/SkColorSpace.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698