From 19f0c04021b6b964459142cadd8aff16e81b5589 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 8 Jun 2012 22:39:18 +0200 Subject: [PATCH] Add screenshot support from backbuffer. --- driver.h | 6 +++++ gfx/gl.c | 62 ++++++++++++++++++++++++------------------- gfx/gl_common.h | 3 ++- retroarch.c | 56 ++++++++++++++++++++++++++++++++------- screenshot.c | 70 ++++++++++++++++++++++++++++++++----------------- screenshot.h | 4 +-- 6 files changed, 137 insertions(+), 64 deletions(-) diff --git a/driver.h b/driver.h index 260e57109e..4117f654a8 100644 --- a/driver.h +++ b/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() diff --git a/gfx/gl.c b/gfx/gl.c index 518ff7cd27..d4efadd75b 100644 --- a/gfx/gl.c +++ b/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, }; diff --git a/gfx/gl_common.h b/gfx/gl_common.h index 6b43d7b8cf..9f113a4e83 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -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 diff --git a/retroarch.c b/retroarch.c index 098d771c98..26a2432667 100644 --- a/retroarch.c +++ b/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) diff --git a/screenshot.c b/screenshot.c index a62be875c3..f7add52c6e 100644 --- a/screenshot.c +++ b/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; } + diff --git a/screenshot.h b/screenshot.h index f27d176964..50c5c87652 100644 --- a/screenshot.h +++ b/screenshot.h @@ -19,7 +19,7 @@ #include #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