0.5.1
Loading...
Searching...
No Matches
Screenshotter.cpp
Go to the documentation of this file.
1// This file is part of INSTINCT, the INS Toolkit for Integrated
2// Navigation Concepts and Training by the Institute of Navigation of
3// the University of Stuttgart, Germany.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9#include "Screenshotter.hpp"
10
11#ifdef IMGUI_IMPL_OPENGL_LOADER_GL3W
12 #include <cstdlib>
13 #include <string>
14
15 #include <imgui.h>
16 #include <imgui_internal.h>
17 #include <imgui_node_editor.h>
18 #include <imgui_node_editor_internal.h>
19 #include <imgui_canvas.h>
20namespace ed = ax::NodeEditor;
21
22 #include <fmt/core.h>
23
27
29 #include "util/ImPlot.hpp"
30 #include "util/Logger.hpp"
31
32namespace NAV::gui::windows
33{
34
35std::string plotScreenshotImPlotStyleFile = "implot-light.json";
36bool copyScreenshotsToClipboard = true;
37bool printScreenshotSaveLocation = true;
38
39namespace
40{
41ImRect _screenshotNavigateRect;
42ImRect _screenshotCaptureRect;
43bool _showScreenshotCaptureRect = false;
44size_t _screenshotFrameCnt = 0;
45
46} // namespace
47} // namespace NAV::gui::windows
48
49void NAV::gui::windows::ShowScreenshotter(bool* show /* = nullptr*/)
50{
51 if (_screenshotFrameCnt > 0)
52 {
53 _screenshotFrameCnt++;
54
55 if (_screenshotFrameCnt == 8)
56 {
57 ImGuiIO& io = ImGui::GetIO();
58 ImGuiScreenshotImageBuf Output(static_cast<int>(_screenshotCaptureRect.Min.x),
59 static_cast<int>(io.DisplaySize.y) - static_cast<int>(_screenshotCaptureRect.Max.y),
60 static_cast<size_t>(_screenshotCaptureRect.GetWidth()),
61 static_cast<size_t>(_screenshotCaptureRect.GetHeight()));
62
63 std::time_t t = std::time(nullptr);
64 std::tm* now = std::localtime(&t); // NOLINT(concurrency-mt-unsafe)
65
66 auto savePath = flow::GetOutputPath()
67 / fmt::format("Screenshot_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}.png",
68 now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
69 Output.SaveFile(savePath.c_str());
70 if (printScreenshotSaveLocation)
71 {
72 LOG_INFO("Screenshot saved as: {}", savePath);
73 }
74
75 if (copyScreenshotsToClipboard)
76 {
77 CopyFileToClipboard(savePath.c_str());
78 }
79 }
80 else if (_screenshotFrameCnt == 10)
81 {
82 _screenshotFrameCnt = 0;
83 }
84
85 return;
86 }
87
88 if (_showScreenshotCaptureRect)
89 {
90 constexpr float thickness = 1.0F;
91 auto rect = _screenshotCaptureRect;
92 rect.Expand(thickness);
93 ImGui::GetForegroundDrawList()->AddRect(rect.Min, rect.Max, ImColor(255, 0, 0),
94 0.0F, ImDrawFlags_None, thickness);
95 }
96
97 if (!ImGui::Begin("Screenshotter", show, ImGuiWindowFlags_AlwaysAutoResize))
98 {
99 ImGui::End();
100 return;
101 }
102
103 const float ITEM_WIDTH = 200.0F * NodeEditorApplication::windowFontRatio();
104 const float ITEM_WIDTH_HALF = (ITEM_WIDTH - ImGui::GetStyle().ItemInnerSpacing.x) / 2.0F;
105
106 ImGui::TextUnformatted("Plot screenshot style: ");
107 ImGui::SameLine();
108 ImGui::SetNextItemWidth(100.0F * NodeEditorApplication::windowFontRatio());
109 if (widgets::FileDialogLoad(plotScreenshotImPlotStyleFile, "Plot screenshot config file", ".json", { ".json" },
110 flow::GetConfigPath(), 1, "ImPlotStyleEditorScreenshot"))
111 {
112 LOG_DEBUG("Plot screenshot config file changed to: {}", plotScreenshotImPlotStyleFile);
113 }
114
115 ImGui::Checkbox("Copy screenshots to clipboard", &copyScreenshotsToClipboard);
116
117 bool transparentWindows = ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w != 1.0F;
118 if (ImGui::Checkbox("Light mode", &nodeEditorLightMode))
119 {
120 ApplyDarkLightMode(NodeEditorApplication::m_colors, transparentWindows);
121 flow::ApplyChanges();
122 }
123 ImGui::SameLine();
124 bool showGridLines = ed::GetStyle().Colors[ed::StyleColor_Grid].w != 0.0F;
125 if (ImGui::Checkbox("Grid lines", &showGridLines))
126 {
127 ed::GetStyle().Colors[ed::StyleColor_Grid].w = showGridLines ? ed::Style().Colors[ed::StyleColor_Grid].w : 0.0F;
128 flow::ApplyChanges();
129 }
130 ImGui::SameLine();
131 if (ImGui::Checkbox("Transparent windows", &transparentWindows))
132 {
133 ImGui::GetStyle().Colors[ImGuiCol_WindowBg].w = transparentWindows ? ImGuiStyle().Colors[ImGuiCol_WindowBg].w : 1.0F;
134 ImGui::GetStyle().Colors[ImGuiCol_PopupBg].w = transparentWindows ? ImGuiStyle().Colors[ImGuiCol_PopupBg].w : 1.0F;
135 flow::ApplyChanges();
136 }
137
138 if (ImGui::Checkbox("Hide left pane", &NodeEditorApplication::hideLeftPane))
139 {
140 flow::ApplyChanges();
141 }
142 if (!NodeEditorApplication::hideLeftPane)
143 {
144 ImGui::SameLine();
145 ImGui::SetNextItemWidth(ITEM_WIDTH_HALF);
146 ImGui::DragFloat("Left pane width", &NodeEditorApplication::leftPaneWidth);
147 }
148 if (ImGui::Checkbox("Hide FPS count", &NodeEditorApplication::hideFPS))
149 {
150 flow::ApplyChanges();
151 }
152 ImGui::SameLine();
153 ImGui::Checkbox("Print save location", &printScreenshotSaveLocation);
154
155 ImGui::Separator();
156
157 auto* editor = reinterpret_cast<ed::Detail::EditorContext*>(ed::GetCurrentEditor());
158 if (_screenshotNavigateRect.GetArea() == 0.0F) { _screenshotNavigateRect = editor->GetContentBounds(); }
159
160 ImGui::SetNextItemOpen(true, ImGuiCond_Once);
161 if (ImGui::TreeNode("Screenshot navigate area"))
162 {
163 ImGui::TextUnformatted(fmt::format("Current Content Bounds: Min({},{}), Max({},{})",
164 editor->GetContentBounds().Min.x, editor->GetContentBounds().Min.y,
165 editor->GetContentBounds().Max.x, editor->GetContentBounds().Max.y)
166 .c_str());
167 ImGui::TextUnformatted(fmt::format("Current View: Min({},{}), Max({},{})",
168 editor->GetViewRect().Min.x, editor->GetViewRect().Min.y,
169 editor->GetViewRect().Max.x, editor->GetViewRect().Max.y)
170 .c_str());
171
172 if (ImGui::Button("Set area to content")) { _screenshotNavigateRect = editor->GetContentBounds(); }
173 ImGui::SameLine();
174 if (ImGui::Button("Set area to current view"))
175 {
176 _screenshotNavigateRect = editor->GetViewRect();
177
178 auto extend = ImMax(_screenshotNavigateRect.GetWidth(), _screenshotNavigateRect.GetHeight());
179 constexpr float c_NavigationZoomMargin = 0.1F;
180 _screenshotNavigateRect.Expand(-extend * c_NavigationZoomMargin * 0.5F);
181 }
182
183 ImGui::SetNextItemWidth(ITEM_WIDTH);
184 ImGui::DragFloat2("Min##Nav area", &_screenshotNavigateRect.Min.x);
185 ImGui::SetNextItemWidth(ITEM_WIDTH);
186 ImGui::DragFloat2("Max##Nav area", &_screenshotNavigateRect.Max.x);
187
188 if (ImGui::Button("Navigate to area"))
189 {
190 editor->NavigateTo(_screenshotNavigateRect, true);
191 }
192 ImGui::SameLine();
193
194 static float zoomFactor = editor->GetView().Scale;
195 if (ImGui::Button("Zoom"))
196 {
197 auto targetRect = editor->GetCanvas().CalcViewRect(ImGuiEx::CanvasView(editor->GetView().Origin, zoomFactor));
198 editor->NavigateTo(targetRect, true, 0.15F);
199 }
200 ImGui::SameLine();
201 ImGui::SetNextItemWidth(ITEM_WIDTH_HALF / 2.F);
202 ImGui::InputFloat("##Zoom Factor", &zoomFactor);
203 ImGui::SameLine();
204 ImGui::TextUnformatted(fmt::format("Current: {:.3f}", editor->GetView().Scale).c_str());
205
206 ImGui::TreePop();
207 }
208
209 ImGuiIO& io = ImGui::GetIO();
210 if (_screenshotCaptureRect.GetArea() == 0.0F && ImGui::GetFrameCount() > 10) { _screenshotCaptureRect.Max = io.DisplaySize; }
211
212 ImGui::SetNextItemOpen(true, ImGuiCond_Once);
213 if (ImGui::TreeNode("Screenshot capture area"))
214 {
215 if (ImGui::Button("Set to display size"))
216 {
217 _screenshotCaptureRect.Min = ImVec2();
218 _screenshotCaptureRect.Max = io.DisplaySize;
219 }
220 ImGui::SameLine();
221 ImGui::SetNextItemWidth(ITEM_WIDTH_HALF);
222 if (ImGui::BeginCombo("##Window list", "Set to window"))
223 {
224 ImVector<ImGuiWindow*>& windows = ImGui::GetCurrentContext()->Windows;
225 for (ImGuiWindow* window : windows)
226 {
227 std::string windowName = window->Name;
228 if (!window->WasActive || windowName.find('#') != std::string::npos || windowName.starts_with("Content")) { continue; }
229 const bool is_selected = window->Rect().Min == _screenshotCaptureRect.Min && window->Rect().Max == _screenshotCaptureRect.Max;
230 if (ImGui::Selectable(window->Name, is_selected))
231 {
232 _screenshotCaptureRect = window->Rect();
233 }
234
235 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
236 if (is_selected) { ImGui::SetItemDefaultFocus(); }
237 }
238
239 ImGui::EndCombo();
240 }
241 ImGui::SameLine();
242 ImGui::Checkbox("Show Capture rect", &_showScreenshotCaptureRect);
243
244 ImGui::SetNextItemWidth(ITEM_WIDTH_HALF);
245 ImGui::DragFloat("##Min Capture area x", &_screenshotCaptureRect.Min.x, 1.0F, 0.0F, io.DisplaySize.x - 1);
246 ImGui::SameLine();
247 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x + ImGui::GetStyle().ItemInnerSpacing.x);
248 ImGui::SetNextItemWidth(ITEM_WIDTH_HALF);
249 ImGui::DragFloat("Min##Capture area y", &_screenshotCaptureRect.Min.y, 1.0F, 0.0F, io.DisplaySize.y - 1);
250
251 ImGui::SetNextItemWidth(ITEM_WIDTH_HALF);
252 ImGui::DragFloat("##Max Capture area x", &_screenshotCaptureRect.Max.x, 1.0F, _screenshotCaptureRect.Min.x + 1, io.DisplaySize.x);
253 ImGui::SameLine();
254 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x + ImGui::GetStyle().ItemInnerSpacing.x);
255 ImGui::SetNextItemWidth(ITEM_WIDTH_HALF);
256 ImGui::DragFloat("Max##Capture area y", &_screenshotCaptureRect.Max.y, 1.0F, _screenshotCaptureRect.Min.y + 1, io.DisplaySize.y);
257
258 if (ImGui::Button("Take screenshot"))
259 {
260 _screenshotFrameCnt = 1; // Starts taking a screenshot after some frames
261 }
262
263 ImGui::TreePop();
264 }
265
266 ImGui::End();
267}
268
269void NAV::gui::windows::CopyFileToClipboard(const char* path)
270{
271 // NOLINTNEXTLINE
272 [[maybe_unused]] int exitCode = system(fmt::format("command -v xclip > /dev/null 2>&1 && xclip -selection clipboard -target image/png -i {} && exit 0"
273 "|| command -v wl-copy > /dev/null 2>&1 && wl-copy < {}",
274 path, path)
275 .c_str());
276}
277
278#endif
File Chooser.
Save/Load the Nodes.
ImPlot utilities.
Utility class for logging to console and file.
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
Definition Logger.hpp:67
#define LOG_INFO
Info to the user on the state of the program.
Definition Logger.hpp:69
Style Editor window.
Screenshot utility.
void ApplyDarkLightMode(std::vector< ImVec4 > &colors, bool transparentWindows)