Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dosbox-staging/dosbox-staging.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkcgen <kcgen@users.noreply.github.com>2022-11-09 11:03:29 +0300
committerkcgen <1557255+kcgen@users.noreply.github.com>2022-11-10 06:10:21 +0300
commitbb5025d4c1f2f9b013a3b0704f8585cdf3fc7518 (patch)
treef4de9da6205a2d692632fbf3c765457db87d8df4
parent083dfb93c5d76a7bddfeb7580069447325030f01 (diff)
Support Alt+F5 screenshots of rendered output
-rw-r--r--include/hardware.h2
-rw-r--r--src/dos/program_intro.cpp6
-rw-r--r--src/gui/sdlmain.cpp152
-rw-r--r--src/hardware/hardware.cpp36
4 files changed, 169 insertions, 27 deletions
diff --git a/include/hardware.h b/include/hardware.h
index 383ba4fc9..9a09fb4bc 100644
--- a/include/hardware.h
+++ b/include/hardware.h
@@ -47,7 +47,7 @@ bool TS_Get_Address(Bitu& tsaddr, Bitu& tsirq, Bitu& tsdma);
extern uint8_t adlib_commandreg;
-std::string GetScreenshotFilename(const char *type, const char *ext);
+std::string CAPTURE_GetScreenshotFilename(const char *type, const char *ext);
FILE *OpenCaptureFile(const char *type, const char *ext);
void CAPTURE_AddWave(uint32_t freq, uint32_t len, int16_t * data);
diff --git a/src/dos/program_intro.cpp b/src/dos/program_intro.cpp
index b20f22f0d..2d6a1ffd8 100644
--- a/src/dos/program_intro.cpp
+++ b/src/dos/program_intro.cpp
@@ -32,7 +32,8 @@ void INTRO::WriteOutProgramIntroSpecial()
PRIMARY_MOD_PAD,
PRIMARY_MOD_NAME, // Ctrl/Cmd, for swap disk image
PRIMARY_MOD_PAD,
- PRIMARY_MOD_NAME, // Ctrl/Cmd, for screenshot
+ MMOD2_NAME, // Alt, to screenshot the rendered output
+ PRIMARY_MOD_NAME, // Ctrl/Cmd, to screenshot the image source
PRIMARY_MOD_PAD,
PRIMARY_MOD_NAME, // Ctrl/Cmd, for sound recording
PRIMARY_MOD_PAD,
@@ -212,7 +213,8 @@ void INTRO::AddMessages() {
"[color=yellow]%s+Pause[reset] Pause/Unpause emulator.\n"
"[color=yellow]%s+F1[reset] %s Start the [color=light-yellow]keymapper[reset].\n"
"[color=yellow]%s+F4[reset] %s Swap mounted disk image, scan for changes on all drives.\n"
- "[color=yellow]%s+F5[reset] %s Save a screenshot.\n"
+ "[color=yellow]%s+F5[reset] Screenshot the video's rendered output.\n"
+ "[color=yellow]%s+F5[reset] %s Screenshot the video's source image.\n"
"[color=yellow]%s+F6[reset] %s Start/Stop recording sound output to a wave file.\n"
"[color=yellow]%s+F7[reset] %s Start/Stop recording video output to a zmbv file.\n"
"[color=yellow]%s+F8[reset] %s Mute/Unmute the audio.\n"
diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp
index 10a38bfaa..d26c019da 100644
--- a/src/gui/sdlmain.cpp
+++ b/src/gui/sdlmain.cpp
@@ -46,6 +46,9 @@
#if C_OPENGL
#include <SDL_opengl.h>
#endif
+#if C_SDL_IMAGE
+# include <SDL_image.h>
+#endif
#include "../ints/int10.h"
#include "control.h"
@@ -54,6 +57,7 @@
#include "debug.h"
#include "fs_utils.h"
#include "gui_msgs.h"
+#include "hardware.h"
#include "joystick.h"
#include "keyboard.h"
#include "mapper.h"
@@ -199,8 +203,6 @@ PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = NULL;
SDL_Block sdl;
// Masks to be passed when creating SDL_Surface.
-// Remove ifndef if they'll be needed for MacOS X builds.
-#ifndef MACOSX
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
constexpr uint32_t RMASK = 0xff000000;
constexpr uint32_t GMASK = 0x00ff0000;
@@ -212,7 +214,6 @@ constexpr uint32_t GMASK = 0x0000ff00;
constexpr uint32_t BMASK = 0x00ff0000;
constexpr uint32_t AMASK = 0xff000000;
#endif
-#endif // !MACOSX
// Size and ratio constants
// ------------------------
@@ -3386,7 +3387,132 @@ static void set_output(Section *sec, bool should_stretch_pixels)
SDL_SetWindowOpacity(sdl.window, alpha);
}
-//extern void UI_Run(bool);
+static std::optional<SDL_Surface *> get_rendered_surface()
+{
+ // Variables common to all screen-modes
+ const auto renderer = SDL_GetRenderer(sdl.window);
+ const auto canvas = get_canvas_size(sdl.desktop.type);
+
+#if C_OPENGL
+ // Get the OpenGL-renderer surface
+ // -------------------------------
+ if (sdl.desktop.type == SCREEN_OPENGL) {
+ // Setup our OpenGL image properties
+ constexpr int gl_channels = 3; // RBG (no alpha)
+ constexpr int gl_bits_per_pixel = gl_channels * 8; // 8-bpp
+ const size_t bytes_per_row = gl_channels * canvas.w;
+
+ // Allocate a 24-bit surface to be populated
+ const auto surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ canvas.w,
+ canvas.h,
+ gl_bits_per_pixel,
+ RMASK,
+ GMASK,
+ BMASK,
+ AMASK);
+ if (!surface) {
+ LOG_WARNING("SDL: Failed creating a surface for OpenGL because %s",
+ SDL_GetError());
+ return {};
+ }
+ // Per OpenGL's documentation:
+ // The glReadPixels function starts at the lower left corner: (x
+ // + i, y + j) and iterates for 0 <= i < width and 0 <= j <
+ // height. It describes the pxiels as being "the i'th pixel in
+ // the j'th row". Pixels are returned in row-order from the
+ // lowest to the highest row, left to right in each row.
+ std::vector<uint8_t> pixels(bytes_per_row * canvas.h);
+ glReadPixels(0, 0, canvas.w, canvas.h, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());
+
+ // To match SDL's surface ordering, we invert the rows (outer)
+ // and lines (inner):
+ auto surface_pixels = static_cast<char *>(surface->pixels);
+ for (int j = 0; j < canvas.h; ++j) {
+ auto target_row = surface_pixels + surface->pitch * j;
+ const auto source_row = pixels.data() +
+ bytes_per_row * (canvas.h - j - 1);
+ memcpy(target_row, source_row, bytes_per_row);
+ }
+ return surface;
+ }
+#endif
+
+ // Get the SDL texture-renderer surface
+ // ------------------------------------
+ else if (sdl.desktop.type == SCREEN_TEXTURE) {
+ // Get the renderer's pixel format
+ SDL_RendererInfo rinfo;
+ if (SDL_GetRendererInfo(renderer, &rinfo) != 0) {
+ LOG_MSG("SDL: Failed to get a rendering info because %s",
+ SDL_GetError());
+ return {};
+ }
+ const auto pixel_format = rinfo.texture_formats[0];
+
+ // Create a 32-bit surface with color format matching the renderer
+ const auto surface = SDL_CreateRGBSurfaceWithFormat(
+ SDL_SWSURFACE, canvas.w, canvas.h, 32, pixel_format);
+ if (!surface || !renderer) {
+ LOG_WARNING("SDL: Failed creating a surface because %s",
+ SDL_GetError());
+ return {};
+ }
+ // Copy the pixels from the renderer into our new surface
+ if (SDL_RenderReadPixels(renderer,
+ nullptr,
+ surface->format->format,
+ surface->pixels,
+ surface->pitch) != 0) {
+ LOG_WARNING("SDL: Failed rendering to the surface because %s",
+ SDL_GetError());
+ SDL_FreeSurface(surface);
+ return {};
+ }
+ return surface;
+ }
+
+ // We're already in surface-mode, how convenient ;)
+ // -----------------------------------------------
+ else if (sdl.desktop.type == SCREEN_SURFACE) {
+ assert(sdl.surface);
+ // Simply return a copy
+ return SDL_ConvertSurfaceFormat(sdl.surface,
+ sdl.surface->format->format,
+ 0);
+ }
+
+ LOG_WARNING("SDL: unhandled screen-type (bug)");
+ return {};
+}
+
+static void screenshot_rendered_surface(bool pressed)
+{
+ if (!pressed)
+ return;
+
+ const auto surface = get_rendered_surface();
+ if (!surface)
+ return;
+
+#if C_SDL_IMAGE
+ const auto filename = CAPTURE_GetScreenshotFilename("Screenshot", ".png");
+ const auto is_saved = IMG_SavePNG(*surface, filename.c_str()) == 0;
+#else
+ const auto filename = CAPTURE_GetScreenshotFilename("Screenshot", ".bmp");
+ const auto is_saved = SDL_SaveBMP(*surface, filename.c_str()) == 0;
+#endif
+ SDL_FreeSurface(*surface);
+
+ if (is_saved)
+ LOG_MSG("SDL: Captured rendered output to %s", filename.c_str());
+ else
+ LOG_MSG("SDL: Failed capturing rendered output to %s because %s",
+ filename.c_str(),
+ SDL_GetError());
+}
+
+// extern void UI_Run(bool);
void Restart(bool pressed);
static void ApplyActiveSettings()
@@ -3542,12 +3668,18 @@ static void GUI_StartUp(Section *sec)
/* Get some Event handlers */
MAPPER_AddHandler(GFX_RequestExit, SDL_SCANCODE_F9, PRIMARY_MOD,
"shutdown", "Shutdown");
- MAPPER_AddHandler(SwitchFullScreen, SDL_SCANCODE_RETURN, MMOD2,
- "fullscr", "Fullscreen");
- MAPPER_AddHandler(Restart, SDL_SCANCODE_HOME, MMOD1 | MMOD2,
- "restart", "Restart");
- MAPPER_AddHandler(MOUSE_ToggleUserCapture, SDL_SCANCODE_F10, PRIMARY_MOD,
- "capmouse", "Cap Mouse");
+ MAPPER_AddHandler(screenshot_rendered_surface,
+ SDL_SCANCODE_F5,
+ MMOD2,
+ "rendshot",
+ "Rend Screenshot");
+ MAPPER_AddHandler(SwitchFullScreen, SDL_SCANCODE_RETURN, MMOD2, "fullscr", "Fullscreen");
+ MAPPER_AddHandler(Restart, SDL_SCANCODE_HOME, MMOD1 | MMOD2, "restart", "Restart");
+ MAPPER_AddHandler(MOUSE_ToggleUserCapture,
+ SDL_SCANCODE_F10,
+ PRIMARY_MOD,
+ "capmouse",
+ "Cap Mouse");
#if C_DEBUG
/* Pause binds with activate-debugger */
diff --git a/src/hardware/hardware.cpp b/src/hardware/hardware.cpp
index 5ee309a13..db8b9ec0f 100644
--- a/src/hardware/hardware.cpp
+++ b/src/hardware/hardware.cpp
@@ -92,30 +92,33 @@ static struct {
} capture = {};
-FILE * OpenCaptureFile(const char * type,const char * ext) {
- if(capturedir.empty()) {
+std::string CAPTURE_GetScreenshotFilename(const char *type, const char *ext)
+{
+ if (capturedir.empty()) {
LOG_MSG("Please specify a capture directory");
return 0;
}
+ char file_start[16];
+ dir_information *dir;
/* Find a filename to open */
- dir_information *dir = open_directory(capturedir.c_str());
+ dir = open_directory(capturedir.c_str());
if (!dir) {
// Try creating it first
if (create_dir(capturedir.c_str(), 0700, OK_IF_EXISTS) != 0) {
- LOG_MSG("ERROR: Can't create dir '%s': %s",
- capturedir.c_str(), safe_strerror(errno).c_str());
+ LOG_WARNING("Can't create dir '%s' for capturing: %s",
+ capturedir.c_str(),
+ safe_strerror(errno).c_str());
+ return 0;
}
-
dir = open_directory(capturedir.c_str());
if (!dir) {
- LOG_MSG("ERROR: Can't open dir '%s' for capturing %s",
- capturedir.c_str(), type);
+ LOG_MSG("Can't open dir %s for capturing %s",
+ capturedir.c_str(),
+ type);
return 0;
}
}
-
- char file_start[16];
safe_strcpy(file_start, RunningProgram);
lowcase(file_start);
strcat(file_start,"_");
@@ -137,12 +140,17 @@ FILE * OpenCaptureFile(const char * type,const char * ext) {
char file_name[CROSS_LEN];
sprintf(file_name, "%s%c%s%03d%s",
capturedir.c_str(), CROSS_FILESPLIT, file_start, last, ext);
- /* Open the actual file */
- FILE * handle=fopen(file_name,"wb");
+ return file_name;
+}
+
+FILE *OpenCaptureFile(const char *type, const char *ext)
+{
+ const auto file_name = CAPTURE_GetScreenshotFilename(type, ext);
+ FILE *handle = fopen(file_name.c_str(), "wb");
if (handle) {
- LOG_MSG("Capturing %s to %s",type,file_name);
+ LOG_MSG("Capturing %s to %s", type, file_name.c_str());
} else {
- LOG_MSG("Failed to open %s for capturing %s",file_name,type);
+ LOG_MSG("Failed to open %s for capturing %s", file_name.c_str(), type);
}
return handle;
}