diff --git a/Makefile b/Makefile index 7dd4f02236..d5f2b4b9b7 100644 --- a/Makefile +++ b/Makefile @@ -278,11 +278,6 @@ ifeq ($(HAVE_ZLIB), 1) DEFINES += $(ZLIB_CFLAGS) endif -ifeq ($(HAVE_LIBPNG), 1) - LIBS += $(LIBPNG_LIBS) - DEFINES += $(LIBPNG_CFLAGS) -endif - ifeq ($(HAVE_FFMPEG), 1) OBJ += record/ffemu.o LIBS += $(AVCODEC_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) diff --git a/gfx/rpng/Makefile b/gfx/rpng/Makefile index 0771fee23a..b0d90b2d5a 100644 --- a/gfx/rpng/Makefile +++ b/gfx/rpng/Makefile @@ -3,7 +3,7 @@ TARGET := rpng SOURCES := $(wildcard *.c) OBJS := $(SOURCES:.c=.o) -CFLAGS += -Wall -pedantic -std=gnu99 -O0 -g -DHAVE_ZLIB +CFLAGS += -Wall -pedantic -std=gnu99 -O0 -g -DHAVE_ZLIB -DHAVE_ZLIB_DEFLATE all: $(TARGET) diff --git a/gfx/rpng/rpng.c b/gfx/rpng/rpng.c index d7f2b6d63d..f0d652f57c 100644 --- a/gfx/rpng/rpng.c +++ b/gfx/rpng/rpng.c @@ -500,7 +500,7 @@ static bool png_write_iend(FILE *file) return true; } -static void copy_rgba_line(uint8_t *dst, const uint32_t *src, unsigned width) +static void copy_argb_line(uint8_t *dst, const uint32_t *src, unsigned width) { for (unsigned i = 0; i < width; i++) { @@ -512,6 +512,16 @@ static void copy_rgba_line(uint8_t *dst, const uint32_t *src, unsigned width) } } +static void copy_bgr24_line(uint8_t *dst, const uint8_t *src, unsigned width) +{ + for (unsigned i = 0; i < width; i++, dst += 3, src += 3) + { + dst[2] = src[0]; + dst[1] = src[1]; + dst[0] = src[2]; + } +} + static unsigned count_zeroes(const uint8_t *data, size_t size) { unsigned cnt = 0; @@ -566,8 +576,8 @@ static unsigned filter_paeth(uint8_t *target, const uint8_t *line, const uint8_t return count_zeroes(target, width); } -bool rpng_save_image_argb(const char *path, const uint32_t *data, - unsigned width, unsigned height, unsigned pitch) +static bool rpng_save_image(const char *path, const uint8_t *data, + unsigned width, unsigned height, unsigned pitch, unsigned bpp) { bool ret = true; struct png_ihdr ihdr = {0}; @@ -582,7 +592,6 @@ bool rpng_save_image_argb(const char *path, const uint32_t *data, uint8_t *paeth_filtered = NULL; uint8_t *prev_encoded = NULL; uint8_t *encode_target = NULL; - unsigned bpp = sizeof(uint32_t); z_stream stream = {0}; @@ -596,11 +605,11 @@ bool rpng_save_image_argb(const char *path, const uint32_t *data, ihdr.width = width; ihdr.height = height; ihdr.depth = 8; - ihdr.color_type = 6; // RGBA + ihdr.color_type = bpp == sizeof(uint32_t) ? 6 : 2; // RGBA or RGB if (!png_write_ihdr(file, &ihdr)) GOTO_END_ERROR(); - encode_buf_size = (width * sizeof(uint32_t) + 1) * height; + encode_buf_size = (width * bpp + 1) * height; encode_buf = (uint8_t*)malloc(encode_buf_size); if (!encode_buf) GOTO_END_ERROR(); @@ -619,9 +628,12 @@ bool rpng_save_image_argb(const char *path, const uint32_t *data, encode_target = encode_buf; for (unsigned h = 0; h < height; - h++, encode_target += width * bpp, data += pitch >> 2) + h++, encode_target += width * bpp, data += pitch) { - copy_rgba_line(rgba_line, data, width); + if (bpp == sizeof(uint32_t)) + copy_argb_line(rgba_line, (const uint32_t*)data, width); + else + copy_bgr24_line(rgba_line, data, width); // Try every filtering method, and choose the method // which has most entries as zero. @@ -706,5 +718,18 @@ end: free(paeth_filtered); return ret; } + +bool rpng_save_image_argb(const char *path, const uint32_t *data, + unsigned width, unsigned height, unsigned pitch) +{ + return rpng_save_image(path, (const uint8_t*)data, width, height, pitch, sizeof(uint32_t)); +} + +bool rpng_save_image_bgr24(const char *path, const uint8_t *data, + unsigned width, unsigned height, unsigned pitch) +{ + return rpng_save_image(path, (const uint8_t*)data, width, height, pitch, 3); +} + #endif diff --git a/gfx/rpng/rpng.h b/gfx/rpng/rpng.h index 633c9b7881..32de0581dc 100644 --- a/gfx/rpng/rpng.h +++ b/gfx/rpng/rpng.h @@ -28,6 +28,8 @@ bool rpng_load_image_argb(const char *path, uint32_t **data, unsigned *width, un #ifdef HAVE_ZLIB_DEFLATE bool rpng_save_image_argb(const char *path, const uint32_t *data, unsigned width, unsigned height, unsigned pitch); +bool rpng_save_image_bgr24(const char *path, const uint8_t *data, + unsigned width, unsigned height, unsigned pitch); #endif #endif diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 1ad730e60d..1d2edede2f 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -132,7 +132,6 @@ if [ "$HAVE_SDL" = "no" ]; then fi check_pkgconf SDL_IMAGE SDL_image -check_pkgconf LIBPNG libpng 1.5 check_pkgconf ZLIB zlib if [ "$HAVE_THREADS" != 'no' ]; then @@ -206,6 +205,6 @@ check_macro NEON __ARM_NEON__ add_define_make OS "$OS" # Creates config.mk and config.h. -VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL GLES VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG LIBXML2 SDL_IMAGE LIBPNG ZLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE FREETYPE XVIDEO X11 XEXT XF86VM XINERAMA NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 SINC BSV_MOVIE VIDEOCORE NEON" +VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL GLES VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG LIBXML2 SDL_IMAGE ZLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE FREETYPE XVIDEO X11 XEXT XF86VM XINERAMA NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 SINC BSV_MOVIE VIDEOCORE NEON" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/screenshot.c b/screenshot.c index c1d99538c6..c4dee0c435 100644 --- a/screenshot.c +++ b/screenshot.c @@ -22,80 +22,15 @@ #include #include "general.h" #include "file.h" +#include "gfx/scaler/scaler.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif -#ifdef HAVE_LIBPNG -#include -#endif - -#ifdef HAVE_LIBPNG -static png_structp png_ptr; -static png_infop png_info_ptr; - -static void destroy_png(void) -{ - if (png_ptr) - png_destroy_write_struct(&png_ptr, &png_info_ptr); -} - -static bool write_header_png(FILE *file, unsigned width, unsigned height) -{ - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) - return false; - - if (setjmp(png_jmpbuf(png_ptr))) - goto error; - - png_info_ptr = png_create_info_struct(png_ptr); - if (!png_info_ptr) - goto error; - - png_init_io(png_ptr, file); - - png_set_IHDR(png_ptr, png_info_ptr, width, height, 8, - PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - - png_write_info(png_ptr, png_info_ptr); - png_set_compression_level(png_ptr, 2); - - return true; - -error: - destroy_png(); - return false; -} - -static void dump_lines_png(uint8_t **lines, int height) -{ - if (setjmp(png_jmpbuf(png_ptr))) - { - RARCH_ERR("PNG: dump_lines_png() failed!\n"); - goto end; - } - - // PNG is top-down, BMP is bottom-up. - for (int i = 0, j = height - 1; i < j; i++, j--) - { - uint8_t *tmp = lines[i]; - lines[i] = lines[j]; - lines[j] = tmp; - } - - png_set_rows(png_ptr, png_info_ptr, lines); - png_write_png(png_ptr, png_info_ptr, PNG_TRANSFORM_BGR, NULL); - png_write_end(png_ptr, NULL); - -end: - destroy_png(); -} - +#ifdef HAVE_ZLIB_DEFLATE +#include "gfx/rpng/rpng.h" #else - static bool write_header_bmp(FILE *file, unsigned width, unsigned height) { unsigned line_size = (width * 3 + 3) & ~3; @@ -130,8 +65,6 @@ static void dump_lines_file(FILE *file, uint8_t **lines, size_t line_size, unsig fwrite(lines[i], 1, line_size, file); } -#endif - static void dump_line_bgr(uint8_t *line, const uint8_t *src, unsigned width) { memcpy(line, src, width * 3); @@ -202,24 +135,21 @@ static void dump_content(FILE *file, const void *frame, dump_line_16(lines[j], u.u16, width); } -#ifdef HAVE_LIBPNG - dump_lines_png(lines, height); -#else dump_lines_file(file, lines, line_size, height); -#endif end: for (int i = 0; i < height; i++) free(lines[i]); free(lines); } +#endif void screenshot_generate_filename(char *filename, size_t size) { time_t cur_time; time(&cur_time); -#ifdef HAVE_LIBPNG +#ifdef HAVE_ZLIB_DEFLATE #define IMG_EXT "png" #else #define IMG_EXT "bmp" @@ -228,6 +158,7 @@ void screenshot_generate_filename(char *filename, size_t size) strftime(filename, size, "RetroArch-%m%d-%H%M%S." IMG_EXT, localtime(&cur_time)); } +// Take frame bottom-up. bool screenshot_dump(const char *folder, const void *frame, unsigned width, unsigned height, int pitch, bool bgr24) { @@ -237,6 +168,39 @@ bool screenshot_dump(const char *folder, const void *frame, screenshot_generate_filename(shotname, sizeof(shotname)); fill_pathname_join(filename, folder, shotname, sizeof(filename)); +#ifdef HAVE_ZLIB_DEFLATE + uint8_t *out_buffer = (uint8_t*)malloc(width * height * 3); + if (!out_buffer) + return false; + + struct scaler_ctx scaler = {0}; + scaler.in_width = width; + scaler.in_height = height; + scaler.out_width = width; + scaler.out_height = height; + scaler.in_stride = -pitch; + scaler.out_stride = width * 3; + scaler.out_fmt = SCALER_FMT_BGR24; + scaler.scaler_type = SCALER_TYPE_POINT; + + if (bgr24) + scaler.in_fmt = SCALER_FMT_BGR24; + else if (g_extern.system.pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888) + scaler.in_fmt = SCALER_FMT_ARGB8888; + else + scaler.in_fmt = SCALER_FMT_RGB565; + + scaler_ctx_gen_filter(&scaler); + scaler_ctx_scale(&scaler, out_buffer, (const uint8_t*)frame + ((int)height - 1) * pitch); + scaler_ctx_gen_reset(&scaler); + + RARCH_LOG("Using RPNG for PNG screenshots.\n"); + bool ret = rpng_save_image_bgr24(filename, out_buffer, width, height, width * 3); + if (!ret) + RARCH_ERR("Failed to take screenshot.\n"); + free(out_buffer); + return ret; +#else FILE *file = fopen(filename, "wb"); if (!file) { @@ -244,11 +208,7 @@ bool screenshot_dump(const char *folder, const void *frame, return false; } -#ifdef HAVE_LIBPNG - bool ret = write_header_png(file, width, height); -#else bool ret = write_header_bmp(file, width, height); -#endif if (ret) dump_content(file, frame, width, height, pitch, bgr24); @@ -257,5 +217,6 @@ bool screenshot_dump(const char *folder, const void *frame, fclose(file); return ret; +#endif }