2 // Copyright 2012 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_PNM_DETAIL_READ_HPP
9 #define BOOST_GIL_EXTENSION_IO_PNM_DETAIL_READ_HPP
11 #include <boost/gil/extension/io/pnm/tags.hpp>
12 #include <boost/gil/extension/io/pnm/detail/reader_backend.hpp>
13 #include <boost/gil/extension/io/pnm/detail/is_allowed.hpp>
15 #include <boost/gil.hpp> // FIXME: Include what you use!
16 #include <boost/gil/io/base.hpp>
17 #include <boost/gil/io/bit_operations.hpp>
18 #include <boost/gil/io/conversion_policies.hpp>
19 #include <boost/gil/io/device.hpp>
20 #include <boost/gil/io/dynamic_io_new.hpp>
21 #include <boost/gil/io/reader_base.hpp>
22 #include <boost/gil/io/row_buffer_helper.hpp>
23 #include <boost/gil/io/typedefs.hpp>
25 #include <type_traits>
28 namespace boost { namespace gil {
30 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
32 #pragma warning(disable:4512) //assignment operator could not be generated
38 template< typename Device
39 , typename ConversionPolicy
45 : public reader_base< pnm_tag
48 , public reader_backend< Device
55 using this_t = reader<Device, pnm_tag, ConversionPolicy>;
56 using cc_t = typename ConversionPolicy::color_converter_type;
60 using backend_t = reader_backend<Device, pnm_tag>;
62 reader( const Device& io_dev
63 , const image_read_settings< pnm_tag >& settings
65 : reader_base< pnm_tag
73 reader( const Device& io_dev
75 , const image_read_settings< pnm_tag >& settings
77 : reader_base< pnm_tag
85 template<typename View>
86 void apply( const View& view )
88 using is_read_and_convert_t = typename std::is_same
91 detail::read_and_no_convert
94 io_error_if( !detail::is_allowed< View >( this->_info
95 , is_read_and_convert_t()
97 , "Image types aren't compatible."
100 switch( this->_info._type )
102 // reading mono text is reading grayscale but with only two values
103 case pnm_image_type::mono_asc_t::value:
104 case pnm_image_type::gray_asc_t::value:
106 this->_scanline_length = this->_info._width;
108 read_text_data< gray8_view_t >( view );
113 case pnm_image_type::color_asc_t::value:
115 this->_scanline_length = this->_info._width * num_channels< rgb8_view_t >::value;
117 read_text_data< rgb8_view_t >( view );
122 case pnm_image_type::mono_bin_t::value:
125 this->_scanline_length = ( this->_info._width + 7 ) >> 3;
127 read_bin_data< gray1_image_t::view_t >( view );
132 case pnm_image_type::gray_bin_t::value:
135 this->_scanline_length = this->_info._width;
137 read_bin_data< gray8_view_t >( view );
142 case pnm_image_type::color_bin_t::value:
145 this->_scanline_length = this->_info._width * num_channels< rgb8_view_t >::value;
147 read_bin_data< rgb8_view_t >( view );
155 template< typename View_Src
158 void read_text_data( const View_Dst& dst )
160 using y_t = typename View_Dst::y_coord_t;
162 byte_vector_t row( this->_scanline_length );
164 //Skip scanlines if necessary.
165 for( int y = 0; y < this->_settings._top_left.y; ++y )
167 read_text_row< View_Src >( dst, row, y, false );
170 for( y_t y = 0; y < dst.height(); ++y )
172 read_text_row< View_Src >( dst, row, y, true );
176 template< typename View_Src
179 void read_text_row( const View_Dst& dst
181 , typename View_Dst::y_coord_t y
185 View_Src src = interleaved_view( this->_info._width
187 , (typename View_Src::value_type*) &row.front()
188 , this->_scanline_length
191 for( uint32_t x = 0; x < this->_scanline_length; ++x )
193 for( uint32_t k = 0; ; )
195 int ch = this->_io_dev.getc_unchecked();
199 buf[ k++ ] = static_cast< char >( ch );
206 else if( ch == EOF || !isspace( ch ))
214 int value = atoi( buf );
216 if( this->_info._max_value == 1 )
218 using channel_t = typename channel_type<typename get_pixel_type<View_Dst>::type>::type;
220 // for pnm format 0 is white
221 row[x] = ( value != 0 )
222 ? typename channel_traits< channel_t >::value_type( 0 )
223 : channel_traits< channel_t >::max_value();
227 row[x] = static_cast< byte_t >( value );
234 // We are reading a gray1_image like a gray8_image but the two pixel_t
235 // aren't compatible. Though, read_and_no_convert::read(...) wont work.
240 , typename std::is_same< View_Dst
241 , gray1_image_t::view_t
247 template< typename View_Dst
250 void copy_data( const View_Dst& dst
251 , const View_Src& src
252 , typename View_Dst::y_coord_t y
253 , std::true_type // is gray1_view
256 if( this->_info._max_value == 1 )
258 typename View_Dst::x_iterator it = dst.row_begin( y );
260 for( typename View_Dst::x_coord_t x = 0
270 copy_data(dst, src, y, std::false_type{});
274 template< typename View_Dst
277 void copy_data( const View_Dst& view
278 , const View_Src& src
279 , typename View_Dst::y_coord_t y
280 , std::false_type // is gray1_view
283 typename View_Src::x_iterator beg = src.row_begin( 0 ) + this->_settings._top_left.x;
284 typename View_Src::x_iterator end = beg + this->_settings._dim.x;
286 this->_cc_policy.read( beg
288 , view.row_begin( y )
293 template< typename View_Src
296 void read_bin_data( const View_Dst& view )
298 using y_t = typename View_Dst::y_coord_t;
299 using is_bit_aligned_t = typename is_bit_aligned<typename View_Src::value_type>::type;
301 using rh_t = detail::row_buffer_helper_view<View_Src>;
302 rh_t rh( this->_scanline_length, true );
304 typename rh_t::iterator_t beg = rh.begin() + this->_settings._top_left.x;
305 typename rh_t::iterator_t end = beg + this->_settings._dim.x;
307 // For bit_aligned images we need to negate all bytes in the row_buffer
308 // to make sure that 0 is black and 255 is white.
311 typename rh_t::buffer_t,
312 std::integral_constant<bool, is_bit_aligned_t::value> // TODO: Simplify after MPL removal
315 detail::swap_half_bytes
317 typename rh_t::buffer_t,
318 std::integral_constant<bool, is_bit_aligned_t::value> // TODO: Simplify after MPL removal
321 //Skip scanlines if necessary.
322 for( y_t y = 0; y < this->_settings._top_left.y; ++y )
324 this->_io_dev.read( reinterpret_cast< byte_t* >( rh.data() )
325 , this->_scanline_length
329 for( y_t y = 0; y < view.height(); ++y )
331 this->_io_dev.read( reinterpret_cast< byte_t* >( rh.data() )
332 , this->_scanline_length
338 this->_cc_policy.read( beg
340 , view.row_begin( y )
354 struct pnm_type_format_checker
356 pnm_type_format_checker( pnm_image_type::type type )
360 template< typename Image >
363 using is_supported_t = is_read_supported
365 typename get_pixel_type<typename Image::view_t>::type,
369 return is_supported_t::_asc_type == _type
370 || is_supported_t::_bin_type == _type;
375 pnm_image_type::type _type;
378 struct pnm_read_is_supported
380 template< typename View >
381 struct apply : public is_read_supported< typename get_pixel_type< View >::type
387 } // namespace detail
390 /// PNM Dynamic Image Reader
392 template< typename Device
394 class dynamic_image_reader< Device
397 : public reader< Device
399 , detail::read_and_no_convert
402 using parent_t = reader
406 detail::read_and_no_convert
411 dynamic_image_reader( const Device& io_dev
412 , const image_read_settings< pnm_tag >& settings
419 template< typename Images >
420 void apply( any_image< Images >& images )
422 detail::pnm_type_format_checker format_checker( this->_info._type );
424 if( !construct_matched( images
428 io_error( "No matching image type between those of the given any_image and that of the file" );
432 this->init_image( images
436 detail::dynamic_io_fnobj< detail::pnm_read_is_supported
440 apply_operation( view( images )
447 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)