Skip to content

Commit b361b15

Browse files
Tom LallyTom LallyExzap
authored
Cross-platform screenshots (#543)
Co-authored-by: Tom Lally <[email protected]> Co-authored-by: Exzap <[email protected]>
1 parent 2cfb7f3 commit b361b15

File tree

2 files changed

+61
-100
lines changed

2 files changed

+61
-100
lines changed

src/Cafe/HW/Latte/Renderer/Renderer.cpp

Lines changed: 58 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
#include "config/ActiveSettings.h"
1212

13+
#include <wx/image.h>
14+
#include <wx/dataobj.h>
15+
#include <wx/clipbrd.h>
16+
1317
std::unique_ptr<Renderer> g_renderer;
1418

1519
bool Renderer::GetVRAMInfo(int& usageInMB, int& totalInMB) const
@@ -77,126 +81,80 @@ uint8 Renderer::RGBComponentToSRGB(uint8 cli)
7781
return (uint8)(cs * 255.0f);
7882
}
7983

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+
80113
void Renderer::SaveScreenshot(const std::vector<uint8>& rgb_data, int width, int height, bool mainWindow) const
81114
{
82-
#if BOOST_OS_WINDOWS
83115
const bool save_screenshot = GetConfig().save_screenshot;
84116
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+
86126
if (mainWindow)
87127
{
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())
101130
{
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);
111135
}
112-
113-
if (OpenClipboard(nullptr))
136+
else
114137
{
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);
128139
}
129-
130-
LatteOverlay_pushNotification("Screenshot saved", 2500);
140+
s_clipboardMutex.unlock();
131141
}
132142

133143
// save to png file
134144
if (save_screenshot)
135145
{
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()))
138148
fs::create_directory(screendir);
139-
140-
auto counter = 0;
141-
for (const auto& it : fs::directory_iterator(screendir))
149+
if (image.SaveFile(screendir.wstring()))
142150
{
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);
146153
}
147-
148-
screendir /= fmt::format(L"screenshot_{}.png", ++counter);
149-
FileStream* fs = FileStream::createFile2(screendir);
150-
if (fs)
154+
else
151155
{
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);
195157
}
196158
}
197159
}, rgb_data, save_screenshot, width, height, mainWindow).detach();
198-
199-
#else
200-
cemuLog_log(LogType::Force, "Screenshot feature not implemented");
201-
#endif
202160
}

src/gui/MainWindow.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "gui/guiWrapper.h"
44

55
#include <wx/mstream.h>
6+
#include <wx/clipbrd.h>
67

78
#include "gui/GameUpdateWindow.h"
89
#include "gui/PadViewFrame.h"
@@ -386,6 +387,8 @@ namespace coreinit
386387

387388
void MainWindow::OnClose(wxCloseEvent& event)
388389
{
390+
wxTheClipboard->Flush();
391+
389392
if(m_game_list)
390393
m_game_list->OnClose(event);
391394

0 commit comments

Comments
 (0)