mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-22 17:19:18 +01:00
Cross-platform screenshots (#543)
Co-authored-by: Tom Lally <tomlally@protonmail.com> Co-authored-by: Exzap <13877693+Exzap@users.noreply.github.com>
This commit is contained in:
parent
2cfb7f3737
commit
b361b154d8
@ -10,6 +10,10 @@
|
||||
|
||||
#include "config/ActiveSettings.h"
|
||||
|
||||
#include <wx/image.h>
|
||||
#include <wx/dataobj.h>
|
||||
#include <wx/clipbrd.h>
|
||||
|
||||
std::unique_ptr<Renderer> g_renderer;
|
||||
|
||||
bool Renderer::GetVRAMInfo(int& usageInMB, int& totalInMB) const
|
||||
@ -77,126 +81,80 @@ uint8 Renderer::RGBComponentToSRGB(uint8 cli)
|
||||
return (uint8)(cs * 255.0f);
|
||||
}
|
||||
|
||||
fs::path _GenerateScreenshotFilename(bool isDRC)
|
||||
{
|
||||
fs::path screendir = ActiveSettings::GetUserDataPath("screenshots");
|
||||
// build screenshot name with format Screenshot_YYYY-MM-DD_HH-MM-SS[_GamePad].png
|
||||
// if the file already exists add a suffix counter (_2.png, _3.png etc)
|
||||
std::time_t time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
std::tm* tm = std::localtime(&time_t);
|
||||
|
||||
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);
|
||||
if (isDRC)
|
||||
screenshotFileName.append("_GamePad");
|
||||
|
||||
fs::path screenshotPath;
|
||||
for(sint32 i=0; i<999; i++)
|
||||
{
|
||||
screenshotPath = screendir;
|
||||
if (i == 0)
|
||||
screenshotPath.append(fmt::format("{}.png", screenshotFileName));
|
||||
else
|
||||
screenshotPath.append(fmt::format("{}_{}.png", screenshotFileName, i + 1));
|
||||
std::error_code ec;
|
||||
if (!fs::exists(screenshotPath))
|
||||
return screenshotPath;
|
||||
}
|
||||
return screenshotPath; // if all exist checks fail, return the last path we tried
|
||||
}
|
||||
|
||||
std::mutex s_clipboardMutex;
|
||||
|
||||
void Renderer::SaveScreenshot(const std::vector<uint8>& rgb_data, int width, int height, bool mainWindow) const
|
||||
{
|
||||
#if BOOST_OS_WINDOWS
|
||||
const bool save_screenshot = GetConfig().save_screenshot;
|
||||
std::thread([](std::vector<uint8> data, bool save_screenshot, int width, int height, bool mainWindow)
|
||||
{
|
||||
{
|
||||
#if BOOST_OS_WINDOWS
|
||||
// on Windows wxWidgets uses OLE API for the clipboard
|
||||
// to make this work we need to call OleInitialize() on the same thread
|
||||
OleInitialize(nullptr);
|
||||
#endif
|
||||
|
||||
wxImage image(width, height, data.data(), true);
|
||||
|
||||
if (mainWindow)
|
||||
{
|
||||
// copy to clipboard
|
||||
std::vector<uint8> buffer(sizeof(BITMAPINFO) + data.size());
|
||||
auto* bmpInfo = (BITMAPINFO*)buffer.data();
|
||||
bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmpInfo->bmiHeader.biWidth = width;
|
||||
bmpInfo->bmiHeader.biHeight = height;
|
||||
bmpInfo->bmiHeader.biPlanes = 1;
|
||||
bmpInfo->bmiHeader.biBitCount = 24;
|
||||
bmpInfo->bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
uint8* clipboard_image = buffer.data() + sizeof(BITMAPINFOHEADER);
|
||||
// RGB -> BGR
|
||||
for (sint32 iy = 0; iy < height; ++iy)
|
||||
s_clipboardMutex.lock();
|
||||
if (wxTheClipboard->Open())
|
||||
{
|
||||
for (sint32 ix = 0; ix < width; ++ix)
|
||||
{
|
||||
uint8* pIn = data.data() + (ix + iy * width) * 3;
|
||||
uint8* pOut = clipboard_image + (ix + (height - iy - 1) * width) * 3;
|
||||
|
||||
pOut[0] = pIn[2];
|
||||
pOut[1] = pIn[1];
|
||||
pOut[2] = pIn[0];
|
||||
}
|
||||
wxTheClipboard->SetData(new wxImageDataObject(image));
|
||||
wxTheClipboard->Close();
|
||||
if(!save_screenshot && mainWindow)
|
||||
LatteOverlay_pushNotification("Screenshot saved to clipboard", 2500);
|
||||
}
|
||||
|
||||
if (OpenClipboard(nullptr))
|
||||
else
|
||||
{
|
||||
EmptyClipboard();
|
||||
|
||||
const HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, buffer.size());
|
||||
if (hGlobal)
|
||||
{
|
||||
memcpy(GlobalLock(hGlobal), buffer.data(), buffer.size());
|
||||
GlobalUnlock(hGlobal);
|
||||
|
||||
SetClipboardData(CF_DIB, hGlobal);
|
||||
GlobalFree(hGlobal);
|
||||
}
|
||||
|
||||
CloseClipboard();
|
||||
LatteOverlay_pushNotification("Failed to open clipboard", 2500);
|
||||
}
|
||||
|
||||
LatteOverlay_pushNotification("Screenshot saved", 2500);
|
||||
s_clipboardMutex.unlock();
|
||||
}
|
||||
|
||||
// save to png file
|
||||
if (save_screenshot)
|
||||
{
|
||||
fs::path screendir = ActiveSettings::GetUserDataPath("screenshots");
|
||||
if (!fs::exists(screendir))
|
||||
fs::path screendir = _GenerateScreenshotFilename(!mainWindow);
|
||||
if (!fs::exists(screendir.parent_path()))
|
||||
fs::create_directory(screendir);
|
||||
|
||||
auto counter = 0;
|
||||
for (const auto& it : fs::directory_iterator(screendir))
|
||||
if (image.SaveFile(screendir.wstring()))
|
||||
{
|
||||
int tmp;
|
||||
if (swscanf_s(it.path().filename().c_str(), L"screenshot_%d", &tmp) == 1)
|
||||
counter = std::max(counter, tmp);
|
||||
if(mainWindow)
|
||||
LatteOverlay_pushNotification("Screenshot saved", 2500);
|
||||
}
|
||||
|
||||
screendir /= fmt::format(L"screenshot_{}.png", ++counter);
|
||||
FileStream* fs = FileStream::createFile2(screendir);
|
||||
if (fs)
|
||||
else
|
||||
{
|
||||
bool success = true;
|
||||
auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (png_ptr)
|
||||
{
|
||||
auto info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr)
|
||||
{
|
||||
if (!setjmp(png_jmpbuf(png_ptr)))
|
||||
{
|
||||
auto pngWriter = [](png_structp png_ptr, png_bytep data, png_size_t length) -> void
|
||||
{
|
||||
FileStream* fs = (FileStream*)png_get_io_ptr(png_ptr);
|
||||
fs->writeData(data, length);
|
||||
};
|
||||
|
||||
//png_init_io(png_ptr, file);
|
||||
png_set_write_fn(png_ptr, (void*)fs, pngWriter, nullptr);
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
uint8* pData = data.data() + (i * width) * 3;
|
||||
png_write_row(png_ptr, pData);
|
||||
}
|
||||
|
||||
png_write_end(png_ptr, nullptr);
|
||||
}
|
||||
else
|
||||
success = false;
|
||||
|
||||
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
|
||||
}
|
||||
|
||||
png_destroy_write_struct(&png_ptr, nullptr);
|
||||
}
|
||||
delete fs;
|
||||
if (!success)
|
||||
{
|
||||
std::error_code ec;
|
||||
fs::remove(screendir, ec);
|
||||
}
|
||||
LatteOverlay_pushNotification("Failed to save screenshot to file", 2500);
|
||||
}
|
||||
}
|
||||
}, rgb_data, save_screenshot, width, height, mainWindow).detach();
|
||||
|
||||
#else
|
||||
cemuLog_log(LogType::Force, "Screenshot feature not implemented");
|
||||
#endif
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "gui/guiWrapper.h"
|
||||
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/clipbrd.h>
|
||||
|
||||
#include "gui/GameUpdateWindow.h"
|
||||
#include "gui/PadViewFrame.h"
|
||||
@ -386,6 +387,8 @@ namespace coreinit
|
||||
|
||||
void MainWindow::OnClose(wxCloseEvent& event)
|
||||
{
|
||||
wxTheClipboard->Flush();
|
||||
|
||||
if(m_game_list)
|
||||
m_game_list->OnClose(event);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user