123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- //
- // Copyright 2007-2012 Christian Henning, Andreas Pokorny, Lubomir Bourdev
- //
- // 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_PNG_DETAIL_READ_HPP
- #define BOOST_GIL_EXTENSION_IO_PNG_DETAIL_READ_HPP
- #include <boost/gil/extension/io/png/tags.hpp>
- #include <boost/gil/extension/io/png/detail/reader_backend.hpp>
- #include <boost/gil/extension/io/png/detail/is_allowed.hpp>
- #include <boost/gil.hpp> // FIXME: Include what you use!
- #include <boost/gil/io/detail/dynamic.hpp>
- #include <boost/gil/io/base.hpp>
- #include <boost/gil/io/conversion_policies.hpp>
- #include <boost/gil/io/device.hpp>
- #include <boost/gil/io/error.hpp>
- #include <boost/gil/io/reader_base.hpp>
- #include <boost/gil/io/row_buffer_helper.hpp>
- #include <boost/gil/io/typedefs.hpp>
- #include <type_traits>
- namespace boost { namespace gil {
- #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
- #pragma warning(push)
- #pragma warning(disable:4512) //assignment operator could not be generated
- #endif
- ///
- /// PNG Reader
- ///
- template< typename Device
- , typename ConversionPolicy
- >
- class reader< Device
- , png_tag
- , ConversionPolicy
- >
- : public reader_base< png_tag
- , ConversionPolicy >
- , public reader_backend< Device
- , png_tag
- >
- {
- private:
- using this_t = reader<Device, png_tag, ConversionPolicy>;
- using cc_t = typename ConversionPolicy::color_converter_type;
- public:
- using backend_t = reader_backend<Device, png_tag>;
- public:
- reader( const Device& io_dev
- , const image_read_settings< png_tag >& settings
- )
- : reader_base< png_tag
- , ConversionPolicy
- >()
- , backend_t( io_dev
- , settings
- )
- {}
- reader( const Device& io_dev
- , const typename ConversionPolicy::color_converter_type& cc
- , const image_read_settings< png_tag >& settings
- )
- : reader_base< png_tag
- , ConversionPolicy
- >( cc )
- , backend_t( io_dev
- , settings
- )
- {}
- template< typename View >
- void apply( const View& view )
- {
- // guard from errors in the following functions
- if (setjmp( png_jmpbuf( this->get_struct() )))
- {
- io_error("png is invalid");
- }
- // The info structures are filled at this point.
- // Now it's time for some transformations.
- if( little_endian() )
- {
- if( this->_info._bit_depth == 16 )
- {
- // Swap bytes of 16 bit files to least significant byte first.
- png_set_swap( this->get_struct() );
- }
- if( this->_info._bit_depth < 8 )
- {
- // swap bits of 1, 2, 4 bit packed pixel formats
- png_set_packswap( this->get_struct() );
- }
- }
- if( this->_info._color_type == PNG_COLOR_TYPE_PALETTE )
- {
- png_set_palette_to_rgb( this->get_struct() );
- }
- if( png_get_valid( this->get_struct(), this->get_info(), PNG_INFO_tRNS ) )
- {
- png_set_tRNS_to_alpha( this->get_struct() );
- }
- // Tell libpng to handle the gamma conversion for you. The final call
- // is a good guess for PC generated images, but it should be configurable
- // by the user at run time by the user. It is strongly suggested that
- // your application support gamma correction.
- if( this->_settings._apply_screen_gamma )
- {
- // png_set_gamma will change the image data!
- #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
- png_set_gamma( this->get_struct()
- , this->_settings._screen_gamma
- , this->_info._file_gamma
- );
- #else
- png_set_gamma( this->get_struct()
- , this->_settings._screen_gamma
- , this->_info._file_gamma
- );
- #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
- }
- // Turn on interlace handling. REQUIRED if you are not using
- // png_read_image(). To see how to handle interlacing passes,
- // see the png_read_row() method below:
- this->_number_passes = png_set_interlace_handling( this->get_struct() );
- // The above transformation might have changed the bit_depth and color type.
- png_read_update_info( this->get_struct()
- , this->get_info()
- );
- this->_info._bit_depth = png_get_bit_depth( this->get_struct()
- , this->get_info()
- );
- this->_info._num_channels = png_get_channels( this->get_struct()
- , this->get_info()
- );
- this->_info._color_type = png_get_color_type( this->get_struct()
- , this->get_info()
- );
- this->_scanline_length = png_get_rowbytes( this->get_struct()
- , this->get_info()
- );
- switch( this->_info._color_type )
- {
- case PNG_COLOR_TYPE_GRAY:
- {
- switch( this->_info._bit_depth )
- {
- case 1: read_rows< gray1_image_t::view_t::reference >( view ); break;
- case 2: read_rows< gray2_image_t::view_t::reference >( view ); break;
- case 4: read_rows< gray4_image_t::view_t::reference >( view ); break;
- case 8: read_rows< gray8_pixel_t >( view ); break;
- case 16: read_rows< gray16_pixel_t >( view ); break;
- default: io_error( "png_reader::read_data(): unknown combination of color type and bit depth" );
- }
- break;
- }
- case PNG_COLOR_TYPE_GA:
- {
- #ifdef BOOST_GIL_IO_ENABLE_GRAY_ALPHA
- switch( this->_info._bit_depth )
- {
- case 8: read_rows< gray_alpha8_pixel_t > ( view ); break;
- case 16: read_rows< gray_alpha16_pixel_t >( view ); break;
- default: io_error( "png_reader::read_data(): unknown combination of color type and bit depth" );
- }
- #else
- io_error( "gray_alpha isn't enabled. Define BOOST_GIL_IO_ENABLE_GRAY_ALPHA when building application." );
- #endif // BOOST_GIL_IO_ENABLE_GRAY_ALPHA
- break;
- }
- case PNG_COLOR_TYPE_RGB:
- {
- switch( this->_info._bit_depth )
- {
- case 8: read_rows< rgb8_pixel_t > ( view ); break;
- case 16: read_rows< rgb16_pixel_t >( view ); break;
- default: io_error( "png_reader::read_data(): unknown combination of color type and bit depth" );
- }
- break;
- }
- case PNG_COLOR_TYPE_RGBA:
- {
- switch( this->_info._bit_depth )
- {
- case 8: read_rows< rgba8_pixel_t > ( view ); break;
- case 16: read_rows< rgba16_pixel_t >( view ); break;
- default: io_error( "png_reader_color_convert::read_data(): unknown combination of color type and bit depth" );
- }
- break;
- }
- default: io_error( "png_reader_color_convert::read_data(): unknown color type" );
- }
- // read rest of file, and get additional chunks in info_ptr
- png_read_end( this->get_struct()
- , nullptr
- );
- }
- private:
- template< typename ImagePixel
- , typename View
- >
- void read_rows( const View& view )
- {
- // guard from errors in the following functions
- if (setjmp( png_jmpbuf( this->get_struct() )))
- {
- io_error("png is invalid");
- }
- using row_buffer_helper_t = detail::row_buffer_helper_view<ImagePixel>;
- using it_t = typename row_buffer_helper_t::iterator_t;
- using is_read_and_convert_t = typename std::is_same
- <
- ConversionPolicy,
- detail::read_and_no_convert
- >::type;
- io_error_if( !detail::is_allowed< View >( this->_info
- , is_read_and_convert_t()
- )
- , "Image types aren't compatible."
- );
- std::size_t rowbytes = png_get_rowbytes( this->get_struct()
- , this->get_info()
- );
- row_buffer_helper_t buffer( rowbytes
- , true
- );
- png_bytep row_ptr = (png_bytep)( &( buffer.data()[0]));
- for( std::size_t pass = 0; pass < this->_number_passes; pass++ )
- {
- if( pass == this->_number_passes - 1 )
- {
- // skip lines if necessary
- for( std::ptrdiff_t y = 0; y < this->_settings._top_left.y; ++y )
- {
- // Read the image using the "sparkle" effect.
- png_read_rows( this->get_struct()
- , &row_ptr
- , nullptr
- , 1
- );
- }
- for( std::ptrdiff_t y = 0
- ; y < this->_settings._dim.y
- ; ++y
- )
- {
- // Read the image using the "sparkle" effect.
- png_read_rows( this->get_struct()
- , &row_ptr
- , nullptr
- , 1
- );
- it_t first = buffer.begin() + this->_settings._top_left.x;
- it_t last = first + this->_settings._dim.x; // one after last element
- this->_cc_policy.read( first
- , last
- , view.row_begin( y ));
- }
- // Read the rest of the image. libpng needs that.
- std::ptrdiff_t remaining_rows = static_cast< std::ptrdiff_t >( this->_info._height )
- - this->_settings._top_left.y
- - this->_settings._dim.y;
- for( std::ptrdiff_t y = 0
- ; y < remaining_rows
- ; ++y
- )
- {
- // Read the image using the "sparkle" effect.
- png_read_rows( this->get_struct()
- , &row_ptr
- , nullptr
- , 1
- );
- }
- }
- else
- {
- for( int y = 0; y < view.height(); ++y )
- {
- // Read the image using the "sparkle" effect.
- png_read_rows( this->get_struct()
- , &row_ptr
- , nullptr
- , 1
- );
- }
- }
- }
- }
- };
- namespace detail {
- struct png_type_format_checker
- {
- png_type_format_checker( png_bitdepth::type bit_depth
- , png_color_type::type color_type
- )
- : _bit_depth ( bit_depth )
- , _color_type( color_type )
- {}
- template< typename Image >
- bool apply()
- {
- using is_supported_t = is_read_supported
- <
- typename get_pixel_type<typename Image::view_t>::type,
- png_tag
- >;
- return is_supported_t::_bit_depth == _bit_depth
- && is_supported_t::_color_type == _color_type;
- }
- private:
- png_bitdepth::type _bit_depth;
- png_color_type::type _color_type;
- };
- struct png_read_is_supported
- {
- template< typename View >
- struct apply : public is_read_supported< typename get_pixel_type< View >::type
- , png_tag
- >
- {};
- };
- } // namespace detail
- ///
- /// PNG Dynamic Image Reader
- ///
- template< typename Device
- >
- class dynamic_image_reader< Device
- , png_tag
- >
- : public reader< Device
- , png_tag
- , detail::read_and_no_convert
- >
- {
- using parent_t = reader
- <
- Device,
- png_tag,
- detail::read_and_no_convert
- >;
- public:
- dynamic_image_reader( const Device& io_dev
- , const image_read_settings< png_tag >& settings
- )
- : parent_t( io_dev
- , settings
- )
- {}
- template< typename ...Images >
- void apply( any_image< Images... >& images )
- {
- detail::png_type_format_checker format_checker( this->_info._bit_depth
- , this->_info._color_type
- );
- if( !detail::construct_matched( images
- , format_checker
- ))
- {
- io_error( "No matching image type between those of the given any_image and that of the file" );
- }
- else
- {
- this->init_image( images
- , this->_settings
- );
- detail::dynamic_io_fnobj< detail::png_read_is_supported
- , parent_t
- > op( this );
- variant2::visit( op
- , view( images )
- );
- }
- }
- };
- #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
- #pragma warning(pop)
- #endif
- } // namespace gil
- } // namespace boost
- #endif
|