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

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

View File

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

View File

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

View File

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

View File

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