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