aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar raftias <raftias@google.com>2016-12-16 17:24:30 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2016-12-16 23:03:01 +0000
commita97a60c8ecaa5c59334e404750d6299f7b08015d (patch)
tree63ef2da66cc3f1b71663196e70082b5e78e95e5b
parentab2d73b06fe6c518be1d399a79c9cc39db21abb6 (diff)
Added gamma visualizations to colorspaceinfo tool
Visualizations for gamma curves were added. Tool now outputs at the end following 9 ='s a list of all output images. BUG=skia: Change-Id: Id934c4c8cceec68291527554c2c951be08593ef5 Reviewed-on: https://skia-review.googlesource.com/6085 Commit-Queue: Robert Aftias <raftias@google.com> Reviewed-by: Matt Sarett <msarett@google.com>
-rw-r--r--src/core/SkColorLookUpTable.h5
-rw-r--r--src/core/SkColorSpace_Base.h5
-rw-r--r--tools/colorspaceinfo.cpp239
3 files changed, 202 insertions, 47 deletions
diff --git a/src/core/SkColorLookUpTable.h b/src/core/SkColorLookUpTable.h
index e563b2d4a2..0cde767a47 100644
--- a/src/core/SkColorLookUpTable.h
+++ b/src/core/SkColorLookUpTable.h
@@ -36,6 +36,11 @@ public:
int inputChannels() const { return fInputChannels; }
int outputChannels() const { return kOutputChannels; }
+
+ int gridPoints(int dimension) const {
+ SkASSERT(dimension >= 0 && dimension < inputChannels());
+ return fGridPoints[dimension];
+ }
private:
const float* table() const {
diff --git a/src/core/SkColorSpace_Base.h b/src/core/SkColorSpace_Base.h
index b3247de5c3..af8ec3133f 100644
--- a/src/core/SkColorSpace_Base.h
+++ b/src/core/SkColorSpace_Base.h
@@ -96,6 +96,11 @@ struct SkGammas : SkRefCnt {
return this->data(i).fTable.table(this);
}
+ int tableSize(int i) const {
+ SkASSERT(isTable(i));
+ return this->data(i).fTable.fSize;
+ }
+
const SkColorSpaceTransferFn& params(int i) const {
SkASSERT(isParametric(i));
return this->data(i).params(this);
diff --git a/tools/colorspaceinfo.cpp b/tools/colorspaceinfo.cpp
index f413038d6c..8527b775d1 100644
--- a/tools/colorspaceinfo.cpp
+++ b/tools/colorspaceinfo.cpp
@@ -10,7 +10,9 @@
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkCodec.h"
+#include "SkColorSpace_A2B.h"
#include "SkColorSpace_XYZ.h"
+#include "SkColorSpacePriv.h"
#include "SkCommandLineFlags.h"
#include "SkImageEncoder.h"
#include "SkMatrix44.h"
@@ -19,13 +21,25 @@
#include "sk_tool_utils.h"
DEFINE_string(input, "input.png", "A path to the input image or icc profile.");
-DEFINE_string(output, "output.png", "A path to the output image.");
-DEFINE_bool(sRGB, false, "Draws the sRGB gamut.");
-DEFINE_bool(adobeRGB, false, "Draws the Adobe RGB gamut.");
+DEFINE_string(gamut_output, "gamut_output.png", "A path to the output gamut image.");
+DEFINE_string(gamma_output, "gamma_output.png", "A path to the output gamma image.");
+DEFINE_bool(sRGB_gamut, false, "Draws the sRGB gamut on the gamut visualization.");
+DEFINE_bool(adobeRGB, false, "Draws the Adobe RGB gamut on the gamut visualization.");
+DEFINE_bool(sRGB_gamma, false, "Draws the sRGB gamma on all gamma output images.");
DEFINE_string(uncorrected, "", "A path to reencode the uncorrected input image.");
-static void dump_transfer_fn(SkColorSpace_XYZ* colorSpace) {
- switch (colorSpace->gammaNamed()) {
+static const char* kRGBChannelNames[3] = {
+ "Red ", "Green", "Blue "
+};
+
+static const SkColor kRGBChannelColors[3] = {
+ SkColorSetARGB(164, 255, 32, 32),
+ SkColorSetARGB(164, 32, 255, 32),
+ SkColorSetARGB(164, 32, 32, 255)
+};
+
+static void dump_transfer_fn(SkGammaNamed gammaNamed) {
+ switch (gammaNamed) {
case kSRGB_SkGammaNamed:
SkDebugf("Transfer Function: sRGB\n");
return;
@@ -39,37 +53,134 @@ static void dump_transfer_fn(SkColorSpace_XYZ* colorSpace) {
break;
}
- static const char* kChannels[] = { "Red ", "Green", "Blue ", };
- const SkGammas* gammas = colorSpace->gammas();
- for (int i = 0; i < 3; i++) {
- if (gammas->isNamed(i)) {
- switch (gammas->data(i).fNamed) {
+}
+
+static void dump_transfer_fn(const SkGammas& gammas) {
+ SkASSERT(gammas.channels() == 3);
+ for (int i = 0; i < gammas.channels(); i++) {
+ if (gammas.isNamed(i)) {
+ switch (gammas.data(i).fNamed) {
case kSRGB_SkGammaNamed:
- SkDebugf("%s Transfer Function: sRGB\n", kChannels[i]);
+ SkDebugf("%s Transfer Function: sRGB\n", kRGBChannelNames[i]);
return;
case k2Dot2Curve_SkGammaNamed:
- SkDebugf("%s Transfer Function: Exponent 2.2\n", kChannels[i]);
+ SkDebugf("%s Transfer Function: Exponent 2.2\n", kRGBChannelNames[i]);
return;
case kLinear_SkGammaNamed:
- SkDebugf("%s Transfer Function: Linear\n", kChannels[i]);
+ SkDebugf("%s Transfer Function: Linear\n", kRGBChannelNames[i]);
return;
default:
SkASSERT(false);
continue;
}
- } else if (gammas->isValue(i)) {
- SkDebugf("%s Transfer Function: Exponent %.3f\n", kChannels[i], gammas->data(i).fValue);
- } else if (gammas->isParametric(i)) {
- const SkColorSpaceTransferFn& fn = gammas->data(i).params(gammas);
+ } else if (gammas.isValue(i)) {
+ SkDebugf("%s Transfer Function: Exponent %.3f\n", kRGBChannelNames[i],
+ gammas.data(i).fValue);
+ } else if (gammas.isParametric(i)) {
+ const SkColorSpaceTransferFn& fn = gammas.data(i).params(&gammas);
SkDebugf("%s Transfer Function: Parametric A = %.3f, B = %.3f, C = %.3f, D = %.3f, "
- "E = %.3f, F = %.3f, G = %.3f\n", kChannels[i], fn.fA, fn.fB, fn.fC, fn.fD,
- fn.fE, fn.fF, fn.fG);
+ "E = %.3f, F = %.3f, G = %.3f\n", kRGBChannelNames[i], fn.fA, fn.fB, fn.fC,
+ fn.fD, fn.fE, fn.fF, fn.fG);
+ } else {
+ SkASSERT(gammas.isTable(i));
+ SkDebugf("%s Transfer Function: Table (%d entries)\n", kRGBChannelNames[i],
+ gammas.data(i).fTable.fSize);
+ }
+ }
+}
+
+static inline float parametric(const SkColorSpaceTransferFn& fn, float x) {
+ return x >= fn.fD ? powf(fn.fA*x + fn.fB, fn.fG) + fn.fC
+ : fn.fE*x + fn.fF;
+}
+
+static void draw_transfer_fn(SkCanvas* canvas, SkGammaNamed gammaNamed, const SkGammas* gammas,
+ SkColor color, int col) {
+ SkColorSpaceTransferFn fn[4];
+ struct TableInfo {
+ const float* fTable;
+ int fSize;
+ };
+ TableInfo table[4];
+ bool isTable[4] = {false, false, false, false};
+ const int channels = gammas ? gammas->channels() : 1;
+ SkASSERT(channels <= 4);
+ if (kNonStandard_SkGammaNamed != gammaNamed) {
+ dump_transfer_fn(gammaNamed);
+ for (int i = 0; i < channels; ++i) {
+ named_to_parametric(&fn[i], gammaNamed);
+ }
+ } else {
+ SkASSERT(gammas);
+ dump_transfer_fn(*gammas);
+ for (int i = 0; i < channels; ++i) {
+ if (gammas->isTable(i)) {
+ table[i].fTable = gammas->table(i);
+ table[i].fSize = gammas->data(i).fTable.fSize;
+ isTable[i] = true;
+ } else {
+ switch (gammas->type(i)) {
+ case SkGammas::Type::kNamed_Type:
+ named_to_parametric(&fn[i], gammas->data(i).fNamed);
+ break;
+ case SkGammas::Type::kValue_Type:
+ value_to_parametric(&fn[i], gammas->data(i).fValue);
+ break;
+ case SkGammas::Type::kParam_Type:
+ fn[i] = gammas->params(i);
+ break;
+ default:
+ SkASSERT(false);
+ }
+ }
+ }
+ }
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(color);
+ paint.setStrokeWidth(2.0f);
+ // note: gamma has positive values going up in this image so this origin is
+ // the bottom left and we must subtract y instead of adding.
+ const float gap = 16.0f;
+ const float cellWidth = 500.0f;
+ const float cellHeight = 500.0f;
+ const float gammaWidth = cellWidth - 2 * gap;
+ const float gammaHeight = cellHeight - 2 * gap;
+ // gamma origin point
+ const float ox = gap + cellWidth * col;
+ const float oy = gap + gammaHeight;
+ for (int i = 0; i < channels; ++i) {
+ if (kNonStandard_SkGammaNamed == gammaNamed) {
+ paint.setColor(kRGBChannelColors[i]);
+ } else {
+ paint.setColor(color);
+ }
+ if (isTable[i]) {
+ auto tx = [&table,i](int index) {
+ return index / (table[i].fSize - 1.0f);
+ };
+ for (int ti = 1; ti < table[i].fSize; ++ti) {
+ canvas->drawLine(ox + gammaWidth * tx(ti - 1),
+ oy - gammaHeight * table[i].fTable[ti - 1],
+ ox + gammaWidth * tx(ti),
+ oy - gammaHeight * table[i].fTable[ti],
+ paint);
+ }
} else {
- SkASSERT(gammas->isTable(i));
- SkDebugf("%s Transfer Function: Table (%d entries)\n", kChannels[i],
- gammas->data(i).fTable.fSize);
+ const float step = 0.01f;
+ float yPrev = parametric(fn[i], 0.0f);
+ for (float x = step; x <= 1.0f; x += step) {
+ const float y = parametric(fn[i], x);
+ canvas->drawLine(ox + gammaWidth * (x - step), oy - gammaHeight * yPrev,
+ ox + gammaWidth * x, oy - gammaHeight * y,
+ paint);
+ yPrev = y;
+ }
}
}
+ paint.setColor(0xFF000000);
+ paint.setStrokeWidth(3.0f);
+ canvas->drawRectCoords(ox, oy - gammaHeight, ox + gammaWidth, oy, paint);
}
/**
@@ -150,17 +261,19 @@ static void draw_gamut(SkCanvas* canvas, const SkMatrix44& xyz, const char* name
int main(int argc, char** argv) {
SkCommandLineFlags::SetUsage(
"Usage: colorspaceinfo --input <path to input image or icc profile> "
- "--output <path to output image> "
+ "--gamma_output <path to output gamma image> "
+ "--gamut_output <path to output gamut image>"
"--sRGB <draw canonical sRGB gamut> "
"--adobeRGB <draw canonical Adobe RGB gamut> "
"--uncorrected <path to reencoded, uncorrected input image>\n"
- "Description: Writes a visualization of the color space to the output image ."
+ "Description: Writes visualizations of the color space to the output image(s) ."
"Also, if a path is provided, writes uncorrected bytes to an unmarked "
"png, for comparison with the input image.\n");
SkCommandLineFlags::Parse(argc, argv);
const char* input = FLAGS_input[0];
- const char* output = FLAGS_output[0];
- if (!input || !output) {
+ const char* gamut_output = FLAGS_gamut_output[0];
+ const char* gamma_output = FLAGS_gamma_output[0];
+ if (!input || !gamut_output || !gamma_output) {
SkCommandLineFlags::PrintUsage();
return -1;
}
@@ -172,7 +285,8 @@ int main(int argc, char** argv) {
}
std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
sk_sp<SkColorSpace> colorSpace = nullptr;
- if (codec) {
+ const bool isImage = (codec != nullptr);
+ if (isImage) {
colorSpace = sk_ref_sp(codec->getInfo().colorSpace());
} else {
colorSpace = SkColorSpace::MakeICC(data->bytes(), data->size());
@@ -184,19 +298,23 @@ int main(int argc, char** argv) {
}
// Load a graph of the CIE XYZ color gamut.
- SkBitmap gamut;
- if (!GetResourceAsBitmap("gamut.png", &gamut)) {
+ SkBitmap gamutCanvasBitmap;
+ if (!GetResourceAsBitmap("gamut.png", &gamutCanvasBitmap)) {
SkDebugf("Program failure.\n");
return -1;
}
- SkCanvas canvas(gamut);
+ SkCanvas gamutCanvas(gamutCanvasBitmap);
+
+ SkBitmap gammaCanvasBitmap;
+ gammaCanvasBitmap.allocN32Pixels(500, 500);
+ SkCanvas gammaCanvas(gammaCanvasBitmap);
// Draw the sRGB gamut if requested.
- if (FLAGS_sRGB) {
+ if (FLAGS_sRGB_gamut) {
sk_sp<SkColorSpace> sRGBSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
const SkMatrix44* mat = as_CSB(sRGBSpace)->toXYZD50();
SkASSERT(mat);
- draw_gamut(&canvas, *mat, "sRGB", 0xFFFF9394, false);
+ draw_gamut(&gamutCanvas, *mat, "sRGB", 0xFFFF9394, false);
}
// Draw the Adobe RGB gamut if requested.
@@ -204,34 +322,60 @@ int main(int argc, char** argv) {
sk_sp<SkColorSpace> adobeRGBSpace = SkColorSpace::MakeNamed(SkColorSpace::kAdobeRGB_Named);
const SkMatrix44* mat = as_CSB(adobeRGBSpace)->toXYZD50();
SkASSERT(mat);
- draw_gamut(&canvas, *mat, "Adobe RGB", 0xFF31a9e1, false);
+ draw_gamut(&gamutCanvas, *mat, "Adobe RGB", 0xFF31a9e1, false);
}
+ int gammaCol = 0;
if (SkColorSpace_Base::Type::kXYZ == as_CSB(colorSpace)->type()) {
const SkMatrix44* mat = as_CSB(colorSpace)->toXYZD50();
SkASSERT(mat);
- draw_gamut(&canvas, *mat, input, 0xFF000000, true);
- dump_transfer_fn((SkColorSpace_XYZ*) colorSpace.get());
+ auto xyz = static_cast<SkColorSpace_XYZ*>(colorSpace.get());
+ draw_gamut(&gamutCanvas, *mat, input, 0xFF000000, true);
+ if (FLAGS_sRGB_gamma) {
+ draw_transfer_fn(&gammaCanvas, kSRGB_SkGammaNamed, nullptr, 0xFFFF9394, gammaCol);
+ }
+ draw_transfer_fn(&gammaCanvas, xyz->gammaNamed(), xyz->gammas(), 0xFF000000, gammaCol++);
} else {
SkDebugf("Color space is defined using an A2B tag. It cannot be represented by "
"a transfer function and to D50 matrix.\n");
+ return -1;
}
- // Finally, encode the result to the output file.
- sk_sp<SkData> out = sk_tool_utils::EncodeImageToData(gamut, SkEncodedImageFormat::kPNG, 100);
- if (!out) {
- SkDebugf("Failed to encode gamut output.\n");
+ // marker to tell the web-tool the names of all images output
+ SkDebugf("=========\n");
+ auto saveCanvasBitmap = [](const SkBitmap& bitmap, const char *fname) {
+ // Finally, encode the result to the output file.
+ sk_sp<SkData> out = sk_tool_utils::EncodeImageToData(bitmap, SkEncodedImageFormat::kPNG,
+ 100);
+ if (!out) {
+ SkDebugf("Failed to encode %s output.\n", fname);
+ return false;
+ }
+ SkFILEWStream stream(fname);
+ if (!stream.write(out->data(), out->size())) {
+ SkDebugf("Failed to write %s output.\n", fname);
+ return false;
+ }
+ // record name of canvas
+ SkDebugf("%s\n", fname);
+ return true;
+ };
+
+ // only XYZ images have a gamut visualization since the matrix in A2B is not
+ // a gamut adjustment from RGB->XYZ always (or ever)
+ if (SkColorSpace_Base::Type::kXYZ == as_CSB(colorSpace)->type() &&
+ !saveCanvasBitmap(gamutCanvasBitmap, gamut_output)) {
return -1;
}
- SkFILEWStream stream(output);
- bool result = stream.write(out->data(), out->size());
- if (!result) {
- SkDebugf("Failed to write gamut output.\n");
+ if (gammaCol > 0 && !saveCanvasBitmap(gammaCanvasBitmap, gamma_output)) {
return -1;
}
+ if (isImage) {
+ SkDebugf("%s\n", input);
+ }
// Also, if requested, decode and reencode the uncorrected input image.
- if (!FLAGS_uncorrected.isEmpty() && codec) {
+ if (!FLAGS_uncorrected.isEmpty() && isImage) {
SkBitmap bitmap;
int width = codec->getInfo().width();
int height = codec->getInfo().height();
@@ -242,17 +386,18 @@ int main(int argc, char** argv) {
SkDebugf("Could not decode input image.\n");
return -1;
}
- out = sk_tool_utils::EncodeImageToData(bitmap, SkEncodedImageFormat::kPNG, 100);
+ sk_sp<SkData> out = sk_tool_utils::EncodeImageToData(bitmap, SkEncodedImageFormat::kPNG,
+ 100);
if (!out) {
SkDebugf("Failed to encode uncorrected image.\n");
return -1;
}
SkFILEWStream bitmapStream(FLAGS_uncorrected[0]);
- result = bitmapStream.write(out->data(), out->size());
- if (!result) {
+ if (!bitmapStream.write(out->data(), out->size())) {
SkDebugf("Failed to write uncorrected image output.\n");
return -1;
}
+ SkDebugf("%s\n", FLAGS_uncorrected[0]);
}
return 0;