aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/viewer
diff options
context:
space:
mode:
authorGravatar Brian Osman <brianosman@google.com>2017-02-24 09:49:14 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-02-24 15:26:40 +0000
commita109e3926ed2fbbb41471a140b64297ea8554357 (patch)
tree8831d5f9c84fbd23ec8f23d9c158e25b534c55bd /tools/viewer
parente127523b4af6f415dd9c1dc730a264781fbd1d5b (diff)
ImGui color space controls
Adds radio buttons for switching among legacy, sRGB and F16. Also adds a list of primaries you can pick from, as well as a gamut diagram showing the primaries. The primaries can be dragged around to alter the working space. BUG=skia: Change-Id: Ibd8c67dfe085594c0d7462f0efe4d79d73999919 Reviewed-on: https://skia-review.googlesource.com/8311 Reviewed-by: Matt Sarett <msarett@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
Diffstat (limited to 'tools/viewer')
-rw-r--r--tools/viewer/Viewer.cpp195
-rw-r--r--tools/viewer/Viewer.h6
2 files changed, 182 insertions, 19 deletions
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index c7517cc500..535ece8b9e 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -15,6 +15,7 @@
#include "SkATrace.h"
#include "SkCanvas.h"
+#include "SkColorSpace_Base.h"
#include "SkCommandLineFlags.h"
#include "SkCommonFlagsPathRenderer.h"
#include "SkDashPathEffect.h"
@@ -161,6 +162,44 @@ static sk_app::Window::BackendType get_backend_type(const char* str) {
}
}
+static SkColorSpacePrimaries gSrgbPrimaries = {
+ 0.64f, 0.33f,
+ 0.30f, 0.60f,
+ 0.15f, 0.06f,
+ 0.3127f, 0.3290f };
+
+static SkColorSpacePrimaries gAdobePrimaries = {
+ 0.64f, 0.33f,
+ 0.21f, 0.71f,
+ 0.15f, 0.06f,
+ 0.3127f, 0.3290f };
+
+static SkColorSpacePrimaries gP3Primaries = {
+ 0.680f, 0.320f,
+ 0.265f, 0.690f,
+ 0.150f, 0.060f,
+ 0.3127f, 0.3290f };
+
+static SkColorSpacePrimaries gRec2020Primaries = {
+ 0.708f, 0.292f,
+ 0.170f, 0.797f,
+ 0.131f, 0.046f,
+ 0.3127f, 0.3290f };
+
+struct NamedPrimaries {
+ const char* fName;
+ SkColorSpacePrimaries* fPrimaries;
+} gNamedPrimaries[] = {
+ { "sRGB", &gSrgbPrimaries },
+ { "AdobeRGB", &gAdobePrimaries },
+ { "P3", &gP3Primaries },
+ { "Rec. 2020", &gRec2020Primaries },
+};
+
+static bool primaries_equal(const SkColorSpacePrimaries& a, const SkColorSpacePrimaries& b) {
+ return memcmp(&a, &b, sizeof(SkColorSpacePrimaries)) == 0;
+}
+
const char* kName = "name";
const char* kValue = "value";
const char* kOptions = "options";
@@ -184,7 +223,8 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
, fLastImage(nullptr)
, fBackendType(sk_app::Window::kNativeGL_BackendType)
, fColorType(kN32_SkColorType)
- , fColorSpace(nullptr)
+ , fColorManaged(false)
+ , fColorSpacePrimaries(gSrgbPrimaries)
, fZoomCenterX(0.0f)
, fZoomCenterY(0.0f)
, fZoomLevel(0.0f)
@@ -243,15 +283,15 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
fWindow->inval();
});
fCommands.addCommand('c', "Modes", "Cycle color mode", [this]() {
- if (!fColorSpace) {
- // Legacy -> sRGB
- this->setColorMode(kN32_SkColorType, SkColorSpace::MakeSRGB());
+ if (!fColorManaged) {
+ // Legacy -> Color correct 8888
+ this->setColorMode(kN32_SkColorType, true);
} else if (kN32_SkColorType == fColorType) {
- // sRGB -> F16 sRGB
- this->setColorMode(kRGBA_F16_SkColorType, SkColorSpace::MakeSRGBLinear());
+ // Color correct 8888 -> Color correct F16
+ this->setColorMode(kRGBA_F16_SkColorType, true);
} else {
- // F16 sRGB -> Legacy
- this->setColorMode(kN32_SkColorType, nullptr);
+ // Color correct F16 -> Legacy
+ this->setColorMode(kN32_SkColorType, false);
}
});
fCommands.addCommand(Window::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
@@ -373,6 +413,15 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
fImGuiFontPaint.setFilterQuality(kLow_SkFilterQuality);
io.Fonts->TexID = &fImGuiFontPaint;
+ auto gamutImage = GetResourceAsImage("gamut.png");
+ if (gamutImage) {
+ auto gamutShader = gamutImage->makeShader(SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode);
+ fImGuiGamutPaint.setShader(gamutShader);
+ }
+ fImGuiGamutPaint.setColor(SK_ColorWHITE);
+ fImGuiGamutPaint.setFilterQuality(kLow_SkFilterQuality);
+
fWindow->show();
}
@@ -467,9 +516,17 @@ void Viewer::updateTitle() {
title.appendf(" %s", sk_tool_utils::colortype_name(fColorType));
- // TODO: Find a short string to describe the gamut of the color space?
- if (fColorSpace) {
+ if (fColorManaged) {
title.append(" ColorManaged");
+
+ int curPrimaries = -1;
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
+ if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
+ curPrimaries = i;
+ break;
+ }
+ }
+ title.appendf(" %s", curPrimaries >= 0 ? gNamedPrimaries[curPrimaries].fName : "Custom");
}
title.append(kBackendTypeStrings[fBackendType]);
@@ -579,15 +636,15 @@ SkMatrix Viewer::computeMatrix() {
return m;
}
-void Viewer::setColorMode(SkColorType colorType, sk_sp<SkColorSpace> colorSpace) {
+void Viewer::setColorMode(SkColorType colorType, bool colorManaged) {
fColorType = colorType;
- fColorSpace = std::move(colorSpace);
+ fColorManaged = colorManaged;
// When we're in color managed mode, we tag our window surface as sRGB. If we've switched into
// or out of legacy mode, we need to update our window configuration.
DisplayParams params = fWindow->getDisplayParams();
- if (SkToBool(fColorSpace) != SkToBool(params.fColorSpace)) {
- params.fColorSpace = fColorSpace ? SkColorSpace::MakeSRGB() : nullptr;
+ if (fColorManaged != SkToBool(params.fColorSpace)) {
+ params.fColorSpace = fColorManaged ? SkColorSpace::MakeSRGB() : nullptr;
fWindow->setDisplayParams(params);
}
@@ -611,9 +668,17 @@ void Viewer::drawSlide(SkCanvas* canvas) {
// If we're in F16, or the gamut isn't sRGB, or we're zooming, we need to render offscreen
sk_sp<SkSurface> offscreenSurface = nullptr;
if (kRGBA_F16_SkColorType == fColorType || fShowZoomWindow ||
- (fColorSpace && fColorSpace != SkColorSpace::MakeSRGB())) {
+ (fColorManaged && !primaries_equal(fColorSpacePrimaries, gSrgbPrimaries))) {
+ sk_sp<SkColorSpace> cs = nullptr;
+ if (fColorManaged) {
+ SkColorSpace::RenderTargetGamma transferFn = (kRGBA_F16_SkColorType == fColorType)
+ ? SkColorSpace::kLinear_RenderTargetGamma : SkColorSpace::kSRGB_RenderTargetGamma;
+ SkMatrix44 toXYZ;
+ SkAssertResult(fColorSpacePrimaries.toXYZD50(&toXYZ));
+ cs = SkColorSpace::MakeRGB(transferFn, toXYZ);
+ }
SkImageInfo info = SkImageInfo::Make(fWindow->width(), fWindow->height(), fColorType,
- kPremul_SkAlphaType, fColorSpace);
+ kPremul_SkAlphaType, std::move(cs));
offscreenSurface = canvas->makeSurface(info);
slideCanvas = offscreenSurface->getCanvas();
}
@@ -769,14 +834,79 @@ void Viewer::drawStats(SkCanvas* canvas) {
canvas->restore();
}
+static ImVec2 ImGui_DragPrimary(const char* label, float* x, float* y,
+ const ImVec2& pos, const ImVec2& size) {
+ // Transform primaries ([0, 0] - [0.8, 0.9]) to screen coords (including Y-flip)
+ ImVec2 center(pos.x + (*x / 0.8f) * size.x, pos.y + (1.0f - (*y / 0.9f)) * size.y);
+
+ // Invisible 10x10 button
+ ImGui::SetCursorScreenPos(ImVec2(center.x - 5, center.y - 5));
+ ImGui::InvisibleButton(label, ImVec2(10, 10));
+
+ if (ImGui::IsItemActive() && ImGui::IsMouseDragging()) {
+ ImGuiIO& io = ImGui::GetIO();
+ // Normalized mouse position, relative to our gamut box
+ ImVec2 mousePosXY((io.MousePos.x - pos.x) / size.x, (io.MousePos.y - pos.y) / size.y);
+ // Clamp to edge of box, convert back to primary scale
+ *x = SkTPin(mousePosXY.x, 0.0f, 1.0f) * 0.8f;
+ *y = SkTPin(1 - mousePosXY.y, 0.0f, 1.0f) * 0.9f;
+ }
+
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("x: %.3f\ny: %.3f", *x, *y);
+ }
+
+ // Return screen coordinates for the caller. We could just return center here, but we'd have
+ // one frame of lag during drag.
+ return ImVec2(pos.x + (*x / 0.8f) * size.x, pos.y + (1.0f - (*y / 0.9f)) * size.y);
+}
+
+static void ImGui_Primaries(SkColorSpacePrimaries* primaries, SkPaint* gamutPaint) {
+ ImDrawList* drawList = ImGui::GetWindowDrawList();
+
+ // The gamut image covers a (0.8 x 0.9) shaped region, so fit our image/canvas to the available
+ // width, and scale the height to maintain aspect ratio.
+ float canvasWidth = SkTMax(ImGui::GetContentRegionAvailWidth(), 50.0f);
+ ImVec2 size = ImVec2(canvasWidth, canvasWidth * (0.9f / 0.8f));
+ ImVec2 pos = ImGui::GetCursorScreenPos();
+
+ // Background image. Only draw a subset of the image, to avoid the regions less than zero.
+ // Simplifes re-mapping math, clipping behavior, and increases resolution in the useful area.
+ // Magic numbers are pixel locations of the origin and upper-right corner.
+ drawList->AddImage(gamutPaint, pos, ImVec2(pos.x + size.x, pos.y + size.y),
+ ImVec2(242, 61), ImVec2(1897, 1922));
+ ImVec2 endPos = ImGui::GetCursorPos();
+
+ // Primary markers
+ ImVec2 r = ImGui_DragPrimary("R", &primaries->fRX, &primaries->fRY, pos, size);
+ ImVec2 g = ImGui_DragPrimary("G", &primaries->fGX, &primaries->fGY, pos, size);
+ ImVec2 b = ImGui_DragPrimary("B", &primaries->fBX, &primaries->fBY, pos, size);
+ ImVec2 w = ImGui_DragPrimary("W", &primaries->fWX, &primaries->fWY, pos, size);
+
+ // Gamut triangle
+ drawList->AddCircle(r, 5.0f, 0xFF000040);
+ drawList->AddCircle(g, 5.0f, 0xFF004000);
+ drawList->AddCircle(b, 5.0f, 0xFF400000);
+ drawList->AddCircle(w, 5.0f, 0xFFFFFFFF);
+ drawList->AddTriangle(r, g, b, 0xFFFFFFFF);
+
+ // Re-position cursor immediate after the diagram for subsequent controls
+ ImGui::SetCursorPos(endPos);
+}
+
void Viewer::drawImGui(SkCanvas* canvas) {
// Support drawing the ImGui demo window. Superfluous, but gives a good idea of what's possible
if (fShowImGuiTestWindow) {
ImGui::ShowTestWindow(&fShowImGuiTestWindow);
}
+ SkPaint gamutImagePaint;
if (fShowImGuiDebugWindow) {
- if (ImGui::Begin("Debug", &fShowImGuiDebugWindow)) {
+ // We have some dynamic content that sizes to fill available size. If the scroll bar isn't
+ // always visible, we can end up in a layout feedback loop.
+ ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiSetCond_FirstUseEver);
+ if (ImGui::Begin("Tools", &fShowImGuiDebugWindow,
+ ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
if (ImGui::CollapsingHeader("Slide")) {
static ImGuiTextFilter filter;
filter.Draw();
@@ -796,6 +926,37 @@ void Viewer::drawImGui(SkCanvas* canvas) {
fCurrentSlide = previousSlide;
}
}
+
+ if (ImGui::CollapsingHeader("Color Mode")) {
+ int oldMode = fColorManaged ? (kRGBA_F16_SkColorType == fColorType) ? 2 : 1 : 0;
+ int newMode = oldMode;
+ ImGui::RadioButton("Legacy", &newMode, 0);
+ ImGui::RadioButton("Color Managed 8888", &newMode, 1);
+ ImGui::RadioButton("Color Managed F16", &newMode, 2);
+ if (newMode != oldMode) {
+ this->setColorMode(2 == newMode ? kRGBA_F16_SkColorType : kN32_SkColorType,
+ 0 != newMode);
+ }
+
+ // Pick from common gamuts:
+ int primariesIdx = 4; // Default: Custom
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
+ if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
+ primariesIdx = i;
+ break;
+ }
+ }
+
+ if (ImGui::Combo("Primaries", &primariesIdx,
+ "sRGB\0AdobeRGB\0P3\0Rec. 2020\0Custom\0\0")) {
+ if (primariesIdx >= 0 && primariesIdx <= 3) {
+ fColorSpacePrimaries = *gNamedPrimaries[primariesIdx].fPrimaries;
+ }
+ }
+
+ // Allow direct editing of gamut
+ ImGui_Primaries(&fColorSpacePrimaries, &fImGuiGamutPaint);
+ }
}
ImGui::End();
diff --git a/tools/viewer/Viewer.h b/tools/viewer/Viewer.h
index f4847e5c79..b17bd250db 100644
--- a/tools/viewer/Viewer.h
+++ b/tools/viewer/Viewer.h
@@ -33,7 +33,7 @@ public:
private:
void initSlides();
void updateTitle();
- void setColorMode(SkColorType, sk_sp<SkColorSpace>);
+ void setColorMode(SkColorType, bool colorManaged);
void setStartupSlide();
void setupCurrentSlide(int previousSlide);
void listNames();
@@ -64,6 +64,7 @@ private:
bool fRefresh; // whether to continuously refresh for measuring render time
SkPaint fImGuiFontPaint;
+ SkPaint fImGuiGamutPaint;
bool fShowImGuiDebugWindow;
bool fShowImGuiTestWindow;
@@ -74,7 +75,8 @@ private:
// Color properties for slide rendering
SkColorType fColorType;
- sk_sp<SkColorSpace> fColorSpace;
+ bool fColorManaged;
+ SkColorSpacePrimaries fColorSpacePrimaries;
// transform data
SkScalar fZoomCenterX;