From 1c494f6dd2b845c8aeb8892ab3a35c3f34f66d0a Mon Sep 17 00:00:00 2001 From: Nicole Reid Date: Sat, 17 Apr 2010 01:07:06 -0400 Subject: [PATCH] libpng 1.4 compatibility patch. --- src/RageSurface_Load_PNG.cpp | 636 +++++++++++++++++------------------ src/RageSurface_Save_PNG.cpp | 336 +++++++++--------- 2 files changed, 486 insertions(+), 486 deletions(-) diff --git a/src/RageSurface_Load_PNG.cpp b/src/RageSurface_Load_PNG.cpp index 1646c21fd2..dd7014bf15 100644 --- a/src/RageSurface_Load_PNG.cpp +++ b/src/RageSurface_Load_PNG.cpp @@ -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 -#endif - -#if defined(_XBOX) -# include // 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 +#endif + +#if defined(_XBOX) +# include // 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. + */ diff --git a/src/RageSurface_Save_PNG.cpp b/src/RageSurface_Save_PNG.cpp index 8bda2d94f5..f971760fbd 100644 --- a/src/RageSurface_Save_PNG.cpp +++ b/src/RageSurface_Save_PNG.cpp @@ -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 -#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 +#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. + */