Introduce SkGammas type to represent ICC gamma curves
BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1928123002 Committed: https://skia.googlesource.com/skia/+/7b2c6dd8c918209cb92e1338905d511c68da3eb2 Review-Url: https://codereview.chromium.org/1928123002
This commit is contained in:
parent
c578b06319
commit
ffc2aea3cb
@ -210,7 +210,7 @@ sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
|
|||||||
png_fixed_point XYZ[9];
|
png_fixed_point XYZ[9];
|
||||||
SkFloat3x3 toXYZD50;
|
SkFloat3x3 toXYZD50;
|
||||||
png_fixed_point gamma;
|
png_fixed_point gamma;
|
||||||
SkFloat3 gammas;
|
SkColorSpace::SkGammas gammas;
|
||||||
if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &XYZ[0], &XYZ[1], &XYZ[2], &XYZ[3], &XYZ[4],
|
if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &XYZ[0], &XYZ[1], &XYZ[2], &XYZ[3], &XYZ[4],
|
||||||
&XYZ[5], &XYZ[6], &XYZ[7], &XYZ[8])) {
|
&XYZ[5], &XYZ[6], &XYZ[7], &XYZ[8])) {
|
||||||
|
|
||||||
@ -225,16 +225,17 @@ sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
|
if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
|
||||||
gammas.fVec[0] = gammas.fVec[1] = gammas.fVec[2] =
|
float value = png_inverted_fixed_point_to_float(gamma);
|
||||||
png_inverted_fixed_point_to_float(gamma);
|
gammas = SkColorSpace::SkGammas(value, value, value);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// If the image does not specify gamma, let's choose linear. Should we default
|
// Default to sRGB (gamma = 2.2f) if the image has color space information,
|
||||||
// to sRGB? Most images are intended to be sRGB (gamma = 2.2f).
|
// but does not specify gamma.
|
||||||
gammas.fVec[0] = gammas.fVec[1] = gammas.fVec[2] = 1.0f;
|
gammas = SkColorSpace::SkGammas(2.2f, 2.2f, 2.2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return SkColorSpace::NewRGB(toXYZD50, gammas);
|
return SkColorSpace::NewRGB(toXYZD50, std::move(gammas));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last, check for gamma.
|
// Last, check for gamma.
|
||||||
@ -247,9 +248,10 @@ sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
|
|||||||
toXYZD50.fMat[0] = toXYZD50.fMat[4] = toXYZD50.fMat[8] = 1.0f;
|
toXYZD50.fMat[0] = toXYZD50.fMat[4] = toXYZD50.fMat[8] = 1.0f;
|
||||||
|
|
||||||
// Set the gammas.
|
// Set the gammas.
|
||||||
gammas.fVec[0] = gammas.fVec[1] = gammas.fVec[2] = png_inverted_fixed_point_to_float(gamma);
|
float value = png_inverted_fixed_point_to_float(gamma);
|
||||||
|
gammas = SkColorSpace::SkGammas(value, value, value);
|
||||||
|
|
||||||
return SkColorSpace::NewRGB(toXYZD50, gammas);
|
return SkColorSpace::NewRGB(toXYZD50, std::move(gammas));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // LIBPNG >= 1.6
|
#endif // LIBPNG >= 1.6
|
||||||
|
@ -23,29 +23,28 @@ void SkFloat3x3::dump() const {
|
|||||||
|
|
||||||
static int32_t gUniqueColorSpaceID;
|
static int32_t gUniqueColorSpaceID;
|
||||||
|
|
||||||
SkColorSpace::SkColorSpace(const SkFloat3& gamma, const SkFloat3x3& toXYZD50, Named named)
|
SkColorSpace::SkColorSpace(SkGammas gammas, const SkFloat3x3& toXYZD50, Named named)
|
||||||
: fGamma(gamma)
|
: fGammas(std::move(gammas))
|
||||||
, fToXYZD50(toXYZD50)
|
, fToXYZD50(toXYZD50)
|
||||||
, fToXYZOffset({{ 0.0f, 0.0f, 0.0f }})
|
, fToXYZOffset({{ 0.0f, 0.0f, 0.0f }})
|
||||||
, fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
|
, fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
|
||||||
, fNamed(named)
|
, fNamed(named)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SkColorSpace::SkColorSpace(SkColorLookUpTable colorLUT, const SkFloat3& gamma,
|
SkColorSpace::SkColorSpace(SkColorLookUpTable colorLUT, SkGammas gammas,
|
||||||
const SkFloat3x3& toXYZD50, const SkFloat3& toXYZOffset)
|
const SkFloat3x3& toXYZD50, const SkFloat3& toXYZOffset)
|
||||||
: fColorLUT(std::move(colorLUT))
|
: fColorLUT(std::move(colorLUT))
|
||||||
, fGamma(gamma)
|
, fGammas(std::move(gammas))
|
||||||
, fToXYZD50(toXYZD50)
|
, fToXYZD50(toXYZD50)
|
||||||
, fToXYZOffset(toXYZOffset)
|
, fToXYZOffset(toXYZOffset)
|
||||||
, fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
|
, fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
|
||||||
, fNamed(kUnknown_Named)
|
, fNamed(kUnknown_Named)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFloat3& gamma) {
|
sk_sp<SkColorSpace> SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, SkGammas gammas) {
|
||||||
return sk_sp<SkColorSpace>(new SkColorSpace(gamma, toXYZD50, kUnknown_Named));
|
return sk_sp<SkColorSpace>(new SkColorSpace(std::move(gammas), toXYZD50, kUnknown_Named));
|
||||||
}
|
}
|
||||||
|
|
||||||
const SkFloat3 gSRGB_gamma {{ 2.2f, 2.2f, 2.2f }};
|
|
||||||
const SkFloat3x3 gSRGB_toXYZD50 {{
|
const SkFloat3x3 gSRGB_toXYZD50 {{
|
||||||
0.4358f, 0.2224f, 0.0139f, // * R
|
0.4358f, 0.2224f, 0.0139f, // * R
|
||||||
0.3853f, 0.7170f, 0.0971f, // * G
|
0.3853f, 0.7170f, 0.0971f, // * G
|
||||||
@ -55,7 +54,8 @@ const SkFloat3x3 gSRGB_toXYZD50 {{
|
|||||||
sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) {
|
sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) {
|
||||||
switch (named) {
|
switch (named) {
|
||||||
case kSRGB_Named:
|
case kSRGB_Named:
|
||||||
return sk_sp<SkColorSpace>(new SkColorSpace(gSRGB_gamma, gSRGB_toXYZD50, kSRGB_Named));
|
return sk_sp<SkColorSpace>(new SkColorSpace(SkGammas(2.2f, 2.2f, 2.2f), gSRGB_toXYZD50,
|
||||||
|
kSRGB_Named));
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -264,9 +264,8 @@ 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_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v');
|
||||||
static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');
|
static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a');
|
||||||
|
|
||||||
// FIXME (msarett):
|
bool SkColorSpace::LoadGammas(SkGammaCurve* gammas, uint32_t numGammas, const uint8_t* src,
|
||||||
// We need to handle the possibility that the gamma curve does not correspond to 2.2f.
|
size_t len) {
|
||||||
static bool load_gammas(float* gammas, uint32_t numGammas, const uint8_t* src, size_t len) {
|
|
||||||
for (uint32_t i = 0; i < numGammas; i++) {
|
for (uint32_t i = 0; i < numGammas; i++) {
|
||||||
if (len < 12) {
|
if (len < 12) {
|
||||||
// FIXME (msarett):
|
// FIXME (msarett):
|
||||||
@ -289,7 +288,7 @@ static bool load_gammas(float* gammas, uint32_t numGammas, const uint8_t* src, s
|
|||||||
// Some tags require a gamma curve, but the author doesn't actually want
|
// 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
|
// to transform the data. In this case, it is common to see a curve with
|
||||||
// a count of 0.
|
// a count of 0.
|
||||||
gammas[i] = 1.0f;
|
gammas[i].fValue = 1.0f;
|
||||||
break;
|
break;
|
||||||
} else if (len < 12 + 2 * count) {
|
} else if (len < 12 + 2 * count) {
|
||||||
SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
|
SkColorSpacePrintf("gamma tag is too small (%d bytes)", len);
|
||||||
@ -298,27 +297,31 @@ static bool load_gammas(float* gammas, uint32_t numGammas, const uint8_t* src, s
|
|||||||
|
|
||||||
const uint16_t* table = (const uint16_t*) (src + 12);
|
const uint16_t* table = (const uint16_t*) (src + 12);
|
||||||
if (1 == count) {
|
if (1 == count) {
|
||||||
// Table entry is the exponent (bias 256).
|
// The table entry is the gamma (with a bias of 256).
|
||||||
uint16_t value = read_big_endian_short((const uint8_t*) table);
|
uint16_t value = read_big_endian_short((const uint8_t*) table);
|
||||||
gammas[i] = value / 256.0f;
|
gammas[i].fValue = value / 256.0f;
|
||||||
SkColorSpacePrintf("gamma %d %g\n", value, *gamma);
|
SkColorSpacePrintf("gamma %d %g\n", value, *gamma);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the interpolation table. For now, we ignore this and guess 2.2f.
|
// Fill in the interpolation table.
|
||||||
|
// FIXME (msarett):
|
||||||
|
// We should recognize commonly occurring tables and just set gamma to 2.2f.
|
||||||
|
gammas[i].fTableSize = count;
|
||||||
|
gammas[i].fTable = std::unique_ptr<float[]>(new float[count]);
|
||||||
for (uint32_t j = 0; j < count; j++) {
|
for (uint32_t j = 0; j < count; j++) {
|
||||||
SkColorSpacePrintf("curve[%d] %d\n", j,
|
gammas[i].fTable[j] =
|
||||||
read_big_endian_short((const uint8_t*) &table[j]));
|
(read_big_endian_short((const uint8_t*) &table[j])) / 65535.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
gammas[i] = 2.2f;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kTAG_ParaCurveType:
|
case kTAG_ParaCurveType:
|
||||||
// Guess 2.2f.
|
// Guess 2.2f.
|
||||||
|
// FIXME (msarett): Handle parametric curves.
|
||||||
SkColorSpacePrintf("parametric curve\n");
|
SkColorSpacePrintf("parametric curve\n");
|
||||||
gammas[i] = 2.2f;
|
gammas[i].fValue = 2.2f;
|
||||||
|
|
||||||
|
// Determine the size of the parametric curve tag.
|
||||||
switch(read_big_endian_short(src + 8)) {
|
switch(read_big_endian_short(src + 8)) {
|
||||||
case 0:
|
case 0:
|
||||||
tagBytes = 12 + 4;
|
tagBytes = 12 + 4;
|
||||||
@ -358,28 +361,19 @@ static bool load_gammas(float* gammas, uint32_t numGammas, const uint8_t* src, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If all of the gammas we encounter are 1.0f, indicate that we failed to load gammas.
|
return true;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
|
static const uint32_t kTAG_AtoBType = SkSetFourByteTag('m', 'A', 'B', ' ');
|
||||||
|
|
||||||
bool load_color_lut(SkColorLookUpTable* colorLUT, uint32_t inputChannels, uint32_t outputChannels,
|
bool SkColorSpace::LoadColorLUT(SkColorLookUpTable* colorLUT, uint32_t inputChannels,
|
||||||
const uint8_t* src, size_t len) {
|
uint32_t outputChannels, const uint8_t* src, size_t len) {
|
||||||
if (len < 20) {
|
if (len < 20) {
|
||||||
SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
|
SkColorSpacePrintf("Color LUT tag is too small (%d bytes).", len);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SkASSERT(inputChannels <= SkColorLookUpTable::kMaxChannels &&
|
SkASSERT(inputChannels <= SkColorLookUpTable::kMaxChannels && 3 == outputChannels);
|
||||||
outputChannels <= SkColorLookUpTable::kMaxChannels);
|
|
||||||
colorLUT->fInputChannels = inputChannels;
|
colorLUT->fInputChannels = inputChannels;
|
||||||
colorLUT->fOutputChannels = outputChannels;
|
colorLUT->fOutputChannels = outputChannels;
|
||||||
uint32_t numEntries = 1;
|
uint32_t numEntries = 1;
|
||||||
@ -441,8 +435,8 @@ bool load_matrix(SkFloat3x3* toXYZ, SkFloat3* toXYZOffset, const uint8_t* src, s
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load_a2b0(SkColorLookUpTable* colorLUT, SkFloat3* gamma, SkFloat3x3* toXYZ,
|
bool SkColorSpace::LoadA2B0(SkColorLookUpTable* colorLUT, SkGammas* gammas, SkFloat3x3* toXYZ,
|
||||||
SkFloat3* toXYZOffset, const uint8_t* src, size_t len) {
|
SkFloat3* toXYZOffset, const uint8_t* src, size_t len) {
|
||||||
if (len < 32) {
|
if (len < 32) {
|
||||||
SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
|
SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
|
||||||
return false;
|
return false;
|
||||||
@ -460,7 +454,7 @@ bool load_a2b0(SkColorLookUpTable* colorLUT, SkFloat3* gamma, SkFloat3x3* toXYZ,
|
|||||||
uint8_t inputChannels = src[8];
|
uint8_t inputChannels = src[8];
|
||||||
uint8_t outputChannels = src[9];
|
uint8_t outputChannels = src[9];
|
||||||
if (0 == inputChannels || inputChannels > SkColorLookUpTable::kMaxChannels ||
|
if (0 == inputChannels || inputChannels > SkColorLookUpTable::kMaxChannels ||
|
||||||
0 < outputChannels || outputChannels > SkColorLookUpTable::kMaxChannels) {
|
3 != outputChannels) {
|
||||||
// The color LUT assumes that there are at most 16 input channels. For RGB
|
// The color LUT assumes that there are at most 16 input channels. For RGB
|
||||||
// profiles, output channels should be 3.
|
// profiles, output channels should be 3.
|
||||||
SkColorSpacePrintf("Too many input or output channels in A to B tag.\n");
|
SkColorSpacePrintf("Too many input or output channels in A to B tag.\n");
|
||||||
@ -483,16 +477,16 @@ bool load_a2b0(SkColorLookUpTable* colorLUT, SkFloat3* gamma, SkFloat3x3* toXYZ,
|
|||||||
|
|
||||||
uint32_t offsetToColorLUT = read_big_endian_int(src + 24);
|
uint32_t offsetToColorLUT = read_big_endian_int(src + 24);
|
||||||
if (0 != offsetToColorLUT && offsetToColorLUT < len) {
|
if (0 != offsetToColorLUT && offsetToColorLUT < len) {
|
||||||
if (!load_color_lut(colorLUT, inputChannels, outputChannels, src + offsetToColorLUT,
|
if (!SkColorSpace::LoadColorLUT(colorLUT, inputChannels, outputChannels,
|
||||||
len - offsetToColorLUT)) {
|
src + offsetToColorLUT, len - offsetToColorLUT)) {
|
||||||
SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n");
|
SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t offsetToMCurves = read_big_endian_int(src + 20);
|
uint32_t offsetToMCurves = read_big_endian_int(src + 20);
|
||||||
if (0 != offsetToMCurves && offsetToMCurves < len) {
|
if (0 != offsetToMCurves && offsetToMCurves < len) {
|
||||||
if (!load_gammas(gamma->fVec, outputChannels, src + offsetToMCurves, len - offsetToMCurves))
|
if (!SkColorSpace::LoadGammas(&gammas->fRed, outputChannels, src + offsetToMCurves,
|
||||||
{
|
len - offsetToMCurves)) {
|
||||||
SkColorSpacePrintf("Failed to read M curves from A to B tag.\n");
|
SkColorSpacePrintf("Failed to read M curves from A to B tag.\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -567,42 +561,39 @@ sk_sp<SkColorSpace> SkColorSpace::NewICC(const void* base, size_t len) {
|
|||||||
|
|
||||||
// It is not uncommon to see missing or empty gamma tags. This indicates
|
// It is not uncommon to see missing or empty gamma tags. This indicates
|
||||||
// that we should use unit gamma.
|
// that we should use unit gamma.
|
||||||
SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }};
|
SkGammas gammas;
|
||||||
r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
|
r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
|
||||||
g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
|
g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
|
||||||
b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
|
b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
|
||||||
if (!r ||
|
if (!r || !SkColorSpace::LoadGammas(&gammas.fRed, 1,
|
||||||
!load_gammas(&gamma.fVec[0], 1, r->addr((const uint8_t*) base), r->fLength))
|
r->addr((const uint8_t*) base), r->fLength)) {
|
||||||
{
|
|
||||||
SkColorSpacePrintf("Failed to read R gamma tag.\n");
|
SkColorSpacePrintf("Failed to read R gamma tag.\n");
|
||||||
}
|
}
|
||||||
if (!g ||
|
if (!g || !SkColorSpace::LoadGammas(&gammas.fGreen, 1,
|
||||||
!load_gammas(&gamma.fVec[1], 1, g->addr((const uint8_t*) base), g->fLength))
|
g->addr((const uint8_t*) base), g->fLength)) {
|
||||||
{
|
|
||||||
SkColorSpacePrintf("Failed to read G gamma tag.\n");
|
SkColorSpacePrintf("Failed to read G gamma tag.\n");
|
||||||
}
|
}
|
||||||
if (!b ||
|
if (!b || !SkColorSpace::LoadGammas(&gammas.fBlue, 1,
|
||||||
!load_gammas(&gamma.fVec[2], 1, b->addr((const uint8_t*) base), b->fLength))
|
b->addr((const uint8_t*) base), b->fLength)) {
|
||||||
{
|
|
||||||
SkColorSpacePrintf("Failed to read B gamma tag.\n");
|
SkColorSpacePrintf("Failed to read B gamma tag.\n");
|
||||||
}
|
}
|
||||||
return SkColorSpace::NewRGB(toXYZ, gamma);
|
return SkColorSpace::NewRGB(toXYZ, std::move(gammas));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recognize color profile specified by A2B0 tag.
|
// Recognize color profile specified by A2B0 tag.
|
||||||
const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
|
const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
|
||||||
if (a2b0) {
|
if (a2b0) {
|
||||||
SkColorLookUpTable colorLUT;
|
SkColorLookUpTable colorLUT;
|
||||||
SkFloat3 gamma;
|
SkGammas gammas;
|
||||||
SkFloat3x3 toXYZ;
|
SkFloat3x3 toXYZ;
|
||||||
SkFloat3 toXYZOffset;
|
SkFloat3 toXYZOffset;
|
||||||
if (!load_a2b0(&colorLUT, &gamma, &toXYZ, &toXYZOffset,
|
if (!SkColorSpace::LoadA2B0(&colorLUT, &gammas, &toXYZ, &toXYZOffset,
|
||||||
a2b0->addr((const uint8_t*) base), a2b0->fLength)) {
|
a2b0->addr((const uint8_t*) base), a2b0->fLength)) {
|
||||||
return_null("Failed to parse A2B0 tag");
|
return_null("Failed to parse A2B0 tag");
|
||||||
}
|
}
|
||||||
|
|
||||||
return sk_sp<SkColorSpace>(new SkColorSpace(std::move(colorLUT), gamma, toXYZ,
|
return sk_sp<SkColorSpace>(new SkColorSpace(std::move(colorLUT), std::move(gammas),
|
||||||
toXYZOffset));
|
toXYZ, toXYZOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,57 +35,121 @@ struct SkFloat3x3 {
|
|||||||
void dump() const;
|
void dump() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SkColorLookUpTable {
|
|
||||||
static const uint8_t kMaxChannels = 16;
|
|
||||||
|
|
||||||
uint8_t fInputChannels;
|
|
||||||
uint8_t fOutputChannels;
|
|
||||||
uint8_t fGridPoints[kMaxChannels];
|
|
||||||
std::unique_ptr<float[]> fTable;
|
|
||||||
|
|
||||||
SkColorLookUpTable() {
|
|
||||||
memset(this, 0, sizeof(struct SkColorLookUpTable));
|
|
||||||
}
|
|
||||||
|
|
||||||
SkColorLookUpTable(SkColorLookUpTable&& that)
|
|
||||||
: fInputChannels(that.fInputChannels)
|
|
||||||
, fOutputChannels(that.fOutputChannels)
|
|
||||||
, fTable(std::move(that.fTable))
|
|
||||||
{
|
|
||||||
memcpy(fGridPoints, that.fGridPoints, kMaxChannels);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SkColorSpace : public SkRefCnt {
|
class SkColorSpace : public SkRefCnt {
|
||||||
|
private:
|
||||||
|
struct SkGammaCurve {
|
||||||
|
bool isValue() const {
|
||||||
|
bool result = (0.0f != fValue);
|
||||||
|
SkASSERT(!result || (0 == fTableSize));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTable() const {
|
||||||
|
bool result = (0 != fTableSize);
|
||||||
|
SkASSERT(!result || (0.0f == fValue));
|
||||||
|
SkASSERT(!result || fTable);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isParametric() const { return false; }
|
||||||
|
|
||||||
|
// We have three different ways to represent gamma.
|
||||||
|
// (1) A single value:
|
||||||
|
float fValue;
|
||||||
|
|
||||||
|
// (2) A lookup table:
|
||||||
|
uint32_t fTableSize;
|
||||||
|
std::unique_ptr<float[]> fTable;
|
||||||
|
|
||||||
|
// (3) Parameters for a curve:
|
||||||
|
// FIXME (msarett): Handle parametric curves.
|
||||||
|
|
||||||
|
SkGammaCurve() {
|
||||||
|
memset(this, 0, sizeof(struct SkGammaCurve));
|
||||||
|
}
|
||||||
|
|
||||||
|
SkGammaCurve(float value)
|
||||||
|
: fValue(value)
|
||||||
|
, fTableSize(0)
|
||||||
|
, fTable(nullptr)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SkColorLookUpTable {
|
||||||
|
static const uint8_t kMaxChannels = 16;
|
||||||
|
|
||||||
|
uint8_t fInputChannels;
|
||||||
|
uint8_t fOutputChannels;
|
||||||
|
uint8_t fGridPoints[kMaxChannels];
|
||||||
|
std::unique_ptr<float[]> fTable;
|
||||||
|
|
||||||
|
SkColorLookUpTable() {
|
||||||
|
memset(this, 0, sizeof(struct SkColorLookUpTable));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Named {
|
enum Named {
|
||||||
kUnknown_Named,
|
kUnknown_Named,
|
||||||
kSRGB_Named,
|
kSRGB_Named,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SkGammas {
|
||||||
|
public:
|
||||||
|
SkGammas(float red, float green, float blue)
|
||||||
|
: fRed(red)
|
||||||
|
, fGreen(green)
|
||||||
|
, fBlue(blue)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SkGammas() {}
|
||||||
|
|
||||||
|
SkDEBUGCODE(float red() const { return fRed.fValue; })
|
||||||
|
SkDEBUGCODE(float green() const { return fGreen.fValue; })
|
||||||
|
SkDEBUGCODE(float blue() const { return fBlue.fValue; })
|
||||||
|
|
||||||
|
private:
|
||||||
|
SkGammaCurve fRed;
|
||||||
|
SkGammaCurve fGreen;
|
||||||
|
SkGammaCurve fBlue;
|
||||||
|
|
||||||
|
friend class SkColorSpace;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a colorspace instance, given a 3x3 transform from linear_RGB to D50_XYZ
|
* Return a colorspace instance, given a 3x3 transform from linear_RGB to D50_XYZ
|
||||||
* and the src-gamma, return a ColorSpace
|
* and the src-gamma, return a ColorSpace
|
||||||
*/
|
*/
|
||||||
static sk_sp<SkColorSpace> NewRGB(const SkFloat3x3& toXYZD50, const SkFloat3& gamma);
|
static sk_sp<SkColorSpace> NewRGB(const SkFloat3x3& toXYZD50, SkGammas gammas);
|
||||||
|
|
||||||
static sk_sp<SkColorSpace> NewNamed(Named);
|
static sk_sp<SkColorSpace> NewNamed(Named);
|
||||||
static sk_sp<SkColorSpace> NewICC(const void*, size_t);
|
static sk_sp<SkColorSpace> NewICC(const void*, size_t);
|
||||||
|
|
||||||
SkFloat3 gamma() const { return fGamma; }
|
const SkGammas& gammas() const { return fGammas; }
|
||||||
SkFloat3x3 xyz() const { return fToXYZD50; }
|
SkFloat3x3 xyz() const { return fToXYZD50; }
|
||||||
SkFloat3 xyzOffset() const { return fToXYZOffset; }
|
SkFloat3 xyzOffset() const { return fToXYZOffset; }
|
||||||
Named named() const { return fNamed; }
|
Named named() const { return fNamed; }
|
||||||
uint32_t uniqueID() const { return fUniqueID; }
|
uint32_t uniqueID() const { return fUniqueID; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SkColorSpace(const SkFloat3& gamma, const SkFloat3x3& toXYZ, Named);
|
|
||||||
|
|
||||||
SkColorSpace(SkColorLookUpTable colorLUT, const SkFloat3& gamma, const SkFloat3x3& toXYZ,
|
static bool LoadGammas(SkGammaCurve* gammas, uint32_t num, const uint8_t* src, size_t len);
|
||||||
const SkFloat3& toXYZOffset);
|
|
||||||
|
|
||||||
|
static bool LoadColorLUT(SkColorLookUpTable* colorLUT, uint32_t inputChannels,
|
||||||
|
uint32_t outputChannels, const uint8_t* src, size_t len);
|
||||||
|
|
||||||
|
|
||||||
|
static bool LoadA2B0(SkColorLookUpTable* colorLUT, SkGammas* gammas, SkFloat3x3* toXYZ,
|
||||||
|
SkFloat3* toXYZOffset, const uint8_t* src, size_t len);
|
||||||
|
|
||||||
|
SkColorSpace(SkGammas gammas, const SkFloat3x3& toXYZ, Named);
|
||||||
|
|
||||||
|
SkColorSpace(SkColorLookUpTable colorLUT, SkGammas gammas,
|
||||||
|
const SkFloat3x3& toXYZ, const SkFloat3& toXYZOffset);
|
||||||
|
|
||||||
const SkColorLookUpTable fColorLUT;
|
const SkColorLookUpTable fColorLUT;
|
||||||
const SkFloat3 fGamma;
|
const SkGammas fGammas;
|
||||||
const SkFloat3x3 fToXYZD50;
|
const SkFloat3x3 fToXYZD50;
|
||||||
const SkFloat3 fToXYZOffset;
|
const SkFloat3 fToXYZOffset;
|
||||||
|
|
||||||
|
@ -35,12 +35,14 @@ DEF_TEST(ColorSpaceParsePngICCProfile, r) {
|
|||||||
SkColorSpace* colorSpace = codec->getColorSpace();
|
SkColorSpace* colorSpace = codec->getColorSpace();
|
||||||
REPORTER_ASSERT(r, nullptr != colorSpace);
|
REPORTER_ASSERT(r, nullptr != colorSpace);
|
||||||
|
|
||||||
// No need to use almost equal here. The color profile that we have extracted
|
// The color profile that we have extracted has represents gamma with a lookup table.
|
||||||
// actually has a table of gammas. And our current implementation guesses 2.2f.
|
// So we expect the gamma value to be zero.
|
||||||
SkFloat3 gammas = colorSpace->gamma();
|
#ifdef SK_DEBUG
|
||||||
REPORTER_ASSERT(r, 2.2f == gammas.fVec[0]);
|
const SkColorSpace::SkGammas& gammas = colorSpace->gammas();
|
||||||
REPORTER_ASSERT(r, 2.2f == gammas.fVec[1]);
|
REPORTER_ASSERT(r, 0.0f == gammas.red());
|
||||||
REPORTER_ASSERT(r, 2.2f == gammas.fVec[2]);
|
REPORTER_ASSERT(r, 0.0f == gammas.green());
|
||||||
|
REPORTER_ASSERT(r, 0.0f == gammas.blue());
|
||||||
|
#endif
|
||||||
|
|
||||||
// These nine values were extracted from the color profile in isolation (before
|
// These nine values were extracted from the color profile in isolation (before
|
||||||
// we embedded it in the png). Here we check that we still extract the same values.
|
// we embedded it in the png). Here we check that we still extract the same values.
|
||||||
@ -75,10 +77,12 @@ DEF_TEST(ColorSpaceParseJpegICCProfile, r) {
|
|||||||
|
|
||||||
// It's important to use almost equal here. This profile sets gamma as
|
// It's important to use almost equal here. This profile sets gamma as
|
||||||
// 563 / 256, which actually comes out to about 2.19922.
|
// 563 / 256, which actually comes out to about 2.19922.
|
||||||
SkFloat3 gammas = colorSpace->gamma();
|
#ifdef SK_DEBUG
|
||||||
REPORTER_ASSERT(r, almost_equal(2.2f, gammas.fVec[0]));
|
const SkColorSpace::SkGammas& gammas = colorSpace->gammas();
|
||||||
REPORTER_ASSERT(r, almost_equal(2.2f, gammas.fVec[1]));
|
REPORTER_ASSERT(r, almost_equal(2.2f, gammas.red()));
|
||||||
REPORTER_ASSERT(r, almost_equal(2.2f, gammas.fVec[2]));
|
REPORTER_ASSERT(r, almost_equal(2.2f, gammas.green()));
|
||||||
|
REPORTER_ASSERT(r, almost_equal(2.2f, gammas.blue()));
|
||||||
|
#endif
|
||||||
|
|
||||||
// These nine values were extracted from the color profile. Until we know any
|
// These nine values were extracted from the color profile. Until we know any
|
||||||
// better, we'll assume these are the right values and test that we continue
|
// better, we'll assume these are the right values and test that we continue
|
||||||
|
Loading…
Reference in New Issue
Block a user