mirror of
https://github.com/libretro/RetroArch
synced 2025-04-15 14:42:27 +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
|
||||
|
||||
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;
|
||||
|
||||
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_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_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 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)
|
||||
{
|
||||
unsigned vp_x_temp, vp_y_temp, vp_width_temp, vp_height_temp;
|
||||
struct gl_ortho ortho = {0};
|
||||
|
||||
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;
|
||||
unsigned x = 0, y = 0;
|
||||
struct gl_ortho ortho = {0, 1, 0, 1, -1, 1};
|
||||
|
||||
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
|
||||
if (g_console.aspect_ratio_index == ASPECT_RATIO_CUSTOM)
|
||||
{
|
||||
delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
|
||||
vp_x_temp = g_console.viewports.custom_vp.x;
|
||||
vp_y_temp = g_console.viewports.custom_vp.y;
|
||||
vp_width_temp = g_console.viewports.custom_vp.width;
|
||||
vp_height_temp = g_console.viewports.custom_vp.height;
|
||||
x = g_console.viewports.custom_vp.x;
|
||||
y = g_console.viewports.custom_vp.y;
|
||||
width = g_console.viewports.custom_vp.width;
|
||||
height = g_console.viewports.custom_vp.height;
|
||||
}
|
||||
else
|
||||
#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)
|
||||
{
|
||||
delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
|
||||
vp_x_temp = (GLint)(width * (0.5 - delta));
|
||||
vp_width_temp = (GLint)(2.0 * width * delta);
|
||||
x = (unsigned)(width * (0.5 - delta));
|
||||
width = (unsigned)(2.0 * width * delta);
|
||||
}
|
||||
else
|
||||
{
|
||||
delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
|
||||
vp_y_temp = (GLint)(height * (0.5 - delta));
|
||||
vp_height_temp = (GLint)(2.0 * height * delta);
|
||||
delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
|
||||
y = (unsigned)(height * (0.5 - 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->vp_width = width;
|
||||
gl->vp_width = width;
|
||||
gl->vp_height = height;
|
||||
|
||||
// Set last backbuffer viewport.
|
||||
if (!force_full)
|
||||
{
|
||||
gl->vp_out_width = width;
|
||||
gl->vp_out_width = width;
|
||||
gl->vp_out_height = height;
|
||||
gl->vp_out_x = x;
|
||||
gl->vp_out_y = y;
|
||||
}
|
||||
|
||||
//RARCH_LOG("Setting viewport @ %ux%u\n", width, height);
|
||||
@ -1227,6 +1214,25 @@ static bool gl_xml_shader(void *data, const char *path)
|
||||
}
|
||||
#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
|
||||
static void gl_start(void)
|
||||
{
|
||||
@ -1323,5 +1329,7 @@ const video_driver_t video_gl = {
|
||||
#endif
|
||||
|
||||
gl_set_rotation,
|
||||
gl_viewport_size,
|
||||
gl_read_viewport,
|
||||
};
|
||||
|
||||
|
@ -176,6 +176,7 @@ typedef struct gl
|
||||
unsigned win_height;
|
||||
unsigned vp_width, vp_out_width;
|
||||
unsigned vp_height, vp_out_height;
|
||||
unsigned vp_out_x, vp_out_y;
|
||||
unsigned last_width[TEXTURES];
|
||||
unsigned last_height[TEXTURES];
|
||||
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_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);
|
||||
|
||||
#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
|
||||
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)
|
||||
{
|
||||
if (!(*g_settings.screenshot_directory))
|
||||
return;
|
||||
|
||||
bool ret = false;
|
||||
if (g_extern.frame_cache.data)
|
||||
{
|
||||
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;
|
||||
size_t pitch = g_extern.frame_cache.pitch;
|
||||
ret = screenshot_dump(g_settings.screenshot_directory,
|
||||
data,
|
||||
width, height, pitch);
|
||||
}
|
||||
|
||||
if (driver.video->read_viewport && driver.video->viewport_size)
|
||||
ret = take_screenshot_viewport();
|
||||
else if (g_extern.frame_cache.data)
|
||||
ret = take_screenshot_raw();
|
||||
|
||||
const char *msg = NULL;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
uint8_t *line = (uint8_t*)calloc(1, line_size);
|
||||
if (!line)
|
||||
return;
|
||||
|
||||
// BMP likes reverse ordering for some reason :v
|
||||
for (int j = height - 1; j >= 0; j--)
|
||||
if (bgr24)
|
||||
{
|
||||
uint8_t *dst = line;
|
||||
const uint16_t *src = frame + j * pitch;
|
||||
for (unsigned i = 0; i < width; i++)
|
||||
for (int j = 0; j < height; j++, frame_bgr += pitch)
|
||||
{
|
||||
uint16_t pixel = *src++;
|
||||
uint8_t b = (pixel >> 0) & 0x1f;
|
||||
uint8_t g = (pixel >> 5) & 0x1f;
|
||||
uint8_t r = (pixel >> 10) & 0x1f;
|
||||
*dst++ = (b << 3) | (b >> 2);
|
||||
*dst++ = (g << 3) | (g >> 2);
|
||||
*dst++ = (r << 3) | (r >> 2);
|
||||
dump_line_bgr(line, frame_bgr, width);
|
||||
fwrite(line, 1, line_size, file);
|
||||
}
|
||||
}
|
||||
else // ARGB1555
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
bool screenshot_dump(const char *folder, const uint16_t *frame,
|
||||
unsigned width, unsigned height, unsigned pitch)
|
||||
bool screenshot_dump(const char *folder, const void *frame,
|
||||
unsigned width, unsigned height, int pitch, bool bgr24)
|
||||
{
|
||||
time_t cur_time;
|
||||
time(&cur_time);
|
||||
|
||||
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];
|
||||
strlcpy(filename, folder, sizeof(filename));
|
||||
strlcat(filename, "/", sizeof(filename));
|
||||
strlcat(filename, timefmt, sizeof(filename));
|
||||
char filename[PATH_MAX];
|
||||
snprintf(filename, sizeof(filename), "%s/%s", folder, timefmt);
|
||||
|
||||
FILE *file = fopen(filename, "wb");
|
||||
if (!file)
|
||||
@ -104,9 +125,10 @@ bool screenshot_dump(const char *folder, const uint16_t *frame,
|
||||
}
|
||||
|
||||
write_header(file, width, height);
|
||||
dump_content(file, frame, width, height, pitch);
|
||||
dump_content(file, frame, width, height, pitch, bgr24);
|
||||
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <stdint.h>
|
||||
#include "boolean.h"
|
||||
|
||||
bool screenshot_dump(const char *folder, const uint16_t *frame,
|
||||
unsigned width, unsigned height, unsigned pitch);
|
||||
bool screenshot_dump(const char *folder, const void *frame,
|
||||
unsigned width, unsigned height, int pitch, bool bgr24);
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user