123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- //
- // Copyright 2012 Kenneth Riddile, 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_TARGA_DETAIL_READ_HPP
- #define BOOST_GIL_EXTENSION_IO_TARGA_DETAIL_READ_HPP
- #include <boost/gil/extension/io/targa/tags.hpp>
- #include <boost/gil/extension/io/targa/detail/reader_backend.hpp>
- #include <boost/gil/extension/io/targa/detail/is_allowed.hpp>
- #include <boost/gil/io/detail/dynamic.hpp>
- #include <boost/gil/io/base.hpp>
- #include <boost/gil/io/bit_operations.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/row_buffer_helper.hpp>
- #include <boost/gil/io/typedefs.hpp>
- #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
- #endif
- ///
- /// Targa Reader
- ///
- template< typename Device
- , typename ConversionPolicy
- >
- class reader< Device
- , targa_tag
- , ConversionPolicy
- >
- : public reader_base< targa_tag
- , ConversionPolicy
- >
- , public reader_backend< Device
- , targa_tag
- >
- {
- private:
- using this_t = reader<Device, targa_tag, ConversionPolicy>;
- using cc_t = typename ConversionPolicy::color_converter_type;
- public:
- using backend_t = reader_backend<Device, targa_tag>;
- reader( const Device& io_dev
- , const image_read_settings< targa_tag >& settings
- )
- : reader_base< targa_tag
- , ConversionPolicy
- >()
- , backend_t( io_dev
- , settings
- )
- {}
- reader( const Device& io_dev
- , const cc_t& cc
- , const image_read_settings< targa_tag >& settings
- )
- : reader_base< targa_tag
- , ConversionPolicy
- >( cc )
- , backend_t( io_dev
- , settings
- )
- {}
- template< typename View >
- void apply( const View& dst_view )
- {
- 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."
- );
- switch( this->_info._image_type )
- {
- case targa_image_type::_rgb:
- {
- if( this->_info._color_map_type != targa_color_map_type::_rgb )
- {
- io_error( "Inconsistent color map type and image type in targa file." );
- }
- if( this->_info._color_map_length != 0 )
- {
- io_error( "Non-indexed targa files containing a palette are not supported." );
- }
- switch( this->_info._bits_per_pixel )
- {
- case 24:
- {
- this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 );
- if( this->_info._screen_origin_bit )
- {
- read_data< bgr8_view_t >( flipped_up_down_view( dst_view ) );
- }
- else
- {
- read_data< bgr8_view_t >( dst_view );
- }
- break;
- }
- case 32:
- {
- this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 );
- if( this->_info._screen_origin_bit )
- {
- read_data< bgra8_view_t >( flipped_up_down_view( dst_view ) );
- }
- else
- {
- read_data< bgra8_view_t >( dst_view );
- }
- break;
- }
- default:
- {
- io_error( "Unsupported bit depth in targa file." );
- break;
- }
- }
- break;
- }
- case targa_image_type::_rle_rgb:
- {
- if( this->_info._color_map_type != targa_color_map_type::_rgb )
- {
- io_error( "Inconsistent color map type and image type in targa file." );
- }
- if( this->_info._color_map_length != 0 )
- {
- io_error( "Non-indexed targa files containing a palette are not supported." );
- }
- switch( this->_info._bits_per_pixel )
- {
- case 24:
- {
- if( this->_info._screen_origin_bit )
- {
- read_rle_data< bgr8_view_t >( flipped_up_down_view( dst_view ) );
- }
- else
- {
- read_rle_data< bgr8_view_t >( dst_view );
- }
- break;
- }
- case 32:
- {
- if( this->_info._screen_origin_bit )
- {
- read_rle_data< bgra8_view_t >( flipped_up_down_view( dst_view ) );
- }
- else
- {
- read_rle_data< bgra8_view_t >( dst_view );
- }
- break;
- }
- default:
- {
- io_error( "Unsupported bit depth in targa file." );
- break;
- }
- }
- break;
- }
- default:
- {
- io_error( "Unsupported image type in targa file." );
- break;
- }
- }
- }
- private:
- // 8-8-8 BGR
- // 8-8-8-8 BGRA
- template< typename View_Src, typename View_Dst >
- void read_data( const View_Dst& view )
- {
- byte_vector_t row( this->_info._width * (this->_info._bits_per_pixel / 8) );
- // jump to first scanline
- this->_io_dev.seek( static_cast< long >( this->_info._offset ));
- View_Src v = interleaved_view( this->_info._width,
- 1,
- reinterpret_cast<typename View_Src::value_type*>( &row.front() ),
- this->_info._width * num_channels< View_Src >::value
- );
- typename View_Src::x_iterator beg = v.row_begin( 0 ) + this->_settings._top_left.x;
- typename View_Src::x_iterator end = beg + this->_settings._dim.x;
- // read bottom up since targa origin is bottom left
- for( std::ptrdiff_t y = this->_settings._dim.y - 1; y > -1; --y )
- {
- // @todo: For now we're reading the whole scanline which is
- // slightly inefficient. Later versions should try to read
- // only the bytes which are necessary.
- this->_io_dev.read( &row.front(), row.size() );
- this->_cc_policy.read( beg, end, view.row_begin(y) );
- }
- }
- // 8-8-8 BGR
- // 8-8-8-8 BGRA
- template< typename View_Src, typename View_Dst >
- void read_rle_data( const View_Dst& view )
- {
- targa_depth::type bytes_per_pixel = this->_info._bits_per_pixel / 8;
- size_t image_size = this->_info._width * this->_info._height * bytes_per_pixel;
- byte_vector_t image_data( image_size );
- this->_io_dev.seek( static_cast< long >( this->_info._offset ));
- for( size_t pixel = 0; pixel < image_size; )
- {
- targa_offset::type current_byte = this->_io_dev.read_uint8();
- if( current_byte & 0x80 ) // run length chunk (high bit = 1)
- {
- uint8_t chunk_length = current_byte - 127;
- uint8_t pixel_data[4];
- for( size_t channel = 0; channel < bytes_per_pixel; ++channel )
- {
- pixel_data[channel] = this->_io_dev.read_uint8();
- }
- // Repeat the next pixel chunk_length times
- for( uint8_t i = 0; i < chunk_length; ++i, pixel += bytes_per_pixel )
- {
- memcpy( &image_data[pixel], pixel_data, bytes_per_pixel );
- }
- }
- else // raw chunk
- {
- uint8_t chunk_length = current_byte + 1;
- // Write the next chunk_length pixels directly
- size_t pixels_written = chunk_length * bytes_per_pixel;
- this->_io_dev.read( &image_data[pixel], pixels_written );
- pixel += pixels_written;
- }
- }
- View_Src v = flipped_up_down_view( interleaved_view( this->_info._width,
- this->_info._height,
- reinterpret_cast<typename View_Src::value_type*>( &image_data.front() ),
- this->_info._width * num_channels< View_Src >::value ) );
- for( std::ptrdiff_t y = 0; y != this->_settings._dim.y; ++y )
- {
- typename View_Src::x_iterator beg = v.row_begin( y ) + this->_settings._top_left.x;
- typename View_Src::x_iterator end = beg + this->_settings._dim.x;
- this->_cc_policy.read( beg, end, view.row_begin(y) );
- }
- }
- };
- namespace detail {
- class targa_type_format_checker
- {
- public:
- targa_type_format_checker( const targa_depth::type& bpp )
- : _bpp( bpp )
- {}
- template< typename Image >
- bool apply()
- {
- if( _bpp < 32 )
- {
- return pixels_are_compatible< typename Image::value_type, rgb8_pixel_t >::value
- ? true
- : false;
- }
- else
- {
- return pixels_are_compatible< typename Image::value_type, rgba8_pixel_t >::value
- ? true
- : false;
- }
- }
- private:
- // to avoid C4512
- targa_type_format_checker& operator=( const targa_type_format_checker& ) { return *this; }
- private:
- const targa_depth::type _bpp;
- };
- struct targa_read_is_supported
- {
- template< typename View >
- struct apply : public is_read_supported< typename get_pixel_type< View >::type
- , targa_tag
- >
- {};
- };
- } // namespace detail
- ///
- /// Targa Dynamic Image Reader
- ///
- template< typename Device >
- class dynamic_image_reader< Device
- , targa_tag
- >
- : public reader< Device
- , targa_tag
- , detail::read_and_no_convert
- >
- {
- using parent_t = reader<Device, targa_tag, detail::read_and_no_convert>;
- public:
- dynamic_image_reader( const Device& io_dev
- , const image_read_settings< targa_tag >& settings
- )
- : parent_t( io_dev
- , settings
- )
- {}
- template< typename ...Images >
- void apply( any_image< Images... >& images )
- {
- detail::targa_type_format_checker format_checker( this->_info._bits_per_pixel );
- 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::targa_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
|