png screenshot support
authorJavier S. Pedro <maemo@javispedro.com>
Wed, 3 Aug 2011 00:54:08 +0000 (02:54 +0200)
committerJavier S. Pedro <maemo@javispedro.com>
Wed, 3 Aug 2011 00:54:08 +0000 (02:54 +0200)
Makefile
apu.cpp
platform/config.cpp
platform/platform.h
platform/sdl.cpp
screenshot.cpp
screenshot.h
snapshot.cpp

index ebda569..45f74b5 100644 (file)
--- 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 (file)
--- 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:
index acb44ec..753e5db 100644 (file)
@@ -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) {
index e3fe831..59f5549 100644 (file)
@@ -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);
 
index c15f9cc..cd540c7 100644 (file)
@@ -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);
index 157533a..7908f29 100644 (file)
   Nintendo Co., Limited and its subsidiary companies.
 *******************************************************************************/
 
-
-#ifdef HAVE_CONFIG_H
-       #include <config.h>
-#endif
 #include <stdio.h>
-
-#ifndef __WIN32__
-#include <unistd.h>
-#else
-#include <direct.h>
-#endif
 #include <string.h>
-#include <fcntl.h>
-
-#ifdef HAVE_LIBPNG
+#include <assert.h>
 #include <png.h>
-#endif
 
 #include "snes9x.h"
 #include "memmap.h"
 #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; y<height; y++, screen+=GFX.Pitch){
-        png_byte *rowpix = row_pointer;
-        for(int x=0; x<width; x++){
-            uint32 r, g, b;
-            DECOMPOSE_PIXEL((*(uint16 *)(screen+2*x)), r, g, b);
-            *(rowpix++) = r;
-            *(rowpix++) = g;
-            *(rowpix++) = b;
-            if (imgwidth!=width) {
-                *(rowpix++) = r;
-                *(rowpix++) = g;
-                *(rowpix++) = b;
-            }
-        }
-        png_write_row(png_ptr, row_pointer);
-        if(imgheight!=height)
-            png_write_row(png_ptr, row_pointer);
-    }
-
-    delete [] row_pointer;
-        
-    png_write_end(png_ptr, info_ptr);
-    png_destroy_write_struct(&png_ptr, &info_ptr);
-
-    fclose(fp);
-    fprintf(stderr, "%s saved.\n", fname);
-    return TRUE;
-#else
-    perror("Screenshot support not available (libpng was not found at build time)");
-       return FALSE;
-#endif
+typedef struct {
+       png_bytep buffer;
+       png_size_t size;
+       png_size_t buf_size;
+} ScreenshotPriv;
+
+static void write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+       ScreenshotPriv *p = (ScreenshotPriv*) png_get_io_ptr(png_ptr);
+       png_size_t new_size = p->size + 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;
 }
 
index ce44e42..15da6f6 100644 (file)
@@ -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
 
index b968a09..d2ee654 100644 (file)
 #include <ctype.h>
 #include <stdlib.h>
 
-#if defined(__unix) || defined(__linux) || defined(__sun) || defined(__DJGPP)
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#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);
 }