BOOL OutputPNG::OutputPNGHeader(CCLexFile *File, LPBITMAPINFOHEADER pInfo, BOOL InterlaceState, INT32 TransparentColour, LPLOGPALETTE pPalette, LPRGBQUAD pQuadPalette) { ERROR2IF(File==NULL,FALSE,"OutputPNG::OutputPNGHeader File pointer is null"); if (pInfo == NULL) pInfo = &(DestBitmapInfo->bmiHeader); ERROR2IF(pInfo==NULL,FALSE,"OutputPNG::OutputPNGHeader BitmapInfo pointer is null"); //ERROR2IF(pPalette==NULL && pQuadPalette==NULL,FALSE,"OutputPNG::OutputPNGHeader Bitmap palette pointer is null"); TRACEUSER( "Jonathan", _T("PNG write: Interlace: %s\n"), InterlaceState ? _T("Yes") : _T("No")); // Note file in our class variable as used by all the low level routines OutputFile = File; // Note the specified transparency and interlace states in our class variables Interlace = InterlaceState; if (TransparentColour != -1) Transparent = TRUE; else Transparent = FALSE; // We are just about to start so set the PNG exception handling up with our CCFile pointer PNGUtil::SetCCFilePointer(File); // Must set the exception throwing flag to True and force reporting of errors to False. // This means that the caller must report an error if the function returns False. // Any calls to CCFile::GotError will now throw a file exception and should fall into // the catch handler at the end of the function. // Replaces the goto's that handled this before. BOOL OldThrowingState = File->SetThrowExceptions( TRUE ); BOOL OldReportingState = File->SetReportErrors( FALSE ); // PNG related items (NOTE: p at end means pointer and hence implied *) png_ptr = NULL; info_ptr = NULL; palette = NULL; try { // Work out the palette size INT32 PalSize = pInfo->biClrUsed; // How many entries in palette TRACEUSER( "Jonathan", _T("PNG write: PalSize = %d\n"),PalSize); // Set up the class variables // First the width/height of the bitmap Width = pInfo->biWidth; Height = pInfo->biHeight; TRACEUSER( "Jonathan", _T("PNG write: Width = %d Height = %d\n"),Width,Height); BitsPerPixel = pInfo->biBitCount; // Start up the PNG writing code // allocate the necessary structures // Use the default handlers png_ptr = png_create_write_struct_2( PNG_LIBPNG_VER_STRING, // libpng version 0, // Optional pointer to be sent with errors camelot_png_error, // Function called in case of error camelot_png_warning, // Function called for warnings 0, // Optional pointer to be sent with mem ops camelot_png_malloc, // Function called to alloc memory camelot_png_free // Function called to free memory ); if (!png_ptr) File->GotError( _R(IDS_OUT_OF_MEMORY) ); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); File->GotError( _R(IDS_OUT_OF_MEMORY) ); } // set up the input control to the fstream class // If not a disk file then panic for the present moment // Could use the memfile functions for reading and writing as they give us what // we want. Use the io_ptr and iostream* pFStream = File->GetIOFile(); if (pFStream == NULL) { TRACEUSER( "Jonathan", _T("PNG write: OutputPNG::OutputPNGHeader No access to IOStream!")); File->GotError( _R(IDS_UNKNOWN_PNG_ERROR) ); } // Should use our own function png_set_write_fn(png_ptr, pFStream, camelot_png_write_data, camelot_png_flush_data); // png_init_io(png_ptr, pFStream); // You now have the option of modifying how the compression library // will run. The following functions are mainly for testing, but // may be useful in certain special cases, like if you need to // write png files extremely fast and are willing to give up some // compression, or if you want to get the maximum possible compression // at the expense of slower writing. If you have no special needs // in this area, let the library do what it wants, as it has been // carefully tuned to deliver the best speed/compression ratio. // See the compression library for more details. // turn on or off filtering (1 or 0) //png_set_filtering(png_ptr, 1); // compression level (0 - none, 6 - default, 9 - maximum) //png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); //png_set_compression_mem_level(png_ptr, 8); //png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); //png_set_compression_window_bits(png_ptr, 15); //png_set_compression_method(png_ptr, 8); // - this describes which optional chunks to write to the // file. Note that if you are writing a // PNG_COLOR_TYPE_PALETTE file, the PLTE chunk is not // optional, but must still be marked for writing. To // mark chunks for writing, OR valid with the // appropriate PNG_INFO_<chunk name> define. png_get_valid(png_ptr, info_ptr, 0); // resolution of image png_set_invalid(png_ptr, info_ptr, PNG_INFO_pHYs); png_set_pHYs(png_ptr, info_ptr, pInfo->biXPelsPerMeter, pInfo->biYPelsPerMeter, 1); //meter TRACEUSER( "Jonathan", _T("PNG write: x,y px per cm = %d %d\n"), png_get_x_pixels_per_meter(png_ptr, info_ptr) / 1000, png_get_y_pixels_per_meter(png_ptr, info_ptr) / 1000); BitsPerPixel = pInfo->biBitCount; TRACEUSER( "Jonathan", _T("PNG write: Bitdepth = %d\n"), BitsPerPixel); palette = NULL; num_palette = 0; trans = NULL; // - array of transparent entries for paletted images num_trans = 0; // - number of transparent entries TRACEUSER( "Jonathan", _T("PNG write: TransColour = %d\n"), TransparentColour); if ( BitsPerPixel <= 8 ) { png_set_IHDR(png_ptr, info_ptr, Width, Height, BitsPerPixel, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // set the palette if there is one png_set_invalid(png_ptr, info_ptr, PNG_INFO_PLTE); INT32 PaletteEntries = pInfo->biClrUsed; palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color)); if (palette == NULL) File->GotError( _R(IDS_OUT_OF_MEMORY) ); png_set_PLTE(png_ptr, info_ptr, palette, num_palette); png_color_struct * pPNGPalette = palette; // ... set palette colors ... if (pQuadPalette && PaletteEntries > 0) { // Palette supplied in RGBQUAD form for (INT32 i = 0; i < PaletteEntries; i++) { pPNGPalette->red = pQuadPalette->rgbRed; pPNGPalette->green = pQuadPalette->rgbGreen; pPNGPalette->blue = pQuadPalette->rgbBlue; // skip to the next palette entry pQuadPalette++; pPNGPalette++; } } else if (pPalette && PaletteEntries > 0) { // Palette supplied in LOGPALETTE form for (INT32 i = 0; i < PaletteEntries; i++) { pPNGPalette->red = pPalette->palPalEntry[i].peRed; pPNGPalette->green = pPalette->palPalEntry[i].peGreen; pPNGPalette->blue = pPalette->palPalEntry[i].peBlue; pPNGPalette++; } } else File->GotError(_R(IDS_PNG_ERR_WRITE_PALETTE)); // Now check to see if transparency is present or not if (TransparentColour >= 0 && TransparentColour <= PaletteEntries ) { // Create the array of transparent entries for this palette // 0 is fully transparent, 255 is fully opaque, regardless of image bit depth // We will only create as many as we require, i.e. up to the transparent colour entry // rather a full palettes worth INT32 NumEntries = TransparentColour + 1; trans = (png_byte*)png_malloc(png_ptr, NumEntries * sizeof (png_byte)); if (trans) { // Set the number of transparent entries num_trans = NumEntries; png_byte * pTransEntry = trans; png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); for (INT32 i = 0; i < TransparentColour; i++) { *pTransEntry = 255; // set it fully opaque pTransEntry++; } // We should now be at the transparent entry so set it fully transparent *pTransEntry = 0; } } } else if (BitsPerPixel == 24) { png_set_IHDR(png_ptr, info_ptr, Width, Height, 8, /* bit_depth */ PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } else if (BitsPerPixel == 32) { png_set_IHDR(png_ptr, info_ptr, Width, Height, 8, /* bit_depth */ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } else ERROR2(FALSE,"OutputPNG::OutputPNGHeader Unknown bit depth"); TRACEUSER( "Jonathan", _T("PNG write: bit_depth = %d color_type = %d\n"), png_get_bit_depth(png_ptr, info_ptr), png_get_color_type(png_ptr, info_ptr)); // Could use:- // if we are dealing with a grayscale image then //info_ptr->sig_bit.gray = true_bit_depth; png_set_hIST(png_ptr, info_ptr, NULL); png_set_text(png_ptr, info_ptr, NULL, 0); // write the file information png_write_info(png_ptr, info_ptr); TRACEUSER( "Jonathan", _T("PNG write: pixel_depth %d channels %d\n"), png_get_bit_depth(png_ptr, info_ptr), png_get_channels(png_ptr, info_ptr)); TRACEUSER( "Jonathan", _T("PNG write: rowbytes %d color_type %d\n"), png_get_rowbytes(png_ptr, info_ptr), png_get_color_type(png_ptr, info_ptr)); // Set up the transformations you want. // Note: that these are all optional. Only call them if you want them // invert monocrome pixels //png_set_invert(png_ptr); // shift the pixels up to a legal bit depth and fill in as appropriate // to correctly scale the image //png_set_shift(png_ptr, &(info_ptr->sig_bit)); // pack pixels into bytes //png_set_packing(png_ptr); png_set_bgr(png_ptr); // swap bytes of 16 bit files to most significant bit first png_set_swap(png_ptr); // Must set the exception throwing and reporting flags back to their entry states File->SetThrowExceptions( OldThrowingState ); File->SetReportErrors( OldReportingState ); // er, we seem to have finished OK so say so return TRUE; } catch (...) { // catch our form of a file exception TRACE( _T("OutputPNG::OutputPNGHeader CC catch handler\n")); // Call up function to clean up the png structures CleanUpPngStructures(); // Must set the exception throwing and reporting flags back to their entry states File->SetThrowExceptions( OldThrowingState ); File->SetReportErrors( OldReportingState ); // We have finished so reset the PNG exception handling PNGUtil::SetCCFilePointer(NULL); return FALSE; } ERROR2( FALSE, "Escaped exception clause somehow" ); }
void write_header( const View& view ) { using png_rw_info_t = detail::png_write_support < typename channel_type<typename get_pixel_type<View>::type>::type, typename color_space_type<View>::type >; // Set the image information here. Width and height are up to 2^31, // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED png_set_IHDR( get_struct() , get_info() , static_cast< png_image_width::type >( view.width() ) , static_cast< png_image_height::type >( view.height() ) , static_cast< png_bitdepth::type >( png_rw_info_t::_bit_depth ) , static_cast< png_color_type::type >( png_rw_info_t::_color_type ) , _info._interlace_method , _info._compression_type , _info._filter_method ); #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED if( _info._valid_cie_colors ) { png_set_cHRM( get_struct() , get_info() , _info._white_x , _info._white_y , _info._red_x , _info._red_y , _info._green_x , _info._green_y , _info._blue_x , _info._blue_y ); } if( _info._valid_file_gamma ) { png_set_gAMA( get_struct() , get_info() , _info._file_gamma ); } #else if( _info._valid_cie_colors ) { png_set_cHRM_fixed( get_struct() , get_info() , _info._white_x , _info._white_y , _info._red_x , _info._red_y , _info._green_x , _info._green_y , _info._blue_x , _info._blue_y ); } if( _info._valid_file_gamma ) { png_set_gAMA_fixed( get_struct() , get_info() , _info._file_gamma ); } #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED if( _info._valid_icc_profile ) { #if PNG_LIBPNG_VER_MINOR >= 5 png_set_iCCP( get_struct() , get_info() , const_cast< png_charp >( _info._icc_name.c_str() ) , _info._iccp_compression_type , reinterpret_cast< png_const_bytep >( & (_info._profile.front ()) ) , _info._profile_length ); #else png_set_iCCP( get_struct() , get_info() , const_cast< png_charp >( _info._icc_name.c_str() ) , _info._iccp_compression_type , const_cast< png_charp >( & (_info._profile.front()) ) , _info._profile_length ); #endif } if( _info._valid_intent ) { png_set_sRGB( get_struct() , get_info() , _info._intent ); } if( _info._valid_palette ) { png_set_PLTE( get_struct() , get_info() , const_cast< png_colorp >( &_info._palette.front() ) , _info._num_palette ); } if( _info._valid_background ) { png_set_bKGD( get_struct() , get_info() , const_cast< png_color_16p >( &_info._background ) ); } if( _info._valid_histogram ) { png_set_hIST( get_struct() , get_info() , const_cast< png_uint_16p >( &_info._histogram.front() ) ); } if( _info._valid_offset ) { png_set_oFFs( get_struct() , get_info() , _info._offset_x , _info._offset_y , _info._off_unit_type ); } if( _info._valid_pixel_calibration ) { std::vector< const char* > params( _info._num_params ); for( std::size_t i = 0; i < params.size(); ++i ) { params[i] = _info._params[ i ].c_str(); } png_set_pCAL( get_struct() , get_info() , const_cast< png_charp >( _info._purpose.c_str() ) , _info._X0 , _info._X1 , _info._cal_type , _info._num_params , const_cast< png_charp >( _info._units.c_str() ) , const_cast< png_charpp >( ¶ms.front() ) ); } if( _info._valid_resolution ) { png_set_pHYs( get_struct() , get_info() , _info._res_x , _info._res_y , _info._phy_unit_type ); } if( _info._valid_significant_bits ) { png_set_sBIT( get_struct() , get_info() , const_cast< png_color_8p >( &_info._sig_bits ) ); } #ifndef BOOST_GIL_IO_PNG_1_4_OR_LOWER #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED if( _info._valid_scale_factors ) { png_set_sCAL( get_struct() , get_info() , this->_info._scale_unit , this->_info._scale_width , this->_info._scale_height ); } #else #ifdef BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED if( _info._valid_scale_factors ) { png_set_sCAL_fixed( get_struct() , get_info() , this->_info._scale_unit , this->_info._scale_width , this->_info._scale_height ); } #else if( _info._valid_scale_factors ) { png_set_sCAL_s( get_struct() , get_info() , this->_info._scale_unit , const_cast< png_charp >( this->_info._scale_width.c_str() ) , const_cast< png_charp >( this->_info._scale_height.c_str() ) ); } #endif // BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED #endif // BOOST_GIL_IO_PNG_1_4_OR_LOWER if( _info._valid_text ) { std::vector< png_text > texts( _info._num_text ); for( std::size_t i = 0; i < texts.size(); ++i ) { png_text pt; pt.compression = _info._text[i]._compression; pt.key = const_cast< png_charp >( this->_info._text[i]._key.c_str() ); pt.text = const_cast< png_charp >( this->_info._text[i]._text.c_str() ); pt.text_length = _info._text[i]._text.length(); texts[i] = pt; } png_set_text( get_struct() , get_info() , &texts.front() , _info._num_text ); } if( _info._valid_modification_time ) { png_set_tIME( get_struct() , get_info() , const_cast< png_timep >( &_info._mod_time ) ); } if( _info._valid_transparency_factors ) { int sample_max = ( 1u << _info._bit_depth ); /* libpng doesn't reject a tRNS chunk with out-of-range samples */ if( !( ( _info._color_type == PNG_COLOR_TYPE_GRAY && (int) _info._trans_values[0].gray > sample_max ) || ( _info._color_type == PNG_COLOR_TYPE_RGB &&( (int) _info._trans_values[0].red > sample_max || (int) _info._trans_values[0].green > sample_max || (int) _info._trans_values[0].blue > sample_max ) ) ) ) { //@todo Fix that once reading transparency values works /* png_set_tRNS( get_struct() , get_info() , trans , num_trans , trans_values ); */ } } // Compression Levels - valid values are [0,9] png_set_compression_level( get_struct() , _info._compression_level ); png_set_compression_mem_level( get_struct() , _info._compression_mem_level ); png_set_compression_strategy( get_struct() , _info._compression_strategy ); png_set_compression_window_bits( get_struct() , _info._compression_window_bits ); png_set_compression_method( get_struct() , _info._compression_method ); png_set_compression_buffer_size( get_struct() , _info._compression_buffer_size ); #ifdef BOOST_GIL_IO_PNG_DITHERING_SUPPORTED // Dithering if( _info._set_dithering ) { png_set_dither( get_struct() , &_info._dithering_palette.front() , _info._dithering_num_palette , _info._dithering_maximum_colors , &_info._dithering_histogram.front() , _info._full_dither ); } #endif // BOOST_GIL_IO_PNG_DITHERING_SUPPORTED // Filter if( _info._set_filter ) { png_set_filter( get_struct() , 0 , _info._filter ); } // Invert Mono if( _info._invert_mono ) { png_set_invert_mono( get_struct() ); } // True Bits if( _info._set_true_bits ) { png_set_sBIT( get_struct() , get_info() , &_info._true_bits.front() ); } // sRGB Intent if( _info._set_srgb_intent ) { png_set_sRGB( get_struct() , get_info() , _info._srgb_intent ); } // Strip Alpha if( _info._strip_alpha ) { png_set_strip_alpha( get_struct() ); } // Swap Alpha if( _info._swap_alpha ) { png_set_swap_alpha( get_struct() ); } png_write_info( get_struct() , get_info() ); }
int main( int argc, char *argv[] ) { int f, rowbytes; char buf[256]; static FILE *fpout; /* "static" prevents setjmp corruption */ png_structp write_ptr; png_infop write_info_ptr, end_info_ptr; png_bytep row_buf, here; png_uint_32 y; png_textp text_ptr, new_text_ptr; int num_text; int interlace_type, compression_type, filter_type, bit_depth, color_type; int it, ct, ft, bd, clrt; png_uint_32 width, height, w, h; int duration; if( argc < 4 ) { printf( "makeanim v0.2\nusage: makeanim <duration in milliseconds> <input files ...> <output file>\n" ); printf( "example: makeanim 1500 a00.png a01.png a02.png a03.png a04.png a.anim\n" ); return 1; } duration = atoi( argv[1] ); if( duration < 1 ) { printf( "duration is incorrect\n" ); return 1; } numfiles = argc - 3; input = (struct inputstruct *)malloc( sizeof( struct inputstruct ) * numfiles ); if( !input ) return 1; for( f = 0; f < numfiles; f++ ) { input[f].name = argv[f + 2]; printf( "opening file %d, \"%s\"\n", f, input[f].name ); /* open the file handle */ input[f].file = fopen( input[f].name, "rb" ); if( input[f].file == NULL ) { printf( "fopen() failed\n" ); return 1; } /* check if it's PNG */ if( fread( buf, 1, 8, input[f].file ) != 8 ) { printf( "fread() failed for file \"%s\"\n", input[f].name ); return 1; } if( png_sig_cmp( buf, (png_size_t)0, 8 ) ) { printf( "not a PNG file\n" ); return 1; } fseek( input[f].file, 0, SEEK_SET ); /* allocate read structure */ input[f].read_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL ); if( input[f].read_ptr == NULL ) { printf( "png_create_read_struct() failed\n" ); return 1; } /* allocate read info structure */ input[f].read_info_ptr = png_create_info_struct( input[f].read_ptr ); if( input[f].read_info_ptr == NULL ) { printf( "png_create_info_struct() failed\n" ); return 1; } /* set error handler code */ if( setjmp( input[f].read_ptr->jmpbuf ) ) { printf( "libpng read error\n" ); return 1; } /* initialize stream */ png_init_io( input[f].read_ptr, input[f].file ); png_set_read_status_fn( input[f].read_ptr, NULL ); /* read png info struct */ png_read_info( input[f].read_ptr, input[f].read_info_ptr ); /* get the info */ if( !png_get_IHDR( input[f].read_ptr, input[f].read_info_ptr, &w, &h, &bd, &clrt, &it, &ct, &ft ) ) { printf( "png_get_IHDR() failed\n" ); return 1; } /* save the info of the first frame */ if( f == 0 ) { width = w; height = h; bit_depth = bd; color_type = clrt; interlace_type = it; compression_type = ct; filter_type = ft; } /* compare all other frames to first frame */ else if( (w != width) || (h != height) || (bd != bit_depth) || (clrt != color_type) || (it != interlace_type) || (ct != compression_type) || (ft != filter_type) ) { if( w != width ) printf( "width is different\n" ); if( h != height ) printf( "height is different\n" ); if( bd != bit_depth ) printf( "bit depth is different\n" ); if( clrt != color_type ) printf( "color type is different\n" ); if( it != interlace_type ) printf( "interlace type is different\n" ); if( ct != compression_type ) printf( "compression type is different\n" ); if( ft != filter_type ) printf( "filter type is different\n" ); return 1; } } row_buf = (png_bytep)NULL; /* open output file */ printf( "opening file \"%s\"\n", argv[numfiles + 2] ); fpout = fopen( argv[numfiles + 2], "wb" ); if( fpout == NULL ) { printf( "fopen() failed\n" ); return 1; } /* allocate write structure */ write_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL ); /* allocate info structures */ write_info_ptr = png_create_info_struct( write_ptr ); end_info_ptr = png_create_info_struct( write_ptr ); /* error handling */ if( setjmp( write_ptr->jmpbuf ) ) { printf( "libpng write error\n" ); return 1; } /* initialize output stream */ png_init_io( write_ptr, fpout ); png_set_write_status_fn( write_ptr, NULL ); /* set info */ png_set_IHDR( write_ptr, write_info_ptr, width * numfiles, height, bit_depth, color_type, PNG_INTERLACE_NONE, compression_type, filter_type); /* image characteristics */ { png_color_16p background; double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; double gamma; int intent; png_uint_16p hist; png_uint_32 offset_x, offset_y; int unit_type; png_charp purpose, units; png_charpp params; png_int_32 X0, X1; int type, nparams; png_uint_32 res_x, res_y; png_colorp palette; int num_palette; png_color_8p sig_bit; png_bytep trans; int num_trans; png_color_16p trans_values; /* background color */ if( png_get_bKGD( input[0].read_ptr, input[0].read_info_ptr, &background ) ) { png_set_bKGD( write_ptr, write_info_ptr, background ); } if( png_get_cHRM( input[0].read_ptr, input[0].read_info_ptr, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y ) ) { png_set_cHRM( write_ptr, write_info_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y ); } /* gamma */ if( png_get_gAMA( input[0].read_ptr, input[0].read_info_ptr, &gamma ) ) { png_set_gAMA( write_ptr, write_info_ptr, gamma ); } /* rendering intent */ if( png_get_sRGB( input[0].read_ptr, input[0].read_info_ptr, &intent ) ) { png_set_sRGB( write_ptr, write_info_ptr, intent ); } /* Histogram */ if( png_get_hIST( input[0].read_ptr, input[0].read_info_ptr, &hist ) ) { png_set_hIST( write_ptr, write_info_ptr, hist ); } /* offsets */ if( png_get_oFFs( input[0].read_ptr, input[0].read_info_ptr, &offset_x, &offset_y, &unit_type ) ) { png_set_oFFs( write_ptr, write_info_ptr, offset_x, offset_y, unit_type ); } if( png_get_pCAL( input[0].read_ptr, input[0].read_info_ptr, &purpose, &X0, &X1, &type, &nparams, &units, ¶ms ) ) { png_set_pCAL( write_ptr, write_info_ptr, purpose, X0, X1, type, nparams, units, params ); } /* pixel density */ if( png_get_pHYs( input[0].read_ptr, input[0].read_info_ptr, &res_x, &res_y, &unit_type ) ) { png_set_pHYs( write_ptr, write_info_ptr, res_x, res_y, unit_type ); } /* text chunks */ /* if( png_get_text( input[0].read_ptr, input[0].read_info_ptr, &text_ptr, &num_text ) > 0 ) { printf( "Handling %d tEXt/zTXt chunks\n", num_text ); png_set_text( write_ptr, write_info_ptr, text_ptr, num_text ); } */ /* palette */ if( png_get_PLTE( input[0].read_ptr, input[0].read_info_ptr, &palette, &num_palette ) ) { png_set_PLTE( write_ptr, write_info_ptr, palette, num_palette ); } /* significant bits */ if( png_get_sBIT( input[0].read_ptr, input[0].read_info_ptr, &sig_bit ) ) { png_set_sBIT( write_ptr, write_info_ptr, sig_bit ); } /* transparency */ if( png_get_tRNS( input[0].read_ptr, input[0].read_info_ptr, &trans, &num_trans, &trans_values ) ) { png_set_tRNS( write_ptr, write_info_ptr, trans, num_trans, trans_values ); } } /* text chunks */ num_text = 0; if( !png_get_text( input[0].read_ptr, input[0].read_info_ptr, &text_ptr, &num_text ) ) num_text = 0; new_text_ptr = (struct png_text_struct *)malloc( sizeof( struct png_text_struct ) * num_text + 1 ); if( !new_text_ptr ) { printf( "malloc() failed\n" ); return 1; } memcpy( new_text_ptr, text_ptr, sizeof( struct png_text_struct ) * num_text ); snprintf( buf, 255, "SDL_anim %d %d %d", duration, width, numfiles ); buf[255] = 0; new_text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE; new_text_ptr[num_text].key = "format"; new_text_ptr[num_text].text = buf; new_text_ptr[num_text].text_length = strlen( buf ); num_text++; png_set_text( write_ptr, write_info_ptr, new_text_ptr, num_text ); /* write info */ png_write_info( write_ptr, write_info_ptr ); /* allocate buffer */ rowbytes = png_get_rowbytes( input[0].read_ptr, input[0].read_info_ptr ); row_buf = (png_bytep)png_malloc( write_ptr, rowbytes * numfiles ); if( row_buf == NULL ) { printf( "png_malloc() failed\n" ); return 1; } /* copy raw data */ for( y = 0; y < height; y++ ) { /* grab a scanline from each file */ here = row_buf; for( f = 0; f < numfiles; f++ ) { png_read_rows( input[f].read_ptr, (png_bytepp)&here, (png_bytepp)NULL, 1 ); here += rowbytes; } /* write the long scanline */ png_write_rows( write_ptr, (png_bytepp)&row_buf, 1 ); } /* end io */ for( f = 0; f < numfiles; f++ ) png_read_end( input[f].read_ptr, end_info_ptr ); png_write_end( write_ptr, end_info_ptr ); /* cleanup */ png_free( write_ptr, row_buf ); for( f = 0; f < numfiles; f++ ) { png_destroy_read_struct( &input[f].read_ptr, &input[f].read_info_ptr, &end_info_ptr); fclose( input[f].file ); } png_destroy_write_struct( &write_ptr, &write_info_ptr ); fclose( fpout ); return 0; }