2 // Copyright 2012 Kenneth Riddile, Christian Henning
4 // Distributed under the Boost Software License, Version 1.0
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt
8 #ifndef BOOST_GIL_EXTENSION_IO_TARGA_DETAIL_READ_HPP
9 #define BOOST_GIL_EXTENSION_IO_TARGA_DETAIL_READ_HPP
11 #include <boost/gil/extension/io/targa/tags.hpp>
12 #include <boost/gil/extension/io/targa/detail/reader_backend.hpp>
13 #include <boost/gil/extension/io/targa/detail/is_allowed.hpp>
15 #include <boost/gil/io/base.hpp>
16 #include <boost/gil/io/bit_operations.hpp>
17 #include <boost/gil/io/conversion_policies.hpp>
18 #include <boost/gil/io/device.hpp>
19 #include <boost/gil/io/dynamic_io_new.hpp>
20 #include <boost/gil/io/reader_base.hpp>
21 #include <boost/gil/io/row_buffer_helper.hpp>
22 #include <boost/gil/io/typedefs.hpp>
24 #include <type_traits>
27 namespace boost { namespace gil {
29 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
31 #pragma warning(disable:4512) //assignment operator could not be generated
37 template< typename Device
38 , typename ConversionPolicy
44 : public reader_base< targa_tag
47 , public reader_backend< Device
53 using this_t = reader<Device, targa_tag, ConversionPolicy>;
54 using cc_t = typename ConversionPolicy::color_converter_type;
58 using backend_t = reader_backend<Device, targa_tag>;
60 reader( const Device& io_dev
61 , const image_read_settings< targa_tag >& settings
63 : reader_base< targa_tag
71 reader( const Device& io_dev
73 , const image_read_settings< targa_tag >& settings
75 : reader_base< targa_tag
83 template< typename View >
84 void apply( const View& dst_view )
86 using is_read_and_convert_t = typename std::is_same
89 detail::read_and_no_convert
92 io_error_if( !detail::is_allowed< View >( this->_info, is_read_and_convert_t() )
93 , "Image types aren't compatible."
96 switch( this->_info._image_type )
98 case targa_image_type::_rgb:
100 if( this->_info._color_map_type != targa_color_map_type::_rgb )
102 io_error( "Inconsistent color map type and image type in targa file." );
105 if( this->_info._color_map_length != 0 )
107 io_error( "Non-indexed targa files containing a palette are not supported." );
110 switch( this->_info._bits_per_pixel )
114 this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 );
116 if( this->_info._screen_origin_bit )
118 read_data< bgr8_view_t >( flipped_up_down_view( dst_view ) );
122 read_data< bgr8_view_t >( dst_view );
129 this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 );
131 if( this->_info._screen_origin_bit )
133 read_data< bgra8_view_t >( flipped_up_down_view( dst_view ) );
137 read_data< bgra8_view_t >( dst_view );
144 io_error( "Unsupported bit depth in targa file." );
151 case targa_image_type::_rle_rgb:
153 if( this->_info._color_map_type != targa_color_map_type::_rgb )
155 io_error( "Inconsistent color map type and image type in targa file." );
158 if( this->_info._color_map_length != 0 )
160 io_error( "Non-indexed targa files containing a palette are not supported." );
163 switch( this->_info._bits_per_pixel )
167 if( this->_info._screen_origin_bit )
169 read_rle_data< bgr8_view_t >( flipped_up_down_view( dst_view ) );
173 read_rle_data< bgr8_view_t >( dst_view );
179 if( this->_info._screen_origin_bit )
181 read_rle_data< bgra8_view_t >( flipped_up_down_view( dst_view ) );
185 read_rle_data< bgra8_view_t >( dst_view );
191 io_error( "Unsupported bit depth in targa file." );
200 io_error( "Unsupported image type in targa file." );
210 template< typename View_Src, typename View_Dst >
211 void read_data( const View_Dst& view )
213 byte_vector_t row( this->_info._width * (this->_info._bits_per_pixel / 8) );
215 // jump to first scanline
216 this->_io_dev.seek( static_cast< long >( this->_info._offset ));
218 View_Src v = interleaved_view( this->_info._width,
220 reinterpret_cast<typename View_Src::value_type*>( &row.front() ),
221 this->_info._width * num_channels< View_Src >::value
224 typename View_Src::x_iterator beg = v.row_begin( 0 ) + this->_settings._top_left.x;
225 typename View_Src::x_iterator end = beg + this->_settings._dim.x;
227 // read bottom up since targa origin is bottom left
228 for( std::ptrdiff_t y = this->_settings._dim.y - 1; y > -1; --y )
230 // @todo: For now we're reading the whole scanline which is
231 // slightly inefficient. Later versions should try to read
232 // only the bytes which are necessary.
233 this->_io_dev.read( &row.front(), row.size() );
234 this->_cc_policy.read( beg, end, view.row_begin(y) );
240 template< typename View_Src, typename View_Dst >
241 void read_rle_data( const View_Dst& view )
243 targa_depth::type bytes_per_pixel = this->_info._bits_per_pixel / 8;
244 size_t image_size = this->_info._width * this->_info._height * bytes_per_pixel;
245 byte_vector_t image_data( image_size );
247 this->_io_dev.seek( static_cast< long >( this->_info._offset ));
249 for( size_t pixel = 0; pixel < image_size; )
251 targa_offset::type current_byte = this->_io_dev.read_uint8();
253 if( current_byte & 0x80 ) // run length chunk (high bit = 1)
255 uint8_t chunk_length = current_byte - 127;
256 uint8_t pixel_data[4];
257 for( size_t channel = 0; channel < bytes_per_pixel; ++channel )
259 pixel_data[channel] = this->_io_dev.read_uint8();
262 // Repeat the next pixel chunk_length times
263 for( uint8_t i = 0; i < chunk_length; ++i, pixel += bytes_per_pixel )
265 memcpy( &image_data[pixel], pixel_data, bytes_per_pixel );
270 uint8_t chunk_length = current_byte + 1;
272 // Write the next chunk_length pixels directly
273 size_t pixels_written = chunk_length * bytes_per_pixel;
274 this->_io_dev.read( &image_data[pixel], pixels_written );
275 pixel += pixels_written;
279 View_Src v = flipped_up_down_view( interleaved_view( this->_info._width,
281 reinterpret_cast<typename View_Src::value_type*>( &image_data.front() ),
282 this->_info._width * num_channels< View_Src >::value ) );
284 for( std::ptrdiff_t y = 0; y != this->_settings._dim.y; ++y )
286 typename View_Src::x_iterator beg = v.row_begin( y ) + this->_settings._top_left.x;
287 typename View_Src::x_iterator end = beg + this->_settings._dim.x;
288 this->_cc_policy.read( beg, end, view.row_begin(y) );
295 class targa_type_format_checker
299 targa_type_format_checker( const targa_depth::type& bpp )
303 template< typename Image >
308 return pixels_are_compatible< typename Image::value_type, rgb8_pixel_t >::value
314 return pixels_are_compatible< typename Image::value_type, rgba8_pixel_t >::value
323 targa_type_format_checker& operator=( const targa_type_format_checker& ) { return *this; }
327 const targa_depth::type _bpp;
330 struct targa_read_is_supported
332 template< typename View >
333 struct apply : public is_read_supported< typename get_pixel_type< View >::type
339 } // namespace detail
342 /// Targa Dynamic Image Reader
344 template< typename Device >
345 class dynamic_image_reader< Device
348 : public reader< Device
350 , detail::read_and_no_convert
353 using parent_t = reader<Device, targa_tag, detail::read_and_no_convert>;
357 dynamic_image_reader( const Device& io_dev
358 , const image_read_settings< targa_tag >& settings
365 template< typename Images >
366 void apply( any_image< Images >& images )
368 detail::targa_type_format_checker format_checker( this->_info._bits_per_pixel );
370 if( !construct_matched( images
374 io_error( "No matching image type between those of the given any_image and that of the file" );
378 this->init_image( images
382 detail::dynamic_io_fnobj< detail::targa_read_is_supported
386 apply_operation( view( images )
393 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)