Add ImGui::DragCanvas, to simplify several custom widgets
Bug: skia:
Change-Id: Ic44ab00af1117a6bbc17df7f5005f4db707cb5c1
Reviewed-on: https://skia-review.googlesource.com/c/191002
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/tools/viewer/ImGuiLayer.h b/tools/viewer/ImGuiLayer.h
index 4b10fc7..79a085e 100644
--- a/tools/viewer/ImGuiLayer.h
+++ b/tools/viewer/ImGuiLayer.h
@@ -14,6 +14,104 @@
#include "imgui.h"
+namespace ImGui {
+
+// Helper object for drawing in a widget region, with draggable points
+struct DragCanvas {
+ DragCanvas(const void* id, SkPoint tl = { 0.0f, 0.0f }, SkPoint br = { 1.0f, 1.0f },
+ float aspect = -1.0f)
+ : fID(0), fDragging(false) {
+ ImGui::PushID(id);
+ fDrawList = ImGui::GetWindowDrawList();
+
+ // Logical size
+ SkScalar w = SkTAbs(br.fX - tl.fX),
+ h = SkTAbs(br.fY - tl.fY);
+
+ // Determine aspect ratio automatically by default
+ if (aspect < 0) {
+ aspect = h / w;
+ }
+
+ float availWidth = SkTMax(ImGui::GetContentRegionAvailWidth(), 1.0f);
+ fPos = ImGui::GetCursorScreenPos();
+ fSize = ImVec2(availWidth, availWidth * aspect);
+
+ SkPoint local[4] = {
+ { tl.fX, tl.fY },
+ { br.fX, tl.fY },
+ { tl.fX, br.fY },
+ { br.fX, br.fY },
+ };
+ SkPoint screen[4] = {
+ { fPos.x , fPos.y },
+ { fPos.x + fSize.x, fPos.y },
+ { fPos.x , fPos.y + fSize.y },
+ { fPos.x + fSize.x, fPos.y + fSize.y },
+ };
+ fLocalToScreen.setPolyToPoly(local, screen, 4);
+ fScreenToLocal.setPolyToPoly(screen, local, 4);
+ }
+
+ ~DragCanvas() {
+ ImGui::SetCursorScreenPos(ImVec2(fPos.x, fPos.y + fSize.y));
+ ImGui::Spacing();
+ ImGui::PopID();
+ }
+
+ void fillColor(ImU32 color) {
+ fDrawList->AddRectFilled(fPos, ImVec2(fPos.x + fSize.x, fPos.y + fSize.y), color);
+ }
+
+ void dragPoint(SkPoint* p, bool tooltip = false, ImU32 color = 0xFFFFFFFF) {
+ // Transform points from logical coordinates to screen coordinates
+ SkPoint center = fLocalToScreen.mapXY(p->fX, p->fY);
+
+ // Invisible 10x10 button
+ ImGui::PushID(fID++);
+ ImGui::SetCursorScreenPos(ImVec2(center.fX - 5, center.fY - 5));
+ ImGui::InvisibleButton("", ImVec2(10, 10));
+
+ if (ImGui::IsItemActive() && ImGui::IsMouseDragging()) {
+ // Update screen position to track mouse, clamped to our area
+ ImGuiIO& io = ImGui::GetIO();
+ center.set(SkTPin(io.MousePos.x, fPos.x, fPos.x + fSize.x),
+ SkTPin(io.MousePos.y, fPos.y, fPos.y + fSize.y));
+
+ // Update local coordinates for the caller
+ *p = fScreenToLocal.mapXY(center.fX, center.fY);
+ fDragging = true;
+ }
+
+ if (tooltip && ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("x: %.3f\ny: %.3f", p->fX, p->fY);
+ }
+
+ ImGui::PopID();
+
+ fScreenPoints.push_back(ImVec2(center.fX, center.fY));
+ fDrawList->AddCircle(fScreenPoints.back(), 5.0f, color);
+ }
+
+ ImDrawList* fDrawList;
+
+ // Location and dimensions (in screen coordinates)
+ ImVec2 fPos;
+ ImVec2 fSize;
+
+ // Screen coordinates of points (for additional user drawing)
+ SkSTArray<4, ImVec2, true> fScreenPoints;
+
+ // To simplify dragPoint
+ SkMatrix fLocalToScreen;
+ SkMatrix fScreenToLocal;
+
+ int fID;
+ bool fDragging;
+};
+
+}
+
class ImGuiLayer : public sk_app::Window::Layer {
public:
ImGuiLayer();
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index 945b07f..5ed5d73 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -1355,143 +1355,45 @@
return true;
}
-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();
+ // The gamut image covers a (0.8 x 0.9) shaped region
+ ImGui::DragCanvas dc(primaries, { 0.0f, 0.9f }, { 0.8f, 0.0f });
// 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));
+ dc.fDrawList->AddImage(gamutPaint, dc.fPos,
+ ImVec2(dc.fPos.x + dc.fSize.x, dc.fPos.y + dc.fSize.y),
+ ImVec2(242, 61), ImVec2(1897, 1922));
- // 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::SetCursorScreenPos(ImVec2(pos.x, pos.y + size.y));
-}
-
-static ImVec2 ImGui_DragPoint(const char* label, SkPoint* p,
- const ImVec2& pos, const ImVec2& size, bool* dragging) {
- // Transform points ([0, 0] - [1.0, 1.0]) to screen coords
- ImVec2 center(pos.x + p->fX * size.x, pos.y + p->fY * 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
- p->fX = SkTPin(mousePosXY.x, 0.0f, 1.0f);
- p->fY = SkTPin(mousePosXY.y, 0.0f, 1.0f);
- *dragging = true;
- }
-
- // 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 + p->fX * size.x, pos.y + p->fY * size.y);
+ dc.dragPoint((SkPoint*)(&primaries->fRX), true, 0xFF000040);
+ dc.dragPoint((SkPoint*)(&primaries->fGX), true, 0xFF004000);
+ dc.dragPoint((SkPoint*)(&primaries->fBX), true, 0xFF400000);
+ dc.dragPoint((SkPoint*)(&primaries->fWX), true);
+ dc.fDrawList->AddPolyline(dc.fScreenPoints.begin(), 3, 0xFFFFFFFF, true, 1.5f);
}
static bool ImGui_DragLocation(SkPoint* pt) {
- ImDrawList* drawList = ImGui::GetWindowDrawList();
-
- // 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);
- ImVec2 pos = ImGui::GetCursorScreenPos();
-
- // Background rectangle
- drawList->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0, 0, 0, 128));
-
- // Location marker
- bool dragging = false;
- ImVec2 tl = ImGui_DragPoint("SL", pt + 0, pos, size, &dragging);
- drawList->AddCircle(tl, 5.0f, 0xFFFFFFFF);
-
- ImGui::SetCursorScreenPos(ImVec2(pos.x, pos.y + size.y));
- ImGui::Spacing();
-
- return dragging;
+ ImGui::DragCanvas dc(pt);
+ dc.fillColor(IM_COL32(0, 0, 0, 128));
+ dc.dragPoint(pt);
+ return dc.fDragging;
}
static bool ImGui_DragQuad(SkPoint* pts) {
- ImDrawList* drawList = ImGui::GetWindowDrawList();
+ ImGui::DragCanvas dc(pts);
+ dc.fillColor(IM_COL32(0, 0, 0, 128));
- // 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);
- ImVec2 pos = ImGui::GetCursorScreenPos();
+ for (int i = 0; i < 4; ++i) {
+ dc.dragPoint(pts + i);
+ }
- // Background rectangle
- drawList->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0, 0, 0, 128));
+ dc.fDrawList->AddLine(dc.fScreenPoints[0], dc.fScreenPoints[1], 0xFFFFFFFF);
+ dc.fDrawList->AddLine(dc.fScreenPoints[1], dc.fScreenPoints[3], 0xFFFFFFFF);
+ dc.fDrawList->AddLine(dc.fScreenPoints[3], dc.fScreenPoints[2], 0xFFFFFFFF);
+ dc.fDrawList->AddLine(dc.fScreenPoints[2], dc.fScreenPoints[0], 0xFFFFFFFF);
- // Corner markers
- bool dragging = false;
- ImVec2 tl = ImGui_DragPoint("TL", pts + 0, pos, size, &dragging);
- ImVec2 tr = ImGui_DragPoint("TR", pts + 1, pos, size, &dragging);
- ImVec2 bl = ImGui_DragPoint("BL", pts + 2, pos, size, &dragging);
- ImVec2 br = ImGui_DragPoint("BR", pts + 3, pos, size, &dragging);
-
- // Draw markers and quad
- drawList->AddCircle(tl, 5.0f, 0xFFFFFFFF);
- drawList->AddCircle(tr, 5.0f, 0xFFFFFFFF);
- drawList->AddCircle(bl, 5.0f, 0xFFFFFFFF);
- drawList->AddCircle(br, 5.0f, 0xFFFFFFFF);
- drawList->AddLine(tl, tr, 0xFFFFFFFF);
- drawList->AddLine(tr, br, 0xFFFFFFFF);
- drawList->AddLine(br, bl, 0xFFFFFFFF);
- drawList->AddLine(bl, tl, 0xFFFFFFFF);
-
- ImGui::SetCursorScreenPos(ImVec2(pos.x, pos.y + size.y));
- ImGui::Spacing();
-
- return dragging;
+ return dc.fDragging;
}
void Viewer::drawImGui() {