diff options
-rw-r--r-- | dm/DM.cpp | 12 | ||||
-rw-r--r-- | dm/DMSrcSink.cpp | 94 | ||||
-rw-r--r-- | dm/DMSrcSink.h | 11 | ||||
-rw-r--r-- | resources/monitor_profiles/HP_ZR30w.icc | bin | 0 -> 1856 bytes | |||
-rw-r--r-- | src/core/SkColorSpace.cpp | 14 | ||||
-rw-r--r-- | src/core/SkColorSpace_Base.h | 4 |
6 files changed, 116 insertions, 19 deletions
@@ -15,6 +15,7 @@ #include "SkChecksum.h" #include "SkCodec.h" #include "SkColorPriv.h" +#include "SkColorSpace.h" #include "SkCommonFlags.h" #include "SkCommonFlagsConfig.h" #include "SkData.h" @@ -723,9 +724,18 @@ static bool gather_srcs() { return false; } + // Load the dstSpace. This particular dst is fairly similar to Adobe RGB. + SkAutoTUnref<SkData> data(SkData::NewFromFileName( + GetResourcePath("monitor_profiles/HP_ZR30w.icc").c_str())); + sk_sp<SkColorSpace> dstSpace = SkColorSpace::NewICC(data->data(), data->size()); + SkASSERT(dstSpace); + for (auto colorImage : colorImages) { - ColorCodecSrc* src = new ColorCodecSrc(colorImage, ColorCodecSrc::kBaseline_Mode); + ColorCodecSrc* src = new ColorCodecSrc(colorImage, ColorCodecSrc::kBaseline_Mode, nullptr); push_src("image", "color_codec_baseline", src); + + src = new ColorCodecSrc(colorImage, ColorCodecSrc::kDst_HPZR30w_Mode, dstSpace); + push_src("image", "color_codec_HPZR30w", src); } return true; diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index f27acf229a..f8c9bc0b58 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -9,6 +9,8 @@ #include "SkAndroidCodec.h" #include "SkCodec.h" #include "SkCodecImageGenerator.h" +#include "SkColorSpace.h" +#include "SkColorSpace_Base.h" #include "SkCommonFlags.h" #include "SkData.h" #include "SkDocument.h" @@ -825,9 +827,10 @@ Name ImageGenSrc::name() const { /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ -ColorCodecSrc::ColorCodecSrc(Path path, Mode mode) +ColorCodecSrc::ColorCodecSrc(Path path, Mode mode, sk_sp<SkColorSpace> dstSpace) : fPath(path) , fMode(mode) + , fDstSpace(dstSpace) {} bool ColorCodecSrc::veto(SinkFlags flags) const { @@ -835,6 +838,17 @@ bool ColorCodecSrc::veto(SinkFlags flags) const { return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect; } +static uint8_t clampFloatToByte(float v) { + v = v * 255.0f; + if (v > 255.0f) { + return 255; + } else if (v < 0.0f) { + return 0; + } else { + return (uint8_t) (v + 0.5f); + } +} + Error ColorCodecSrc::draw(SkCanvas* canvas) const { if (kRGB_565_SkColorType == canvas->imageInfo().colorType()) { return Error::Nonfatal("No need to test color correction to 565 backend."); @@ -850,24 +864,84 @@ Error ColorCodecSrc::draw(SkCanvas* canvas) const { return SkStringPrintf("Couldn't create codec for %s.", fPath.c_str()); } - SkImageInfo decodeInfo = codec->getInfo().makeColorType(kN32_SkColorType); + SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); SkBitmap bitmap; - if (!bitmap.tryAllocPixels(decodeInfo)) { + if (!bitmap.tryAllocPixels(info)) { return SkStringPrintf("Image(%s) is too large (%d x %d)", fPath.c_str(), - decodeInfo.width(), decodeInfo.height()); + info.width(), info.height()); + } + + SkImageInfo decodeInfo = info; + if (kBaseline_Mode != fMode) { + decodeInfo = decodeInfo.makeColorType(kRGBA_8888_SkColorType); + } + + SkCodec::Result r = codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes()); + if (SkCodec::kSuccess != r) { + return SkStringPrintf("Couldn't getPixels %s. Error code %d", fPath.c_str(), r); } switch (fMode) { case kBaseline_Mode: - switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes())) { - case SkCodec::kSuccess: - break; - default: - // Everything else is considered a failure. - return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str()); + canvas->drawBitmap(bitmap, 0, 0); + break; + case kDst_HPZR30w_Mode: { + sk_sp<SkColorSpace> srcSpace = sk_ref_sp(codec->getColorSpace()); + if (!srcSpace) { + return SkStringPrintf("Cannot test color correction without a src profile."); + } else if (!as_CSB(srcSpace)->gammas()->isValues()) { + // FIXME (msarett): + // The conversion here doesn't cover 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 gamma conversion."); } + + // Build a matrix to transform to dst gamut. + // srcToDst = inverse(dstToXYZ) * srcToXYZ + const SkMatrix44& srcToXYZ = srcSpace->xyz(); + const SkMatrix44& dstToXYZ = fDstSpace->xyz(); + SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); + dstToXYZ.invert(&srcToDst); + srcToDst.postConcat(srcToXYZ); + + for (int y = 0; y < info.height(); y++) { + for (int x = 0; x < info.width(); x++) { + // Extract floats. + uint32_t* pixelPtr = (uint32_t*) bitmap.getAddr(x, y); + float src[3]; + src[0] = ((*pixelPtr >> 0) & 0xFF) / 255.0f; + src[1] = ((*pixelPtr >> 8) & 0xFF) / 255.0f; + src[2] = ((*pixelPtr >> 16) & 0xFF) / 255.0f; + + // Convert to linear. + src[0] = pow(src[0], as_CSB(srcSpace)->gammas()->fRed.fValue); + src[1] = pow(src[1], as_CSB(srcSpace)->gammas()->fGreen.fValue); + src[2] = pow(src[2], as_CSB(srcSpace)->gammas()->fBlue.fValue); + + // Convert to dst gamut. + float dst[3]; + dst[0] = src[0]*srcToDst.getFloat(0, 0) + src[1]*srcToDst.getFloat(1, 0) + + src[2]*srcToDst.getFloat(2, 0) + srcToDst.getFloat(3, 0); + dst[1] = src[0]*srcToDst.getFloat(0, 1) + src[1]*srcToDst.getFloat(1, 1) + + src[2]*srcToDst.getFloat(2, 1) + srcToDst.getFloat(3, 1); + dst[2] = src[0]*srcToDst.getFloat(0, 2) + src[1]*srcToDst.getFloat(1, 2) + + src[2]*srcToDst.getFloat(2, 2) + srcToDst.getFloat(3, 2); + + // Convert to dst gamma. + dst[0] = pow(dst[0], 1.0f / as_CSB(fDstSpace)->gammas()->fRed.fValue); + dst[1] = pow(dst[1], 1.0f / as_CSB(fDstSpace)->gammas()->fGreen.fValue); + dst[2] = pow(dst[2], 1.0f / as_CSB(fDstSpace)->gammas()->fBlue.fValue); + + *pixelPtr = SkPackARGB32NoCheck(((*pixelPtr >> 24) & 0xFF), + clampFloatToByte(dst[0]), + clampFloatToByte(dst[1]), + clampFloatToByte(dst[2])); + } + } + canvas->drawBitmap(bitmap, 0, 0); break; + } default: SkASSERT(false); return "Invalid fMode"; diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h index b0f67dbb29..84096bf0fe 100644 --- a/dm/DMSrcSink.h +++ b/dm/DMSrcSink.h @@ -204,9 +204,17 @@ public: enum Mode { // Mimic legacy behavior and apply no color correction. kBaseline_Mode, + + // Color correct images into a specific dst color space. If you happen to have this + // monitor, you're in luck! The unmarked outputs of this test should display + // correctly on this monitor in the Chrome browser. If not, it's useful to know + // that this monitor has a profile that is fairly similar to Adobe RGB. + // TODO (msarett): Should we add a new test with a new monitor and verify that outputs + // look identical on two different dsts? + kDst_HPZR30w_Mode, }; - ColorCodecSrc(Path, Mode); + ColorCodecSrc(Path, Mode, sk_sp<SkColorSpace>); Error draw(SkCanvas*) const override; SkISize size() const override; @@ -215,6 +223,7 @@ public: private: Path fPath; Mode fMode; + sk_sp<SkColorSpace> fDstSpace; }; class SKPSrc : public Src { diff --git a/resources/monitor_profiles/HP_ZR30w.icc b/resources/monitor_profiles/HP_ZR30w.icc Binary files differnew file mode 100644 index 0000000000..b3f860714c --- /dev/null +++ b/resources/monitor_profiles/HP_ZR30w.icc diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp index e2f983f7b8..8f67a7f958 100644 --- a/src/core/SkColorSpace.cpp +++ b/src/core/SkColorSpace.cpp @@ -623,19 +623,19 @@ bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) { array[ 0] = SkFixedToFloat(read_big_endian_int(src)); array[ 1] = SkFixedToFloat(read_big_endian_int(src + 4)); array[ 2] = SkFixedToFloat(read_big_endian_int(src + 8)); - array[ 3] = 0; + array[ 3] = SkFixedToFloat(read_big_endian_int(src + 36)); // translate R array[ 4] = SkFixedToFloat(read_big_endian_int(src + 12)); array[ 5] = SkFixedToFloat(read_big_endian_int(src + 16)); array[ 6] = SkFixedToFloat(read_big_endian_int(src + 20)); - array[ 7] = 0; + array[ 7] = SkFixedToFloat(read_big_endian_int(src + 40)); // translate G array[ 8] = SkFixedToFloat(read_big_endian_int(src + 24)); array[ 9] = SkFixedToFloat(read_big_endian_int(src + 28)); array[10] = SkFixedToFloat(read_big_endian_int(src + 32)); - array[11] = 0; - array[12] = SkFixedToFloat(read_big_endian_int(src + 36)); // translate R - array[13] = SkFixedToFloat(read_big_endian_int(src + 40)); // translate G - array[14] = SkFixedToFloat(read_big_endian_int(src + 44)); - array[15] = 1; + array[11] = SkFixedToFloat(read_big_endian_int(src + 44)); // translate B + array[12] = 0.0f; + array[13] = 0.0f; + array[14] = 0.0f; + array[15] = 1.0f; toXYZ->setColMajorf(array); return true; } diff --git a/src/core/SkColorSpace_Base.h b/src/core/SkColorSpace_Base.h index dbfe7338cd..63588c42ae 100644 --- a/src/core/SkColorSpace_Base.h +++ b/src/core/SkColorSpace_Base.h @@ -136,4 +136,8 @@ static inline SkColorSpace_Base* as_CSB(SkColorSpace* colorSpace) { return static_cast<SkColorSpace_Base*>(colorSpace); } +static inline SkColorSpace_Base* as_CSB(const sk_sp<SkColorSpace>& colorSpace) { + return static_cast<SkColorSpace_Base*>(colorSpace.get()); +} + #endif |