X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=image_png.c;h=96fb4955049a0e51c26cf479ee3d1cc8261d5663;hp=6cf182a1e94a958f2491754940c60d1bed702ecc;hb=35a210359bf175b1798c62e196116a19b7181006;hpb=4a0c55c2f53d3b18ae28eddb822a0c670636c0f9 diff --git a/image_png.c b/image_png.c index 6cf182a1..96fb4955 100644 --- a/image_png.c +++ b/image_png.c @@ -30,58 +30,99 @@ #include "image.h" #include "image_png.h" + 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*); +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_filter) (void*, int, 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_set_read_fn) (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 unsigned int (*qpng_access_version_number) (void); // FIXME is this return type right? It is a png_uint_32 in libpng +static void (*qpng_write_info) (void*, void*); +static void (*qpng_write_row) (void*, unsigned char*); +static void (*qpng_write_end) (void*, void*); + +// libpng 1.4+ longjmp hack +typedef void (*qpng_longjmp_ptr) (jmp_buf, int); +static jmp_buf* (*qpng_set_longjmp_fn) (void *, qpng_longjmp_ptr, size_t); +#define qpng_jmpbuf_14(png_ptr) (*qpng_set_longjmp_fn((png_ptr), longjmp, sizeof (jmp_buf))) + +// libpng 1.2 longjmp hack +#define qpng_jmpbuf_12(png_ptr) (*((jmp_buf *) png_ptr)) + +// all version support +#define qpng_jmpbuf(png_ptr) \ + (qpng_set_longjmp_fn ? qpng_jmpbuf_14(png_ptr) : qpng_jmpbuf_12(png_ptr)) 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_filter", (void **) &qpng_set_filter}, {"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_access_version_number", (void **) &qpng_access_version_number}, + {"png_write_info", (void **) &qpng_write_info}, + {"png_write_row", (void **) &qpng_write_row}, + {"png_write_end", (void **) &qpng_write_end}, + {NULL, NULL} +}; +static dllfunction_t png14funcs[] = +{ + {"png_set_longjmp_fn", (void **) &qpng_set_longjmp_fn}, {NULL, NULL} }; // Handle for PNG DLL dllhandle_t png_dll = NULL; +dllhandle_t png14_dll = NULL; /* @@ -103,13 +144,23 @@ qboolean PNG_OpenLibrary (void) { const char* dllnames [] = { -#ifdef WIN64 - "libpng12_64.dll", -#elif WIN32 +#if WIN32 + "libpng16.dll", + "libpng16-16.dll", + "libpng15-15.dll", + "libpng15.dll", + "libpng14-14.dll", + "libpng14.dll", "libpng12.dll", #elif defined(MACOSX) + "libpng16.16.dylib", + "libpng15.15.dylib", + "libpng14.14.dylib", "libpng12.0.dylib", #else + "libpng16.so.16", + "libpng15.so.15", // WTF libtool guidelines anyone? + "libpng14.so.14", // WTF libtool guidelines anyone? "libpng12.so.0", "libpng.so", // FreeBSD #endif @@ -121,13 +172,14 @@ qboolean PNG_OpenLibrary (void) return true; // Load the DLL - if (! Sys_LoadLibrary (dllnames, &png_dll, pngfuncs)) - { - Con_Printf ("PNG support disabled\n"); + if(!Sys_LoadLibrary (dllnames, &png_dll, pngfuncs)) return false; - } - - Con_Printf ("PNG support enabled\n"); + if(qpng_access_version_number() / 100 >= 104) + if(!Sys_LoadLibrary (dllnames, &png14_dll, png14funcs)) + { + Sys_UnloadLibrary (&png_dll); + return false; + } return true; } @@ -141,10 +193,10 @@ Unload the PNG DLL */ void PNG_CloseLibrary (void) { + Sys_UnloadLibrary (&png14_dll); Sys_UnloadLibrary (&png_dll); } - /* ================================================================= @@ -153,7 +205,10 @@ void PNG_CloseLibrary (void) ================================================================= */ -#define PNG_LIBPNG_VER_STRING "1.2.4" +#define PNG_LIBPNG_VER_STRING_12 "1.2.4" +#define PNG_LIBPNG_VER_STRING_14 "1.4.0" +#define PNG_LIBPNG_VER_STRING_15 "1.5.0" +#define PNG_LIBPNG_VER_STRING_16 "1.6.0" #define PNG_COLOR_MASK_PALETTE 1 #define PNG_COLOR_MASK_COLOR 2 @@ -196,10 +251,11 @@ 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 -void PNG_fReadData(void *png, unsigned char *data, size_t length) +static void PNG_fReadData(void *png, unsigned char *data, size_t length) { size_t l; l = my_png.tmpBuflength - my_png.tmpi; @@ -216,21 +272,28 @@ void PNG_fReadData(void *png, unsigned char *data, size_t length) //Com_HexDumpToConsole(data, (int)length); } -void PNG_error_fn(void *png, const char *message) +static void PNG_fWriteData(void *png, unsigned char *data, size_t length) +{ + FS_Write(my_png.outfile, data, length); +} + +static void PNG_fFlushData(void *png) +{ +} + +static void PNG_error_fn(void *png, const char *message) { Con_Printf("PNG_LoadImage: error: %s\n", message); } -void PNG_warning_fn(void *png, const char *message) +static void PNG_warning_fn(void *png, const char *message) { Con_Printf("PNG_LoadImage: warning: %s\n", message); } -extern int image_width; -extern int image_height; - -unsigned char *PNG_LoadImage (const unsigned char *raw, int filesize, int matchwidth, int matchheight) +unsigned char *PNG_LoadImage_BGRA (const unsigned char *raw, int filesize, int *miplevel) { + unsigned int c; unsigned int y; void *png, *pnginfo; unsigned char *imagedata = NULL; @@ -244,7 +307,13 @@ unsigned char *PNG_LoadImage (const unsigned char *raw, int filesize, int matchw if(qpng_sig_cmp(raw, 0, filesize)) return NULL; - png = (void *)qpng_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (void *)PNG_error_fn, (void *)PNG_warning_fn); + png = (void *)qpng_create_read_struct( + (qpng_access_version_number() / 100 == 102) ? PNG_LIBPNG_VER_STRING_12 : + (qpng_access_version_number() / 100 == 104) ? PNG_LIBPNG_VER_STRING_14 : + (qpng_access_version_number() / 100 == 105) ? PNG_LIBPNG_VER_STRING_15 : + PNG_LIBPNG_VER_STRING_16, // nasty hack... whatever + 0, PNG_error_fn, PNG_warning_fn + ); if(!png) return NULL; @@ -254,15 +323,7 @@ unsigned char *PNG_LoadImage (const unsigned char *raw, int filesize, int matchw // 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 -#ifdef MACOSX - if (setjmp((int *)png)) -#else - if (setjmp((__jmp_buf_tag *)png)) -#endif -#else - if (setjmp(png)) -#endif + if (setjmp(qpng_jmpbuf(png))) { if (my_png.Data) Mem_Free(my_png.Data); @@ -294,7 +355,7 @@ unsigned char *PNG_LoadImage (const unsigned char *raw, int filesize, int matchw //my_png.Interlace = 0; //my_png.Compression = 0; //my_png.Filter = 0; - qpng_set_read_fn(png, ioBuffer, (void *)PNG_fReadData); + qpng_set_read_fn(png, ioBuffer, PNG_fReadData); qpng_read_info(png, pnginfo); qpng_get_IHDR(png, pnginfo, &my_png.Width, &my_png.Height,&my_png.BitDepth, &my_png.ColorType, &my_png.Interlace, &my_png.Compression, &my_png.Filter); @@ -311,20 +372,10 @@ unsigned char *PNG_LoadImage (const unsigned char *raw, int filesize, int matchw } #endif - if ((matchwidth && my_png.Width != (unsigned long)matchwidth) || (matchheight && my_png.Height != (unsigned long)matchheight)) - { - qpng_destroy_read_struct(&png, &pnginfo, 0); - return NULL; - } if (my_png.ColorType == PNG_COLOR_TYPE_PALETTE) qpng_set_palette_to_rgb(png); if (my_png.ColorType == PNG_COLOR_TYPE_GRAY || my_png.ColorType == PNG_COLOR_TYPE_GRAY_ALPHA) - { qpng_set_gray_to_rgb(png); - if (my_png.BitDepth < 8) - qpng_set_gray_1_2_4_to_8(png); - } - if (qpng_get_valid(png, pnginfo, PNG_INFO_tRNS)) qpng_set_tRNS_to_alpha(png); if (my_png.BitDepth == 8 && !(my_png.ColorType & PNG_COLOR_MASK_ALPHA)) @@ -351,12 +402,21 @@ unsigned char *PNG_LoadImage (const unsigned char *raw, int filesize, int matchw qpng_read_image(png, my_png.FRowPtrs); } else - Con_DPrintf("PNG_LoadImage : not enough memory\n"); + { + Con_Printf("PNG_LoadImage : not enough memory\n"); + qpng_destroy_read_struct(&png, &pnginfo, 0); + Mem_Free(my_png.FRowPtrs); + return NULL; + } Mem_Free(my_png.FRowPtrs); my_png.FRowPtrs = NULL; } else - Con_DPrintf("PNG_LoadImage : not enough memory\n"); + { + Con_Printf("PNG_LoadImage : not enough memory\n"); + qpng_destroy_read_struct(&png, &pnginfo, 0); + return NULL; + } qpng_read_end(png, pnginfo); qpng_destroy_read_struct(&png, &pnginfo, 0); @@ -368,9 +428,134 @@ unsigned char *PNG_LoadImage (const unsigned char *raw, int filesize, int matchw { Con_Printf ("PNG_LoadImage : bad color depth\n"); Mem_Free(imagedata); - imagedata = NULL; + return NULL; + } + + // swizzle RGBA to BGRA + for (y = 0;y < (unsigned int)(image_width*image_height*4);y += 4) + { + c = imagedata[y+0]; + imagedata[y+0] = imagedata[y+2]; + imagedata[y+2] = c; } return imagedata; } +/* +================================================================= + + PNG compression + +================================================================= +*/ + +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define PNG_INTERLACE_NONE 0 +#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 +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* +==================== +PNG_SaveImage_preflipped + +Save a preflipped PNG image to a file +==================== +*/ +qboolean PNG_SaveImage_preflipped (const char *filename, int width, int height, qboolean has_alpha, 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( + (qpng_access_version_number() / 100 == 102) ? PNG_LIBPNG_VER_STRING_12 : + (qpng_access_version_number() / 100 == 104) ? PNG_LIBPNG_VER_STRING_14 : + (qpng_access_version_number() / 100 == 105) ? PNG_LIBPNG_VER_STRING_15 : + PNG_LIBPNG_VER_STRING_16, // nasty hack... whatever + 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 +#ifdef WIN64 + if (setjmp((_JBTYPE *)png)) +#elif defined(MACOSX) || defined(WIN32) + if (setjmp((int *)png)) +#elif defined(__ANDROID__) + if (setjmp((long *)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_compression_level(png, Z_BEST_SPEED); + qpng_set_IHDR(png, pnginfo, width, height, 8, has_alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_ADAM7, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + qpng_set_filter(png, 0, PNG_NO_FILTERS); + qpng_write_info(png, pnginfo); + qpng_set_packing(png); + qpng_set_bgr(png); + + passes = qpng_set_interlace_handling(png); + + linesize = width * (has_alpha ? 4 : 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; +}