mirror of
https://github.com/libretro/RetroArch
synced 2025-04-16 17:43:02 +00:00
Add screenshot support from backbuffer.
This commit is contained in:
parent
f088ba7bb4
commit
19f0c04021
6
driver.h
6
driver.h
@ -166,6 +166,10 @@ typedef struct video_driver
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void (*set_rotation)(void *data, unsigned rotation);
|
void (*set_rotation)(void *data, unsigned rotation);
|
||||||
|
void (*viewport_size)(void *data, unsigned *width, unsigned *height);
|
||||||
|
|
||||||
|
// Reads out in BGR byte order (24bpp).
|
||||||
|
bool (*read_viewport)(void *data, uint8_t *buffer);
|
||||||
} video_driver_t;
|
} video_driver_t;
|
||||||
|
|
||||||
typedef struct driver
|
typedef struct driver
|
||||||
@ -250,6 +254,8 @@ extern const input_driver_t input_xdk360;
|
|||||||
#define video_xml_shader_func(path) driver.video->xml_shader(driver.video_data, path)
|
#define video_xml_shader_func(path) driver.video->xml_shader(driver.video_data, path)
|
||||||
#define video_set_rotation_func(rotate) driver.video->set_rotation(driver.video_data, rotate)
|
#define video_set_rotation_func(rotate) driver.video->set_rotation(driver.video_data, rotate)
|
||||||
#define video_set_aspect_ratio_func(aspect_idx) driver.video->set_aspect_ratio(driver.video_data, aspect_idx)
|
#define video_set_aspect_ratio_func(aspect_idx) driver.video->set_aspect_ratio(driver.video_data, aspect_idx)
|
||||||
|
#define video_viewport_size_func(width, height) driver.video->viewport_size(driver.video_data, width, height)
|
||||||
|
#define video_read_viewport_func(buffer) driver.video->read_viewport(driver.video_data, buffer)
|
||||||
#define video_free_func() driver.video->free(driver.video_data)
|
#define video_free_func() driver.video->free(driver.video_data)
|
||||||
|
|
||||||
#define input_init_func() driver.input->init()
|
#define input_init_func() driver.input->init()
|
||||||
|
62
gfx/gl.c
62
gfx/gl.c
@ -440,20 +440,8 @@ void gl_set_projection(gl_t *gl, struct gl_ortho *ortho, bool allow_rotate)
|
|||||||
|
|
||||||
void gl_set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_full, bool allow_rotate)
|
void gl_set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_full, bool allow_rotate)
|
||||||
{
|
{
|
||||||
unsigned vp_x_temp, vp_y_temp, vp_width_temp, vp_height_temp;
|
unsigned x = 0, y = 0;
|
||||||
struct gl_ortho ortho = {0};
|
struct gl_ortho ortho = {0, 1, 0, 1, -1, 1};
|
||||||
|
|
||||||
vp_x_temp = 0;
|
|
||||||
vp_y_temp = 0;
|
|
||||||
vp_width_temp = width;
|
|
||||||
vp_height_temp = height;
|
|
||||||
|
|
||||||
ortho.left = 0.0f;
|
|
||||||
ortho.right = 1.0f;
|
|
||||||
ortho.bottom = 0.0f;
|
|
||||||
ortho.top = 1.0f;
|
|
||||||
ortho.znear = -1.0f;
|
|
||||||
ortho.zfar = 1.0f;
|
|
||||||
|
|
||||||
if (gl->keep_aspect && !force_full)
|
if (gl->keep_aspect && !force_full)
|
||||||
{
|
{
|
||||||
@ -464,11 +452,10 @@ void gl_set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_full,
|
|||||||
#ifdef RARCH_CONSOLE
|
#ifdef RARCH_CONSOLE
|
||||||
if (g_console.aspect_ratio_index == ASPECT_RATIO_CUSTOM)
|
if (g_console.aspect_ratio_index == ASPECT_RATIO_CUSTOM)
|
||||||
{
|
{
|
||||||
delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
|
x = g_console.viewports.custom_vp.x;
|
||||||
vp_x_temp = g_console.viewports.custom_vp.x;
|
y = g_console.viewports.custom_vp.y;
|
||||||
vp_y_temp = g_console.viewports.custom_vp.y;
|
width = g_console.viewports.custom_vp.width;
|
||||||
vp_width_temp = g_console.viewports.custom_vp.width;
|
height = g_console.viewports.custom_vp.height;
|
||||||
vp_height_temp = g_console.viewports.custom_vp.height;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
@ -481,32 +468,32 @@ void gl_set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_full,
|
|||||||
else if (device_aspect > desired_aspect)
|
else if (device_aspect > desired_aspect)
|
||||||
{
|
{
|
||||||
delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
|
delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
|
||||||
vp_x_temp = (GLint)(width * (0.5 - delta));
|
x = (unsigned)(width * (0.5 - delta));
|
||||||
vp_width_temp = (GLint)(2.0 * width * delta);
|
|
||||||
width = (unsigned)(2.0 * width * delta);
|
width = (unsigned)(2.0 * width * delta);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
|
delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
|
||||||
vp_y_temp = (GLint)(height * (0.5 - delta));
|
y = (unsigned)(height * (0.5 - delta));
|
||||||
vp_height_temp = (GLint)(2.0 * height * delta);
|
|
||||||
height = (unsigned)(2.0 * height * delta);
|
height = (unsigned)(2.0 * height * delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glViewport(vp_x_temp, vp_y_temp, vp_width_temp, vp_height_temp);
|
glViewport(x, y, width, height);
|
||||||
|
|
||||||
gl_set_projection(gl, &ortho, allow_rotate);
|
gl_set_projection(gl, &ortho, allow_rotate);
|
||||||
|
|
||||||
gl->vp_width = width;
|
gl->vp_width = width;
|
||||||
gl->vp_height = height;
|
gl->vp_height = height;
|
||||||
|
|
||||||
// Set last backbuffer viewport.
|
// Set last backbuffer viewport.
|
||||||
if (!force_full)
|
if (!force_full)
|
||||||
{
|
{
|
||||||
gl->vp_out_width = width;
|
gl->vp_out_width = width;
|
||||||
gl->vp_out_height = height;
|
gl->vp_out_height = height;
|
||||||
|
gl->vp_out_x = x;
|
||||||
|
gl->vp_out_y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
//RARCH_LOG("Setting viewport @ %ux%u\n", width, height);
|
//RARCH_LOG("Setting viewport @ %ux%u\n", width, height);
|
||||||
@ -1227,6 +1214,25 @@ static bool gl_xml_shader(void *data, const char *path)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void gl_viewport_size(void *data, unsigned *width, unsigned *height)
|
||||||
|
{
|
||||||
|
gl_t *gl = (gl_t*)data;
|
||||||
|
|
||||||
|
*width = gl->vp_out_width;
|
||||||
|
*height = gl->vp_out_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool gl_read_viewport(void *data, uint8_t *buffer)
|
||||||
|
{
|
||||||
|
gl_t *gl = (gl_t*)data;
|
||||||
|
|
||||||
|
glReadPixels(gl->vp_out_x, gl->vp_out_y,
|
||||||
|
gl->vp_out_width, gl->vp_out_height,
|
||||||
|
GL_BGR, GL_UNSIGNED_BYTE, buffer);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef RARCH_CONSOLE
|
#ifdef RARCH_CONSOLE
|
||||||
static void gl_start(void)
|
static void gl_start(void)
|
||||||
{
|
{
|
||||||
@ -1323,5 +1329,7 @@ const video_driver_t video_gl = {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
gl_set_rotation,
|
gl_set_rotation,
|
||||||
|
gl_viewport_size,
|
||||||
|
gl_read_viewport,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,6 +176,7 @@ typedef struct gl
|
|||||||
unsigned win_height;
|
unsigned win_height;
|
||||||
unsigned vp_width, vp_out_width;
|
unsigned vp_width, vp_out_width;
|
||||||
unsigned vp_height, vp_out_height;
|
unsigned vp_height, vp_out_height;
|
||||||
|
unsigned vp_out_x, vp_out_y;
|
||||||
unsigned last_width[TEXTURES];
|
unsigned last_width[TEXTURES];
|
||||||
unsigned last_height[TEXTURES];
|
unsigned last_height[TEXTURES];
|
||||||
unsigned tex_w, tex_h;
|
unsigned tex_w, tex_h;
|
||||||
@ -225,7 +226,7 @@ void gl_shader_use(unsigned index);
|
|||||||
void gl_set_projection(gl_t *gl, struct gl_ortho *ortho, bool allow_rotate);
|
void gl_set_projection(gl_t *gl, struct gl_ortho *ortho, bool allow_rotate);
|
||||||
void gl_set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_full, bool allow_rotate);
|
void gl_set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_full, bool allow_rotate);
|
||||||
|
|
||||||
void gl_init_fbo(gl_t * gl, unsigned width, unsigned height);
|
void gl_init_fbo(gl_t *gl, unsigned width, unsigned height);
|
||||||
void gl_deinit_fbo(gl_t *gl);
|
void gl_deinit_fbo(gl_t *gl);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
56
retroarch.c
56
retroarch.c
@ -91,22 +91,58 @@ static void set_fast_forward_button(bool new_button_state, bool new_hold_button_
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SCREENSHOTS
|
#ifdef HAVE_SCREENSHOTS
|
||||||
|
static bool take_screenshot_viewport(void)
|
||||||
|
{
|
||||||
|
unsigned width = 0, height = 0;
|
||||||
|
video_viewport_size_func(&width, &height);
|
||||||
|
|
||||||
|
if (!width || !height)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8_t *buffer = (uint8_t*)malloc(width * height * 3);
|
||||||
|
if (!buffer)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!video_read_viewport_func(buffer))
|
||||||
|
{
|
||||||
|
free(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!screenshot_dump(g_settings.screenshot_directory,
|
||||||
|
buffer,
|
||||||
|
width, height, width * 3, true))
|
||||||
|
{
|
||||||
|
free(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool take_screenshot_raw(void)
|
||||||
|
{
|
||||||
|
const uint16_t *data = (const uint16_t*)g_extern.frame_cache.data;
|
||||||
|
unsigned width = g_extern.frame_cache.width;
|
||||||
|
unsigned height = g_extern.frame_cache.height;
|
||||||
|
int pitch = g_extern.frame_cache.pitch;
|
||||||
|
|
||||||
|
return screenshot_dump(g_settings.screenshot_directory,
|
||||||
|
data + (height - 1) * (pitch >> 1),
|
||||||
|
width, height, -pitch, false);
|
||||||
|
}
|
||||||
|
|
||||||
static void take_screenshot(void)
|
static void take_screenshot(void)
|
||||||
{
|
{
|
||||||
if (!(*g_settings.screenshot_directory))
|
if (!(*g_settings.screenshot_directory))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
if (g_extern.frame_cache.data)
|
|
||||||
{
|
if (driver.video->read_viewport && driver.video->viewport_size)
|
||||||
const uint16_t *data = (const uint16_t*)g_extern.frame_cache.data;
|
ret = take_screenshot_viewport();
|
||||||
unsigned width = g_extern.frame_cache.width;
|
else if (g_extern.frame_cache.data)
|
||||||
unsigned height = g_extern.frame_cache.height;
|
ret = take_screenshot_raw();
|
||||||
size_t pitch = g_extern.frame_cache.pitch;
|
|
||||||
ret = screenshot_dump(g_settings.screenshot_directory,
|
|
||||||
data,
|
|
||||||
width, height, pitch);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *msg = NULL;
|
const char *msg = NULL;
|
||||||
if (ret)
|
if (ret)
|
||||||
|
70
screenshot.c
70
screenshot.c
@ -52,49 +52,70 @@ static void write_header(FILE *file, unsigned width, unsigned height)
|
|||||||
fwrite(header, 1, sizeof(header), file);
|
fwrite(header, 1, sizeof(header), file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_content(FILE *file, const uint16_t *frame, unsigned width, unsigned height, unsigned pitch)
|
static void dump_line_bgr(uint8_t *line, const uint8_t *src, unsigned width)
|
||||||
{
|
{
|
||||||
pitch >>= 1;
|
memcpy(line, src, width * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_line_16(uint8_t *line, const uint16_t *src, unsigned width)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
uint16_t pixel = *src++;
|
||||||
|
uint8_t b = (pixel >> 0) & 0x1f;
|
||||||
|
uint8_t g = (pixel >> 5) & 0x1f;
|
||||||
|
uint8_t r = (pixel >> 10) & 0x1f;
|
||||||
|
*line++ = (b << 3) | (b >> 2);
|
||||||
|
*line++ = (g << 3) | (g >> 2);
|
||||||
|
*line++ = (r << 3) | (r >> 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_content(FILE *file, const void *frame,
|
||||||
|
int width, int height, int pitch, bool bgr24)
|
||||||
|
{
|
||||||
|
const uint8_t *frame_bgr = (const uint8_t*)frame;
|
||||||
|
const uint16_t *frame16 = (const uint16_t*)frame;
|
||||||
|
|
||||||
|
if (!bgr24)
|
||||||
|
pitch /= 2;
|
||||||
|
|
||||||
unsigned line_size = (width * 3 + 3) & ~3;
|
unsigned line_size = (width * 3 + 3) & ~3;
|
||||||
uint8_t *line = (uint8_t*)calloc(1, line_size);
|
uint8_t *line = (uint8_t*)calloc(1, line_size);
|
||||||
if (!line)
|
if (!line)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// BMP likes reverse ordering for some reason :v
|
if (bgr24)
|
||||||
for (int j = height - 1; j >= 0; j--)
|
|
||||||
{
|
{
|
||||||
uint8_t *dst = line;
|
for (int j = 0; j < height; j++, frame_bgr += pitch)
|
||||||
const uint16_t *src = frame + j * pitch;
|
|
||||||
for (unsigned i = 0; i < width; i++)
|
|
||||||
{
|
{
|
||||||
uint16_t pixel = *src++;
|
dump_line_bgr(line, frame_bgr, width);
|
||||||
uint8_t b = (pixel >> 0) & 0x1f;
|
fwrite(line, 1, line_size, file);
|
||||||
uint8_t g = (pixel >> 5) & 0x1f;
|
}
|
||||||
uint8_t r = (pixel >> 10) & 0x1f;
|
}
|
||||||
*dst++ = (b << 3) | (b >> 2);
|
else // ARGB1555
|
||||||
*dst++ = (g << 3) | (g >> 2);
|
{
|
||||||
*dst++ = (r << 3) | (r >> 2);
|
for (int j = 0; j < height; j++, frame16 += pitch)
|
||||||
|
{
|
||||||
|
dump_line_16(line, frame16, width);
|
||||||
|
fwrite(line, 1, line_size, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite(line, 1, line_size, file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(line);
|
free(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool screenshot_dump(const char *folder, const uint16_t *frame,
|
bool screenshot_dump(const char *folder, const void *frame,
|
||||||
unsigned width, unsigned height, unsigned pitch)
|
unsigned width, unsigned height, int pitch, bool bgr24)
|
||||||
{
|
{
|
||||||
time_t cur_time;
|
time_t cur_time;
|
||||||
time(&cur_time);
|
time(&cur_time);
|
||||||
|
|
||||||
char timefmt[128];
|
char timefmt[128];
|
||||||
strftime(timefmt, sizeof(timefmt), "SSNES-%m%d-%H%M%S.bmp", localtime(&cur_time));
|
strftime(timefmt, sizeof(timefmt), "RetroArch-%m%d-%H%M%S.bmp", localtime(&cur_time));
|
||||||
|
|
||||||
char filename[256];
|
char filename[PATH_MAX];
|
||||||
strlcpy(filename, folder, sizeof(filename));
|
snprintf(filename, sizeof(filename), "%s/%s", folder, timefmt);
|
||||||
strlcat(filename, "/", sizeof(filename));
|
|
||||||
strlcat(filename, timefmt, sizeof(filename));
|
|
||||||
|
|
||||||
FILE *file = fopen(filename, "wb");
|
FILE *file = fopen(filename, "wb");
|
||||||
if (!file)
|
if (!file)
|
||||||
@ -104,9 +125,10 @@ bool screenshot_dump(const char *folder, const uint16_t *frame,
|
|||||||
}
|
}
|
||||||
|
|
||||||
write_header(file, width, height);
|
write_header(file, width, height);
|
||||||
dump_content(file, frame, width, height, pitch);
|
dump_content(file, frame, width, height, pitch, bgr24);
|
||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "boolean.h"
|
#include "boolean.h"
|
||||||
|
|
||||||
bool screenshot_dump(const char *folder, const uint16_t *frame,
|
bool screenshot_dump(const char *folder, const void *frame,
|
||||||
unsigned width, unsigned height, unsigned pitch);
|
unsigned width, unsigned height, int pitch, bool bgr24);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user