2007-01-15 12:09:25 -08:00
/*
This file is part of Warzone 2100.
Copyright ( C ) 1999 - 2004 Eidos Interactive
2011-02-25 09:45:27 -08:00
Copyright ( C ) 2005 - 2011 Warzone 2100 Project
2007-01-15 12:09:25 -08:00
Warzone 2100 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 .
Warzone 2100 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 Warzone 2100 ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
2006-11-06 06:40:07 -08:00
2006-06-02 12:34:58 -07:00
# include "lib/framework/frame.h"
2009-06-04 10:50:13 -07:00
# include "lib/framework/debug.h"
2010-07-01 12:49:53 -07:00
# include "jpeg_encoder.h"
2007-04-30 07:09:33 -07:00
# include "png_util.h"
2007-04-29 15:19:21 -07:00
# include <png.h>
# include <physfs.h>
2007-06-28 10:47:08 -07:00
2007-05-01 13:34:54 -07:00
# define PNG_BYTES_TO_CHECK 8
2007-06-28 10:47:08 -07:00
2007-04-29 16:00:38 -07:00
// PNG callbacks
2007-06-28 10:47:08 -07:00
static void wzpng_read_data ( png_structp ctx , png_bytep area , png_size_t size )
{
2007-04-16 15:29:44 -07:00
PHYSFS_file * fileHandle = ( PHYSFS_file * ) png_get_io_ptr ( ctx ) ;
2007-06-28 10:47:08 -07:00
2007-04-16 15:29:44 -07:00
PHYSFS_read ( fileHandle , area , 1 , size ) ;
2007-06-28 10:47:08 -07:00
}
2007-04-29 16:00:38 -07:00
static void wzpng_write_data ( png_structp png_ptr , png_bytep data , png_size_t length )
{
PHYSFS_file * fileHandle = ( PHYSFS_file * ) png_get_io_ptr ( png_ptr ) ;
PHYSFS_write ( fileHandle , data , length , 1 ) ;
}
static void wzpng_flush_data ( png_structp png_ptr )
{
PHYSFS_file * fileHandle = ( PHYSFS_file * ) png_get_io_ptr ( png_ptr ) ;
PHYSFS_flush ( fileHandle ) ;
}
// End of PNG callbacks
static inline void PNGReadCleanup ( png_infop * info_ptr , png_structp * png_ptr , PHYSFS_file * fileHandle )
2007-04-16 10:34:11 -07:00
{
if ( * info_ptr ! = NULL )
png_destroy_info_struct ( * png_ptr , info_ptr ) ;
2007-04-16 10:49:15 -07:00
if ( * png_ptr ! = NULL )
2007-04-16 10:34:11 -07:00
png_destroy_read_struct ( png_ptr , NULL , NULL ) ;
2007-04-16 15:29:44 -07:00
if ( fileHandle ! = NULL )
PHYSFS_close ( fileHandle ) ;
2007-04-16 10:34:11 -07:00
}
2007-04-29 16:00:38 -07:00
static inline void PNGWriteCleanup ( png_infop * info_ptr , png_structp * png_ptr , PHYSFS_file * fileHandle )
{
if ( * info_ptr ! = NULL )
png_destroy_info_struct ( * png_ptr , info_ptr ) ;
if ( * png_ptr ! = NULL )
png_destroy_write_struct ( png_ptr , NULL ) ;
if ( fileHandle ! = NULL )
PHYSFS_close ( fileHandle ) ;
}
2007-04-28 13:21:16 -07:00
2011-03-12 17:32:15 -08:00
bool iV_loadImage_PNG ( const char * fileName , iV_Image * image )
2007-06-28 10:47:08 -07:00
{
2007-04-17 05:23:54 -07:00
unsigned char PNGheader [ PNG_BYTES_TO_CHECK ] ;
2007-04-16 15:29:44 -07:00
PHYSFS_sint64 readSize ;
2007-04-23 07:14:40 -07:00
png_structp png_ptr = NULL ;
png_infop info_ptr = NULL ;
2007-04-16 15:29:44 -07:00
// Open file
PHYSFS_file * fileHandle = PHYSFS_openRead ( fileName ) ;
if ( fileHandle = = NULL )
{
2007-04-19 11:04:41 -07:00
debug ( LOG_ERROR , " pie_PNGLoadFile: PHYSFS_openRead(%s) failed with error: %s \n " , fileName , PHYSFS_getLastError ( ) ) ;
2007-04-29 16:00:38 -07:00
PNGReadCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
2008-03-24 09:51:17 -07:00
return false ;
2007-04-16 15:29:44 -07:00
}
2006-06-03 16:46:03 -07:00
2007-04-16 15:29:44 -07:00
// Read PNG header from file
readSize = PHYSFS_read ( fileHandle , PNGheader , 1 , PNG_BYTES_TO_CHECK ) ;
if ( readSize < PNG_BYTES_TO_CHECK )
{
2007-04-19 11:04:41 -07:00
debug ( LOG_ERROR , " pie_PNGLoadFile: PHYSFS_read(%s) failed with error: %s \n " , fileName , PHYSFS_getLastError ( ) ) ;
2007-04-29 16:00:38 -07:00
PNGReadCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
2008-03-24 09:51:17 -07:00
return false ;
2007-04-16 15:29:44 -07:00
}
2007-06-28 10:47:08 -07:00
2007-04-16 15:29:44 -07:00
// Verify the PNG header to be correct
2007-05-01 13:34:54 -07:00
if ( png_sig_cmp ( PNGheader , 0 , PNG_BYTES_TO_CHECK ) )
{
2007-04-19 11:04:41 -07:00
debug ( LOG_3D , " pie_PNGLoadMem: Did not recognize PNG header in %s " , fileName ) ;
2007-04-29 16:00:38 -07:00
PNGReadCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
2008-03-24 09:51:17 -07:00
return false ;
2007-06-28 10:47:08 -07:00
}
2007-04-16 15:29:44 -07:00
png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING , NULL , NULL , NULL ) ;
2007-06-28 10:47:08 -07:00
if ( png_ptr = = NULL ) {
debug ( LOG_3D , " pie_PNGLoadMem: Unable to create png struct " ) ;
2007-04-29 16:00:38 -07:00
PNGReadCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
2008-03-24 09:51:17 -07:00
return false ;
2007-06-28 10:47:08 -07:00
}
info_ptr = png_create_info_struct ( png_ptr ) ;
if ( info_ptr = = NULL ) {
debug ( LOG_3D , " pie_PNGLoadMem: Unable to create png info struct " ) ;
2007-04-29 16:00:38 -07:00
PNGReadCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
2008-03-24 09:51:17 -07:00
return false ;
2007-06-28 10:47:08 -07:00
}
2007-04-16 15:29:44 -07:00
// Set libpng's failure jump position to the if branch,
// setjmp evaluates to false so the else branch will be executed at first
2007-06-28 10:47:08 -07:00
if ( setjmp ( png_jmpbuf ( png_ptr ) ) ) {
2007-04-19 11:04:41 -07:00
debug ( LOG_3D , " pie_PNGLoadMem: Error decoding PNG data in %s " , fileName ) ;
2007-04-29 16:00:38 -07:00
PNGReadCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
2008-03-24 09:51:17 -07:00
return false ;
2007-05-01 13:34:54 -07:00
}
2007-06-28 10:47:08 -07:00
2007-05-01 13:34:54 -07:00
// Tell libpng how many byte we already read
png_set_sig_bytes ( png_ptr , PNG_BYTES_TO_CHECK ) ;
2007-06-28 10:47:08 -07:00
2007-05-01 13:34:54 -07:00
/* Set up the input control */
png_set_read_fn ( png_ptr , fileHandle , wzpng_read_data ) ;
2007-06-28 10:47:08 -07:00
2007-05-01 13:34:54 -07:00
// Most of the following transformations are seemingly not needed
// Filler is, however, for an unknown reason required for tertilesc[23]
2007-06-28 10:47:08 -07:00
2007-05-01 13:34:54 -07:00
/* tell libpng to strip 16 bit/color files down to 8 bits/color */
2007-07-29 10:42:22 -07:00
png_set_strip_16 ( png_ptr ) ;
2007-06-28 10:47:08 -07:00
2007-05-01 13:34:54 -07:00
/* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
* byte into separate bytes ( useful for paletted and grayscale images ) .
*/
// png_set_packing(png_ptr);
2007-06-28 10:47:08 -07:00
2007-05-01 13:34:54 -07:00
/* More transformations to ensure we end up with 32bpp, 4 channel RGBA */
// png_set_gray_to_rgb(png_ptr);
2007-07-29 10:42:22 -07:00
png_set_palette_to_rgb ( png_ptr ) ;
png_set_tRNS_to_alpha ( png_ptr ) ;
2007-05-01 13:34:54 -07:00
png_set_filler ( png_ptr , 0xff , PNG_FILLER_AFTER ) ;
// png_set_gray_1_2_4_to_8(png_ptr);
2007-06-28 10:47:08 -07:00
2007-05-01 13:34:54 -07:00
png_read_png ( png_ptr , info_ptr , PNG_TRANSFORM_IDENTITY , NULL ) ;
2007-06-28 10:47:08 -07:00
2010-01-23 20:32:54 -08:00
image - > width = png_get_image_width ( png_ptr , info_ptr ) ;
image - > height = png_get_image_height ( png_ptr , info_ptr ) ;
image - > depth = png_get_channels ( png_ptr , info_ptr ) ;
2010-12-05 08:25:43 -08:00
image - > bmp = ( unsigned char * ) malloc ( image - > height * png_get_rowbytes ( png_ptr , info_ptr ) ) ;
2007-06-28 10:47:08 -07:00
2007-05-01 13:34:54 -07:00
{
unsigned int i = 0 ;
png_bytepp row_pointers = png_get_rows ( png_ptr , info_ptr ) ;
2010-01-23 20:32:54 -08:00
for ( i = 0 ; i < png_get_image_height ( png_ptr , info_ptr ) ; i + + )
memcpy ( image - > bmp + ( png_get_rowbytes ( png_ptr , info_ptr ) * i ) , row_pointers [ i ] , png_get_rowbytes ( png_ptr , info_ptr ) ) ;
2007-06-28 10:47:08 -07:00
}
2007-04-29 16:00:38 -07:00
PNGReadCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
2009-06-04 10:50:13 -07:00
ASSERT_OR_RETURN ( false , image - > depth > 3 , " Unsupported image depth (%d) found. We only support 3 (RGB) or 4 (ARGB) " , image - > depth ) ;
2008-03-24 09:51:17 -07:00
return true ;
2007-06-28 10:47:08 -07:00
}
2007-04-29 16:00:38 -07:00
2009-06-25 11:51:46 -07:00
static void internal_saveImage_PNG ( const char * fileName , const iV_Image * image , int color_type )
2007-04-29 16:00:38 -07:00
{
2008-03-28 16:28:44 -07:00
unsigned char * * scanlines = NULL ;
2007-04-29 16:00:38 -07:00
png_infop info_ptr = NULL ;
png_structp png_ptr = NULL ;
2009-06-26 15:44:46 -07:00
PHYSFS_file * fileHandle ;
2009-06-25 11:51:46 -07:00
ASSERT ( image - > depth ! = 0 , " Bad depth " ) ;
2007-04-29 16:00:38 -07:00
2009-06-26 15:44:46 -07:00
fileHandle = PHYSFS_openWrite ( fileName ) ;
2007-04-29 16:00:38 -07:00
if ( fileHandle = = NULL )
{
2010-07-01 12:49:53 -07:00
debug ( LOG_ERROR , " pie_PNGSaveFile: PHYSFS_openWrite failed (while opening file %s) with error: %s \n " , fileName , PHYSFS_getLastError ( ) ) ;
2007-04-29 16:00:38 -07:00
return ;
}
png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING , NULL , NULL , NULL ) ;
if ( png_ptr = = NULL )
{
debug ( LOG_ERROR , " pie_PNGSaveFile: Unable to create png struct \n " ) ;
2010-02-19 14:52:23 -08:00
PNGWriteCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
return ;
2007-04-29 16:00:38 -07:00
}
info_ptr = png_create_info_struct ( png_ptr ) ;
if ( info_ptr = = NULL )
{
debug ( LOG_ERROR , " pie_PNGSaveFile: Unable to create png info struct \n " ) ;
2010-02-19 14:52:23 -08:00
PNGWriteCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
return ;
2007-04-29 16:00:38 -07:00
}
// If libpng encounters an error, it will jump into this if-branch
if ( setjmp ( png_jmpbuf ( png_ptr ) ) )
{
debug ( LOG_ERROR , " pie_PNGSaveFile: Error encoding PNG data \n " ) ;
}
else
{
2010-09-19 06:46:44 -07:00
unsigned int channelsPerPixel = 3 ;
unsigned int currentRow , row_stride ;
if ( color_type = = PNG_COLOR_TYPE_GRAY )
{
channelsPerPixel = 1 ;
}
row_stride = image - > width * channelsPerPixel * image - > depth / 8 ;
2007-04-29 16:00:38 -07:00
2010-12-05 08:25:43 -08:00
scanlines = ( unsigned char * * ) malloc ( sizeof ( unsigned char * ) * image - > height ) ;
2007-04-29 16:00:38 -07:00
if ( scanlines = = NULL )
{
debug ( LOG_ERROR , " pie_PNGSaveFile: Couldn't allocate memory \n " ) ;
2010-02-19 14:52:23 -08:00
PNGWriteCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
return ;
2007-04-29 16:00:38 -07:00
}
png_set_write_fn ( png_ptr , fileHandle , wzpng_write_data , wzpng_flush_data ) ;
// Set the compression level of ZLIB
// Right now we stick with the default, since that one is the
// fastest which still produces acceptable filesizes.
// The highest compression level hardly produces smaller files than default.
//
// Below are some benchmarks done while taking screenshots at 1280x1024
// Z_NO_COMPRESSION:
// black (except for GUI): 398 msec
// 381, 391, 404, 360 msec
//
// Z_BEST_SPEED:
// black (except for GUI): 325 msec
// 611, 406, 461, 608 msec
//
// Z_DEFAULT_COMPRESSION:
// black (except for GUI): 374 msec
// 1154, 1121, 627, 790 msec
//
// Z_BEST_COMPRESSION:
// black (except for GUI): 439 msec
// 1600, 1078, 1613, 1700 msec
// Not calling this function is equal to using the default
// so to spare some CPU cycles we comment this out.
// png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
2009-06-25 11:51:46 -07:00
png_set_IHDR ( png_ptr , info_ptr , image - > width , image - > height , image - > depth ,
color_type , PNG_INTERLACE_NONE , PNG_COMPRESSION_TYPE_DEFAULT , PNG_FILTER_TYPE_DEFAULT ) ;
2007-04-29 16:00:38 -07:00
// Create an array of scanlines
for ( currentRow = 0 ; currentRow < image - > height ; + + currentRow )
{
// We're filling the scanline from the bottom up here,
// otherwise we'd have a vertically mirrored image.
scanlines [ currentRow ] = & image - > bmp [ row_stride * ( image - > height - currentRow - 1 ) ] ;
}
2008-03-28 16:28:44 -07:00
png_set_rows ( png_ptr , info_ptr , ( png_bytepp ) scanlines ) ;
2007-04-29 16:00:38 -07:00
png_write_png ( png_ptr , info_ptr , PNG_TRANSFORM_IDENTITY , NULL ) ;
}
free ( scanlines ) ;
2010-02-19 14:52:23 -08:00
PNGWriteCleanup ( & info_ptr , & png_ptr , fileHandle ) ;
2007-04-29 16:00:38 -07:00
}
2009-06-25 11:51:46 -07:00
void iV_saveImage_PNG ( const char * fileName , const iV_Image * image )
{
internal_saveImage_PNG ( fileName , image , PNG_COLOR_TYPE_RGB ) ;
}
void iV_saveImage_PNG_Gray ( const char * fileName , const iV_Image * image )
{
internal_saveImage_PNG ( fileName , image , PNG_COLOR_TYPE_GRAY ) ;
}
2010-07-01 12:49:53 -07:00
void iV_saveImage_JPEG ( const char * fileName , const iV_Image * image )
{
unsigned char * buffer = NULL ;
unsigned char * jpeg = NULL ;
char newfilename [ PATH_MAX ] ;
unsigned int currentRow ;
const unsigned int row_stride = image - > width * 3 ; // 3 bytes per pixel
PHYSFS_file * fileHandle ;
unsigned char * jpeg_end ;
sstrcpy ( newfilename , fileName ) ;
memcpy ( newfilename + strlen ( newfilename ) - 4 , " .jpg " , 4 ) ;
fileHandle = PHYSFS_openWrite ( newfilename ) ;
if ( fileHandle = = NULL )
{
debug ( LOG_ERROR , " pie_JPEGSaveFile: PHYSFS_openWrite failed (while opening file %s) with error: %s \n " , fileName , PHYSFS_getLastError ( ) ) ;
return ;
}
2010-12-05 08:25:43 -08:00
buffer = ( unsigned char * ) malloc ( sizeof ( const char * ) * image - > height * image - > width ) ; // Suspect it should be sizeof(unsigned char)*3 == 3 here, not sizeof(const char *) == 8.
2010-07-01 12:49:53 -07:00
if ( buffer = = NULL )
{
debug ( LOG_ERROR , " pie_JPEGSaveFile: Couldn't allocate memory \n " ) ;
return ;
}
// Create an array of scanlines
for ( currentRow = 0 ; currentRow < image - > height ; + + currentRow )
{
// We're filling the scanline from the bottom up here,
// otherwise we'd have a vertically mirrored image.
memcpy ( buffer + row_stride * currentRow , & image - > bmp [ row_stride * ( image - > height - currentRow - 1 ) ] , row_stride ) ;
}
2010-12-05 08:25:43 -08:00
jpeg = ( unsigned char * ) malloc ( sizeof ( const char * ) * image - > height * image - > width ) ; // Suspect it should be something else here, but sizeof(const char *) == 8 is hopefully big enough...
2010-07-01 12:49:53 -07:00
if ( jpeg = = NULL )
{
debug ( LOG_ERROR , " pie_JPEGSaveFile: Couldn't allocate memory \n " ) ;
2011-02-18 06:54:39 -08:00
free ( buffer ) ;
2010-07-01 12:49:53 -07:00
return ;
}
jpeg_end = jpeg_encode_image ( buffer , jpeg , 1 , JPEG_FORMAT_RGB , image - > width , image - > height ) ;
PHYSFS_write ( fileHandle , jpeg , jpeg_end - jpeg , 1 ) ;
free ( buffer ) ;
free ( jpeg ) ;
PHYSFS_close ( fileHandle ) ;
}