libpng 1.4 compatibility patch.
This commit is contained in:
+318
-318
@@ -1,318 +1,318 @@
|
||||
#include "global.h"
|
||||
#include "RageSurface_Load_PNG.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageFile.h"
|
||||
#include "RageSurface.h"
|
||||
|
||||
|
||||
#if defined(_WINDOWS) || defined(_XBOX)
|
||||
# include "libpng/include/png.h"
|
||||
# if defined(_MSC_VER)
|
||||
# if defined(_XBOX)
|
||||
# pragma comment(lib, "libpng/lib/xboxlibpng.lib")
|
||||
# else
|
||||
# pragma comment(lib, "libpng/lib/libpng.lib")
|
||||
# endif
|
||||
# pragma warning(disable: 4611) /* interaction between '_setjmp' and C++ object destruction is non-portable */
|
||||
# endif // _MSC_VER
|
||||
#else
|
||||
# include <png.h>
|
||||
#endif
|
||||
|
||||
#if defined(_XBOX)
|
||||
# include <malloc.h> // for alloca
|
||||
# include "archutils/Xbox/VirtualMemory.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
void RageFile_png_read( png_struct *png, png_byte *p, png_size_t size )
|
||||
{
|
||||
CHECKPOINT;
|
||||
RageFile *f = (RageFile *) png->io_ptr;
|
||||
|
||||
int got = f->Read( p, size );
|
||||
if( got == -1 )
|
||||
{
|
||||
/* png_error will call PNG_Error, which will longjmp. If we just pass
|
||||
* GetError().c_str() to it, a temporary may be created; since control
|
||||
* never returns here, it may never be destructed and we could leak. */
|
||||
static char error[256];
|
||||
strncpy( error, f->GetError(), sizeof(error) );
|
||||
error[sizeof(error)-1] = 0;
|
||||
png_error( png, error );
|
||||
}
|
||||
else if( got != (int) size )
|
||||
png_error( png, "Unexpected EOF" );
|
||||
}
|
||||
|
||||
struct error_info
|
||||
{
|
||||
char *err;
|
||||
const char *fn;
|
||||
};
|
||||
|
||||
void PNG_Error( png_struct *png, const char *error )
|
||||
{
|
||||
CHECKPOINT;
|
||||
error_info *info = (error_info *) png->error_ptr;
|
||||
strncpy( info->err, error, 1024 );
|
||||
info->err[1023] = 0;
|
||||
LOG->Trace( "loading \"%s\": err: %s", info->fn, info->err );
|
||||
longjmp( png->jmpbuf, 1 );
|
||||
}
|
||||
|
||||
void PNG_Warning( png_struct *png, const char *warning )
|
||||
{
|
||||
CHECKPOINT;
|
||||
error_info *info = (error_info *) png->error_ptr;
|
||||
LOG->Trace( "loading \"%s\": warning: %s", info->fn, warning );
|
||||
}
|
||||
|
||||
/* Since libpng forces us to use longjmp (gross!), this function shouldn't create any C++
|
||||
* objects, and needs to watch out for memleaks. */
|
||||
static RageSurface *RageSurface_Load_PNG( RageFile *f, const char *fn, char errorbuf[1024], bool bHeaderOnly )
|
||||
{
|
||||
error_info error;
|
||||
error.err = errorbuf;
|
||||
error.fn = fn;
|
||||
|
||||
png_struct *png = png_create_read_struct( PNG_LIBPNG_VER_STRING, &error, PNG_Error, PNG_Warning );
|
||||
|
||||
#if defined(XBOX)
|
||||
while(png == NULL)
|
||||
{
|
||||
if(!vmem_Manager.DecommitLRU())
|
||||
break;
|
||||
|
||||
png = png_create_read_struct( PNG_LIBPNG_VER_STRING, &error, PNG_Error, PNG_Warning );
|
||||
}
|
||||
#endif
|
||||
|
||||
if( png == NULL )
|
||||
{
|
||||
sprintf( errorbuf, "creating png_create_read_struct failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
png_info *info_ptr = png_create_info_struct(png);
|
||||
if( info_ptr == NULL )
|
||||
{
|
||||
png_destroy_read_struct( &png, NULL, NULL );
|
||||
sprintf( errorbuf, "creating png_create_info_struct failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RageSurface *volatile img = NULL;
|
||||
CHECKPOINT;
|
||||
if( setjmp(png->jmpbuf) )
|
||||
{
|
||||
png_destroy_read_struct( &png, &info_ptr, NULL );
|
||||
delete img;
|
||||
return NULL;
|
||||
}
|
||||
CHECKPOINT;
|
||||
|
||||
png_set_read_fn( png, f, RageFile_png_read );
|
||||
|
||||
png_read_info( png, info_ptr );
|
||||
|
||||
png_uint_32 width, height;
|
||||
int bit_depth, color_type;
|
||||
png_get_IHDR( png, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL );
|
||||
|
||||
/* If bHeaderOnly is true, don't allocate the pixel storage space or decompress
|
||||
* the image. Just return an empty surface with only the width and height set. */
|
||||
if( bHeaderOnly )
|
||||
{
|
||||
CHECKPOINT;
|
||||
img = CreateSurfaceFrom( width, height, 32, 0, 0, 0, 0, NULL, width*4 );
|
||||
png_destroy_read_struct( &png, &info_ptr, NULL );
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
CHECKPOINT;
|
||||
png_set_strip_16(png); /* 16bit->8bit */
|
||||
png_set_packing( png ); /* 1,2,4 bit->8 bit */
|
||||
|
||||
/* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
|
||||
if( color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8 )
|
||||
png_set_gray_1_2_4_to_8( png );
|
||||
|
||||
/* These are set for type == PALETTE. */
|
||||
RageSurfaceColor colors[256];
|
||||
int iColorKey = -1;
|
||||
|
||||
/* We import three types of files: paletted, RGBX and RGBA. The only difference
|
||||
* between RGBX and RGBA is that RGBX won't set the alpha mask, so it's easier
|
||||
* to tell later on that there's no alpha (without actually having to do a pixel scan). */
|
||||
enum { PALETTE, RGBX, RGBA } type;
|
||||
switch( color_type )
|
||||
{
|
||||
case PNG_COLOR_TYPE_GRAY:
|
||||
/* Fake PNG_COLOR_TYPE_GRAY. */
|
||||
for( int i = 0; i < 256; ++i )
|
||||
{
|
||||
colors[i].r = colors[i].g = colors[i].b = (int8_t) i;
|
||||
colors[i].a = 0xFF;
|
||||
}
|
||||
|
||||
type = PALETTE;
|
||||
break;
|
||||
|
||||
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
||||
type = RGBA;
|
||||
png_set_gray_to_rgb( png );
|
||||
break;
|
||||
case PNG_COLOR_TYPE_PALETTE:
|
||||
type = PALETTE;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
type = RGBX;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||
type = RGBA;
|
||||
break;
|
||||
default:
|
||||
FAIL_M(ssprintf( "%i", color_type) );
|
||||
}
|
||||
|
||||
CHECKPOINT;
|
||||
if( color_type == PNG_COLOR_TYPE_GRAY )
|
||||
{
|
||||
png_color_16 *trans;
|
||||
if( png_get_tRNS( png, info_ptr, NULL, NULL, &trans ) == PNG_INFO_tRNS )
|
||||
iColorKey = trans->gray;
|
||||
}
|
||||
else if( color_type == PNG_COLOR_TYPE_PALETTE )
|
||||
{
|
||||
int num_palette;
|
||||
png_color *palette;
|
||||
int ret = png_get_PLTE( png, info_ptr, &palette, &num_palette );
|
||||
ASSERT( ret == PNG_INFO_PLTE );
|
||||
|
||||
png_byte *trans = NULL;
|
||||
int num_trans = 0;
|
||||
png_get_tRNS( png, info_ptr, &trans, &num_trans, NULL );
|
||||
|
||||
for( int i = 0; i < num_palette; ++i )
|
||||
{
|
||||
colors[i].r = palette[i].red;
|
||||
colors[i].g = palette[i].green;
|
||||
colors[i].b = palette[i].blue;
|
||||
colors[i].a = 0xFF;
|
||||
if( i < num_trans )
|
||||
colors[i].a = trans[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we have RGB image and tRNS, it's a color key. Just convert it to RGBA. */
|
||||
if( png_get_valid(png, info_ptr, PNG_INFO_tRNS) )
|
||||
{
|
||||
/* We don't care about RGB color keys; just convert them to alpha. */
|
||||
png_set_tRNS_to_alpha( png );
|
||||
type = RGBA;
|
||||
}
|
||||
|
||||
/* RGB->RGBX */
|
||||
png_set_filler( png, 0xff, PNG_FILLER_AFTER );
|
||||
}
|
||||
|
||||
png_set_interlace_handling( png );
|
||||
|
||||
CHECKPOINT;
|
||||
png_read_update_info( png, info_ptr );
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case PALETTE:
|
||||
img = CreateSurface( width, height, 8, 0, 0, 0, 0 );
|
||||
memcpy( img->fmt.palette->colors, colors, 256*sizeof(RageSurfaceColor) );
|
||||
|
||||
if( iColorKey != -1 )
|
||||
img->format->palette->colors[ iColorKey ].a = 0;
|
||||
|
||||
break;
|
||||
case RGBX:
|
||||
case RGBA:
|
||||
img = CreateSurface( width, height, 32,
|
||||
Swap32BE( 0xFF000000 ),
|
||||
Swap32BE( 0x00FF0000 ),
|
||||
Swap32BE( 0x0000FF00 ),
|
||||
Swap32BE( type == RGBA? 0x000000FF:0x00000000 ) );
|
||||
break;
|
||||
default:
|
||||
FAIL_M(ssprintf( "%i", type) );
|
||||
}
|
||||
ASSERT( img );
|
||||
|
||||
/* alloca to prevent memleaks if libpng longjmps us */
|
||||
png_byte **row_pointers = (png_byte **) alloca( sizeof(png_byte*) * height );
|
||||
CHECKPOINT_M( ssprintf("%p",row_pointers) );
|
||||
|
||||
for( unsigned y = 0; y < height; ++y )
|
||||
{
|
||||
png_byte *p = (png_byte *) img->pixels;
|
||||
row_pointers[y] = p + img->pitch*y;
|
||||
}
|
||||
|
||||
CHECKPOINT;
|
||||
png_read_image( png, row_pointers );
|
||||
|
||||
CHECKPOINT;
|
||||
png_read_end( png, info_ptr );
|
||||
png_destroy_read_struct( &png, &info_ptr, NULL );
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
RageSurfaceUtils::OpenResult RageSurface_Load_PNG( const RString &sPath, RageSurface *&ret, bool bHeaderOnly, RString &error )
|
||||
{
|
||||
RageFile f;
|
||||
if( !f.Open( sPath ) )
|
||||
{
|
||||
error = f.GetError();
|
||||
return RageSurfaceUtils::OPEN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
char errorbuf[1024];
|
||||
ret = RageSurface_Load_PNG( &f, sPath, errorbuf, bHeaderOnly );
|
||||
if( ret == NULL )
|
||||
{
|
||||
error = errorbuf;
|
||||
return RageSurfaceUtils::OPEN_UNKNOWN_FILE_FORMAT; // XXX
|
||||
}
|
||||
|
||||
return RageSurfaceUtils::OPEN_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#include "global.h"
|
||||
#include "RageSurface_Load_PNG.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageFile.h"
|
||||
#include "RageSurface.h"
|
||||
|
||||
|
||||
#if defined(_WINDOWS) || defined(_XBOX)
|
||||
# include "libpng/include/png.h"
|
||||
# if defined(_MSC_VER)
|
||||
# if defined(_XBOX)
|
||||
# pragma comment(lib, "libpng/lib/xboxlibpng.lib")
|
||||
# else
|
||||
# pragma comment(lib, "libpng/lib/libpng.lib")
|
||||
# endif
|
||||
# pragma warning(disable: 4611) /* interaction between '_setjmp' and C++ object destruction is non-portable */
|
||||
# endif // _MSC_VER
|
||||
#else
|
||||
# include <png.h>
|
||||
#endif
|
||||
|
||||
#if defined(_XBOX)
|
||||
# include <malloc.h> // for alloca
|
||||
# include "archutils/Xbox/VirtualMemory.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
void RageFile_png_read( png_struct *png, png_byte *p, png_size_t size )
|
||||
{
|
||||
CHECKPOINT;
|
||||
RageFile *f = (RageFile *) png_get_io_ptr(png);
|
||||
|
||||
int got = f->Read( p, size );
|
||||
if( got == -1 )
|
||||
{
|
||||
/* png_error will call PNG_Error, which will longjmp. If we just pass
|
||||
* GetError().c_str() to it, a temporary may be created; since control
|
||||
* never returns here, it may never be destructed and we could leak. */
|
||||
static char error[256];
|
||||
strncpy( error, f->GetError(), sizeof(error) );
|
||||
error[sizeof(error)-1] = 0;
|
||||
png_error( png, error );
|
||||
}
|
||||
else if( got != (int) size )
|
||||
png_error( png, "Unexpected EOF" );
|
||||
}
|
||||
|
||||
struct error_info
|
||||
{
|
||||
char *err;
|
||||
const char *fn;
|
||||
};
|
||||
|
||||
void PNG_Error( png_struct *png, const char *error )
|
||||
{
|
||||
CHECKPOINT;
|
||||
error_info *info = (error_info *) png_get_error_ptr(png);
|
||||
strncpy( info->err, error, 1024 );
|
||||
info->err[1023] = 0;
|
||||
LOG->Trace( "loading \"%s\": err: %s", info->fn, info->err );
|
||||
longjmp( png_jmpbuf(png), 1 );
|
||||
}
|
||||
|
||||
void PNG_Warning( png_struct *png, const char *warning )
|
||||
{
|
||||
CHECKPOINT;
|
||||
error_info *info = (error_info *) png_get_io_ptr(png);
|
||||
LOG->Trace( "loading \"%s\": warning: %s", info->fn, warning );
|
||||
}
|
||||
|
||||
/* Since libpng forces us to use longjmp (gross!), this function shouldn't create any C++
|
||||
* objects, and needs to watch out for memleaks. */
|
||||
static RageSurface *RageSurface_Load_PNG( RageFile *f, const char *fn, char errorbuf[1024], bool bHeaderOnly )
|
||||
{
|
||||
error_info error;
|
||||
error.err = errorbuf;
|
||||
error.fn = fn;
|
||||
|
||||
png_struct *png = png_create_read_struct( PNG_LIBPNG_VER_STRING, &error, PNG_Error, PNG_Warning );
|
||||
|
||||
#if defined(XBOX)
|
||||
while(png == NULL)
|
||||
{
|
||||
if(!vmem_Manager.DecommitLRU())
|
||||
break;
|
||||
|
||||
png = png_create_read_struct( PNG_LIBPNG_VER_STRING, &error, PNG_Error, PNG_Warning );
|
||||
}
|
||||
#endif
|
||||
|
||||
if( png == NULL )
|
||||
{
|
||||
sprintf( errorbuf, "creating png_create_read_struct failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
png_info *info_ptr = png_create_info_struct(png);
|
||||
if( info_ptr == NULL )
|
||||
{
|
||||
png_destroy_read_struct( &png, NULL, NULL );
|
||||
sprintf( errorbuf, "creating png_create_info_struct failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RageSurface *volatile img = NULL;
|
||||
CHECKPOINT;
|
||||
if( setjmp(png_jmpbuf(png) ))
|
||||
{
|
||||
png_destroy_read_struct( &png, &info_ptr, NULL );
|
||||
delete img;
|
||||
return NULL;
|
||||
}
|
||||
CHECKPOINT;
|
||||
|
||||
png_set_read_fn( png, f, RageFile_png_read );
|
||||
|
||||
png_read_info( png, info_ptr );
|
||||
|
||||
png_uint_32 width, height;
|
||||
int bit_depth, color_type;
|
||||
png_get_IHDR( png, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL );
|
||||
|
||||
/* If bHeaderOnly is true, don't allocate the pixel storage space or decompress
|
||||
* the image. Just return an empty surface with only the width and height set. */
|
||||
if( bHeaderOnly )
|
||||
{
|
||||
CHECKPOINT;
|
||||
img = CreateSurfaceFrom( width, height, 32, 0, 0, 0, 0, NULL, width*4 );
|
||||
png_destroy_read_struct( &png, &info_ptr, NULL );
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
CHECKPOINT;
|
||||
png_set_strip_16(png); /* 16bit->8bit */
|
||||
png_set_packing( png ); /* 1,2,4 bit->8 bit */
|
||||
|
||||
/* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
|
||||
if( color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8 )
|
||||
png_set_expand_gray_1_2_4_to_8( png );
|
||||
|
||||
/* These are set for type == PALETTE. */
|
||||
RageSurfaceColor colors[256];
|
||||
int iColorKey = -1;
|
||||
|
||||
/* We import three types of files: paletted, RGBX and RGBA. The only difference
|
||||
* between RGBX and RGBA is that RGBX won't set the alpha mask, so it's easier
|
||||
* to tell later on that there's no alpha (without actually having to do a pixel scan). */
|
||||
enum { PALETTE, RGBX, RGBA } type;
|
||||
switch( color_type )
|
||||
{
|
||||
case PNG_COLOR_TYPE_GRAY:
|
||||
/* Fake PNG_COLOR_TYPE_GRAY. */
|
||||
for( int i = 0; i < 256; ++i )
|
||||
{
|
||||
colors[i].r = colors[i].g = colors[i].b = (int8_t) i;
|
||||
colors[i].a = 0xFF;
|
||||
}
|
||||
|
||||
type = PALETTE;
|
||||
break;
|
||||
|
||||
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
||||
type = RGBA;
|
||||
png_set_gray_to_rgb( png );
|
||||
break;
|
||||
case PNG_COLOR_TYPE_PALETTE:
|
||||
type = PALETTE;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
type = RGBX;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||
type = RGBA;
|
||||
break;
|
||||
default:
|
||||
FAIL_M(ssprintf( "%i", color_type) );
|
||||
}
|
||||
|
||||
CHECKPOINT;
|
||||
if( color_type == PNG_COLOR_TYPE_GRAY )
|
||||
{
|
||||
png_color_16 *trans;
|
||||
if( png_get_tRNS( png, info_ptr, NULL, NULL, &trans ) == PNG_INFO_tRNS )
|
||||
iColorKey = trans->gray;
|
||||
}
|
||||
else if( color_type == PNG_COLOR_TYPE_PALETTE )
|
||||
{
|
||||
int num_palette;
|
||||
png_color *palette;
|
||||
int ret = png_get_PLTE( png, info_ptr, &palette, &num_palette );
|
||||
ASSERT( ret == PNG_INFO_PLTE );
|
||||
|
||||
png_byte *trans = NULL;
|
||||
int num_trans = 0;
|
||||
png_get_tRNS( png, info_ptr, &trans, &num_trans, NULL );
|
||||
|
||||
for( int i = 0; i < num_palette; ++i )
|
||||
{
|
||||
colors[i].r = palette[i].red;
|
||||
colors[i].g = palette[i].green;
|
||||
colors[i].b = palette[i].blue;
|
||||
colors[i].a = 0xFF;
|
||||
if( i < num_trans )
|
||||
colors[i].a = trans[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we have RGB image and tRNS, it's a color key. Just convert it to RGBA. */
|
||||
if( png_get_valid(png, info_ptr, PNG_INFO_tRNS) )
|
||||
{
|
||||
/* We don't care about RGB color keys; just convert them to alpha. */
|
||||
png_set_tRNS_to_alpha( png );
|
||||
type = RGBA;
|
||||
}
|
||||
|
||||
/* RGB->RGBX */
|
||||
png_set_filler( png, 0xff, PNG_FILLER_AFTER );
|
||||
}
|
||||
|
||||
png_set_interlace_handling( png );
|
||||
|
||||
CHECKPOINT;
|
||||
png_read_update_info( png, info_ptr );
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case PALETTE:
|
||||
img = CreateSurface( width, height, 8, 0, 0, 0, 0 );
|
||||
memcpy( img->fmt.palette->colors, colors, 256*sizeof(RageSurfaceColor) );
|
||||
|
||||
if( iColorKey != -1 )
|
||||
img->format->palette->colors[ iColorKey ].a = 0;
|
||||
|
||||
break;
|
||||
case RGBX:
|
||||
case RGBA:
|
||||
img = CreateSurface( width, height, 32,
|
||||
Swap32BE( 0xFF000000 ),
|
||||
Swap32BE( 0x00FF0000 ),
|
||||
Swap32BE( 0x0000FF00 ),
|
||||
Swap32BE( type == RGBA? 0x000000FF:0x00000000 ) );
|
||||
break;
|
||||
default:
|
||||
FAIL_M(ssprintf( "%i", type) );
|
||||
}
|
||||
ASSERT( img );
|
||||
|
||||
/* alloca to prevent memleaks if libpng longjmps us */
|
||||
png_byte **row_pointers = (png_byte **) alloca( sizeof(png_byte*) * height );
|
||||
CHECKPOINT_M( ssprintf("%p",row_pointers) );
|
||||
|
||||
for( unsigned y = 0; y < height; ++y )
|
||||
{
|
||||
png_byte *p = (png_byte *) img->pixels;
|
||||
row_pointers[y] = p + img->pitch*y;
|
||||
}
|
||||
|
||||
CHECKPOINT;
|
||||
png_read_image( png, row_pointers );
|
||||
|
||||
CHECKPOINT;
|
||||
png_read_end( png, info_ptr );
|
||||
png_destroy_read_struct( &png, &info_ptr, NULL );
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
RageSurfaceUtils::OpenResult RageSurface_Load_PNG( const RString &sPath, RageSurface *&ret, bool bHeaderOnly, RString &error )
|
||||
{
|
||||
RageFile f;
|
||||
if( !f.Open( sPath ) )
|
||||
{
|
||||
error = f.GetError();
|
||||
return RageSurfaceUtils::OPEN_FATAL_ERROR;
|
||||
}
|
||||
|
||||
char errorbuf[1024];
|
||||
ret = RageSurface_Load_PNG( &f, sPath, errorbuf, bHeaderOnly );
|
||||
if( ret == NULL )
|
||||
{
|
||||
error = errorbuf;
|
||||
return RageSurfaceUtils::OPEN_UNKNOWN_FILE_FORMAT; // XXX
|
||||
}
|
||||
|
||||
return RageSurfaceUtils::OPEN_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
+168
-168
@@ -1,168 +1,168 @@
|
||||
#include "global.h"
|
||||
#include "RageSurface_Save_PNG.h"
|
||||
#include "RageSurface.h"
|
||||
#include "RageSurfaceUtils.h"
|
||||
#include "RageFile.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageUtil.h"
|
||||
|
||||
#if defined(WINDOWS) || defined(_XBOX)
|
||||
#include "libpng/include/png.h"
|
||||
#if defined(_MSC_VER)
|
||||
# if defined(_XBOX)
|
||||
# pragma comment(lib, "libpng/lib/xboxlibpng.lib")
|
||||
# else
|
||||
# pragma comment(lib, "libpng/lib/libpng.lib")
|
||||
# endif
|
||||
#pragma warning(disable: 4611) /* interaction between '_setjmp' and C++ object destruction is non-portable */
|
||||
#endif
|
||||
#else
|
||||
#include <png.h>
|
||||
#endif
|
||||
|
||||
static void SafePngError( png_struct *pPng, const RString &sStr )
|
||||
{
|
||||
/* png_error will call PNG_Error, which will longjmp. If we just pass
|
||||
* GetError().c_str() to it, a temporary may be created; since control
|
||||
* never returns, it may never be destructed and leak. */
|
||||
static char error[256];
|
||||
strncpy( error, sStr, sizeof(error) );
|
||||
error[sizeof(error)-1] = 0;
|
||||
png_error( pPng, error );
|
||||
}
|
||||
|
||||
static void RageFile_png_write( png_struct *pPng, png_byte *pData, png_size_t iSize )
|
||||
{
|
||||
RageFile *pFile = (RageFile *) pPng->io_ptr;
|
||||
|
||||
int iGot = pFile->Write( pData, iSize );
|
||||
if( iGot == -1 )
|
||||
SafePngError( pPng, pFile->GetError() );
|
||||
}
|
||||
|
||||
static void RageFile_png_flush( png_struct *pPng )
|
||||
{
|
||||
RageFile *pFile = (RageFile *) pPng->io_ptr;
|
||||
|
||||
int iGot = pFile->Flush();
|
||||
if( iGot == -1 )
|
||||
SafePngError( pPng, pFile->GetError() );
|
||||
}
|
||||
|
||||
struct error_info
|
||||
{
|
||||
char *szErr;
|
||||
};
|
||||
|
||||
static void PNG_Error( png_struct *pPng, const char *szError )
|
||||
{
|
||||
error_info *pInfo = (error_info *) pPng->error_ptr;
|
||||
strncpy( pInfo->szErr, szError, 1024 );
|
||||
pInfo->szErr[1023] = 0;
|
||||
longjmp( pPng->jmpbuf, 1 );
|
||||
}
|
||||
|
||||
static void PNG_Warning( png_struct *png, const char *warning )
|
||||
{
|
||||
LOG->Trace( "saving PNG: warning: %s", warning );
|
||||
}
|
||||
|
||||
/* Since libpng forces us to use longjmp, this function shouldn't create any C++
|
||||
* objects, and needs to watch out for memleaks. */
|
||||
static bool RageSurface_Save_PNG( RageFile &f, char szErrorbuf[1024], RageSurface *pImgIn )
|
||||
{
|
||||
bool bAlpha = pImgIn->format->Amask != 0;
|
||||
RageSurface *pImg;
|
||||
bool bDeleteImg = RageSurfaceUtils::ConvertSurface( pImgIn, pImg, pImgIn->w, pImgIn->h, 32,
|
||||
Swap32BE( 0xFF000000 ),
|
||||
Swap32BE( 0x00FF0000 ),
|
||||
Swap32BE( 0x0000FF00 ),
|
||||
Swap32BE( 0x000000FF ) );
|
||||
if( !bDeleteImg )
|
||||
pImg = pImgIn;
|
||||
|
||||
error_info error;
|
||||
error.szErr = szErrorbuf;
|
||||
|
||||
png_struct *pPng = png_create_write_struct( PNG_LIBPNG_VER_STRING, &error, PNG_Error, PNG_Warning );
|
||||
if( pPng == NULL )
|
||||
{
|
||||
sprintf( szErrorbuf, "creating png_create_write_struct failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
png_info *pInfo = png_create_info_struct(pPng);
|
||||
if( pInfo == NULL )
|
||||
{
|
||||
png_destroy_read_struct( &pPng, NULL, NULL );
|
||||
if( bDeleteImg )
|
||||
delete pImg;
|
||||
sprintf( szErrorbuf, "creating png_create_info_struct failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if( setjmp(pPng->jmpbuf) )
|
||||
{
|
||||
png_destroy_read_struct( &pPng, &pInfo, png_infopp_NULL );
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_write_fn( pPng, &f, RageFile_png_write, RageFile_png_flush );
|
||||
png_set_compression_level( pPng, 1 );
|
||||
|
||||
png_set_IHDR( pPng, pInfo, pImg->w, pImg->h, 8, bAlpha? PNG_COLOR_TYPE_RGBA:PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE );
|
||||
|
||||
png_write_info( pPng, pInfo );
|
||||
png_set_filler( pPng, 0, PNG_FILLER_AFTER );
|
||||
|
||||
png_byte *pixels = (png_byte *) pImg->pixels;
|
||||
for( int y = 0; y < pImg->h; y++ )
|
||||
png_write_row( pPng, pixels + pImg->pitch*y );
|
||||
|
||||
png_write_end( pPng, pInfo );
|
||||
png_destroy_write_struct( &pPng, &pInfo );
|
||||
|
||||
/* Free the converted image. */
|
||||
if( bDeleteImg )
|
||||
delete pImg;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RageSurfaceUtils::SavePNG( RageSurface *pImg, RageFile &f, RString &sError )
|
||||
{
|
||||
char szErrorBuf[1024];
|
||||
if( !RageSurface_Save_PNG(f, szErrorBuf, pImg) )
|
||||
{
|
||||
sError = szErrorBuf;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2004-2006 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#include "global.h"
|
||||
#include "RageSurface_Save_PNG.h"
|
||||
#include "RageSurface.h"
|
||||
#include "RageSurfaceUtils.h"
|
||||
#include "RageFile.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageUtil.h"
|
||||
|
||||
#if defined(WINDOWS) || defined(_XBOX)
|
||||
#include "libpng/include/png.h"
|
||||
#if defined(_MSC_VER)
|
||||
# if defined(_XBOX)
|
||||
# pragma comment(lib, "libpng/lib/xboxlibpng.lib")
|
||||
# else
|
||||
# pragma comment(lib, "libpng/lib/libpng.lib")
|
||||
# endif
|
||||
#pragma warning(disable: 4611) /* interaction between '_setjmp' and C++ object destruction is non-portable */
|
||||
#endif
|
||||
#else
|
||||
#include <png.h>
|
||||
#endif
|
||||
|
||||
static void SafePngError( png_struct *pPng, const RString &sStr )
|
||||
{
|
||||
/* png_error will call PNG_Error, which will longjmp. If we just pass
|
||||
* GetError().c_str() to it, a temporary may be created; since control
|
||||
* never returns, it may never be destructed and leak. */
|
||||
static char error[256];
|
||||
strncpy( error, sStr, sizeof(error) );
|
||||
error[sizeof(error)-1] = 0;
|
||||
png_error( pPng, error );
|
||||
}
|
||||
|
||||
static void RageFile_png_write( png_struct *pPng, png_byte *pData, png_size_t iSize )
|
||||
{
|
||||
RageFile *pFile = (RageFile *) png_get_io_ptr(pPng);
|
||||
|
||||
int iGot = pFile->Write( pData, iSize );
|
||||
if( iGot == -1 )
|
||||
SafePngError( pPng, pFile->GetError() );
|
||||
}
|
||||
|
||||
static void RageFile_png_flush( png_struct *pPng )
|
||||
{
|
||||
RageFile *pFile = (RageFile *) png_get_io_ptr(pPng);
|
||||
|
||||
int iGot = pFile->Flush();
|
||||
if( iGot == -1 )
|
||||
SafePngError( pPng, pFile->GetError() );
|
||||
}
|
||||
|
||||
struct error_info
|
||||
{
|
||||
char *szErr;
|
||||
};
|
||||
|
||||
static void PNG_Error( png_struct *pPng, const char *szError )
|
||||
{
|
||||
error_info *pInfo = (error_info *) png_get_error_ptr(pPng);
|
||||
strncpy( pInfo->szErr, szError, 1024 );
|
||||
pInfo->szErr[1023] = 0;
|
||||
longjmp( png_jmpbuf(pPng), 1 );
|
||||
}
|
||||
|
||||
static void PNG_Warning( png_struct *png, const char *warning )
|
||||
{
|
||||
LOG->Trace( "saving PNG: warning: %s", warning );
|
||||
}
|
||||
|
||||
/* Since libpng forces us to use longjmp, this function shouldn't create any C++
|
||||
* objects, and needs to watch out for memleaks. */
|
||||
static bool RageSurface_Save_PNG( RageFile &f, char szErrorbuf[1024], RageSurface *pImgIn )
|
||||
{
|
||||
bool bAlpha = pImgIn->format->Amask != 0;
|
||||
RageSurface *pImg;
|
||||
bool bDeleteImg = RageSurfaceUtils::ConvertSurface( pImgIn, pImg, pImgIn->w, pImgIn->h, 32,
|
||||
Swap32BE( 0xFF000000 ),
|
||||
Swap32BE( 0x00FF0000 ),
|
||||
Swap32BE( 0x0000FF00 ),
|
||||
Swap32BE( 0x000000FF ) );
|
||||
if( !bDeleteImg )
|
||||
pImg = pImgIn;
|
||||
|
||||
error_info error;
|
||||
error.szErr = szErrorbuf;
|
||||
|
||||
png_struct *pPng = png_create_write_struct( PNG_LIBPNG_VER_STRING, &error, PNG_Error, PNG_Warning );
|
||||
if( pPng == NULL )
|
||||
{
|
||||
sprintf( szErrorbuf, "creating png_create_write_struct failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
png_info *pInfo = png_create_info_struct(pPng);
|
||||
if( pInfo == NULL )
|
||||
{
|
||||
png_destroy_read_struct( &pPng, NULL, NULL );
|
||||
if( bDeleteImg )
|
||||
delete pImg;
|
||||
sprintf( szErrorbuf, "creating png_create_info_struct failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if( setjmp(png_jmpbuf(pPng)) )
|
||||
{
|
||||
png_destroy_read_struct( &pPng, &pInfo, NULL );
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_write_fn( pPng, &f, RageFile_png_write, RageFile_png_flush );
|
||||
png_set_compression_level( pPng, 1 );
|
||||
|
||||
png_set_IHDR( pPng, pInfo, pImg->w, pImg->h, 8, bAlpha? PNG_COLOR_TYPE_RGBA:PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE );
|
||||
|
||||
png_write_info( pPng, pInfo );
|
||||
png_set_filler( pPng, 0, PNG_FILLER_AFTER );
|
||||
|
||||
png_byte *pixels = (png_byte *) pImg->pixels;
|
||||
for( int y = 0; y < pImg->h; y++ )
|
||||
png_write_row( pPng, pixels + pImg->pitch*y );
|
||||
|
||||
png_write_end( pPng, pInfo );
|
||||
png_destroy_write_struct( &pPng, &pInfo );
|
||||
|
||||
/* Free the converted image. */
|
||||
if( bDeleteImg )
|
||||
delete pImg;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RageSurfaceUtils::SavePNG( RageSurface *pImg, RageFile &f, RString &sError )
|
||||
{
|
||||
char szErrorBuf[1024];
|
||||
if( !RageSurface_Save_PNG(f, szErrorBuf, pImg) )
|
||||
{
|
||||
sError = szErrorBuf;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2004-2006 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user