summaryrefslogtreecommitdiff
path: root/boost/gil/extension/io/targa/detail/read.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/gil/extension/io/targa/detail/read.hpp')
-rw-r--r--boost/gil/extension/io/targa/detail/read.hpp417
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