From 6f057a6bc74f3f422ffc8f8439b1b205ebef66e5 Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Wed, 3 Aug 2011 02:54:08 +0200 Subject: [PATCH] png screenshot support --- Makefile | 7 ++ apu.cpp | 17 +--- platform/config.cpp | 2 + platform/platform.h | 13 +-- platform/sdl.cpp | 8 ++ screenshot.cpp | 271 ++++++++++++++++++++++++++++++--------------------- screenshot.h | 3 +- snapshot.cpp | 23 ++--- 8 files changed, 193 insertions(+), 151 deletions(-) diff --git a/Makefile b/Makefile index ebda569..45f74b5 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,8 @@ else ifeq ($(ARCH),amd64) CONF_BUILD_ASM_SA1?=0 CONF_BUILD_MISC_ROUTINES?=misc_amd64 endif +# PNG screenshot support (requires libpng) +CONF_PNG?=1 # Hardware pixel doubling (in N8x0) CONF_XSP?=0 # Hildon Desktop compositing (in Fremantle) @@ -65,6 +67,11 @@ OBJS += platform/path.o platform/config.o OBJS += platform/sdl.o platform/sdlv.o platform/sdla.o platform/sdli.o OBJS += platform/sdlvscalers.o +ifeq ($(CONF_PNG), 1) + CPPFLAGS += -DCONF_PNG=1 $(shell pkg-config --cflags libpng) + LDLIBS += $(shell pkg-config --libs libpng) + OBJS += screenshot.o +endif ifeq ($(CONF_XSP), 1) CPPFLAGS += -DCONF_XSP=1 $(shell pkg-config --cflags xsp) LDLIBS += $(shell pkg-config --libs xsp) diff --git a/apu.cpp b/apu.cpp index f86acaa..911e64e 100644 --- a/apu.cpp +++ b/apu.cpp @@ -45,9 +45,6 @@ #include "soundux.h" #include "cpuexec.h" -/* For note-triggered SPC dump support */ -//#include "snapshot.h" - //extern int NoiseFreq [32]; #ifdef DEBUGGER void S9xTraceSoundDSP (const char *s, int i1 = 0, int i2 = 0, int i3 = 0, @@ -479,18 +476,6 @@ void S9xSetAPUDSP (uint8 byte) static uint8 KeyOn; static uint8 KeyOnPrev; int i; - -/* char str[64]; - if (byte!=0) - { - sprintf(str,"fr : %d\nwrite dsp %d\ncpu cycle=%d pc=%04X",framecpto,byte,CPU.Cycles,CPU.PC-CPU.PCBase); - S9xMessage(0,0,str); - gp32_pause(); - }*/ - - //extern uint8 spc_dump_dsp[0x100]; - - //spc_dump_dsp[reg] = byte; switch (reg) { @@ -713,7 +698,7 @@ void S9xSetAPUDSP (uint8 byte) S9xTraceSoundDSP ("\n"); #endif } - //spc_is_dumping_temp = byte; + return; case APU_VOL_LEFT + 0x00: diff --git a/platform/config.cpp b/platform/config.cpp index acb44ec..753e5db 100644 --- a/platform/config.cpp +++ b/platform/config.cpp @@ -128,6 +128,8 @@ static unsigned char actionNameToBit(const char *s) { return kActionQuit; } else if (strcasecmp(s, "fullscreen") == 0) { return kActionToggleFullscreen; + } else if (strcasecmp(s, "screenshot") == 0) { + return kActionScreenshot; } else if (strcasecmp(s, "quickload1") == 0) { return kActionQuickLoad1; } else if (strcasecmp(s, "quicksave1") == 0) { diff --git a/platform/platform.h b/platform/platform.h index e3fe831..59f5549 100644 --- a/platform/platform.h +++ b/platform/platform.h @@ -67,12 +67,13 @@ void S9xProcessEvents(bool block); // Input actions #define kActionNone 0 -#define kActionQuit (1U << 0) -#define kActionToggleFullscreen (1U << 1) -#define kActionQuickLoad1 (1U << 2) -#define kActionQuickSave1 (1U << 3) -#define kActionQuickLoad2 (1U << 4) -#define kActionQuickSave2 (1U << 5) +#define kActionQuit (1U << 0) +#define kActionToggleFullscreen (1U << 1) +#define kActionScreenshot (1U << 2) +#define kActionQuickLoad1 (1U << 4) +#define kActionQuickSave1 (1U << 5) +#define kActionQuickLoad2 (1U << 6) +#define kActionQuickSave2 (1U << 7) void S9xDoAction(unsigned char action); diff --git a/platform/sdl.cpp b/platform/sdl.cpp index c15f9cc..cd540c7 100644 --- a/platform/sdl.cpp +++ b/platform/sdl.cpp @@ -13,6 +13,7 @@ #include "soundux.h" #include "hacks.h" #include "snapshot.h" +#include "screenshot.h" #define kPollEveryNFrames 2 //Poll input only every this many frames @@ -300,6 +301,13 @@ void S9xDoAction(unsigned char action) S9xVideoToggleFullscreen(); } +#if CONF_PNG + if (action & kActionScreenshot) { + S9xSaveScreenshot(S9xGetFilename(FILE_SCREENSHOT)); + S9xSetInfoString("Screenshot taken"); + } +#endif + if (action & kActionQuickLoad1) { const char * file = S9xGetQuickSaveFilename(1); int result = S9xUnfreezeGame(file); diff --git a/screenshot.cpp b/screenshot.cpp index 157533a..7908f29 100644 --- a/screenshot.cpp +++ b/screenshot.cpp @@ -87,23 +87,10 @@ Nintendo Co., Limited and its subsidiary companies. *******************************************************************************/ - -#ifdef HAVE_CONFIG_H - #include -#endif #include - -#ifndef __WIN32__ -#include -#else -#include -#endif #include -#include - -#ifdef HAVE_LIBPNG +#include #include -#endif #include "snes9x.h" #include "memmap.h" @@ -111,104 +98,162 @@ #include "ppu.h" #include "screenshot.h" -bool8 S9xDoScreenshot(int width, int height){ -#ifdef HAVE_LIBPNG - FILE *fp; - png_structp png_ptr; - png_infop info_ptr; - png_color_8 sig_bit; - png_color pngpal[256]; - int imgwidth; - int imgheight; - const char *fname=S9xGetFilenameInc(".png"); - - Settings.TakeScreenshot=FALSE; - - if((fp=fopen(fname, "wb"))==NULL){ - perror("Screenshot failed"); - return FALSE; - } - - png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if(!png_ptr){ - fclose(fp); - unlink(fname); - return FALSE; - } - info_ptr=png_create_info_struct(png_ptr); - if(!info_ptr){ - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - fclose(fp); - unlink(fname); - return FALSE; - } - - if(setjmp(png_jmpbuf(png_ptr))){ - perror("Screenshot: setjmp"); - png_destroy_write_struct(&png_ptr, &info_ptr); - fclose(fp); - unlink(fname); - return FALSE; - } - - imgwidth=width; - imgheight=height; - if(Settings.StretchScreenshots==1){ - if(width<=256 && height>SNES_HEIGHT_EXTENDED) imgwidth=width<<1; - if(width>256 && height<=SNES_HEIGHT_EXTENDED) imgheight=height<<1; - } else if(Settings.StretchScreenshots==2){ - if(width<=256) imgwidth=width<<1; - if(height<=SNES_HEIGHT_EXTENDED) imgheight=height<<1; - } - - png_init_io(png_ptr, fp); - png_set_IHDR(png_ptr, info_ptr, imgwidth, imgheight, 8, - PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - - /* 5 bits per color */ - sig_bit.red=5; - sig_bit.green=5; - sig_bit.blue=5; - png_set_sBIT(png_ptr, info_ptr, &sig_bit); - png_set_shift(png_ptr, &sig_bit); - - png_write_info(png_ptr, info_ptr); - - png_set_packing(png_ptr); - - png_byte *row_pointer=new png_byte [png_get_rowbytes(png_ptr, info_ptr)]; - uint8 *screen=GFX.Screen; - for(int y=0; ysize + length; + + if (!p->buffer) { + p->buffer = (png_bytep) malloc(p->buf_size); + p->buf_size = new_size + 256; + p->size = new_size; + if (!p->buffer) { + png_error(png_ptr, "Out of memory"); + return; + } + } + + if (new_size > p->buf_size) { + png_size_t new_buf_size = p->buf_size; + do { + new_buf_size *= 2; + } while (new_size > new_buf_size); + png_bytep new_buf = (png_bytep) realloc(p->buffer, new_buf_size); + if (!new_buf) { + png_error(png_ptr, "Out of memory"); + return; + } + p->buffer = new_buf; + p->buf_size = new_buf_size; + } + + memcpy(p->buffer + p->size, data, length); + p->size += length; + +} + +static void flush_data(png_structp png_ptr) +{ + ScreenshotPriv *p = (ScreenshotPriv*) png_get_io_ptr(png_ptr); + if (p->size < p->buf_size) { + png_bytep newbuf = (png_bytep) realloc(p->buffer, p->size); + if (!newbuf) { + png_error(png_ptr, "Out of memory"); + return; + } + p->buffer = newbuf; + p->buf_size = p->size; + } +} + +static void write_png(png_structp png_ptr, png_infop info_ptr) +{ + const int width = IPPU.RenderedScreenWidth, height = IPPU.RenderedScreenHeight; + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + /* 5 bits per color is around what SNES is capable */ + png_color_8 sig_bit; + sig_bit.red = 5; + sig_bit.green = 5; + sig_bit.blue = 5; + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_shift(png_ptr, &sig_bit); + + png_write_info(png_ptr, info_ptr); + + png_byte *row_data = new png_byte[png_get_rowbytes(png_ptr, info_ptr)]; + uint8 *screen = GFX.Screen; + for (int y = 0; y < height; y++, screen += GFX.Pitch) { + png_byte *pix_data = row_data; + for (int x = 0; x < width; x++) { + uint32 r, g, b; + DECOMPOSE_PIXEL((*(uint16*)(screen+2*x)), r, g, b); + *(pix_data++) = r; + *(pix_data++) = g; + *(pix_data++) = b; + } + png_write_row(png_ptr, row_data); + } + delete row_data; + + png_write_end(png_ptr, info_ptr); +} + +void * S9xScreenshot(size_t *size, bool compression) +{ + ScreenshotPriv priv = { 0 }; + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) { + return 0; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + goto clean_png; + } + + if(setjmp(png_jmpbuf(png_ptr))){ + if (priv.buffer) { + free(priv.buffer); + priv.buffer = 0; + } + priv.size = 0; + goto clean_png; + } + + png_set_write_fn(png_ptr, &priv, write_data, flush_data); + png_set_compression_level(png_ptr, Z_NO_COMPRESSION); + + write_png(png_ptr, info_ptr); + +clean_png: + png_destroy_write_struct(&png_ptr, &info_ptr); + + if (priv.size && size) *size = priv.size; + return priv.buffer; +} + +bool S9xSaveScreenshot(const char * file) +{ + FILE *f = fopen(file, "wb"); + if (!f) return false; + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) { + return 0; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + fclose(f); + png_destroy_write_struct(&png_ptr, 0); + return false; + } + + if(setjmp(png_jmpbuf(png_ptr))){ + fclose(f); + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + png_init_io(png_ptr, f); + png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); + + write_png(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(f); + + return true; } diff --git a/screenshot.h b/screenshot.h index ce44e42..15da6f6 100644 --- a/screenshot.h +++ b/screenshot.h @@ -90,7 +90,8 @@ #ifndef SCREENSHOT_H #define SCREENSHOT_H -bool8 S9xDoScreenshot(int width, int height); +void * S9xScreenshot(size_t *size, bool compression); +bool S9xSaveScreenshot(const char * file); #endif diff --git a/snapshot.cpp b/snapshot.cpp index b968a09..d2ee654 100644 --- a/snapshot.cpp +++ b/snapshot.cpp @@ -43,12 +43,6 @@ #include #include -#if defined(__unix) || defined(__linux) || defined(__sun) || defined(__DJGPP) -#include -#include -#include -#endif - #include "snapshot.h" #include "memmap.h" #include "snes9x.h" @@ -65,8 +59,6 @@ #define dprintf(...) /* disabled */ -extern uint8 *SRAM; - #ifdef ZSNES_FX START_EXTERN_C void S9xSuperFXPreSaveState (); @@ -398,6 +390,8 @@ static STREAM ss_st; static void Freeze (); static int Unfreeze (); + +static void FreezeSnapshot (const char *name); static void FreezeStruct (const char *name, void *base, FreezeData *fields, int num_fields); static void FreezeBlock (const char *name, uint8 *block, int size); @@ -678,7 +672,7 @@ static int Unfreeze() return (SUCCESS); } -int FreezeSize (int size, int type) +static int FreezeSize (int size, int type) { switch (type) { @@ -707,8 +701,7 @@ void FreezeStruct(const char *name, void *base, FreezeData *fields, fields [i].type); } -// uint8 *block = new uint8 [len]; - uint8 *block = (uint8*)malloc(len); + uint8 *block = new uint8[len]; uint8 *ptr = block; uint16 word; uint32 dword; @@ -777,7 +770,7 @@ void FreezeStruct(const char *name, void *base, FreezeData *fields, FreezeBlock (name, block, len); - free(block); + delete block; } void FreezeBlock (const char *name, uint8 *block, int size) @@ -804,7 +797,7 @@ int UnfreezeStruct (const char *name, void *base, FreezeData *fields, fields [i].type); } - uint8 *block = (uint8*)malloc(len); + uint8 *block = new uint8 [len]; uint8 *ptr = block; uint16 word; uint32 dword; @@ -878,8 +871,8 @@ int UnfreezeStruct (const char *name, void *base, FreezeData *fields, } } -// delete block; - free(block); + delete block; + return (result); } -- 1.7.9.5