123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678 |
- //
- // Copyright 2012 Christian Henning
- //
- // Distributed under 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_READER_BACKEND_HPP
- #define BOOST_GIL_EXTENSION_IO_DETAIL_READER_BACKEND_HPP
- #include <boost/gil/extension/io/png/tags.hpp>
- #include <boost/gil/extension/io/png/detail/base.hpp>
- #include <boost/gil/extension/io/png/detail/supported_types.hpp>
- namespace boost { namespace gil {
- #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
- #pragma warning(push)
- #pragma warning(disable:4512) //assignment operator could not be generated
- #pragma warning(disable:4611) //interaction between '_setjmp' and C++ object destruction is non-portable
- #endif
- ///
- /// PNG Backend
- ///
- template<typename Device >
- struct reader_backend< Device
- , png_tag
- >
- : public detail::png_struct_info_wrapper
- {
- public:
- using format_tag_t = png_tag;
- using this_t = reader_backend<Device, png_tag>;
- public:
- reader_backend( const Device& io_dev
- , const image_read_settings< png_tag >& settings
- )
- : _io_dev( io_dev )
- , _settings( settings )
- , _info()
- , _scanline_length( 0 )
- , _number_passes( 0 )
- {
- read_header();
- if( _settings._dim.x == 0 )
- {
- _settings._dim.x = _info._width;
- }
- if( _settings._dim.y == 0 )
- {
- _settings._dim.y = _info._height;
- }
- }
- void read_header()
- {
- using boost::gil::detail::PNG_BYTES_TO_CHECK;
- // check the file's first few bytes
- byte_t buf[PNG_BYTES_TO_CHECK];
- io_error_if( _io_dev.read( buf
- , PNG_BYTES_TO_CHECK
- ) != PNG_BYTES_TO_CHECK
- , "png_check_validity: failed to read image"
- );
- io_error_if( png_sig_cmp( png_bytep(buf)
- , png_size_t(0)
- , PNG_BYTES_TO_CHECK
- ) != 0
- , "png_check_validity: invalid png image"
- );
- // 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 supply the
- // the compiler header file version, so that we know if the application
- // was compiled with a compatible version of the library. REQUIRED
- get()->_struct = png_create_read_struct( PNG_LIBPNG_VER_STRING
- , nullptr // user_error_ptr
- , nullptr // user_error_fn
- , nullptr // user_warning_fn
- );
- io_error_if( get()->_struct == nullptr
- , "png_reader: fail to call png_create_write_struct()"
- );
- png_uint_32 user_chunk_data[4];
- user_chunk_data[0] = 0;
- user_chunk_data[1] = 0;
- user_chunk_data[2] = 0;
- user_chunk_data[3] = 0;
- png_set_read_user_chunk_fn( get_struct()
- , user_chunk_data
- , this_t::read_user_chunk_callback
- );
- // Allocate/initialize the memory for image information. REQUIRED.
- get()->_info = png_create_info_struct( get_struct() );
- if( get_info() == nullptr )
- {
- png_destroy_read_struct( &get()->_struct
- , nullptr
- , nullptr
- );
- io_error( "png_reader: fail to call png_create_info_struct()" );
- }
- // Set error handling if you are using the setjmp/longjmp method (this is
- // the normal method of doing things with libpng). REQUIRED unless you
- // set up your own error handlers in the png_create_read_struct() earlier.
- if( setjmp( png_jmpbuf( get_struct() )))
- {
- //free all of the memory associated with the png_ptr and info_ptr
- png_destroy_read_struct( &get()->_struct
- , &get()->_info
- , nullptr
- );
- io_error( "png is invalid" );
- }
- png_set_read_fn( get_struct()
- , static_cast< png_voidp >( &this->_io_dev )
- , this_t::read_data
- );
- // Set up a callback function that will be
- // called after each row has been read, which you can use to control
- // a progress meter or the like.
- png_set_read_status_fn( get_struct()
- , this_t::read_row_callback
- );
- // Set up a callback which implements user defined transformation.
- // @todo
- png_set_read_user_transform_fn( get_struct()
- , png_user_transform_ptr( nullptr )
- );
- png_set_keep_unknown_chunks( get_struct()
- , PNG_HANDLE_CHUNK_ALWAYS
- , nullptr
- , 0
- );
- // Make sure we read the signature.
- // @todo make it an option
- png_set_sig_bytes( get_struct()
- , PNG_BYTES_TO_CHECK
- );
- // The call to png_read_info() gives us all of the information from the
- // PNG file before the first IDAT (image data chunk). REQUIRED
- png_read_info( get_struct()
- , get_info()
- );
- ///
- /// Start reading the image information
- ///
- // get PNG_IHDR chunk information from png_info structure
- png_get_IHDR( get_struct()
- , get_info()
- , &this->_info._width
- , &this->_info._height
- , &this->_info._bit_depth
- , &this->_info._color_type
- , &this->_info._interlace_method
- , &this->_info._compression_method
- , &this->_info._filter_method
- );
- // get number of color channels in image
- this->_info._num_channels = png_get_channels( get_struct()
- , get_info()
- );
- #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
- // Get CIE chromacities and referenced white point
- if( this->_settings._read_cie_chromacities )
- {
- this->_info._valid_cie_colors = png_get_cHRM( get_struct()
- , get_info()
- , &this->_info._white_x, &this->_info._white_y
- , &this->_info._red_x, &this->_info._red_y
- , &this->_info._green_x, &this->_info._green_y
- , &this->_info._blue_x, &this->_info._blue_y
- );
- }
- // get the gamma value
- if( this->_settings._read_file_gamma )
- {
- this->_info._valid_file_gamma = png_get_gAMA( get_struct()
- , get_info()
- , &this->_info._file_gamma
- );
- if( this->_info._valid_file_gamma == false )
- {
- this->_info._file_gamma = 1.0;
- }
- }
- #else
- // Get CIE chromacities and referenced white point
- if( this->_settings._read_cie_chromacities )
- {
- this->_info._valid_cie_colors = png_get_cHRM_fixed( get_struct()
- , get_info()
- , &this->_info._white_x, &this->_info._white_y
- , &this->_info._red_x, &this->_info._red_y
- , &this->_info._green_x, &this->_info._green_y
- , &this->_info._blue_x, &this->_info._blue_y
- );
- }
- // get the gamma value
- if( this->_settings._read_file_gamma )
- {
- this->_info._valid_file_gamma = png_get_gAMA_fixed( get_struct()
- , get_info()
- , &this->_info._file_gamma
- );
- if( this->_info._valid_file_gamma == false )
- {
- this->_info._file_gamma = 1;
- }
- }
- #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
- // get the embedded ICC profile data
- if( this->_settings._read_icc_profile )
- {
- #if PNG_LIBPNG_VER_MINOR >= 5
- png_charp icc_name = png_charp( nullptr );
- png_bytep profile = png_bytep( nullptr );
- this->_info._valid_icc_profile = png_get_iCCP( get_struct()
- , get_info()
- , &icc_name
- , &this->_info._iccp_compression_type
- , &profile
- , &this->_info._profile_length
- );
- #else
- png_charp icc_name = png_charp( NULL );
- png_charp profile = png_charp( NULL );
- this->_info._valid_icc_profile = png_get_iCCP( get_struct()
- , get_info()
- , &icc_name
- , &this->_info._iccp_compression_type
- , &profile
- , &this->_info._profile_length
- );
- #endif
- if( icc_name )
- {
- this->_info._icc_name.append( icc_name
- , std::strlen( icc_name )
- );
- }
- if( this->_info._profile_length != 0 )
- {
- std:: copy_n (profile, this->_info._profile_length, std:: back_inserter (this->_info._profile));
- }
- }
- // get the rendering intent
- if( this->_settings._read_intent )
- {
- this->_info._valid_intent = png_get_sRGB( get_struct()
- , get_info()
- , &this->_info._intent
- );
- }
- // get image palette information from png_info structure
- if( this->_settings._read_palette )
- {
- png_colorp palette = png_colorp( nullptr );
- this->_info._valid_palette = png_get_PLTE( get_struct()
- , get_info()
- , &palette
- , &this->_info._num_palette
- );
- if( this->_info._num_palette > 0 )
- {
- this->_info._palette.resize( this->_info._num_palette );
- std::copy( palette
- , palette + this->_info._num_palette
- , &this->_info._palette.front()
- );
- }
- }
- // get background color
- if( this->_settings._read_background )
- {
- png_color_16p background = png_color_16p( nullptr );
- this->_info._valid_background = png_get_bKGD( get_struct()
- , get_info()
- , &background
- );
- if( background )
- {
- this->_info._background = *background;
- }
- }
- // get the histogram
- if( this->_settings._read_histogram )
- {
- png_uint_16p histogram = png_uint_16p( nullptr );
- this->_info._valid_histogram = png_get_hIST( get_struct()
- , get_info()
- , &histogram
- );
- if( histogram )
- {
- // the number of values is set by the number of colors inside
- // the palette.
- if( this->_settings._read_palette == false )
- {
- png_colorp palette = png_colorp( nullptr );
- png_get_PLTE( get_struct()
- , get_info()
- , &palette
- , &this->_info._num_palette
- );
- }
- std::copy( histogram
- , histogram + this->_info._num_palette
- , &this->_info._histogram.front()
- );
- }
- }
- // get screen offsets for the given image
- if( this->_settings._read_screen_offsets )
- {
- this->_info._valid_offset = png_get_oFFs( get_struct()
- , get_info()
- , &this->_info._offset_x
- , &this->_info._offset_y
- , &this->_info._off_unit_type
- );
- }
- // get pixel calibration settings
- if( this->_settings._read_pixel_calibration )
- {
- png_charp purpose = png_charp ( nullptr );
- png_charp units = png_charp ( nullptr );
- png_charpp params = png_charpp( nullptr );
- this->_info._valid_pixel_calibration = png_get_pCAL( get_struct()
- , get_info()
- , &purpose
- , &this->_info._X0
- , &this->_info._X1
- , &this->_info._cal_type
- , &this->_info._num_params
- , &units
- , ¶ms
- );
- if( purpose )
- {
- this->_info._purpose.append( purpose
- , std::strlen( purpose )
- );
- }
- if( units )
- {
- this->_info._units.append( units
- , std::strlen( units )
- );
- }
- if( this->_info._num_params > 0 )
- {
- this->_info._params.resize( this->_info._num_params );
- for( png_CAL_nparam::type i = 0
- ; i < this->_info._num_params
- ; ++i
- )
- {
- this->_info._params[i].append( params[i]
- , std::strlen( params[i] )
- );
- }
- }
- }
- // get the physical resolution
- if( this->_settings._read_physical_resolution )
- {
- this->_info._valid_resolution = png_get_pHYs( get_struct()
- , get_info()
- , &this->_info._res_x
- , &this->_info._res_y
- , &this->_info._phy_unit_type
- );
- }
- // get the image resolution in pixels per meter.
- if( this->_settings._read_pixels_per_meter )
- {
- this->_info._pixels_per_meter = png_get_pixels_per_meter( get_struct()
- , get_info()
- );
- }
- // get number of significant bits for each color channel
- if( this->_settings._read_number_of_significant_bits )
- {
- png_color_8p sig_bits = png_color_8p( nullptr );
- this->_info._valid_significant_bits = png_get_sBIT( get_struct()
- , get_info()
- , &sig_bits
- );
- // @todo Is there one or more colors?
- if( sig_bits )
- {
- this->_info._sig_bits = *sig_bits;
- }
- }
- #ifndef BOOST_GIL_IO_PNG_1_4_OR_LOWER
- #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
- // get physical scale settings
- if( this->_settings._read_scale_factors )
- {
- this->_info._valid_scale_factors = png_get_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( this->_settings._read_scale_factors )
- {
- this->_info._valid_scale_factors = png_get_sCAL_fixed( get_struct()
- , get_info()
- , &this->_info._scale_unit
- , &this->_info._scale_width
- , &this->_info._scale_height
- );
- }
- #else
- if( this->_settings._read_scale_factors )
- {
- png_charp scale_width = nullptr;
- png_charp scale_height = nullptr;
- this->_info._valid_scale_factors = png_get_sCAL_s(
- get_struct(), get_info(), &this->_info._scale_unit, &scale_width, &scale_height);
- if (this->_info._valid_scale_factors)
- {
- if( scale_width )
- {
- this->_info._scale_width.append( scale_width
- , std::strlen( scale_width )
- );
- }
- if( scale_height )
- {
- this->_info._scale_height.append( scale_height
- , std::strlen( scale_height )
- );
- }
- }
- }
- #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
- // get comments information from png_info structure
- if( this->_settings._read_comments )
- {
- png_textp text = png_textp( nullptr );
- this->_info._valid_text = png_get_text( get_struct()
- , get_info()
- , &text
- , &this->_info._num_text
- );
- if( this->_info._num_text > 0 )
- {
- this->_info._text.resize( this->_info._num_text );
- for( png_num_text::type i = 0
- ; i < this->_info._num_text
- ; ++i
- )
- {
- this->_info._text[i]._compression = text[i].compression;
- this->_info._text[i]._key.append( text[i].key
- , std::strlen( text[i].key )
- );
- this->_info._text[i]._text.append( text[i].text
- , std::strlen( text[i].text )
- );
- }
- }
- }
- // get last modification time
- if( this->_settings._read_last_modification_time )
- {
- png_timep mod_time = png_timep( nullptr );
- this->_info._valid_modification_time = png_get_tIME( get_struct()
- , get_info()
- , &mod_time
- );
- if( mod_time )
- {
- this->_info._mod_time = *mod_time;
- }
- }
- // get transparency data
- if( this->_settings._read_transparency_data )
- {
- png_bytep trans = png_bytep ( nullptr );
- png_color_16p trans_values = png_color_16p( nullptr );
- this->_info._valid_transparency_factors = png_get_tRNS( get_struct()
- , get_info()
- , &trans
- , &this->_info._num_trans
- , &trans_values
- );
- if( trans )
- {
- //@todo What to do, here? How do I know the length of the "trans" array?
- }
- if( this->_info._num_trans )
- {
- this->_info._trans_values.resize( this->_info._num_trans );
- std::copy( trans_values
- , trans_values + this->_info._num_trans
- , &this->_info._trans_values.front()
- );
- }
- }
- // @todo One day!
- /*
- if( false )
- {
- png_unknown_chunkp unknowns = png_unknown_chunkp( NULL );
- int num_unknowns = static_cast< int >( png_get_unknown_chunks( get_struct()
- , get_info()
- , &unknowns
- )
- );
- }
- */
- }
- /// Check if image is large enough.
- void check_image_size( point_t const& img_dim )
- {
- if( _settings._dim.x > 0 )
- {
- if( img_dim.x < _settings._dim.x ) { io_error( "Supplied image is too small" ); }
- }
- else
- {
- if( img_dim.x < _info._width ) { io_error( "Supplied image is too small" ); }
- }
- if( _settings._dim.y > 0 )
- {
- if( img_dim.y < _settings._dim.y ) { io_error( "Supplied image is too small" ); }
- }
- else
- {
- if( img_dim.y < _info._height ) { io_error( "Supplied image is too small" ); }
- }
- }
- protected:
- static void read_data( png_structp png_ptr
- , png_bytep data
- , png_size_t length
- )
- {
- static_cast<Device*>(png_get_io_ptr(png_ptr) )->read( data
- , length );
- }
- static void flush( png_structp png_ptr )
- {
- static_cast<Device*>(png_get_io_ptr(png_ptr) )->flush();
- }
- static int read_user_chunk_callback( png_struct* /* png_ptr */
- , png_unknown_chunkp /* chunk */
- )
- {
- // @todo
- return 0;
- }
- static void read_row_callback( png_structp /* png_ptr */
- , png_uint_32 /* row_number */
- , int /* pass */
- )
- {
- // @todo
- }
- public:
- Device _io_dev;
- image_read_settings< png_tag > _settings;
- image_read_info < png_tag > _info;
- std::size_t _scanline_length;
- std::size_t _number_passes;
- };
- #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
- #pragma warning(pop)
- #endif
- } // namespace gil
- } // namespace boost
- #endif
|