[0.0.0] Capture window: a zoom slider, and fitting to the window, its width or its height
This commit is contained in:
parent
4323831801
commit
94d56f25b1
@ -9,6 +9,8 @@ VIEWER_MAX_TEXTURES :: 1200; // previews on the GPU at once, about 200 KB
|
|||||||
VIEWER_PREFETCH_ROWS :: 8; // most rows decoded ahead of the view, above and below
|
VIEWER_PREFETCH_ROWS :: 8; // most rows decoded ahead of the view, above and below
|
||||||
VIEWER_UPLOAD_BUDGET_MS :: 4.0; // time per frame spent uploading previews
|
VIEWER_UPLOAD_BUDGET_MS :: 4.0; // time per frame spent uploading previews
|
||||||
VIEWER_FULL_WINDOW :: "Capture";
|
VIEWER_FULL_WINDOW :: "Capture";
|
||||||
|
VIEWER_MIN_ZOOM :: 5.0; // percent, for the full-size window
|
||||||
|
VIEWER_MAX_ZOOM :: 800.0;
|
||||||
|
|
||||||
GPU_TEXTURE_MAX_SIDE :: 16384; // what D3D11 guarantees; larger images are scaled down to fit
|
GPU_TEXTURE_MAX_SIDE :: 16384; // what D3D11 guarantees; larger images are scaled down to fit
|
||||||
|
|
||||||
@ -36,6 +38,14 @@ Viewer_Capture :: struct {
|
|||||||
in_flight: bool; // under viewer.mutex: queued for a worker, or being decoded
|
in_flight: bool; // under viewer.mutex: queued for a worker, or being decoded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// How the full-size window sizes its image.
|
||||||
|
Viewer_Fit :: enum u8 {
|
||||||
|
OFF; // at the zoom the slider sets
|
||||||
|
WINDOW; // the whole image in the window
|
||||||
|
WIDTH; // the window's width, scrolling down
|
||||||
|
HEIGHT; // the window's height, scrolling across
|
||||||
|
}
|
||||||
|
|
||||||
// Pixels a worker decoded, waiting for the main thread to upload them.
|
// Pixels a worker decoded, waiting for the main thread to upload them.
|
||||||
Decoded :: struct {
|
Decoded :: struct {
|
||||||
index: s32;
|
index: s32;
|
||||||
@ -76,7 +86,8 @@ viewer: struct {
|
|||||||
full_source_width, full_source_height: s32;
|
full_source_width, full_source_height: s32;
|
||||||
full_loading: bool;
|
full_loading: bool;
|
||||||
full_failed: bool;
|
full_failed: bool;
|
||||||
full_actual_size: bool;
|
full_fit := Viewer_Fit.WINDOW;
|
||||||
|
full_zoom: float = 100; // percent of the size it was captured at; set by the fit, unless OFF
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- startup and shutdown
|
// ---------------------------------------------------------------- startup and shutdown
|
||||||
@ -498,11 +509,24 @@ viewer_full_window :: () { using ImGui;
|
|||||||
TextColored(app_theme.palette.dim, "% x %", viewer.full_source_width, viewer.full_source_height);
|
TextColored(app_theme.palette.dim, "% x %", viewer.full_source_width, viewer.full_source_height);
|
||||||
}
|
}
|
||||||
if viewer.full_loading { SameLine(); TextColored(app_theme.palette.dim, "Loading..."); }
|
if viewer.full_loading { SameLine(); TextColored(app_theme.palette.dim, "Loading..."); }
|
||||||
SameLine();
|
|
||||||
Checkbox("Actual size", *viewer.full_actual_size);
|
|
||||||
SameLine();
|
|
||||||
TextColored(app_theme.palette.dim, "Left / Right: previous / next");
|
TextColored(app_theme.palette.dim, "Left / Right: previous / next");
|
||||||
|
|
||||||
|
// Zoom is a percentage of the size the capture was taken at. A fit sets it from the window, larger
|
||||||
|
// or smaller, and the slider shows what it works out to; moving the slider turns the fit off,
|
||||||
|
// and the zoom then stays for the next capture.
|
||||||
|
fit := viewer.full_fit;
|
||||||
|
Text("Fit:");
|
||||||
|
SameLine(); if RadioButton("Window", fit == .WINDOW) fit = .WINDOW;
|
||||||
|
SameLine(); if RadioButton("Width", fit == .WIDTH) fit = .WIDTH;
|
||||||
|
SameLine(); if RadioButton("Height", fit == .HEIGHT) fit = .HEIGHT;
|
||||||
|
SameLine(); if RadioButton("Off", fit == .OFF) fit = .OFF;
|
||||||
|
viewer.full_fit = fit;
|
||||||
|
SameLine();
|
||||||
|
SetNextItemWidth(260);
|
||||||
|
if SliderFloat("Zoom", *viewer.full_zoom, VIEWER_MIN_ZOOM, VIEWER_MAX_ZOOM, "%.0f%%", .Logarithmic) viewer.full_fit = .OFF;
|
||||||
|
SameLine();
|
||||||
|
if SmallButton("100%") { viewer.full_fit = .OFF; viewer.full_zoom = 100; }
|
||||||
|
|
||||||
// Left and Right step through the captures in grid order while this window has focus.
|
// Left and Right step through the captures in grid order while this window has focus.
|
||||||
if IsWindowFocused(.RootAndChildWindows) {
|
if IsWindowFocused(.RootAndChildWindows) {
|
||||||
if IsKeyPressed(.LeftArrow) && viewer.full_index > 0 viewer_request_full(viewer.full_index - 1);
|
if IsKeyPressed(.LeftArrow) && viewer.full_index > 0 viewer_request_full(viewer.full_index - 1);
|
||||||
@ -512,16 +536,28 @@ viewer_full_window :: () { using ImGui;
|
|||||||
if viewer.full_failed {
|
if viewer.full_failed {
|
||||||
TextColored(app_theme.palette.warning, "This capture cannot be shown; see the Event Log.");
|
TextColored(app_theme.palette.warning, "This capture cannot be shown; see the Event Log.");
|
||||||
} else if viewer.full_texture.handle {
|
} else if viewer.full_texture.handle {
|
||||||
w, h := cast(float) viewer.full_texture.width, cast(float) viewer.full_texture.height;
|
// Each fit keeps the same scrollbars from frame to frame. Otherwise fitting the width of a tall
|
||||||
BeginChild("image", .{0, 0}, .None, .HorizontalScrollbar);
|
// image would bring up a vertical scrollbar, narrowing the area, changing the fit, and so on.
|
||||||
if viewer.full_actual_size {
|
flags := WindowFlags.HorizontalScrollbar;
|
||||||
// At the size it was captured; an image scaled down to fit a texture is drawn back up.
|
if viewer.full_fit == {
|
||||||
Image(gpu_texture_ref(viewer.full_texture), .{cast(float) viewer.full_source_width, cast(float) viewer.full_source_height});
|
case .WINDOW; flags = .NoScrollbar;
|
||||||
} else {
|
case .WIDTH; flags |= .AlwaysVerticalScrollbar;
|
||||||
available := GetContentRegionAvail();
|
case .HEIGHT; flags |= .AlwaysHorizontalScrollbar;
|
||||||
scale := min(min(available.x / w, available.y / h), 1.0); // fitted, never enlarged
|
|
||||||
Image(gpu_texture_ref(viewer.full_texture), .{w * scale, h * scale});
|
|
||||||
}
|
}
|
||||||
|
BeginChild("image", .{0, 0}, .None, flags);
|
||||||
|
area := GetContentRegionAvail();
|
||||||
|
|
||||||
|
// Sized from the captured size, so an image scaled down to fit a texture is drawn back up.
|
||||||
|
source_w, source_h := cast(float) viewer.full_source_width, cast(float) viewer.full_source_height;
|
||||||
|
if source_w > 0 && source_h > 0 && area.x > 0 && area.y > 0 {
|
||||||
|
if viewer.full_fit == {
|
||||||
|
case .WINDOW; viewer.full_zoom = 100 * min(area.x / source_w, area.y / source_h);
|
||||||
|
case .WIDTH; viewer.full_zoom = 100 * area.x / source_w;
|
||||||
|
case .HEIGHT; viewer.full_zoom = 100 * area.y / source_h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zoom := clamp(viewer.full_zoom, VIEWER_MIN_ZOOM, VIEWER_MAX_ZOOM) / 100;
|
||||||
|
Image(gpu_texture_ref(viewer.full_texture), .{source_w * zoom, source_h * zoom});
|
||||||
EndChild();
|
EndChild();
|
||||||
// The previous image stays up while the next one loads, so stepping through does not flicker.
|
// The previous image stays up while the next one loads, so stepping through does not flicker.
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user