1 // 2 // Copyright 2012 Christian Henning 3 // 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 7 // 8 #ifndef BOOST_GIL_EXTENSION_IO_PNM_DETAIL_READ_HPP 9 #define BOOST_GIL_EXTENSION_IO_PNM_DETAIL_READ_HPP 10 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> 14 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/reader_base.hpp> 21 #include <boost/gil/io/row_buffer_helper.hpp> 22 #include <boost/gil/io/typedefs.hpp> 23 24 #include <boost/bind.hpp> 25 26 #include <vector> 27 28 namespace boost { namespace gil { 29 30 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 31 #pragma warning(push) 32 #pragma warning(disable:4512) //assignment operator could not be generated 33 #endif 34 35 /// 36 /// PNM Reader 37 /// 38 template< typename Device 39 , typename ConversionPolicy 40 > 41 class reader< Device 42 , pnm_tag 43 , ConversionPolicy 44 > 45 : public reader_base< pnm_tag 46 , ConversionPolicy 47 > 48 , public reader_backend< Device 49 , pnm_tag 50 > 51 { 52 53 private: 54 55 typedef reader< Device 56 , pnm_tag 57 , ConversionPolicy 58 > this_t; 59 60 typedef typename ConversionPolicy::color_converter_type cc_t; 61 62 public: 63 64 typedef reader_backend< Device, pnm_tag > backend_t; 65 66 public: 67 68 reader( const Device& io_dev 69 , const image_read_settings< pnm_tag >& settings 70 ) 71 : reader_base< pnm_tag 72 , ConversionPolicy 73 >() 74 , backend_t( io_dev 75 , settings 76 ) 77 {} 78 79 reader( const Device& io_dev 80 , const cc_t& cc 81 , const image_read_settings< pnm_tag >& settings 82 ) 83 : reader_base< pnm_tag 84 , ConversionPolicy 85 >( cc ) 86 , backend_t( io_dev 87 , settings 88 ) 89 {} 90 91 template<typename View> 92 void apply( const View& view ) 93 { 94 typedef typename is_same< ConversionPolicy 95 , detail::read_and_no_convert 96 >::type is_read_and_convert_t; 97 98 io_error_if( !detail::is_allowed< View >( this->_info 99 , is_read_and_convert_t() 100 ) 101 , "Image types aren't compatible." 102 ); 103 104 switch( this->_info._type ) 105 { 106 // reading mono text is reading grayscale but with only two values 107 case pnm_image_type::mono_asc_t::value: 108 case pnm_image_type::gray_asc_t::value: 109 { 110 this->_scanline_length = this->_info._width; 111 112 read_text_data< gray8_view_t >( view ); 113 114 break; 115 } 116 117 case pnm_image_type::color_asc_t::value: 118 { 119 this->_scanline_length = this->_info._width * num_channels< rgb8_view_t >::value; 120 121 read_text_data< rgb8_view_t >( view ); 122 123 break; 124 } 125 126 case pnm_image_type::mono_bin_t::value: 127 { 128 //gray1_image_t 129 this->_scanline_length = ( this->_info._width + 7 ) >> 3; 130 131 read_bin_data< gray1_image_t::view_t >( view ); 132 133 break; 134 } 135 136 case pnm_image_type::gray_bin_t::value: 137 { 138 // gray8_image_t 139 this->_scanline_length = this->_info._width; 140 141 read_bin_data< gray8_view_t >( view ); 142 143 break; 144 } 145 146 case pnm_image_type::color_bin_t::value: 147 { 148 // rgb8_image_t 149 this->_scanline_length = this->_info._width * num_channels< rgb8_view_t >::value; 150 151 read_bin_data< rgb8_view_t >( view ); 152 break; 153 } 154 } 155 } 156 157 private: 158 159 template< typename View_Src 160 , typename View_Dst 161 > 162 void read_text_data( const View_Dst& dst ) 163 { 164 typedef typename View_Dst::y_coord_t y_t; 165 166 byte_vector_t row( this->_scanline_length ); 167 168 //Skip scanlines if necessary. 169 for( int y = 0; y < this->_settings._top_left.y; ++y ) 170 { 171 read_text_row< View_Src >( dst, row, y, false ); 172 } 173 174 for( y_t y = 0; y < dst.height(); ++y ) 175 { 176 read_text_row< View_Src >( dst, row, y, true ); 177 } 178 } 179 180 template< typename View_Src 181 , typename View_Dst 182 > 183 void read_text_row( const View_Dst& dst 184 , byte_vector_t& row 185 , typename View_Dst::y_coord_t y 186 , bool process 187 ) 188 { 189 View_Src src = interleaved_view( this->_info._width 190 , 1 191 , (typename View_Src::value_type*) &row.front() 192 , this->_scanline_length 193 ); 194 195 for( uint32_t x = 0; x < this->_scanline_length; ++x ) 196 { 197 for( uint32_t k = 0; ; ) 198 { 199 int ch = this->_io_dev.getc_unchecked(); 200 201 if( isdigit( ch )) 202 { 203 buf[ k++ ] = static_cast< char >( ch ); 204 } 205 else if( k ) 206 { 207 buf[ k ] = 0; 208 break; 209 } 210 else if( ch == EOF || !isspace( ch )) 211 { 212 return; 213 } 214 } 215 216 if( process ) 217 { 218 int value = atoi( buf ); 219 220 if( this->_info._max_value == 1 ) 221 { 222 typedef typename channel_type< typename get_pixel_type< View_Dst >::type >::type channel_t; 223 224 // for pnm format 0 is white 225 row[x] = ( value != 0 ) 226 ? typename channel_traits< channel_t >::value_type( 0 ) 227 : channel_traits< channel_t >::max_value(); 228 } 229 else 230 { 231 row[x] = static_cast< byte_t >( value ); 232 } 233 } 234 } 235 236 if( process ) 237 { 238 // We are reading a gray1_image like a gray8_image but the two pixel_t 239 // aren't compatible. Though, read_and_no_convert::read(...) wont work. 240 copy_data< View_Dst 241 , View_Src >( dst 242 , src 243 , y 244 , typename is_same< View_Dst 245 , gray1_image_t::view_t 246 >::type() 247 ); 248 } 249 } 250 251 template< typename View_Dst 252 , typename View_Src 253 > 254 void copy_data( const View_Dst& dst 255 , const View_Src& src 256 , typename View_Dst::y_coord_t y 257 , mpl::true_ // is gray1_view 258 ) 259 { 260 if( this->_info._max_value == 1 ) 261 { 262 typename View_Dst::x_iterator it = dst.row_begin( y ); 263 264 for( typename View_Dst::x_coord_t x = 0 265 ; x < dst.width() 266 ; ++x 267 ) 268 { 269 it[x] = src[x]; 270 } 271 } 272 else 273 { 274 copy_data( dst 275 , src 276 , y 277 , mpl::false_() 278 ); 279 } 280 } 281 282 template< typename View_Dst 283 , typename View_Src 284 > 285 void copy_data( const View_Dst& view 286 , const View_Src& src 287 , typename View_Dst::y_coord_t y 288 , mpl::false_ // is gray1_view 289 ) 290 { 291 typename View_Src::x_iterator beg = src.row_begin( 0 ) + this->_settings._top_left.x; 292 typename View_Src::x_iterator end = beg + this->_settings._dim.x; 293 294 this->_cc_policy.read( beg 295 , end 296 , view.row_begin( y ) 297 ); 298 } 299 300 301 template< typename View_Src 302 , typename View_Dst 303 > 304 void read_bin_data( const View_Dst& view ) 305 { 306 typedef typename View_Dst::y_coord_t y_t; 307 typedef typename is_bit_aligned< 308 typename View_Src::value_type >::type is_bit_aligned_t; 309 310 typedef detail::row_buffer_helper_view< View_Src > rh_t; 311 rh_t rh( this->_scanline_length, true ); 312 313 typename rh_t::iterator_t beg = rh.begin() + this->_settings._top_left.x; 314 typename rh_t::iterator_t end = beg + this->_settings._dim.x; 315 316 // For bit_aligned images we need to negate all bytes in the row_buffer 317 // to make sure that 0 is black and 255 is white. 318 detail::negate_bits< typename rh_t::buffer_t 319 , is_bit_aligned_t 320 > neg; 321 322 detail::swap_half_bytes< typename rh_t::buffer_t 323 , is_bit_aligned_t 324 > swhb; 325 326 //Skip scanlines if necessary. 327 for( y_t y = 0; y < this->_settings._top_left.y; ++y ) 328 { 329 this->_io_dev.read( reinterpret_cast< byte_t* >( rh.data() ) 330 , this->_scanline_length 331 ); 332 } 333 334 for( y_t y = 0; y < view.height(); ++y ) 335 { 336 this->_io_dev.read( reinterpret_cast< byte_t* >( rh.data() ) 337 , this->_scanline_length 338 ); 339 340 neg( rh.buffer() ); 341 swhb( rh.buffer() ); 342 343 this->_cc_policy.read( beg 344 , end 345 , view.row_begin( y ) 346 ); 347 } 348 } 349 350 private: 351 352 char buf[16]; 353 354 }; 355 356 357 namespace detail { 358 359 struct pnm_type_format_checker 360 { 361 pnm_type_format_checker( pnm_image_type::type type ) 362 : _type( type ) 363 {} 364 365 template< typename Image > 366 bool apply() 367 { 368 typedef is_read_supported< typename get_pixel_type< typename Image::view_t >::type 369 , pnm_tag 370 > is_supported_t; 371 372 return is_supported_t::_asc_type == _type 373 || is_supported_t::_bin_type == _type; 374 } 375 376 private: 377 378 pnm_image_type::type _type; 379 }; 380 381 struct pnm_read_is_supported 382 { 383 template< typename View > 384 struct apply : public is_read_supported< typename get_pixel_type< View >::type 385 , pnm_tag 386 > 387 {}; 388 }; 389 390 } // namespace detail 391 392 /// 393 /// PNM Dynamic Image Reader 394 /// 395 template< typename Device 396 > 397 class dynamic_image_reader< Device 398 , pnm_tag 399 > 400 : public reader< Device 401 , pnm_tag 402 , detail::read_and_no_convert 403 > 404 { 405 typedef reader< Device 406 , pnm_tag 407 , detail::read_and_no_convert 408 > parent_t; 409 410 public: 411 412 dynamic_image_reader( const Device& io_dev 413 , const image_read_settings< pnm_tag >& settings 414 ) 415 : parent_t( io_dev 416 , settings 417 ) 418 {} 419 420 template< typename Images > 421 void apply( any_image< Images >& images ) 422 { 423 detail::pnm_type_format_checker format_checker( this->_info._type ); 424 425 if( !construct_matched( images 426 , format_checker 427 )) 428 { 429 io_error( "No matching image type between those of the given any_image and that of the file" ); 430 } 431 else 432 { 433 this->init_image( images 434 , this->_settings 435 ); 436 437 detail::dynamic_io_fnobj< detail::pnm_read_is_supported 438 , parent_t 439 > op( this ); 440 441 apply_operation( view( images ) 442 , op 443 ); 444 } 445 } 446 }; 447 448 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 449 #pragma warning(pop) 450 #endif 451 452 } // gil 453 } // boost 454 455 #endif 456