diff options
author | msarett <msarett@google.com> | 2016-07-22 14:07:23 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-07-22 14:07:23 -0700 |
commit | 554479527c669733ea0ef6c1dedd2c9d1773d784 (patch) | |
tree | b8f86d2ee14793d94403118b2194669ef962f54c /src | |
parent | 86a6c6d8bdb1055ee2d0689eeaa4a814402e69dd (diff) |
Convert XYZ values from PNGs to D50
Previously, we were storing whatever values we got out of the file
and pretending they were D50. Brian has demonstrated that this
doesn't really work.
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2180483003
Review-Url: https://codereview.chromium.org/2180483003
Diffstat (limited to 'src')
-rw-r--r-- | src/codec/SkPngCodec.cpp | 99 |
1 files changed, 80 insertions, 19 deletions
diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp index 32308b2630..ee72af58ad 100644 --- a/src/codec/SkPngCodec.cpp +++ b/src/codec/SkPngCodec.cpp @@ -13,6 +13,7 @@ #include "SkMath.h" #include "SkOpts.h" #include "SkPngCodec.h" +#include "SkPoint3.h" #include "SkSize.h" #include "SkStream.h" #include "SkSwizzler.h" @@ -178,6 +179,62 @@ static constexpr float gSRGB_toXYZD50[] { 0.1430f, 0.0606f, 0.7139f, // * B }; +static bool convert_to_D50(SkMatrix44* toXYZD50, float toXYZ[9], float whitePoint[2]) { + float wX = whitePoint[0]; + float wY = whitePoint[1]; + if (wX < 0.0f || wY < 0.0f || (wX + wY > 1.0f)) { + return false; + } + + // Calculate the XYZ illuminant. Call this the src illuminant. + float wZ = 1.0f - wX - wY; + float scale = 1.0f / wY; + // TODO (msarett): + // What are common src illuminants? I'm guessing we will almost always see D65. Should + // we go ahead and save a precomputed D65->D50 Bradford matrix? Should we exit early if + // if the src illuminant is D50? + SkVector3 srcXYZ = SkVector3::Make(wX * scale, 1.0f, wZ * scale); + + // The D50 illuminant. + SkVector3 dstXYZ = SkVector3::Make(0.96422f, 1.0f, 0.82521f); + + // Calculate the chromatic adaptation matrix. We will use the Bradford method, thus + // the matrices below. The Bradford method is used by Adobe and is widely considered + // to be the best. + // http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + SkMatrix mA, mAInv; + mA.setAll(0.8951f, 0.2664f, -0.1614f, -0.7502f, 1.7135f, 0.0367f, 0.0389f, -0.0685f, 1.0296f); + mAInv.setAll(0.9869929f, -0.1470543f, 0.1599627f, 0.4323053f, 0.5183603f, 0.0492912f, + -0.0085287f, 0.0400428f, 0.9684867f); + + // Map illuminant into cone response domain. + SkVector3 srcCone; + srcCone.fX = mA[0] * srcXYZ.fX + mA[1] * srcXYZ.fY + mA[2] * srcXYZ.fZ; + srcCone.fY = mA[3] * srcXYZ.fX + mA[4] * srcXYZ.fY + mA[5] * srcXYZ.fZ; + srcCone.fZ = mA[6] * srcXYZ.fX + mA[7] * srcXYZ.fY + mA[8] * srcXYZ.fZ; + SkVector3 dstCone; + dstCone.fX = mA[0] * dstXYZ.fX + mA[1] * dstXYZ.fY + mA[2] * dstXYZ.fZ; + dstCone.fY = mA[3] * dstXYZ.fX + mA[4] * dstXYZ.fY + mA[5] * dstXYZ.fZ; + dstCone.fZ = mA[6] * dstXYZ.fX + mA[7] * dstXYZ.fY + mA[8] * dstXYZ.fZ; + + SkMatrix DXToD50; + DXToD50.setIdentity(); + DXToD50[0] = dstCone.fX / srcCone.fX; + DXToD50[4] = dstCone.fY / srcCone.fY; + DXToD50[8] = dstCone.fZ / srcCone.fZ; + DXToD50.postConcat(mAInv); + DXToD50.preConcat(mA); + + SkMatrix toXYZ3x3; + toXYZ3x3.setAll(toXYZ[0], toXYZ[3], toXYZ[6], toXYZ[1], toXYZ[4], toXYZ[7], toXYZ[2], toXYZ[5], + toXYZ[8]); + toXYZ3x3.postConcat(DXToD50); + + toXYZD50->set3x3(toXYZ3x3[0], toXYZ3x3[1], toXYZ3x3[2], toXYZ3x3[3], toXYZ3x3[4], toXYZ3x3[5], + toXYZ3x3[6], toXYZ3x3[7], toXYZ3x3[8]); + return true; +} + // Returns a colorSpace object that represents any color space information in // the encoded data. If the encoded data contains no color space, this will // return NULL. @@ -213,24 +270,28 @@ sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) { } // Next, check for chromaticities. - png_fixed_point XYZ[9]; - float toXYZD50[9]; + png_fixed_point toXYZFixed[9]; + float toXYZ[9]; + png_fixed_point whitePointFixed[2]; + float whitePoint[2]; png_fixed_point gamma; float gammas[3]; - 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])) { - - // FIXME (msarett): Here we are treating XYZ values as D50 even though the color - // temperature is unspecified. I suspect that this assumption - // is most often ok, but we could also calculate the color - // temperature (D value) and then convert the XYZ to D50. Maybe - // we should add a new constructor to SkColorSpace that accepts - // XYZ with D-Unkown? + if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &toXYZFixed[0], &toXYZFixed[1], &toXYZFixed[2], + &toXYZFixed[3], &toXYZFixed[4], &toXYZFixed[5], &toXYZFixed[6], + &toXYZFixed[7], &toXYZFixed[8]) && + png_get_cHRM_fixed(png_ptr, info_ptr, &whitePointFixed[0], &whitePointFixed[1], nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr)) + { for (int i = 0; i < 9; i++) { - toXYZD50[i] = png_fixed_point_to_float(XYZ[i]); + toXYZ[i] = png_fixed_point_to_float(toXYZFixed[i]); + } + whitePoint[0] = png_fixed_point_to_float(whitePointFixed[0]); + whitePoint[1] = png_fixed_point_to_float(whitePointFixed[1]); + + SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); + if (!convert_to_D50(&toXYZD50, toXYZ, whitePoint)) { + toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50); } - SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); - mat.set3x3RowMajorf(toXYZD50); if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { float value = png_inverted_fixed_point_to_float(gamma); @@ -238,12 +299,12 @@ sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) { gammas[1] = value; gammas[2] = value; - return SkColorSpace_Base::NewRGB(gammas, mat); + return SkColorSpace_Base::NewRGB(gammas, toXYZD50); } // Default to sRGB gamma if the image has color space information, // but does not specify gamma. - return SkColorSpace::NewRGB(SkColorSpace::kSRGB_GammaNamed, mat); + return SkColorSpace::NewRGB(SkColorSpace::kSRGB_GammaNamed, toXYZD50); } // Last, check for gamma. @@ -256,10 +317,10 @@ sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) { gammas[2] = value; // Since there is no cHRM, we will guess sRGB gamut. - SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); - mat.set3x3RowMajorf(gSRGB_toXYZD50); + SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); + toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50); - return SkColorSpace_Base::NewRGB(gammas, mat); + return SkColorSpace_Base::NewRGB(gammas, toXYZD50); } #endif // LIBPNG >= 1.6 |