/* Copyright (C) 2006 Serge "(515)" Ziryukin, Forest "LordHavoc" Hale This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ //[515]: png implemented into DP ONLY FOR TESTING 2d stuff with csqc // so delete this bullshit :D // //LordHavoc: rewrote most of this. #include "quakedef.h" #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_info_struct) (void*); static void (*qpng_read_info) (void*, void*); 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_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 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 int*, unsigned int*, int *, int *, int *, int *, int *); static char* (*qpng_get_libpng_ver) (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_info_struct", (void **) &qpng_create_info_struct}, {"png_read_info", (void **) &qpng_read_info}, {"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_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_set_read_fn", (void **) &qpng_set_read_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}, {NULL, NULL} }; // Handle for PNG DLL dllhandle_t png_dll = NULL; /* ================================================================= DLL load & unload ================================================================= */ /* ==================== PNG_OpenLibrary Try to load the PNG DLL ==================== */ qboolean PNG_OpenLibrary (void) { const char* dllnames [] = { #ifdef WIN64 "libpng12_64.dll", #elif WIN32 "libpng12.dll", #elif defined(MACOSX) "libpng12.0.dylib", #else "libpng12.so.0", "libpng.so", // FreeBSD #endif NULL }; // Already loaded? if (png_dll) return true; // Load the DLL if (! Sys_LoadLibrary (dllnames, &png_dll, pngfuncs)) { Con_Printf ("PNG support disabled\n"); return false; } Con_Printf ("PNG support enabled\n"); return true; } /* ==================== PNG_CloseLibrary Unload the PNG DLL ==================== */ void PNG_CloseLibrary (void) { Sys_UnloadLibrary (&png_dll); } /* ================================================================= PNG decompression ================================================================= */ #define PNG_LIBPNG_VER_STRING "1.2.4" #define PNG_COLOR_MASK_PALETTE 1 #define PNG_COLOR_MASK_COLOR 2 #define PNG_COLOR_MASK_ALPHA 4 #define PNG_COLOR_TYPE_GRAY 0 #define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) #define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) #define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) #define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) #define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA #define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA #define PNG_INFO_tRNS 0x0010 // this struct is only used for status information during loading static struct { const unsigned char *tmpBuf; int tmpBuflength; int tmpi; //int FBgColor; //int FTransparent; unsigned int FRowBytes; //double FGamma; //double FScreenGamma; unsigned char **FRowPtrs; unsigned char *Data; //char *Title; //char *Author; //char *Description; int BitDepth; int BytesPerPixel; int ColorType; unsigned int Height; unsigned int Width; int Interlace; int Compression; int Filter; //double LastModified; //int Transparent; } 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) { size_t l; l = my_png.tmpBuflength - my_png.tmpi; if (l < length) { Con_Printf("PNG_fReadData: overrun by %i bytes\n", (int)(length - l)); // a read going past the end of the file, fill in the remaining bytes // with 0 just to be consistent memset(data + l, 0, length - l); length = l; } memcpy(data, my_png.tmpBuf + my_png.tmpi, length); my_png.tmpi += (int)length; //Com_HexDumpToConsole(data, (int)length); } 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) { 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 int y; void *png, *pnginfo; unsigned char *imagedata; unsigned char ioBuffer[8192]; // FIXME: register an error handler so that abort() won't be called on error // No DLL = no PNGs if (!png_dll) return NULL; 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); if(!png) return NULL; // 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 { qpng_destroy_read_struct(&png, &pnginfo, 0); return NULL; } // pnginfo = qpng_create_info_struct(png); if(!pnginfo) { qpng_destroy_read_struct(&png, &pnginfo, 0); return NULL; } qpng_set_sig_bytes(png, 0); memset(&my_png, 0, sizeof(my_png)); my_png.tmpBuf = raw; my_png.tmpBuflength = filesize; my_png.tmpi = 0; //my_png.Data = NULL; //my_png.FRowPtrs = NULL; //my_png.Height = 0; //my_png.Width = 0; my_png.ColorType = PNG_COLOR_TYPE_RGB; //my_png.Interlace = 0; //my_png.Compression = 0; //my_png.Filter = 0; qpng_set_read_fn(png, ioBuffer, (void *)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); if ((matchwidth && my_png.Width != (unsigned int)matchwidth) || (matchheight && my_png.Height != (unsigned int)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)) qpng_set_filler(png, 255, 1); 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_expand(png); qpng_read_update_info(png, pnginfo); my_png.FRowBytes = qpng_get_rowbytes(png, pnginfo); my_png.BytesPerPixel = qpng_get_channels(png, pnginfo); my_png.FRowPtrs = (unsigned char **)Mem_Alloc(tempmempool, my_png.Height * sizeof(*my_png.FRowPtrs)); if (my_png.FRowPtrs) { my_png.Data = (unsigned char *)Mem_Alloc(tempmempool, my_png.Height * my_png.FRowBytes); if(my_png.Data) { for(y = 0;y < my_png.Height;y++) my_png.FRowPtrs[y] = my_png.Data + y * my_png.FRowBytes; qpng_read_image(png, my_png.FRowPtrs); } else Con_DPrintf("PNG_LoadImage : not enough memory\n"); Mem_Free(my_png.FRowPtrs); } else Con_DPrintf("PNG_LoadImage : not enough memory\n"); qpng_read_end(png, pnginfo); qpng_destroy_read_struct(&png, &pnginfo, 0); image_width = my_png.Width; image_height = my_png.Height; imagedata = my_png.Data; if (my_png.BitDepth != 8) { Con_Printf ("PNG_LoadImage : bad color depth\n"); Mem_Free(imagedata); imagedata = NULL; } return imagedata; }