diff options
Diffstat (limited to 'common/pngloader.cpp')
| -rw-r--r-- | common/pngloader.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/common/pngloader.cpp b/common/pngloader.cpp new file mode 100644 index 0000000..1216727 --- /dev/null +++ b/common/pngloader.cpp @@ -0,0 +1,236 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "pngloader.h" +#include <stdio.h> +#include <stdlib.h> +//#include "vstdlib/strtools.h" + +#include <setjmp.h> + +// clang3 on OSX folks the attribute into the prototype, causing a compile failure +// filed radar bug 10397783 +#if ( __clang_major__ == 3 ) +extern void longjmp( jmp_buf, int ) __attribute__((noreturn)); +#endif + +#include "libpng/png.h" +#include "libpng/pngstruct.h" +#include "tier0/dbg.h" + +// +// struct to encapsulate png data in memory that we are walking through +// +struct PngDataStream_t +{ + PngDataStream_t( const byte *pchData, int cbData ) + { + m_pPNGData = pchData; + m_cbPNGData = cbData; + m_iPNGData = 0; + } + + const byte *m_pPNGData; // the png data + uint32 m_cbPNGData; // length of the data + uint32 m_iPNGData; // offset for next read +}; + + +// helper function to nibble some more png data from our memory buffer +void ReadPNGData( png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead ) +{ + if(png_ptr->io_ptr == NULL) + return; // we didn't have our mem buffer set + + PngDataStream_t *pData = (PngDataStream_t *)png_ptr->io_ptr; + + if ( byteCountToRead + pData->m_iPNGData > pData->m_cbPNGData ) + return; + + Q_memcpy( outBytes, pData->m_pPNGData + pData->m_iPNGData, byteCountToRead ); + pData->m_iPNGData += byteCountToRead; +} + + +// called when we failed to load a png file, we just bail to the previously set exit +static void PNGErrorFunction(png_structp png_ptr, png_const_charp msg) +{ + Warning( "PNG load error %s\n", msg ); + longjmp( png_jmpbuf(png_ptr), 1 ); +} + + +// we had a warning lets log it +static void PNGWarningFunction(png_structp png_ptr, png_const_charp msg) +{ + Warning( "PNG load error %s\n", msg ); +} + + +// Get dimensions out of PNG header +bool GetPNGDimensions( const byte *pubPNGData, int cubPNGData, uint32 &width, uint32 &height ) +{ + png_const_bytep pngData = (png_const_bytep)pubPNGData; + if (png_sig_cmp( pngData, 0, 8)) + return false; /* bad signature */ + + PngDataStream_t pngDataStream( pubPNGData, cubPNGData ); // keeps a local copy of the data we a reading + + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + + png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, &PNGErrorFunction, &PNGWarningFunction ); + if (!png_ptr) + return false; /* out of memory */ + + info_ptr = png_create_info_struct( png_ptr ); + if ( !info_ptr ) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return false; /* out of memory */ + } + + /* setjmp() must be called in every function that calls a PNG-reading + * libpng function */ + if ( setjmp( png_jmpbuf(png_ptr) ) ) + { + png_destroy_read_struct( &png_ptr, &info_ptr, NULL ); + return false; + } + + png_set_read_fn( png_ptr, (void *)&pngDataStream, ReadPNGData ); + png_read_info( png_ptr, info_ptr ); /* read all PNG info up to image data */ + + width = png_get_image_width( png_ptr, info_ptr ); + height = png_get_image_height( png_ptr, info_ptr ); + png_destroy_read_struct( &png_ptr, &info_ptr, NULL ); + + return true; +} + + +// given a png formatted memory buffer return a raw rgba buffer +bool ConvertPNGToRGBA( const byte *pubPNGData, int cubPNGData, CUtlBuffer &bufOutput, int &width, int &height ) +{ + png_const_bytep pngData = (png_const_bytep)pubPNGData; + if (png_sig_cmp( pngData, 0, 8)) + return false; /* bad signature */ + + PngDataStream_t pngDataStream( pubPNGData, cubPNGData ); // keeps a local copy of the data we a reading + + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + + /* could pass pointers to user-defined error handlers instead of NULLs: */ + + png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, &PNGErrorFunction, &PNGWarningFunction ); + if (!png_ptr) + return false; /* out of memory */ + + info_ptr = png_create_info_struct( png_ptr ); + if ( !info_ptr ) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return false; /* out of memory */ + } + + /* setjmp() must be called in every function that calls a PNG-reading + * libpng function */ + + if ( setjmp( png_jmpbuf(png_ptr) ) ) + { + png_destroy_read_struct( &png_ptr, &info_ptr, NULL ); + return false; + } + + png_set_read_fn( png_ptr, (void *)&pngDataStream, ReadPNGData ); + png_read_info( png_ptr, info_ptr ); /* read all PNG info up to image data */ + + + /* alternatively, could make separate calls to png_get_image_width(), + * etc., but want bit_depth and color_type for later [don't care about + * compression_type and filter_type => NULLs] */ + + int bit_depth; + int color_type; + uint32 png_width; + uint32 png_height; + + png_get_IHDR( png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type, NULL, NULL, NULL ); + + width = png_width; + height = png_height; + + png_uint_32 rowbytes; + + /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits, + * transparency chunks to full alpha channel; strip 16-bit-per-sample + * images to 8 bits per sample; and convert grayscale to RGB[A] */ + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_expand( png_ptr ); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand( png_ptr ); + if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ) ) + png_set_expand( png_ptr ); + if (bit_depth == 16) + png_set_strip_16( png_ptr ); + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb( png_ptr ); + + /* + double gamma; + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma(png_ptr, display_exponent, gamma); + +*/ + /* all transformations have been registered; now update info_ptr data, + * get rowbytes and channels, and allocate image memory */ + + png_read_update_info( png_ptr, info_ptr ); + + rowbytes = png_get_rowbytes( png_ptr, info_ptr ); + png_byte channels = (int)png_get_channels( png_ptr, info_ptr ); + Assert( channels == 4 ); + if ( channels != 4 ) + return false; + + png_bytepp row_pointers = NULL; + + if ( ( row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep) ) ) == NULL ) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return false; + } + + bufOutput.EnsureCapacity( rowbytes*height ); + bufOutput.SeekPut( CUtlBuffer::SEEK_HEAD, rowbytes*height ); + + /* set the individual row_pointers to point at the correct offsets */ + + for ( int i = 0; i < height; ++i) + row_pointers[i] = (png_byte *)bufOutput.Base() + i*rowbytes; + + + /* now we can go ahead and just read the whole image */ + + png_read_image( png_ptr, row_pointers ); + + free( row_pointers ); + row_pointers = NULL; + + png_read_end(png_ptr, NULL); + + if ( png_ptr && info_ptr ) + { + png_destroy_read_struct( &png_ptr, &info_ptr, NULL ); + png_ptr = NULL; + info_ptr = NULL; + } + + return true; +} |