diff options
author | 2017-02-24 09:49:14 -0500 | |
---|---|---|
committer | 2017-02-24 15:26:40 +0000 | |
commit | a109e3926ed2fbbb41471a140b64297ea8554357 (patch) | |
tree | 8831d5f9c84fbd23ec8f23d9c158e25b534c55bd /tools/viewer | |
parent | e127523b4af6f415dd9c1dc730a264781fbd1d5b (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.cpp | 195 | ||||
-rw-r--r-- | tools/viewer/Viewer.h | 6 |
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; |