new cvar: scr_screenshot_png (and obvious PNG screenshotting code to support it)
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 31 Dec 2009 14:44:18 +0000 (14:44 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 31 Dec 2009 14:44:18 +0000 (14:44 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9760 d7cf8633-e32d-0410-b094-e92efae38249

cl_screen.c
gl_backend.h
image_png.c
image_png.h

index 8b34eda..9ccbfd6 100644 (file)
@@ -3,6 +3,7 @@
 #include "cl_video.h"
 #include "image.h"
 #include "jpeg.h"
+#include "image_png.h"
 #include "cl_collision.h"
 #include "libcurl.h"
 #include "csprogs.h"
@@ -30,6 +31,7 @@ cvar_t vid_conheight = {CVAR_SAVE, "vid_conheight", "480", "virtual height of 2D
 cvar_t vid_pixelheight = {CVAR_SAVE, "vid_pixelheight", "1", "adjusts vertical field of vision to account for non-square pixels (1280x1024 on a CRT monitor for example)"};
 cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","1", "save jpeg instead of targa"};
 cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9", "image quality of saved jpeg"};
+cvar_t scr_screenshot_png = {CVAR_SAVE, "scr_screenshot_png","0", "save png instead of targa"};
 cvar_t scr_screenshot_gammaboost = {CVAR_SAVE, "scr_screenshot_gammaboost","1", "gamma correction on saved screenshots and videos, 1.0 saves unmodified images"};
 cvar_t scr_screenshot_hwgamma = {CVAR_SAVE, "scr_screenshot_hwgamma","1", "apply the video gamma ramp to saved screenshots and videos"};
 // scr_screenshot_name is defined in fs.c
@@ -860,6 +862,7 @@ void CL_Screen_Init(void)
        Cvar_RegisterVariable (&vid_pixelheight);
        Cvar_RegisterVariable (&scr_screenshot_jpeg);
        Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
+       Cvar_RegisterVariable (&scr_screenshot_png);
        Cvar_RegisterVariable (&scr_screenshot_gammaboost);
        Cvar_RegisterVariable (&scr_screenshot_hwgamma);
        Cvar_RegisterVariable (&scr_screenshot_name_in_mapdir);
@@ -917,6 +920,7 @@ void SCR_ScreenShot_f (void)
        unsigned char *buffer2;
        unsigned char *buffer3;
        qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
+       qboolean png = (scr_screenshot_png.integer != 0) && !jpeg;
 
        if (Cmd_Argc() == 2)
        {
@@ -924,12 +928,23 @@ void SCR_ScreenShot_f (void)
                strlcpy(filename, Cmd_Argv(1), sizeof(filename));
                ext = FS_FileExtension(filename);
                if (!strcasecmp(ext, "jpg"))
+               {
                        jpeg = true;
+                       png = false;
+               }
                else if (!strcasecmp(ext, "tga"))
+               {
                        jpeg = false;
+                       png = false;
+               }
+               else if (!strcasecmp(ext, "png"))
+               {
+                       jpeg = false;
+                       png = true;
+               }
                else
                {
-                       Con_Printf("screenshot: supplied filename must end in .jpg or .tga\n");
+                       Con_Printf("screenshot: supplied filename must end in .jpg or .tga or .png\n");
                        return;
                }
        }
@@ -954,7 +969,7 @@ void SCR_ScreenShot_f (void)
 
                // find a file name to save it to
                for (;shotnumber < 1000000;shotnumber++)
-                       if (!FS_SysFileExists(va("%s/screenshots/%s%06d.tga", fs_gamedir, prefix_name, shotnumber)) && !FS_SysFileExists(va("%s/screenshots/%s%06d.jpg", fs_gamedir, prefix_name, shotnumber)))
+                       if (!FS_SysFileExists(va("%s/screenshots/%s%06d.tga", fs_gamedir, prefix_name, shotnumber)) && !FS_SysFileExists(va("%s/screenshots/%s%06d.jpg", fs_gamedir, prefix_name, shotnumber)) && !FS_SysFileExists(va("%s/screenshots/%s%06d.png", fs_gamedir, prefix_name, shotnumber)))
                                break;
                if (shotnumber >= 1000000)
                {
@@ -962,17 +977,27 @@ void SCR_ScreenShot_f (void)
                        return;
                }
 
-               dpsnprintf(filename, sizeof(filename), "screenshots/%s%06d.%s", prefix_name, shotnumber, jpeg ? "jpg" : "tga");
+               dpsnprintf(filename, sizeof(filename), "screenshots/%s%06d.%s", prefix_name, shotnumber, jpeg ? "jpg" : png ? "png" : "tga");
        }
 
        buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
        buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
        buffer3 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
 
-       if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, true))
+       if (SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, jpeg, png, true))
                Con_Printf("Wrote %s\n", filename);
        else
+       {
                Con_Printf("Unable to write %s\n", filename);
+               if(jpeg || png)
+               {
+                       if(SCR_ScreenShot (filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, false, false, true))
+                       {
+                               strlcpy(filename + strlen(filename) - 3, "tga", 4);
+                               Con_Printf("Wrote %s\n", filename);
+                       }
+               }
+       }
 
        Mem_Free (buffer1);
        Mem_Free (buffer2);
@@ -1344,7 +1369,7 @@ static void R_Envmap_f (void)
                R_Mesh_Start();
                R_RenderView();
                R_Mesh_Finish();
-               SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, vid.height - (r_refdef.view.y + r_refdef.view.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false);
+               SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, vid.height - (r_refdef.view.y + r_refdef.view.height), size, size, envmapinfo[j].flipx, envmapinfo[j].flipy, envmapinfo[j].flipdiagonaly, false, false, false);
        }
 
        Mem_Free (buffer1);
@@ -1427,7 +1452,7 @@ void SHOWLMP_drawall(void)
 ==============================================================================
 */
 
-qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *buffer2, unsigned char *buffer3, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg, qboolean gammacorrect)
+qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *buffer2, unsigned char *buffer3, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg, qboolean png, qboolean gammacorrect)
 {
        int     indices[3] = {0,1,2};
        qboolean ret;
@@ -1468,6 +1493,8 @@ qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *b
 
        if (jpeg)
                ret = JPEG_SaveImage_preflipped (filename, width, height, buffer2);
+       else if (png)
+               ret = PNG_SaveImage_preflipped (filename, width, height, buffer2);
        else
                ret = Image_WriteTGABGR_preflipped (filename, width, height, buffer2, buffer3);
 
@@ -1638,7 +1665,7 @@ void SCR_DrawScreen (void)
                        buffer1 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
                        buffer2 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
                        buffer3 = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3 + 18);
-                       SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, false, true);
+                       SCR_ScreenShot(filename, buffer1, buffer2, buffer3, 0, 0, vid.width, vid.height, false, false, false, false, false, true);
                        Mem_Free(buffer1);
                        Mem_Free(buffer2);
                        Mem_Free(buffer3);
index 50b76d4..72f2bc4 100644 (file)
@@ -101,7 +101,7 @@ void R_Mesh_ResetTextureState(void);
 void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const unsigned short *element3s, int bufferobject3i, int bufferobject3s);
 
 // saves a section of the rendered frame to a .tga or .jpg file
-qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *buffer2, unsigned char *buffer3, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg, qboolean gammacorrect);
+qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *buffer2, unsigned char *buffer3, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg, qboolean png, qboolean gammacorrect);
 // used by R_Envmap_f and internally in backend, clears the frame
 void R_ClearScreen(qboolean fogcolor);
 
index d57c189..4dc0fd9 100644 (file)
 static void                            (*qpng_set_sig_bytes)           (void*, int);
 static int                             (*qpng_sig_cmp)                         (const unsigned char*, size_t, size_t);
 static void*                   (*qpng_create_read_struct)      (const char*, void*, void(*)(void *png, const char *message), void(*)(void *png, const char *message));
+static void*                   (*qpng_create_write_struct)     (const char*, void*, void(*)(void *png, const char *message), void(*)(void *png, const char *message));
 static void*                   (*qpng_create_info_struct)      (void*);
 static void                            (*qpng_read_info)                       (void*, void*);
+static void                            (*qpng_set_compression_level)   (void*, int);
 static void                            (*qpng_set_expand)                      (void*);
 static void                            (*qpng_set_gray_1_2_4_to_8)     (void*);
 static void                            (*qpng_set_palette_to_rgb)      (void*);
 static void                            (*qpng_set_tRNS_to_alpha)       (void*);
 static void                            (*qpng_set_gray_to_rgb)         (void*);
 static void                            (*qpng_set_filler)                      (void*, unsigned int, int);
+static void                            (*qpng_set_IHDR)                        (void*, void*, unsigned long, unsigned long, int, int, int, int, int);
+static void                            (*qpng_set_packing)                     (void*);
+static void                            (*qpng_set_bgr)                         (void*);
+static int                             (*qpng_set_interlace_handling)  (void*);
 static void                            (*qpng_read_update_info)        (void*, void*);
 static void                            (*qpng_read_image)                      (void*, unsigned char**);
 static void                            (*qpng_read_end)                        (void*, void*);
 static void                            (*qpng_destroy_read_struct)     (void**, void**, void**);
+static void                            (*qpng_destroy_write_struct)    (void**, void**);
 static void                            (*qpng_set_read_fn)                     (void*, void*, void(*)(void *png, unsigned char *data, size_t length));
+static void                            (*qpng_set_write_fn)            (void*, void*, void(*)(void *png, unsigned char *data, size_t length), void(*)(void *png));
 static unsigned int            (*qpng_get_valid)                       (void*, void*, unsigned int);
 static unsigned int            (*qpng_get_rowbytes)            (void*, void*);
 static unsigned char   (*qpng_get_channels)            (void*, void*);
 static unsigned char   (*qpng_get_bit_depth)           (void*, void*);
 static unsigned int            (*qpng_get_IHDR)                        (void*, void*, unsigned long*, unsigned long*, int *, int *, int *, int *, int *);
 static char*                   (*qpng_get_libpng_ver)          (void*);
+static void                            (*qpng_write_info)                      (void*, void*);
+static void                            (*qpng_write_row)                       (void*, unsigned char*);
+static void                            (*qpng_write_end)                       (void*, void*);
 
 static dllfunction_t pngfuncs[] =
 {
        {"png_set_sig_bytes",           (void **) &qpng_set_sig_bytes},
        {"png_sig_cmp",                         (void **) &qpng_sig_cmp},
        {"png_create_read_struct",      (void **) &qpng_create_read_struct},
+       {"png_create_write_struct",     (void **) &qpng_create_write_struct},
        {"png_create_info_struct",      (void **) &qpng_create_info_struct},
        {"png_read_info",                       (void **) &qpng_read_info},
+       {"png_set_compression_level",   (void **) &qpng_set_compression_level},
        {"png_set_expand",                      (void **) &qpng_set_expand},
        {"png_set_gray_1_2_4_to_8",     (void **) &qpng_set_gray_1_2_4_to_8},
        {"png_set_palette_to_rgb",      (void **) &qpng_set_palette_to_rgb},
        {"png_set_tRNS_to_alpha",       (void **) &qpng_set_tRNS_to_alpha},
        {"png_set_gray_to_rgb",         (void **) &qpng_set_gray_to_rgb},
        {"png_set_filler",                      (void **) &qpng_set_filler},
+       {"png_set_IHDR",                        (void **) &qpng_set_IHDR},
+       {"png_set_packing",                     (void **) &qpng_set_packing},
+       {"png_set_bgr",                         (void **) &qpng_set_bgr},
+       {"png_set_interlace_handling",  (void **) &qpng_set_interlace_handling},
        {"png_read_update_info",        (void **) &qpng_read_update_info},
        {"png_read_image",                      (void **) &qpng_read_image},
        {"png_read_end",                        (void **) &qpng_read_end},
        {"png_destroy_read_struct",     (void **) &qpng_destroy_read_struct},
+       {"png_destroy_write_struct",    (void **) &qpng_destroy_write_struct},
        {"png_set_read_fn",                     (void **) &qpng_set_read_fn},
+       {"png_set_write_fn",            (void **) &qpng_set_write_fn},
        {"png_get_valid",                       (void **) &qpng_get_valid},
        {"png_get_rowbytes",            (void **) &qpng_get_rowbytes},
        {"png_get_channels",            (void **) &qpng_get_channels},
        {"png_get_bit_depth",           (void **) &qpng_get_bit_depth},
        {"png_get_IHDR",                        (void **) &qpng_get_IHDR},
        {"png_get_libpng_ver",          (void **) &qpng_get_libpng_ver},
+       {"png_write_info",                      (void **) &qpng_write_info},
+       {"png_write_row",                       (void **) &qpng_write_row},
+       {"png_write_end",                       (void **) &qpng_write_end},
        {NULL, NULL}
 };
 
@@ -135,7 +157,6 @@ void PNG_CloseLibrary (void)
        Sys_UnloadLibrary (&png_dll);
 }
 
-
 /*
 =================================================================
 
@@ -187,6 +208,7 @@ static struct
        int             Filter;
        //double        LastModified;
        //int           Transparent;
+       qfile_t *outfile;
 } my_png;
 
 //LordHavoc: removed __cdecl prefix, added overrun protection, and rewrote this to be more efficient
@@ -207,6 +229,15 @@ void PNG_fReadData(void *png, unsigned char *data, size_t length)
        //Com_HexDumpToConsole(data, (int)length);
 }
 
+void PNG_fWriteData(void *png, unsigned char *data, size_t length)
+{
+       FS_Write(my_png.outfile, data, length);
+}
+
+void PNG_fFlushData(void *png)
+{
+}
+
 void PNG_error_fn(void *png, const char *message)
 {
        Con_Printf("PNG_LoadImage: error: %s\n", message);
@@ -378,3 +409,99 @@ unsigned char *PNG_LoadImage_BGRA (const unsigned char *raw, int filesize)
        return imagedata;
 }
 
+/*
+=================================================================
+
+       PNG compression
+
+=================================================================
+*/
+
+#define Z_BEST_COMPRESSION 9
+#define PNG_INTERLACE_ADAM7 1
+#define PNG_FILTER_TYPE_BASE 0
+#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE
+#define PNG_COMPRESSION_TYPE_BASE 0
+#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE
+
+
+/*
+====================
+PNG_SaveImage_preflipped
+
+Save a preflipped PNG image to a file
+====================
+*/
+qboolean PNG_SaveImage_preflipped (const char *filename, int width, int height, unsigned char *data)
+{
+       unsigned int offset, linesize;
+       qfile_t* file = NULL;
+       void *png, *pnginfo;
+       unsigned char ioBuffer[8192];
+       int passes, i, j;
+
+       // No DLL = no JPEGs
+       if (!png_dll)
+       {
+               Con_Print("You need the libpng library to save PNG images\n");
+               return false;
+       }
+
+       png = (void *)qpng_create_write_struct(PNG_LIBPNG_VER_STRING, 0, PNG_error_fn, PNG_warning_fn);
+       if(!png)
+               return false;
+       pnginfo = (void *)qpng_create_info_struct(png);
+       if(!pnginfo)
+       {
+                qpng_destroy_write_struct(&png, NULL);
+                return false;
+       }
+
+       // this must be memset before the setjmp error handler, because it relies
+       // on the fields in this struct for cleanup
+       memset(&my_png, 0, sizeof(my_png));
+
+       // NOTE: this relies on jmp_buf being the first thing in the png structure
+       // created by libpng! (this is correct for libpng 1.2.x)
+#ifdef __cplusplus
+#if defined(MACOSX) || defined(WIN32)
+       if (setjmp((int *)png))
+#else
+       if (setjmp((__jmp_buf_tag *)png))
+#endif
+#else
+       if (setjmp(png))
+#endif
+       {
+               qpng_destroy_write_struct(&png, &pnginfo);
+               return false;
+       }
+
+       // Open the file
+       file = FS_OpenRealFile(filename, "wb", true);
+       if (!file)
+               return false;
+       my_png.outfile = file;
+       qpng_set_write_fn(png, ioBuffer, PNG_fWriteData, PNG_fFlushData);
+
+       qpng_set_compression_level(png, Z_BEST_COMPRESSION);
+
+       qpng_set_IHDR(png, pnginfo, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_ADAM7, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+       qpng_write_info(png, pnginfo);
+       qpng_set_packing(png);
+       qpng_set_bgr(png);
+
+       passes = qpng_set_interlace_handling(png);
+
+       linesize = width * 3;
+       offset = linesize * (height - 1);
+       for(i = 0; i < passes; ++i)
+               for(j = 0; j < height; ++j)
+                       qpng_write_row(png, &data[offset - j * linesize]);
+
+       qpng_write_end(png, NULL);
+       qpng_destroy_write_struct(&png, &pnginfo);
+
+       FS_Close (file);
+       return true;
+}
index 045b1cb..c521afd 100644 (file)
@@ -27,6 +27,7 @@
 qboolean PNG_OpenLibrary (void);
 void PNG_CloseLibrary (void);
 unsigned char* PNG_LoadImage_BGRA (const unsigned char *f, int filesize);
+qboolean PNG_SaveImage_preflipped (const char *filename, int width, int height, unsigned char *data);
 
 #endif