123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- //
- // 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_JPEG_DETAIL_READ_HPP
- #define BOOST_GIL_EXTENSION_IO_JPEG_DETAIL_READ_HPP
- #include <boost/gil/extension/io/jpeg/tags.hpp>
- #include <boost/gil/extension/io/jpeg/detail/base.hpp>
- #include <boost/gil/extension/io/jpeg/detail/is_allowed.hpp>
- #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/reader_base.hpp>
- #include <boost/gil/io/typedefs.hpp>
- #include <csetjmp>
- #include <type_traits>
- #include <vector>
- 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
- ///
- /// JPEG Reader
- ///
- template< typename Device
- , typename ConversionPolicy
- >
- class reader< Device
- , jpeg_tag
- , ConversionPolicy
- >
- : public reader_base< jpeg_tag
- , ConversionPolicy
- >
- , public reader_backend< Device
- , jpeg_tag
- >
- {
- private:
- using this_t = reader<Device, jpeg_tag, ConversionPolicy>;
- using cc_t = typename ConversionPolicy::color_converter_type;
- public:
- using backend_t = reader_backend<Device, jpeg_tag>;
- public:
- //
- // Constructor
- //
- reader( const Device& io_dev
- , const image_read_settings< jpeg_tag >& settings
- )
- : reader_base< jpeg_tag
- , ConversionPolicy
- >()
- , backend_t( io_dev
- , settings
- )
- {}
- //
- // Constructor
- //
- reader( const Device& io_dev
- , const typename ConversionPolicy::color_converter_type& cc
- , const image_read_settings< jpeg_tag >& settings
- )
- : reader_base< jpeg_tag
- , ConversionPolicy
- >( cc )
- , backend_t( io_dev
- , settings
- )
- {}
- template<typename View>
- void apply( const View& view )
- {
- // Fire exception in case of error.
- if( setjmp( this->_mark ))
- {
- this->raise_error();
- }
- this->get()->dct_method = this->_settings._dct_method;
- 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."
- );
- if( jpeg_start_decompress( this->get() ) == false )
- {
- io_error( "Cannot start decompression." );
- }
- switch( this->_info._color_space )
- {
- case JCS_GRAYSCALE:
- {
- this->_scanline_length = this->_info._width;
- read_rows< gray8_pixel_t >( view );
- break;
- }
- case JCS_RGB:
- //!\todo add Y'CbCr? We loose image quality when reading JCS_YCbCr as JCS_RGB
- case JCS_YCbCr:
- {
- this->_scanline_length = this->_info._width * num_channels< rgb8_view_t >::value;
- read_rows< rgb8_pixel_t >( view );
- break;
- }
- case JCS_CMYK:
- //!\todo add Y'CbCrK? We loose image quality when reading JCS_YCCK as JCS_CMYK
- case JCS_YCCK:
- {
- this->get()->out_color_space = JCS_CMYK;
- this->_scanline_length = this->_info._width * num_channels< cmyk8_view_t >::value;
- read_rows< cmyk8_pixel_t >( view );
- break;
- }
- default: { io_error( "Unsupported jpeg color space." ); }
- }
- jpeg_finish_decompress ( this->get() );
- }
- private:
- template< typename ImagePixel
- , typename View
- >
- void read_rows( const View& view )
- {
- using buffer_t = std::vector<ImagePixel>;
- buffer_t buffer( this->_info._width );
- // In case of an error we'll jump back to here and fire an exception.
- // @todo Is the buffer above cleaned up when the exception is thrown?
- // The strategy right now is to allocate necessary memory before
- // the setjmp.
- if( setjmp( this->_mark ))
- {
- this->raise_error();
- }
- JSAMPLE *row_adr = reinterpret_cast< JSAMPLE* >( &buffer[0] );
- //Skip scanlines if necessary.
- for( int y = 0; y < this->_settings._top_left.y; ++y )
- {
- io_error_if( jpeg_read_scanlines( this->get()
- , &row_adr
- , 1
- ) !=1
- , "jpeg_read_scanlines: fail to read JPEG file"
- );
- }
- // Read data.
- for( int y = 0; y < view.height(); ++y )
- {
- io_error_if( jpeg_read_scanlines( this->get()
- , &row_adr
- , 1
- ) != 1
- , "jpeg_read_scanlines: fail to read JPEG file"
- );
- typename buffer_t::iterator beg = buffer.begin() + this->_settings._top_left.x;
- typename buffer_t::iterator end = beg + this->_settings._dim.x;
- this->_cc_policy.read( beg
- , end
- , view.row_begin( y )
- );
- }
- //@todo: There might be a better way to do that.
- while( this->get()->output_scanline < this->get()->image_height )
- {
- io_error_if( jpeg_read_scanlines( this->get()
- , &row_adr
- , 1
- ) !=1
- , "jpeg_read_scanlines: fail to read JPEG file"
- );
- }
- }
- };
- namespace detail {
- struct jpeg_type_format_checker
- {
- jpeg_type_format_checker( jpeg_color_space::type color_space )
- : _color_space( color_space )
- {}
- template< typename Image >
- bool apply()
- {
- return is_read_supported< typename get_pixel_type< typename Image::view_t >::type
- , jpeg_tag
- >::_color_space == _color_space;
- }
- private:
- jpeg_color_space::type _color_space;
- };
- struct jpeg_read_is_supported
- {
- template< typename View >
- struct apply : public is_read_supported< typename get_pixel_type< View >::type
- , jpeg_tag
- >
- {};
- };
- } // namespace detail
- ///
- /// JPEG Dynamic Reader
- ///
- template< typename Device >
- class dynamic_image_reader< Device
- , jpeg_tag
- >
- : public reader< Device
- , jpeg_tag
- , detail::read_and_no_convert
- >
- {
- using parent_t = reader<Device, jpeg_tag, detail::read_and_no_convert>;
- public:
- dynamic_image_reader( const Device& io_dev
- , const image_read_settings< jpeg_tag >& settings
- )
- : parent_t( io_dev
- , settings
- )
- {}
- template< typename ...Images >
- void apply( any_image< Images... >& images )
- {
- detail::jpeg_type_format_checker format_checker( this->_info._color_space != JCS_YCbCr
- ? this->_info._color_space
- : JCS_RGB
- );
- 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::jpeg_read_is_supported
- , parent_t
- > op( this );
- variant2::visit( op
- , view( images )
- );
- }
- }
- };
- #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
- #pragma warning(pop)
- #endif
- } // gil
- } // boost
- #endif
|