/* Copyright 2007-2008 Christian Henning, Andreas Pokorny, Lubomir Bourdev Use, modification and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt). */ /*************************************************************************************************/ #ifndef BOOST_GIL_EXTENSION_IO_DETAIL_PNG_IO_WRITE_HPP #define BOOST_GIL_EXTENSION_IO_DETAIL_PNG_IO_WRITE_HPP //////////////////////////////////////////////////////////////////////////////////////// /// \file /// \brief /// \author Christian Henning, Andreas Pokorny, Lubomir Bourdev \n /// /// \date 2007-2008 \n /// //////////////////////////////////////////////////////////////////////////////////////// extern "C" { #include } #include #include #include #include "base.hpp" #include "supported_types.hpp" namespace boost { namespace gil { namespace detail { template class writer< Device , png_tag > : png_io_base< Device > { public: writer( Device& io_dev ) : png_io_base< Device >( io_dev ) , _png_ptr ( NULL ) , _info_ptr( NULL ) { // Create and initialize the png_struct with the desired error handler // functions. If you want to use the default stderr and longjump method, // you can supply NULL for the last three parameters. We also check that // the library version is compatible with the one used at compile time, // in case we are using dynamically linked libraries. REQUIRED. _png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING , NULL // user_error_ptr , NULL // user_error_fn , NULL // user_warning_fn ); io_error_if( _png_ptr == NULL , "png_writer: fail to call png_create_write_struct()" ); // Allocate/initialize the image information data. REQUIRED _info_ptr = png_create_info_struct( _png_ptr ); if( _info_ptr == NULL ) { png_destroy_write_struct( &_png_ptr , NULL ); io_error( "png_writer: fail to call png_create_info_struct()" ); } // Set error handling. REQUIRED if you aren't supplying your own // error handling functions in the png_create_write_struct() call. if( setjmp( png_jmpbuf( _png_ptr ))) { //free all of the memory associated with the png_ptr and info_ptr png_destroy_write_struct( &_png_ptr , &_info_ptr ); io_error( "png_writer: fail to call setjmp()" ); } this->init_io( _png_ptr ); } ~writer() { png_destroy_write_struct( &_png_ptr , &_info_ptr ); } template< typename View > void apply( const View& view ) { apply( view, image_write_info< png_tag >() ); } template void apply( const View& view , const image_write_info< png_tag >& info ) { typedef png_write_support< typename channel_type < typename get_pixel_type< View >::type >::type , typename color_space_type< View >::type > png_rw_info_t; io_error_if( view.width() == 0 && view.height() == 0 , "png format cannot handle empty views." ); // 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( _png_ptr , _info_ptr , 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_method , info._filter_method ); #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED if( info._valid_cie_colors ) { png_set_cHRM( _png_ptr , _info_ptr , 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( _png_ptr , _info_ptr , info._gamma ); } #else if( info._valid_cie_colors ) { png_set_cHRM_fixed( _png_ptr , _info_ptr , 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( _png_ptr , _info_ptr , info._file_gamma ); } #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED if( info._valid_icc_profile ) { png_set_iCCP( _png_ptr , _info_ptr , const_cast< png_charp >( info._icc_name.c_str() ) , info._iccp_compression_type , const_cast< png_charp >( info._profile.c_str() ) , info._profile_length ); } if( info._valid_intent ) { png_set_sRGB( _png_ptr , _info_ptr , info._intent ); } if( info._valid_palette ) { png_set_PLTE( _png_ptr , _info_ptr , const_cast< png_colorp >( &info._palette.front() ) , info._num_palette ); } if( info._valid_background ) { png_set_bKGD( _png_ptr , _info_ptr , const_cast< png_color_16p >( &info._background ) ); } if( info._valid_histogram ) { png_set_hIST( _png_ptr , _info_ptr , const_cast< png_uint_16p >( &info._histogram.front() ) ); } if( info._valid_offset ) { png_set_oFFs( _png_ptr , _info_ptr , 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( _png_ptr , _info_ptr , 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( _png_ptr , _info_ptr , info._res_x , info._res_y , info._phy_unit_type ); } if( info._valid_significant_bits ) { png_set_sBIT( _png_ptr , _info_ptr , const_cast< png_color_8p >( &info._sig_bits ) ); } #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED if( info._valid_scale_factors ) { png_set_sCAL( _png_ptr , _info_ptr , info._scale_unit , info._scale_width , info._scale_height ); } #else #ifdef BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED if( info._valid_scale_factors ) { png_set_sCAL_s( _png_ptr , _info_ptr , _scale_unit , const_cast< png_charp >( _scale_width.c_str() ) , const_cast< png_charp >( _scale_height.c_str() ) ); } #endif // BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED 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 >( info._text[i]._key.c_str() ); pt.text = const_cast< png_charp >( info._text[i]._text.c_str() ); pt.text_length = info._text[i]._text.length(); texts[i] = pt; } png_set_text( _png_ptr , _info_ptr , &texts.front() , info._num_text ); } if( info._valid_modification_time ) { png_set_tIME( _png_ptr , _info_ptr , const_cast< png_timep >( &info._mod_time ) ); } if( info._valid_transparency_factors ) { int sample_max = ( 1 << 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( _png_ptr , _info_ptr , trans , num_trans , trans_values ); */ } } png_write_info( _png_ptr ,_info_ptr ); write_view( view , typename is_bit_aligned< View >::type() ); } private: template void write_view( const View& view , mpl::false_ // is bit aligned ) { typedef typename get_pixel_type< View >::type pixel_t; typedef png_write_support< typename channel_type < pixel_t >::type , typename color_space_type< pixel_t >::type > png_rw_info; if( little_endian() ) { if( png_rw_info::_bit_depth == 16 ) { png_set_swap( _png_ptr ); } if( png_rw_info::_bit_depth < 8 ) { png_set_packswap( _png_ptr ); } } row_buffer_helper_view< View > row_buffer( view.width() , false ); for( int y = 0; y != view.height(); ++ y) { std::copy( view.row_begin( y ) , view.row_end ( y ) , row_buffer.begin() ); png_write_row( _png_ptr , reinterpret_cast< png_bytep >( row_buffer.data() ) ); } png_write_end( _png_ptr , _info_ptr ); } template void write_view( const View& view , mpl::true_ // is bit aligned ) { typedef png_write_support< typename kth_semantic_element_type< typename View::value_type , 0 >::type , typename color_space_type::type > png_rw_info; if (little_endian() ) { if( png_rw_info::_bit_depth == 16 ) { png_set_swap( _png_ptr ); } if( png_rw_info::_bit_depth < 8 ) { png_set_packswap( _png_ptr ); } } row_buffer_helper_view< View > row_buffer( view.width() , false ); for( int y = 0; y != view.height(); ++y ) { std::copy( view.row_begin( y ) , view.row_end ( y ) , row_buffer.begin() ); png_write_row( _png_ptr , reinterpret_cast< png_bytep >( row_buffer.data() ) ); } png_free_data( _png_ptr , _info_ptr , PNG_FREE_UNKN , -1 ); png_write_end( _png_ptr , _info_ptr ); } void init_io( png_structp png_ptr ) { png_set_write_fn( png_ptr , static_cast< void* > ( &this->_io_dev ) , static_cast< png_rw_ptr > ( &png_io_base::write_data ) , static_cast< png_flush_ptr >( &png_io_base::flush ) ); } png_structp _png_ptr; png_infop _info_ptr; }; struct png_write_is_supported { template< typename View > struct apply : public is_write_supported< typename get_pixel_type< View >::type , png_tag > {}; }; template< typename Device > class dynamic_image_writer< Device , png_tag > : public writer< Device , png_tag > { typedef writer< Device , png_tag > parent_t; public: dynamic_image_writer( Device& file ) : parent_t( file ) {} template< typename Views > void apply( const any_image_view< Views >& views ) { dynamic_io_fnobj< png_write_is_supported , parent_t > op( this ); apply_operation( views, op ); } }; } // namespace detail } // namespace gil } // namespace boost #endif // BOOST_GIL_EXTENSION_IO_DETAIL_PNG_IO_WRITE_HPP