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_READER_BACKEND_HPP
9 #define BOOST_GIL_EXTENSION_IO_PNM_DETAIL_READER_BACKEND_HPP
10 
11 #include <boost/gil/extension/io/pnm/tags.hpp>
12 
13 namespace boost { namespace gil {
14 
15 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
16 #pragma warning(push)
17 #pragma warning(disable:4512) //assignment operator could not be generated
18 #endif
19 
20 ///
21 /// PNM Backend
22 ///
23 template< typename Device >
24 struct reader_backend< Device
25                      , pnm_tag
26                      >
27 {
28 public:
29 
30     using format_tag_t = pnm_tag;
31 
32 public:
33 
reader_backendboost::gil::reader_backend34     reader_backend( const Device&                         io_dev
35                   , const image_read_settings< pnm_tag >& settings
36                   )
37     : _io_dev  ( io_dev   )
38     , _settings( settings )
39     , _info()
40 
41     , _scanline_length( 0 )
42     {
43         read_header();
44 
45         if( _settings._dim.x == 0 )
46         {
47             _settings._dim.x = _info._width;
48         }
49 
50         if( _settings._dim.y == 0 )
51         {
52             _settings._dim.y = _info._height;
53         }
54     }
55 
read_headerboost::gil::reader_backend56     void read_header()
57     {
58         // read signature
59         io_error_if( read_char() != 'P', "Invalid PNM signature" );
60 
61         _info._type = read_char() - '0';
62 
63         io_error_if( _info._type < pnm_image_type::mono_asc_t::value || _info._type > pnm_image_type::color_bin_t::value
64                    , "Invalid PNM file (supports P1 to P6)"
65                    );
66 
67         _info._width  = read_int();
68         _info._height = read_int();
69 
70         if( _info._type == pnm_image_type::mono_asc_t::value || _info._type == pnm_image_type::mono_bin_t::value )
71         {
72             _info._max_value = 1;
73         }
74         else
75         {
76             _info._max_value = read_int();
77 
78             io_error_if( _info._max_value > 255
79                        , "Unsupported PNM format (supports maximum value 255)"
80                        );
81         }
82     }
83 
84     /// Check if image is large enough.
check_image_sizeboost::gil::reader_backend85     void check_image_size( const point_t& img_dim )
86     {
87         if( _settings._dim.x > 0 )
88         {
89             if( img_dim.x < _settings._dim.x ) { io_error( "Supplied image is too small" ); }
90         }
91         else
92         {
93             if( img_dim.x < _info._width ) { io_error( "Supplied image is too small" ); }
94         }
95 
96 
97         if( _settings._dim.y > 0 )
98         {
99             if( img_dim.y < _settings._dim.y ) { io_error( "Supplied image is too small" ); }
100         }
101         else
102         {
103             if( img_dim.y < _info._height ) { io_error( "Supplied image is too small" ); }
104         }
105     }
106 
107 private:
108 
109     // Read a character and skip a comment if necessary.
read_charboost::gil::reader_backend110     char read_char()
111     {
112         char ch;
113 
114         if(( ch = _io_dev.getc() ) == '#' )
115         {
116             // skip comment to EOL
117             do
118             {
119                 ch = _io_dev.getc();
120             }
121             while (ch != '\n' && ch != '\r');
122         }
123 
124         return ch;
125     }
126 
read_intboost::gil::reader_backend127     unsigned int read_int()
128     {
129         char ch;
130 
131         // skip whitespaces, tabs, and new lines
132         do
133         {
134             ch = read_char();
135         }
136         while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
137 
138         if( ch < '0' || ch > '9' )
139         {
140             io_error( "Unexpected characters reading decimal digits" );
141         }
142 
143         unsigned val = 0;
144 
145         do
146         {
147             unsigned dig = ch - '0';
148 
149             if( val > INT_MAX / 10 - dig )
150             {
151                 io_error( "Integer too large" );
152             }
153 
154             val = val * 10 + dig;
155 
156             ch = read_char();
157         }
158         while( '0' <= ch && ch <= '9' );
159 
160         return val;
161     }
162 
163 
164 public:
165 
166     Device _io_dev;
167 
168     image_read_settings< pnm_tag > _settings;
169     image_read_info< pnm_tag >     _info;
170 
171     std::size_t _scanline_length;
172 };
173 
174 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
175 #pragma warning(pop)
176 #endif
177 
178 } // namespace gil
179 } // namespace boost
180 
181 #endif
182