|
10 | 10 |
|
11 | 11 | #include "config/ActiveSettings.h"
|
12 | 12 |
|
| 13 | +#include <wx/image.h> |
| 14 | +#include <wx/dataobj.h> |
| 15 | +#include <wx/clipbrd.h> |
| 16 | + |
13 | 17 | std::unique_ptr<Renderer> g_renderer;
|
14 | 18 |
|
15 | 19 | bool Renderer::GetVRAMInfo(int& usageInMB, int& totalInMB) const
|
@@ -77,126 +81,80 @@ uint8 Renderer::RGBComponentToSRGB(uint8 cli)
|
77 | 81 | return (uint8)(cs * 255.0f);
|
78 | 82 | }
|
79 | 83 |
|
| 84 | +fs::path _GenerateScreenshotFilename(bool isDRC) |
| 85 | +{ |
| 86 | + fs::path screendir = ActiveSettings::GetUserDataPath("screenshots"); |
| 87 | + // build screenshot name with format Screenshot_YYYY-MM-DD_HH-MM-SS[_GamePad].png |
| 88 | + // if the file already exists add a suffix counter (_2.png, _3.png etc) |
| 89 | + std::time_t time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); |
| 90 | + std::tm* tm = std::localtime(&time_t); |
| 91 | + |
| 92 | + std::string screenshotFileName = fmt::format("Screenshot_{:04}-{:02}-{:02}_{:02}-{:02}-{:02}", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); |
| 93 | + if (isDRC) |
| 94 | + screenshotFileName.append("_GamePad"); |
| 95 | + |
| 96 | + fs::path screenshotPath; |
| 97 | + for(sint32 i=0; i<999; i++) |
| 98 | + { |
| 99 | + screenshotPath = screendir; |
| 100 | + if (i == 0) |
| 101 | + screenshotPath.append(fmt::format("{}.png", screenshotFileName)); |
| 102 | + else |
| 103 | + screenshotPath.append(fmt::format("{}_{}.png", screenshotFileName, i + 1)); |
| 104 | + std::error_code ec; |
| 105 | + if (!fs::exists(screenshotPath)) |
| 106 | + return screenshotPath; |
| 107 | + } |
| 108 | + return screenshotPath; // if all exist checks fail, return the last path we tried |
| 109 | +} |
| 110 | + |
| 111 | +std::mutex s_clipboardMutex; |
| 112 | + |
80 | 113 | void Renderer::SaveScreenshot(const std::vector<uint8>& rgb_data, int width, int height, bool mainWindow) const
|
81 | 114 | {
|
82 |
| -#if BOOST_OS_WINDOWS |
83 | 115 | const bool save_screenshot = GetConfig().save_screenshot;
|
84 | 116 | std::thread([](std::vector<uint8> data, bool save_screenshot, int width, int height, bool mainWindow)
|
85 |
| - { |
| 117 | + { |
| 118 | +#if BOOST_OS_WINDOWS |
| 119 | + // on Windows wxWidgets uses OLE API for the clipboard |
| 120 | + // to make this work we need to call OleInitialize() on the same thread |
| 121 | + OleInitialize(nullptr); |
| 122 | +#endif |
| 123 | + |
| 124 | + wxImage image(width, height, data.data(), true); |
| 125 | + |
86 | 126 | if (mainWindow)
|
87 | 127 | {
|
88 |
| - // copy to clipboard |
89 |
| - std::vector<uint8> buffer(sizeof(BITMAPINFO) + data.size()); |
90 |
| - auto* bmpInfo = (BITMAPINFO*)buffer.data(); |
91 |
| - bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); |
92 |
| - bmpInfo->bmiHeader.biWidth = width; |
93 |
| - bmpInfo->bmiHeader.biHeight = height; |
94 |
| - bmpInfo->bmiHeader.biPlanes = 1; |
95 |
| - bmpInfo->bmiHeader.biBitCount = 24; |
96 |
| - bmpInfo->bmiHeader.biCompression = BI_RGB; |
97 |
| - |
98 |
| - uint8* clipboard_image = buffer.data() + sizeof(BITMAPINFOHEADER); |
99 |
| - // RGB -> BGR |
100 |
| - for (sint32 iy = 0; iy < height; ++iy) |
| 128 | + s_clipboardMutex.lock(); |
| 129 | + if (wxTheClipboard->Open()) |
101 | 130 | {
|
102 |
| - for (sint32 ix = 0; ix < width; ++ix) |
103 |
| - { |
104 |
| - uint8* pIn = data.data() + (ix + iy * width) * 3; |
105 |
| - uint8* pOut = clipboard_image + (ix + (height - iy - 1) * width) * 3; |
106 |
| - |
107 |
| - pOut[0] = pIn[2]; |
108 |
| - pOut[1] = pIn[1]; |
109 |
| - pOut[2] = pIn[0]; |
110 |
| - } |
| 131 | + wxTheClipboard->SetData(new wxImageDataObject(image)); |
| 132 | + wxTheClipboard->Close(); |
| 133 | + if(!save_screenshot && mainWindow) |
| 134 | + LatteOverlay_pushNotification("Screenshot saved to clipboard", 2500); |
111 | 135 | }
|
112 |
| - |
113 |
| - if (OpenClipboard(nullptr)) |
| 136 | + else |
114 | 137 | {
|
115 |
| - EmptyClipboard(); |
116 |
| - |
117 |
| - const HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, buffer.size()); |
118 |
| - if (hGlobal) |
119 |
| - { |
120 |
| - memcpy(GlobalLock(hGlobal), buffer.data(), buffer.size()); |
121 |
| - GlobalUnlock(hGlobal); |
122 |
| - |
123 |
| - SetClipboardData(CF_DIB, hGlobal); |
124 |
| - GlobalFree(hGlobal); |
125 |
| - } |
126 |
| - |
127 |
| - CloseClipboard(); |
| 138 | + LatteOverlay_pushNotification("Failed to open clipboard", 2500); |
128 | 139 | }
|
129 |
| - |
130 |
| - LatteOverlay_pushNotification("Screenshot saved", 2500); |
| 140 | + s_clipboardMutex.unlock(); |
131 | 141 | }
|
132 | 142 |
|
133 | 143 | // save to png file
|
134 | 144 | if (save_screenshot)
|
135 | 145 | {
|
136 |
| - fs::path screendir = ActiveSettings::GetUserDataPath("screenshots"); |
137 |
| - if (!fs::exists(screendir)) |
| 146 | + fs::path screendir = _GenerateScreenshotFilename(!mainWindow); |
| 147 | + if (!fs::exists(screendir.parent_path())) |
138 | 148 | fs::create_directory(screendir);
|
139 |
| - |
140 |
| - auto counter = 0; |
141 |
| - for (const auto& it : fs::directory_iterator(screendir)) |
| 149 | + if (image.SaveFile(screendir.wstring())) |
142 | 150 | {
|
143 |
| - int tmp; |
144 |
| - if (swscanf_s(it.path().filename().c_str(), L"screenshot_%d", &tmp) == 1) |
145 |
| - counter = std::max(counter, tmp); |
| 151 | + if(mainWindow) |
| 152 | + LatteOverlay_pushNotification("Screenshot saved", 2500); |
146 | 153 | }
|
147 |
| - |
148 |
| - screendir /= fmt::format(L"screenshot_{}.png", ++counter); |
149 |
| - FileStream* fs = FileStream::createFile2(screendir); |
150 |
| - if (fs) |
| 154 | + else |
151 | 155 | {
|
152 |
| - bool success = true; |
153 |
| - auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); |
154 |
| - if (png_ptr) |
155 |
| - { |
156 |
| - auto info_ptr = png_create_info_struct(png_ptr); |
157 |
| - if (info_ptr) |
158 |
| - { |
159 |
| - if (!setjmp(png_jmpbuf(png_ptr))) |
160 |
| - { |
161 |
| - auto pngWriter = [](png_structp png_ptr, png_bytep data, png_size_t length) -> void |
162 |
| - { |
163 |
| - FileStream* fs = (FileStream*)png_get_io_ptr(png_ptr); |
164 |
| - fs->writeData(data, length); |
165 |
| - }; |
166 |
| - |
167 |
| - //png_init_io(png_ptr, file); |
168 |
| - png_set_write_fn(png_ptr, (void*)fs, pngWriter, nullptr); |
169 |
| - |
170 |
| - png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, |
171 |
| - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); |
172 |
| - png_write_info(png_ptr, info_ptr); |
173 |
| - for (int i = 0; i < height; ++i) |
174 |
| - { |
175 |
| - uint8* pData = data.data() + (i * width) * 3; |
176 |
| - png_write_row(png_ptr, pData); |
177 |
| - } |
178 |
| - |
179 |
| - png_write_end(png_ptr, nullptr); |
180 |
| - } |
181 |
| - else |
182 |
| - success = false; |
183 |
| - |
184 |
| - png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); |
185 |
| - } |
186 |
| - |
187 |
| - png_destroy_write_struct(&png_ptr, nullptr); |
188 |
| - } |
189 |
| - delete fs; |
190 |
| - if (!success) |
191 |
| - { |
192 |
| - std::error_code ec; |
193 |
| - fs::remove(screendir, ec); |
194 |
| - } |
| 156 | + LatteOverlay_pushNotification("Failed to save screenshot to file", 2500); |
195 | 157 | }
|
196 | 158 | }
|
197 | 159 | }, rgb_data, save_screenshot, width, height, mainWindow).detach();
|
198 |
| - |
199 |
| -#else |
200 |
| -cemuLog_log(LogType::Force, "Screenshot feature not implemented"); |
201 |
| -#endif |
202 | 160 | }
|
0 commit comments