Add screenshot support from backbuffer.

This commit is contained in:
Themaister 2012-06-08 22:39:18 +02:00
parent f088ba7bb4
commit 19f0c04021
6 changed files with 137 additions and 64 deletions

View File

@ -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()

View File

@ -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,
};

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -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