Skip to content

Commit 99441bb

Browse files
committed
Add Vulkan capture and refactor UI modules
Add Vulkan screen-capture support and split/refactor the viewer UI into modular files. New files include imiv_capture (Vulkan immediate-submit readback and screen capture for GLFW backend), imiv_aux_windows (auxiliary ImGui windows: Info, Preferences, Preview), imiv_image_view, imiv_menu, imiv_overlays, imiv_vulkan_preview, imiv_vulkan_window and an internal Vulkan texture header. Update CMakeLists to register the new sources and modify imiv_frame to use the new image view/menu/action abstractions and to delegate menu/shortcut handling to the refactored components. Several Vulkan-related source files were also adjusted to integrate the new capture and preview code. This reorganizes UI logic and adds platform-specific capture functionality for improved modularity and maintainability.
1 parent 88de38a commit 99441bb

16 files changed

Lines changed: 3324 additions & 3015 deletions

src/imiv/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,21 @@ record_build_dependency (ImGui FOUND)
6464
set (_imiv_core_sources
6565
imiv_actions.cpp
6666
imiv_app.cpp
67+
imiv_aux_windows.cpp
68+
imiv_capture.cpp
6769
imiv_file_dialog.cpp
6870
imiv_frame.cpp
71+
imiv_image_view.cpp
72+
imiv_menu.cpp
6973
imiv_navigation.cpp
74+
imiv_overlays.cpp
7075
imiv_ui.cpp
7176
imiv_viewer.cpp
7277
imiv_vulkan_setup.cpp
78+
imiv_vulkan_preview.cpp
7379
imiv_vulkan_runtime.cpp
7480
imiv_vulkan_texture.cpp
81+
imiv_vulkan_window.cpp
7582
imiv_test_engine.cpp
7683
imiv_main.cpp)
7784

src/imiv/imiv_aux_windows.cpp

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
// Copyright Contributors to the OpenImageIO project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
// https://github.com/AcademySoftwareFoundation/OpenImageIO
4+
5+
#include "imiv_ui.h"
6+
7+
#include "imiv_test_engine.h"
8+
9+
#include <algorithm>
10+
#include <string>
11+
#include <utility>
12+
13+
#include <imgui.h>
14+
15+
#include <OpenImageIO/strutil.h>
16+
17+
using namespace OIIO;
18+
19+
namespace Imiv {
20+
21+
namespace {
22+
23+
void set_aux_window_defaults(const ImVec2& offset, const ImVec2& size)
24+
{
25+
const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
26+
ImVec2 base_pos(0.0f, 0.0f);
27+
if (main_viewport != nullptr)
28+
base_pos = main_viewport->WorkPos;
29+
ImGui::SetNextWindowPos(ImVec2(base_pos.x + offset.x,
30+
base_pos.y + offset.y),
31+
ImGuiCond_FirstUseEver);
32+
ImGui::SetNextWindowSize(size, ImGuiCond_FirstUseEver);
33+
}
34+
35+
void push_preview_active_button_style(bool active)
36+
{
37+
if (!active)
38+
return;
39+
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(66, 112, 171, 255));
40+
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
41+
IM_COL32(80, 133, 200, 255));
42+
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
43+
IM_COL32(57, 95, 146, 255));
44+
}
45+
46+
void pop_preview_active_button_style(bool active)
47+
{
48+
if (!active)
49+
return;
50+
ImGui::PopStyleColor(3);
51+
}
52+
53+
void preview_form_next_row(const char* label)
54+
{
55+
ImGui::TableNextRow();
56+
ImGui::TableSetColumnIndex(0);
57+
ImGui::AlignTextToFramePadding();
58+
ImGui::TextUnformatted(label);
59+
ImGui::TableSetColumnIndex(1);
60+
}
61+
62+
bool draw_preview_row_button_cell(const char* label, bool active)
63+
{
64+
ImGui::TableNextColumn();
65+
push_preview_active_button_style(active);
66+
const bool pressed
67+
= ImGui::Button(label,
68+
ImVec2(ImGui::GetContentRegionAvail().x, 0.0f));
69+
pop_preview_active_button_style(active);
70+
return pressed;
71+
}
72+
73+
void preview_set_rgb_mode(PlaceholderUiState& ui)
74+
{
75+
ui.color_mode = 1;
76+
ui.current_channel = 0;
77+
}
78+
79+
void preview_set_luma_mode(PlaceholderUiState& ui)
80+
{
81+
ui.color_mode = 3;
82+
ui.current_channel = 0;
83+
}
84+
85+
void preview_set_single_channel_mode(PlaceholderUiState& ui, int channel)
86+
{
87+
ui.color_mode = 2;
88+
ui.current_channel = channel;
89+
}
90+
91+
void preview_set_heat_mode(PlaceholderUiState& ui)
92+
{
93+
ui.color_mode = 4;
94+
if (ui.current_channel <= 0)
95+
ui.current_channel = 1;
96+
}
97+
98+
void preview_reset_adjustments(PlaceholderUiState& ui)
99+
{
100+
ui.exposure = 0.0f;
101+
ui.gamma = 1.0f;
102+
ui.offset = 0.0f;
103+
}
104+
105+
void draw_info_table_row(const char* label, const std::string& value)
106+
{
107+
ImGui::TableNextRow();
108+
ImGui::TableNextColumn();
109+
ImGui::TextUnformatted(label);
110+
ImGui::TableNextColumn();
111+
ImGui::PushTextWrapPos(0.0f);
112+
ImGui::TextUnformatted(value.c_str());
113+
ImGui::PopTextWrapPos();
114+
}
115+
116+
} // namespace
117+
118+
void
119+
draw_info_window(const ViewerState& viewer, bool& show_window)
120+
{
121+
if (!show_window)
122+
return;
123+
set_aux_window_defaults(ImVec2(72.0f, 72.0f), ImVec2(640.0f, 420.0f));
124+
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 10.0f));
125+
if (ImGui::Begin("iv Info", &show_window)) {
126+
const float close_height = ImGui::GetFrameHeightWithSpacing();
127+
const float body_height = std::max(100.0f,
128+
ImGui::GetContentRegionAvail().y
129+
- close_height - 4.0f);
130+
ImGui::BeginChild("##iv_info_scroll", ImVec2(0.0f, body_height), true,
131+
ImGuiWindowFlags_HorizontalScrollbar);
132+
if (viewer.image.path.empty()) {
133+
draw_padded_message("No image loaded.", 8.0f, 8.0f);
134+
register_layout_dump_synthetic_item("text", "No image loaded.");
135+
} else {
136+
if (ImGui::BeginTable("##iv_info_table", 2,
137+
ImGuiTableFlags_SizingStretchProp
138+
| ImGuiTableFlags_BordersInnerV
139+
| ImGuiTableFlags_RowBg)) {
140+
ImGui::TableSetupColumn("Field",
141+
ImGuiTableColumnFlags_WidthFixed,
142+
190.0f);
143+
ImGui::TableSetupColumn("Value",
144+
ImGuiTableColumnFlags_WidthStretch);
145+
146+
draw_info_table_row("Path", viewer.image.path);
147+
for (const std::pair<std::string, std::string>& row :
148+
viewer.image.longinfo_rows) {
149+
draw_info_table_row(row.first.c_str(), row.second);
150+
}
151+
draw_info_table_row(
152+
"Orientation",
153+
Strutil::fmt::format("{}", viewer.image.orientation));
154+
draw_info_table_row(
155+
"Subimage",
156+
Strutil::fmt::format("{}/{}", viewer.image.subimage + 1,
157+
viewer.image.nsubimages));
158+
draw_info_table_row(
159+
"MIP level",
160+
Strutil::fmt::format("{}/{}", viewer.image.miplevel + 1,
161+
viewer.image.nmiplevels));
162+
draw_info_table_row(
163+
"Row pitch (bytes)",
164+
Strutil::fmt::format("{}", viewer.image.row_pitch_bytes));
165+
ImGui::EndTable();
166+
}
167+
register_layout_dump_synthetic_item("text", "iv Info content");
168+
}
169+
ImGui::EndChild();
170+
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 3.0f);
171+
if (ImGui::Button("Close"))
172+
show_window = false;
173+
}
174+
ImGui::End();
175+
ImGui::PopStyleVar();
176+
}
177+
178+
void
179+
draw_preferences_window(PlaceholderUiState& ui, bool& show_window)
180+
{
181+
if (!show_window)
182+
return;
183+
set_aux_window_defaults(ImVec2(740.0f, 72.0f), ImVec2(520.0f, 360.0f));
184+
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 10.0f));
185+
if (ImGui::Begin("iv Preferences", &show_window)) {
186+
const float close_height = ImGui::GetFrameHeightWithSpacing();
187+
const float body_height = std::max(120.0f,
188+
ImGui::GetContentRegionAvail().y
189+
- close_height - 4.0f);
190+
ImGui::BeginChild("##iv_prefs_body", ImVec2(0.0f, body_height), false,
191+
ImGuiWindowFlags_NoScrollbar);
192+
193+
ImGui::Checkbox("Pixel view follows mouse",
194+
&ui.pixelview_follows_mouse);
195+
register_layout_dump_synthetic_item("text", "Pixel view follows mouse");
196+
197+
ImGui::Spacing();
198+
ImGui::TextUnformatted("# closeup pixels");
199+
ImGui::SameLine();
200+
ImGui::SetNextItemWidth(76.0f);
201+
ImGui::InputInt("##pref_closeup_pixels", &ui.closeup_pixels, 2, 2);
202+
203+
ImGui::TextUnformatted("# closeup avg pixels");
204+
ImGui::SameLine();
205+
ImGui::SetNextItemWidth(76.0f);
206+
ImGui::InputInt("##pref_closeup_avg_pixels", &ui.closeup_avg_pixels, 2,
207+
2);
208+
209+
ImGui::Spacing();
210+
ImGui::Checkbox("Linear interpolation", &ui.linear_interpolation);
211+
ImGui::Checkbox("Dark palette", &ui.dark_palette);
212+
ImGui::Checkbox("Generate mipmaps (requires restart)", &ui.auto_mipmap);
213+
214+
ImGui::Spacing();
215+
ImGui::TextUnformatted("Image Cache max memory (requires restart)");
216+
ImGui::SameLine();
217+
ImGui::SetNextItemWidth(90.0f);
218+
ImGui::InputInt("##pref_max_mem", &ui.max_memory_ic_mb);
219+
ImGui::SameLine();
220+
ImGui::TextUnformatted("MB");
221+
222+
ImGui::TextUnformatted("Slide Show delay");
223+
ImGui::SameLine();
224+
ImGui::SetNextItemWidth(90.0f);
225+
ImGui::InputInt("##pref_slide_delay", &ui.slide_duration_seconds);
226+
ImGui::SameLine();
227+
ImGui::TextUnformatted("s");
228+
229+
ImGui::EndChild();
230+
clamp_placeholder_ui_state(ui);
231+
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 3.0f);
232+
if (ImGui::Button("Close"))
233+
show_window = false;
234+
register_layout_dump_synthetic_item("text", "iv Preferences content");
235+
}
236+
ImGui::End();
237+
ImGui::PopStyleVar();
238+
}
239+
240+
void
241+
draw_preview_window(PlaceholderUiState& ui, bool& show_window)
242+
{
243+
if (!show_window)
244+
return;
245+
set_aux_window_defaults(ImVec2(1030.0f, 72.0f), ImVec2(500.0f, 360.0f));
246+
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 10.0f));
247+
if (ImGui::Begin("iv Preview", &show_window)) {
248+
const float close_height = ImGui::GetFrameHeightWithSpacing();
249+
const float body_height = std::max(120.0f,
250+
ImGui::GetContentRegionAvail().y
251+
- close_height - 4.0f);
252+
ImGui::BeginChild("##iv_preview_body", ImVec2(0.0f, body_height), false,
253+
ImGuiWindowFlags_NoScrollbar);
254+
255+
if (ImGui::BeginTable("##iv_preview_form", 2,
256+
ImGuiTableFlags_SizingStretchProp
257+
| ImGuiTableFlags_NoSavedSettings)) {
258+
ImGui::TableSetupColumn("Label", ImGuiTableColumnFlags_WidthFixed,
259+
90.0f);
260+
ImGui::TableSetupColumn("Control",
261+
ImGuiTableColumnFlags_WidthStretch);
262+
263+
preview_form_next_row("Interpolation");
264+
ImGui::Checkbox("Linear##preview_interp", &ui.linear_interpolation);
265+
266+
preview_form_next_row("Exposure");
267+
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
268+
ImGui::SliderFloat("##preview_exposure", &ui.exposure, -10.0f,
269+
10.0f, "%.2f");
270+
271+
preview_form_next_row("");
272+
if (ImGui::BeginTable("##preview_exposure_steps", 4,
273+
ImGuiTableFlags_SizingStretchSame
274+
| ImGuiTableFlags_NoSavedSettings)) {
275+
if (draw_preview_row_button_cell("-1/2", false))
276+
ui.exposure -= 0.5f;
277+
if (draw_preview_row_button_cell("-1/10", false))
278+
ui.exposure -= 0.1f;
279+
if (draw_preview_row_button_cell("+1/10", false))
280+
ui.exposure += 0.1f;
281+
if (draw_preview_row_button_cell("+1/2", false))
282+
ui.exposure += 0.5f;
283+
ImGui::EndTable();
284+
}
285+
286+
preview_form_next_row("Gamma");
287+
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
288+
ImGui::SliderFloat("##preview_gamma", &ui.gamma, 0.1f, 4.0f,
289+
"%.2f");
290+
291+
preview_form_next_row("");
292+
if (ImGui::BeginTable("##preview_gamma_steps", 2,
293+
ImGuiTableFlags_SizingStretchSame
294+
| ImGuiTableFlags_NoSavedSettings)) {
295+
if (draw_preview_row_button_cell("-0.1", false))
296+
ui.gamma = std::max(0.1f, ui.gamma - 0.1f);
297+
if (draw_preview_row_button_cell("+0.1", false))
298+
ui.gamma += 0.1f;
299+
ImGui::EndTable();
300+
}
301+
302+
preview_form_next_row("Offset");
303+
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
304+
ImGui::SliderFloat("##preview_offset", &ui.offset, -1.0f, 1.0f,
305+
"%+.3f");
306+
307+
preview_form_next_row("");
308+
if (ImGui::Button("Reset",
309+
ImVec2(ImGui::GetContentRegionAvail().x, 0.0f))) {
310+
preview_reset_adjustments(ui);
311+
}
312+
313+
preview_form_next_row("");
314+
if (ImGui::BeginTable("##preview_modes", 7,
315+
ImGuiTableFlags_SizingStretchSame
316+
| ImGuiTableFlags_NoSavedSettings)) {
317+
const bool rgb_active = ui.current_channel == 0
318+
&& (ui.color_mode == 0
319+
|| ui.color_mode == 1);
320+
const bool red_active = ui.current_channel == 1
321+
&& ui.color_mode != 3
322+
&& ui.color_mode != 4;
323+
const bool green_active = ui.current_channel == 2
324+
&& ui.color_mode != 3
325+
&& ui.color_mode != 4;
326+
const bool blue_active = ui.current_channel == 3
327+
&& ui.color_mode != 3
328+
&& ui.color_mode != 4;
329+
const bool alpha_active = ui.current_channel == 4
330+
&& ui.color_mode != 3
331+
&& ui.color_mode != 4;
332+
if (draw_preview_row_button_cell("RGB", rgb_active))
333+
preview_set_rgb_mode(ui);
334+
if (draw_preview_row_button_cell("Luma",
335+
ui.color_mode == 3
336+
&& ui.current_channel
337+
== 0)) {
338+
preview_set_luma_mode(ui);
339+
}
340+
if (draw_preview_row_button_cell("R", red_active))
341+
preview_set_single_channel_mode(ui, 1);
342+
if (draw_preview_row_button_cell("G", green_active))
343+
preview_set_single_channel_mode(ui, 2);
344+
if (draw_preview_row_button_cell("B", blue_active))
345+
preview_set_single_channel_mode(ui, 3);
346+
if (draw_preview_row_button_cell("A", alpha_active))
347+
preview_set_single_channel_mode(ui, 4);
348+
if (draw_preview_row_button_cell("Heat", ui.color_mode == 4))
349+
preview_set_heat_mode(ui);
350+
ImGui::EndTable();
351+
}
352+
353+
ImGui::EndTable();
354+
}
355+
356+
ImGui::EndChild();
357+
clamp_placeholder_ui_state(ui);
358+
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 3.0f);
359+
if (ImGui::Button("Close"))
360+
show_window = false;
361+
register_layout_dump_synthetic_item("text", "iv Preview content");
362+
}
363+
ImGui::End();
364+
ImGui::PopStyleVar();
365+
}
366+
367+
} // namespace Imiv

0 commit comments

Comments
 (0)