mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 16:09:47 +00:00
Add BGR24 saving mode.
This commit is contained in:
parent
26e0c3b06d
commit
291a0360a2
5
Makefile
5
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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
119
screenshot.c
119
screenshot.c
@ -22,80 +22,15 @@
|
||||
#include <string.h>
|
||||
#include "general.h"
|
||||
#include "file.h"
|
||||
#include "gfx/scaler/scaler.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBPNG
|
||||
#include <png.h>
|
||||
#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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user