aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar msarett <msarett@google.com>2016-05-23 10:21:17 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-05-23 10:21:17 -0700
commit888dc16684da2158702639be2023f998b7c27f66 (patch)
tree13a3917acd2279b80fe540c4525cbeaeb88a6222
parent3d7356a833ec30b33944b52dcd429f7b641d9559 (diff)
Test color correction in DM
This will allow me to test and visualize some assumptions on parsing and applying color profiles. Also, it should help me find and fix bugs. This is certainly not an optimized implementation, and, as far as I know, it doesn't take any shortcuts to improve performance. We'll probably want to do both of these once we know where it fits in the pipeline. Right now this test is only run on an arbitrary set of ~100 images from the top 10k skps. I'll continue to add more "interesting" images and probably tweak the code as necessary. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1995233003 Review-Url: https://codereview.chromium.org/1995233003
-rw-r--r--dm/DM.cpp12
-rw-r--r--dm/DMSrcSink.cpp94
-rw-r--r--dm/DMSrcSink.h11
-rw-r--r--resources/monitor_profiles/HP_ZR30w.iccbin0 -> 1856 bytes
-rw-r--r--src/core/SkColorSpace.cpp14
-rw-r--r--src/core/SkColorSpace_Base.h4
6 files changed, 116 insertions, 19 deletions
diff --git a/dm/DM.cpp b/dm/DM.cpp
index d35787e1c7..f29726e04c 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -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
new file mode 100644
index 0000000000..b3f860714c
--- /dev/null
+++ b/resources/monitor_profiles/HP_ZR30w.icc
Binary files differ
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