diff options
Diffstat (limited to 'boost/gil/extension/io/targa/detail/read.hpp')
-rw-r--r-- | boost/gil/extension/io/targa/detail/read.hpp | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/boost/gil/extension/io/targa/detail/read.hpp b/boost/gil/extension/io/targa/detail/read.hpp new file mode 100644 index 0000000000..b208a679f4 --- /dev/null +++ b/boost/gil/extension/io/targa/detail/read.hpp @@ -0,0 +1,417 @@ +/* + Copyright 2012 Kenneth Riddile, Christian Henning + 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_TARGA_DETAIL_READ_HPP +#define BOOST_GIL_EXTENSION_IO_TARGA_DETAIL_READ_HPP + +//////////////////////////////////////////////////////////////////////////////////////// +/// \file +/// \brief +/// \author Kenneth Riddile, Christian Henning \n +/// +/// \date 2012 \n +/// +//////////////////////////////////////////////////////////////////////////////////////// + +#include <vector> + +#include <boost/gil/extension/io/targa/tags.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/row_buffer_helper.hpp> +#include <boost/gil/io/reader_base.hpp> +#include <boost/gil/io/device.hpp> +#include <boost/gil/io/typedefs.hpp> + +#include <boost/gil/extension/io/targa/detail/reader_backend.hpp> +#include <boost/gil/extension/io/targa/detail/is_allowed.hpp> + +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: + + typedef reader< Device + , targa_tag + , ConversionPolicy + > this_t; + + typedef typename ConversionPolicy::color_converter_type cc_t; + +public: + + typedef reader_backend< Device, targa_tag > backend_t; + +public: + + 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 ) + { + typedef typename is_same< ConversionPolicy + , detail::read_and_no_convert + >::type is_read_and_convert_t; + + 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 + > +{ + typedef reader< Device + , targa_tag + , detail::read_and_no_convert + > parent_t; + +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( !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 ); + + apply_operation( view( images ) + , op + ); + } + } +}; + +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) +#pragma warning(pop) +#endif + +} // namespace gil +} // namespace boost + +#endif |