From b3d5703b92aa5ec1fff7b7829a033c25ada29168 Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 3 Dec 2021 18:50:12 +0100 Subject: [PATCH] glrage: make .jpg screenshots --- meson.build | 1 - src/ddraw/DirectDrawSurface.cpp | 2 +- src/game/output.c | 5 +++ src/game/output.h | 2 ++ src/game/shell.c | 14 ++++++++ src/game/shell.h | 3 ++ src/glrage/Context.hpp | 1 + src/glrage/ContextImpl.cpp | 25 +++++--------- src/glrage/ContextImpl.hpp | 8 ++--- src/glrage/Interop.cpp | 7 ++++ src/glrage/Interop.hpp | 2 ++ src/glrage/Screenshot.cpp | 51 ---------------------------- src/glrage/Screenshot.hpp | 18 ---------- src/glrage_gl/Screenshot.cpp | 59 +++++++++++++++------------------ src/glrage_gl/Screenshot.hpp | 4 +-- src/specific/s_output.c | 5 +++ src/specific/s_output.h | 2 ++ src/specific/s_shell.c | 7 ++++ 18 files changed, 90 insertions(+), 126 deletions(-) delete mode 100644 src/glrage/Screenshot.cpp delete mode 100644 src/glrage/Screenshot.hpp diff --git a/meson.build b/meson.build index a3543375b..8d176f79a 100644 --- a/meson.build +++ b/meson.build @@ -221,7 +221,6 @@ sources = [ 'src/glrage/ContextImpl.cpp', 'src/glrage/GLRage.cpp', 'src/glrage/Interop.cpp', - 'src/glrage/Screenshot.cpp', 'src/glrage_gl/Buffer.cpp', 'src/glrage_gl/Program.cpp', 'src/glrage_gl/Sampler.cpp', diff --git a/src/ddraw/DirectDrawSurface.cpp b/src/ddraw/DirectDrawSurface.cpp index 478e81a9b..d62b999d6 100644 --- a/src/ddraw/DirectDrawSurface.cpp +++ b/src/ddraw/DirectDrawSurface.cpp @@ -83,7 +83,7 @@ HRESULT DirectDrawSurface::Blt( gl::Screenshot::capture( buffer, width, height, depth, GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8_REV); + GL_UNSIGNED_INT_8_8_8_8_REV, false); Blitter::Rect srcRect { 0, height, width, 0 }; diff --git a/src/game/output.c b/src/game/output.c index 84c2acd94..5b5464801 100644 --- a/src/game/output.c +++ b/src/game/output.c @@ -856,3 +856,8 @@ void Output_ApplyWaterEffect(float *r, float *g, float *b) *b *= m_WaterColor.b; } } + +bool Output_MakeScreenshot(const char *path) +{ + return S_Output_MakeScreenshot(path); +} diff --git a/src/game/output.h b/src/game/output.h index dfc923b55..a3692572c 100644 --- a/src/game/output.h +++ b/src/game/output.h @@ -71,4 +71,6 @@ void Output_AnimateTextures(int32_t ticks); void Output_ApplyWaterEffect(float *r, float *g, float *b); +bool Output_MakeScreenshot(const char *path); + #endif diff --git a/src/game/shell.c b/src/game/shell.c index 1c4a4ab88..b12d90262 100644 --- a/src/game/shell.c +++ b/src/game/shell.c @@ -2,6 +2,7 @@ #include "3dsystem/phd_math.h" #include "config.h" +#include "filesystem.h" #include "game/clock.h" #include "game/demo.h" #include "game/fmv.h" @@ -186,3 +187,16 @@ void Shell_Wait(int nticks) Input_Update(); } } + +bool Shell_MakeScreenshot() +{ + char path[20]; + for (int i = 0; i < 10000; i++) { + sprintf(path, "screenshot%04d.jpg", i); + if (File_Exists(path)) { + continue; + } + return Output_MakeScreenshot(path); + } + return false; +} diff --git a/src/game/shell.h b/src/game/shell.h index 6302d9a49..71fc8b9af 100644 --- a/src/game/shell.h +++ b/src/game/shell.h @@ -1,9 +1,12 @@ #ifndef T1M_GAME_SHELL_H #define T1M_GAME_SHELL_H +#include + void Shell_Main(); void Shell_ExitSystem(const char *message); void Shell_ExitSystemFmt(const char *fmt, ...); void Shell_Wait(int nticks); +bool Shell_MakeScreenshot(); #endif diff --git a/src/glrage/Context.hpp b/src/glrage/Context.hpp index 5507755f8..65b403a57 100644 --- a/src/glrage/Context.hpp +++ b/src/glrage/Context.hpp @@ -28,6 +28,7 @@ public: virtual bool isRendered() = 0; virtual HWND getHWnd() = 0; virtual std::string getBasePath() = 0; + virtual void scheduleScreenshot(const std::string &path) = 0; virtual ~Context() = default; }; diff --git a/src/glrage/ContextImpl.cpp b/src/glrage/ContextImpl.cpp index ae0159b95..b30e0c876 100644 --- a/src/glrage/ContextImpl.cpp +++ b/src/glrage/ContextImpl.cpp @@ -1,5 +1,6 @@ #include "glrage/ContextImpl.hpp" +#include "glrage_gl/Screenshot.hpp" #include "glrage_gl/gl_core_3_3.h" #include "glrage_gl/wgl_ext.h" #include "glrage_util/ErrorUtils.hpp" @@ -109,17 +110,6 @@ LRESULT ContextImpl::windowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { - // Printscreen on Windows with OpenGL doesn't work in fullscreen, so - // hook the key and implement screenshot saving to files. - // For some reason, VK_SNAPSHOT doesn't generate WM_KEYDOWN events but - // only WM_KEYUP. Works just as well, though. - case WM_KEYUP: - if (wParam == VK_SNAPSHOT) { - m_screenshot.schedule(true); - return TRUE; - } - break; - // force default handling for some window messages when in windowed // mode, especially important for Tomb Raider case WM_MOVE: @@ -223,11 +213,9 @@ void ContextImpl::swapBuffers() { glFinish(); - try { - m_screenshot.captureScheduled(); - } catch (const std::exception &ex) { - ErrorUtils::warning("Can't capture screenshot", ex); - m_screenshot.schedule(false); + if (!m_screenshotScheduledPath.empty()) { + gl::Screenshot::capture(m_screenshotScheduledPath); + m_screenshotScheduledPath.clear(); } SwapBuffers(m_hdc); @@ -270,4 +258,9 @@ std::string ContextImpl::getBasePath() return path; } +void ContextImpl::scheduleScreenshot(const std::string &path) +{ + m_screenshotScheduledPath = path; +} + } diff --git a/src/glrage/ContextImpl.hpp b/src/glrage/ContextImpl.hpp index 6cfbe0a77..7d4553208 100644 --- a/src/glrage/ContextImpl.hpp +++ b/src/glrage/ContextImpl.hpp @@ -1,7 +1,6 @@ #pragma once #include "glrage/Context.hpp" -#include "glrage/Screenshot.hpp" namespace glrage { @@ -30,6 +29,7 @@ public: bool isRendered(); HWND getHWnd(); std::string getBasePath(); + void scheduleScreenshot(const std::string &path); private: ContextImpl(); @@ -66,9 +66,6 @@ private: // rendering flag bool m_render = false; - // screenshot object - Screenshot m_screenshot; - // DirectDraw display mode int32_t m_displayWidth = 0; int32_t m_displayHeight = 0; @@ -80,6 +77,9 @@ private: // Window size, controlled by SDL int32_t m_windowWidth = 0; int32_t m_windowHeight = 0; + + // whether to capture screenshot on next redraw + std::string m_screenshotScheduledPath; }; } diff --git a/src/glrage/Interop.cpp b/src/glrage/Interop.cpp index 766610950..dc7594d55 100644 --- a/src/glrage/Interop.cpp +++ b/src/glrage/Interop.cpp @@ -27,3 +27,10 @@ void GLRage_SetWindowSize(int width, int height) auto &context = GLRage::getContext(); context.setWindowSize(width, height); } + +bool GLRage_MakeScreenshot(const char *path) +{ + auto &context = GLRage::getContext(); + context.scheduleScreenshot(std::string(path)); + return true; +} diff --git a/src/glrage/Interop.hpp b/src/glrage/Interop.hpp index 1827af69f..4c4fb7c25 100644 --- a/src/glrage/Interop.hpp +++ b/src/glrage/Interop.hpp @@ -14,6 +14,8 @@ void GLRage_Detach(); void GLRage_SetFullscreen(bool fullscreen); void GLRage_SetWindowSize(int width, int height); +bool GLRage_MakeScreenshot(const char *path); + #ifdef __cplusplus } #endif diff --git a/src/glrage/Screenshot.cpp b/src/glrage/Screenshot.cpp deleted file mode 100644 index fb490b9de..000000000 --- a/src/glrage/Screenshot.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "glrage/Screenshot.hpp" - -#include "glrage/ContextImpl.hpp" -#include "glrage_gl/Screenshot.hpp" -#include "glrage_util/StringUtils.hpp" - -#include -#include -#include -#include -#include -#include - -namespace glrage { - -void Screenshot::schedule(bool schedule) -{ - m_schedule = schedule; -} - -void Screenshot::captureScheduled() -{ - if (m_schedule) { - capture(); - m_schedule = false; - } -} - -void Screenshot::capture() -{ - // find unused screenshot file name - std::string basePath = ContextImpl::instance().getBasePath(); - std::string path; - DWORD dwAttrib; - do { - std::string fileName = - StringUtils::format("screenshot%04d.tga", m_index++); - path = basePath + "\\" + fileName; - dwAttrib = GetFileAttributes(path.c_str()); - } while (dwAttrib != INVALID_FILE_ATTRIBUTES && m_index < 9999); - - // rather unlikely, but better safe than sorry - if (dwAttrib != INVALID_FILE_ATTRIBUTES) { - throw std::runtime_error("All available screenshot slots are used up!"); - } - - // actual capture - gl::Screenshot::capture(path); -} - -} diff --git a/src/glrage/Screenshot.hpp b/src/glrage/Screenshot.hpp deleted file mode 100644 index 0f43325d9..000000000 --- a/src/glrage/Screenshot.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include - -namespace glrage { - -class Screenshot { -public: - void schedule(bool schedule); - void captureScheduled(); - void capture(); - -private: - uint32_t m_index = 0; - bool m_schedule = false; -}; - -} diff --git a/src/glrage_gl/Screenshot.cpp b/src/glrage_gl/Screenshot.cpp index 06134064b..203517f3d 100644 --- a/src/glrage_gl/Screenshot.cpp +++ b/src/glrage_gl/Screenshot.cpp @@ -3,6 +3,11 @@ #include "glrage_util/ErrorUtils.hpp" #include "glrage_util/StringUtils.hpp" +extern "C" { +#include "memory.h" +#include "game/picture.h" +} + #include #include #include @@ -14,46 +19,34 @@ namespace glrage { namespace gl { -// heavily simplified Targa header struct for raw BGR(A) data -struct TGAHeader { - uint8_t blank1[2]; - uint8_t format; - uint8_t blank2[9]; - uint16_t width; - uint16_t height; - uint8_t depth; - uint8_t blank3; -}; - -void Screenshot::capture(const std::string &path) +bool Screenshot::capture(const std::string &path) { - // open screenshot file - std::ofstream file(path, std::ofstream::binary); - if (!file.good()) { - throw std::runtime_error( - "Can't open screenshot file '" + path - + "': " + ErrorUtils::getSystemErrorString()); - } + bool ret = false; - // copy framebuffer to local buffer GLint width; GLint height; - GLint depth = 3; - std::vector buffer; - capture(buffer, width, height, depth, GL_BGR, GL_UNSIGNED_BYTE, false); - // create Targa header - TGAHeader tgaHeader = { { 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0, - 0, 0 }; - tgaHeader.format = 2; - tgaHeader.width = width; - tgaHeader.height = height; - tgaHeader.depth = depth * 8; + Screenshot::capture( + buffer, width, height, 3, GL_RGB, GL_UNSIGNED_BYTE, true); - file.write(reinterpret_cast(&tgaHeader), sizeof(TGAHeader)); - file.write(reinterpret_cast(&buffer[0]), buffer.size()); - file.close(); + PICTURE *pic = Picture_Create(); + if (!pic) { + goto cleanup; + } + pic->width = width; + pic->height = height; + pic->data = static_cast(Memory_Alloc(width * height * 3)); + std::copy( + buffer.begin(), buffer.end(), reinterpret_cast(pic->data)); + + ret = Picture_SaveToFile(pic, path.c_str()); + +cleanup: + if (pic) { + Picture_Free(pic); + } + return ret; } void Screenshot::capture( diff --git a/src/glrage_gl/Screenshot.hpp b/src/glrage_gl/Screenshot.hpp index 62d9dd89f..fae1f7186 100644 --- a/src/glrage_gl/Screenshot.hpp +++ b/src/glrage_gl/Screenshot.hpp @@ -11,10 +11,10 @@ namespace gl { class Screenshot { public: - static void capture(const std::string &path); + static bool capture(const std::string &path); static void capture( std::vector &buffer, GLint &width, GLint &height, GLint depth, - GLenum format, GLenum type, bool vflip = false); + GLenum format, GLenum type, bool vflip); }; } diff --git a/src/specific/s_output.c b/src/specific/s_output.c index 482b16527..a1009c6ab 100644 --- a/src/specific/s_output.c +++ b/src/specific/s_output.c @@ -1543,3 +1543,8 @@ void S_Output_DownloadTextures(int32_t pages) m_SelectedTexture = -1; } + +bool S_Output_MakeScreenshot(const char *path) +{ + return GLRage_MakeScreenshot(path); +} diff --git a/src/specific/s_output.h b/src/specific/s_output.h index 0bbae2e4b..197b54d92 100644 --- a/src/specific/s_output.h +++ b/src/specific/s_output.h @@ -54,4 +54,6 @@ void S_Output_DrawLightningSegment( int x1, int y1, int z1, int thickness1, int x2, int y2, int z2, int thickness2); +bool S_Output_MakeScreenshot(const char *path); + #endif diff --git a/src/specific/s_shell.c b/src/specific/s_shell.c index a56a339d0..9652164e5 100644 --- a/src/specific/s_shell.c +++ b/src/specific/s_shell.c @@ -87,6 +87,13 @@ void S_Shell_SpinMessageLoop() break; } + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_PRINTSCREEN) { + Shell_MakeScreenshot(); + break; + } + break; + case SDL_WINDOWEVENT: switch (event.window.event) { case SDL_WINDOWEVENT_FOCUS_GAINED: