Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / gil / extension / io / jpeg / detail / read.hpp
1 //
2 // Copyright 2007-2012 Christian Henning, Andreas Pokorny, Lubomir Bourdev
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_JPEG_DETAIL_READ_HPP
9 #define BOOST_GIL_EXTENSION_IO_JPEG_DETAIL_READ_HPP
10
11 #include <boost/gil/extension/io/jpeg/tags.hpp>
12 #include <boost/gil/extension/io/jpeg/detail/base.hpp>
13 #include <boost/gil/extension/io/jpeg/detail/is_allowed.hpp>
14
15 #include <boost/gil/io/base.hpp>
16 #include <boost/gil/io/conversion_policies.hpp>
17 #include <boost/gil/io/device.hpp>
18 #include <boost/gil/io/dynamic_io_new.hpp>
19 #include <boost/gil/io/reader_base.hpp>
20 #include <boost/gil/io/typedefs.hpp>
21
22 #include <csetjmp>
23 #include <type_traits>
24 #include <vector>
25
26 namespace boost { namespace gil {
27
28 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
29 #pragma warning(push)
30 #pragma warning(disable:4512) //assignment operator could not be generated
31 #pragma warning(disable:4611) //interaction between '_setjmp' and C++ object destruction is non-portable
32 #endif
33
34 ///
35 /// JPEG Reader
36 ///
37 template< typename Device
38         , typename ConversionPolicy
39         >
40 class reader< Device
41             , jpeg_tag
42             , ConversionPolicy
43             >
44     : public reader_base< jpeg_tag
45                         , ConversionPolicy
46                         >
47     , public reader_backend< Device
48                            , jpeg_tag
49                            >
50 {
51 private:
52
53     using this_t = reader<Device, jpeg_tag, ConversionPolicy>;
54     using cc_t = typename ConversionPolicy::color_converter_type;
55
56 public:
57
58     using backend_t = reader_backend<Device, jpeg_tag>;
59
60 public:
61
62     //
63     // Constructor
64     //
65     reader( const Device&                          io_dev
66           , const image_read_settings< jpeg_tag >& settings
67           )
68     : reader_base< jpeg_tag
69                  , ConversionPolicy
70                  >()
71
72     , backend_t( io_dev
73                , settings
74                )
75     {}
76
77     //
78     // Constructor
79     //
80     reader( const Device&                                          io_dev
81           , const typename ConversionPolicy::color_converter_type& cc
82           , const image_read_settings< jpeg_tag >&                 settings
83           )
84     : reader_base< jpeg_tag
85                  , ConversionPolicy
86                  >( cc )
87     , backend_t( io_dev
88                , settings
89                )
90     {}
91
92     template<typename View>
93     void apply( const View& view )
94     {
95         // Fire exception in case of error.
96         if( setjmp( this->_mark ))
97         {
98             this->raise_error();
99         }
100
101         this->get()->dct_method = this->_settings._dct_method;
102
103         using is_read_and_convert_t = typename std::is_same
104             <
105                 ConversionPolicy,
106                 detail::read_and_no_convert
107             >::type;
108
109         io_error_if( !detail::is_allowed< View >( this->_info
110                                                 , is_read_and_convert_t()
111                                                 )
112                    , "Image types aren't compatible."
113                    );
114
115         if( jpeg_start_decompress( this->get() ) == false )
116         {
117             io_error( "Cannot start decompression." );
118         }
119
120         switch( this->_info._color_space )
121         {
122             case JCS_GRAYSCALE:
123             {
124                 this->_scanline_length = this->_info._width;
125                 read_rows< gray8_pixel_t >( view );
126
127                 break;
128             }
129
130             case JCS_RGB:
131             //!\todo add Y'CbCr? We loose image quality when reading JCS_YCbCr as JCS_RGB
132             case JCS_YCbCr:
133             {
134                 this->_scanline_length = this->_info._width * num_channels< rgb8_view_t >::value;
135
136                 read_rows< rgb8_pixel_t  >( view );
137                 break;
138             }
139
140             case JCS_CMYK:
141             //!\todo add Y'CbCrK? We loose image quality when reading JCS_YCCK as JCS_CMYK
142             case JCS_YCCK:
143             {
144                 this->get()->out_color_space = JCS_CMYK;
145                 this->_scanline_length = this->_info._width * num_channels< cmyk8_view_t >::value;
146
147                 read_rows< cmyk8_pixel_t >( view );
148
149                 break;
150             }
151             default: { io_error( "Unsupported jpeg color space." ); }
152         }
153
154         jpeg_finish_decompress ( this->get() );
155     }
156
157 private:
158
159     template< typename ImagePixel
160             , typename View
161             >
162     void read_rows( const View& view )
163     {
164         using buffer_t = std::vector<ImagePixel>;
165         buffer_t buffer( this->_info._width );
166
167         // In case of an error we'll jump back to here and fire an exception.
168         // @todo Is the buffer above cleaned up when the exception is thrown?
169         //       The strategy right now is to allocate necessary memory before
170         //       the setjmp.
171         if( setjmp( this->_mark ))
172         {
173             this->raise_error();
174         }
175
176
177         JSAMPLE *row_adr = reinterpret_cast< JSAMPLE* >( &buffer[0] );
178
179         //Skip scanlines if necessary.
180         for( int y = 0; y <  this->_settings._top_left.y; ++y )
181         {
182             io_error_if( jpeg_read_scanlines( this->get()
183                                             , &row_adr
184                                             , 1
185                                             ) !=1
186                        , "jpeg_read_scanlines: fail to read JPEG file"
187                        );
188         }
189
190         // Read data.
191         for( int y = 0; y < view.height(); ++y )
192         {
193             io_error_if( jpeg_read_scanlines( this->get()
194                                             , &row_adr
195                                             , 1
196                                             ) != 1
197                        , "jpeg_read_scanlines: fail to read JPEG file"
198                        );
199
200             typename buffer_t::iterator beg = buffer.begin() + this->_settings._top_left.x;
201             typename buffer_t::iterator end = beg + this->_settings._dim.x;
202
203             this->_cc_policy.read( beg
204                                  , end
205                                  , view.row_begin( y )
206                                  );
207         }
208
209         //@todo: There might be a better way to do that.
210         while( this->get()->output_scanline < this->get()->image_height )
211         {
212             io_error_if( jpeg_read_scanlines( this->get()
213                                             , &row_adr
214                                             , 1
215                                             ) !=1
216                        , "jpeg_read_scanlines: fail to read JPEG file"
217                        );
218         }
219
220     }
221 };
222
223 namespace detail {
224
225 struct jpeg_type_format_checker
226 {
227     jpeg_type_format_checker( jpeg_color_space::type color_space )
228     : _color_space( color_space )
229     {}
230
231     template< typename Image >
232     bool apply()
233     {
234         return is_read_supported< typename get_pixel_type< typename Image::view_t >::type
235                                 , jpeg_tag
236                                 >::_color_space == _color_space;
237     }
238
239 private:
240
241     jpeg_color_space::type _color_space;
242 };
243
244 struct jpeg_read_is_supported
245 {
246     template< typename View >
247     struct apply : public is_read_supported< typename get_pixel_type< View >::type
248                                            , jpeg_tag
249                                            >
250     {};
251 };
252
253 } // namespace detail
254
255 ///
256 /// JPEG Dynamic Reader
257 ///
258 template< typename Device >
259 class dynamic_image_reader< Device
260                           , jpeg_tag
261                           >
262     : public reader< Device
263                    , jpeg_tag
264                    , detail::read_and_no_convert
265                    >
266 {
267     using parent_t = reader<Device, jpeg_tag, detail::read_and_no_convert>;
268
269 public:
270
271     dynamic_image_reader( const Device&                          io_dev
272                         , const image_read_settings< jpeg_tag >& settings
273                         )
274     : parent_t( io_dev
275               , settings
276               )
277     {}
278
279     template< typename Images >
280     void apply( any_image< Images >& images )
281     {
282         detail::jpeg_type_format_checker format_checker( this->_info._color_space != JCS_YCbCr
283                                                        ? this->_info._color_space
284                                                        : JCS_RGB
285                                                        );
286
287         if( !construct_matched( images
288                               , format_checker
289                               ))
290         {
291             io_error( "No matching image type between those of the given any_image and that of the file" );
292         }
293         else
294         {
295             this->init_image( images
296                             , this->_settings
297                             );
298
299             detail::dynamic_io_fnobj< detail::jpeg_read_is_supported
300                                     , parent_t
301                                     > op( this );
302
303             apply_operation( view( images )
304                            , op
305                            );
306         }
307     }
308 };
309
310 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
311 #pragma warning(pop)
312 #endif
313
314 } // gil
315 } // boost
316
317 #endif