Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / gil / extension / io / png / 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_PNG_DETAIL_READ_HPP
9 #define BOOST_GIL_EXTENSION_IO_PNG_DETAIL_READ_HPP
10
11 #include <boost/gil/extension/io/png/tags.hpp>
12 #include <boost/gil/extension/io/png/detail/reader_backend.hpp>
13 #include <boost/gil/extension/io/png/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/conversion_policies.hpp>
18 #include <boost/gil/io/device.hpp>
19 #include <boost/gil/io/dynamic_io_new.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 <type_traits>
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 #endif
32
33 ///
34 /// PNG Reader
35 ///
36 template< typename Device
37         , typename ConversionPolicy
38         >
39 class reader< Device
40             , png_tag
41             , ConversionPolicy
42             >
43     : public reader_base< png_tag
44                         , ConversionPolicy >
45     , public reader_backend< Device
46                            , png_tag
47                            >
48 {
49 private:
50
51     using this_t = reader<Device, png_tag, ConversionPolicy>;
52     using cc_t = typename ConversionPolicy::color_converter_type;
53
54 public:
55
56     using backend_t = reader_backend<Device, png_tag>;
57
58 public:
59
60     reader( const Device&                         io_dev
61           , const image_read_settings< png_tag >& settings
62           )
63     : reader_base< png_tag
64                  , ConversionPolicy
65                  >()
66     , backend_t( io_dev
67                , settings
68                )
69     {}
70
71     reader( const Device&                                          io_dev
72           , const typename ConversionPolicy::color_converter_type& cc
73           , const image_read_settings< png_tag >&                  settings
74           )
75     : reader_base< png_tag
76                  , ConversionPolicy
77                  >( cc )
78     , backend_t( io_dev
79                , settings
80                )
81     {}
82
83     template< typename View >
84     void apply( const View& view )
85     {
86         // The info structures are filled at this point.
87
88         // Now it's time for some transformations.
89
90         if( little_endian() )
91         {
92             if( this->_info._bit_depth == 16 )
93             {
94                 // Swap bytes of 16 bit files to least significant byte first.
95                 png_set_swap( this->get_struct() );
96             }
97
98             if( this->_info._bit_depth < 8 )
99             {
100                 // swap bits of 1, 2, 4 bit packed pixel formats
101                 png_set_packswap( this->get_struct() );
102             }
103         }
104
105         if( this->_info._color_type == PNG_COLOR_TYPE_PALETTE )
106         {
107             png_set_palette_to_rgb( this->get_struct() );
108         }
109
110         if( png_get_valid( this->get_struct(), this->get_info(), PNG_INFO_tRNS ) )
111         {
112             png_set_tRNS_to_alpha( this->get_struct() );
113         }
114
115         // Tell libpng to handle the gamma conversion for you.  The final call
116         // is a good guess for PC generated images, but it should be configurable
117         // by the user at run time by the user.  It is strongly suggested that
118         // your application support gamma correction.
119         if( this->_settings._apply_screen_gamma )
120         {
121             // png_set_gamma will change the image data!
122
123 #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
124         png_set_gamma( this->get_struct()
125                      , this->_settings._screen_gamma
126                      , this->_info._file_gamma
127                      );
128 #else
129         png_set_gamma( this->get_struct()
130                      , this->_settings._screen_gamma
131                      , this->_info._file_gamma
132                      );
133 #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
134         }
135
136         // Turn on interlace handling.  REQUIRED if you are not using
137         // png_read_image().  To see how to handle interlacing passes,
138         // see the png_read_row() method below:
139         this->_number_passes = png_set_interlace_handling( this->get_struct() );
140
141
142         // The above transformation might have changed the bit_depth and color type.
143         png_read_update_info( this->get_struct()
144                             , this->get_info()
145                             );
146
147         this->_info._bit_depth = png_get_bit_depth( this->get_struct()
148                                                   , this->get_info()
149                                                   );
150
151         this->_info._num_channels = png_get_channels( this->get_struct()
152                                                     , this->get_info()
153                                                     );
154
155         this->_info._color_type = png_get_color_type( this->get_struct()
156                                                     , this->get_info()
157                                                     );
158
159         this->_scanline_length = png_get_rowbytes( this->get_struct()
160                                                  , this->get_info()
161                                                  );
162
163         switch( this->_info._color_type )
164         {
165             case PNG_COLOR_TYPE_GRAY:
166             {
167                 switch( this->_info._bit_depth )
168                 {
169                     case  1: read_rows< gray1_image_t::view_t::reference >( view ); break;
170                     case  2: read_rows< gray2_image_t::view_t::reference >( view ); break;
171                     case  4: read_rows< gray4_image_t::view_t::reference >( view ); break;
172                     case  8: read_rows< gray8_pixel_t  >( view ); break;
173                     case 16: read_rows< gray16_pixel_t >( view ); break;
174                     default: io_error( "png_reader::read_data(): unknown combination of color type and bit depth" );
175                 }
176
177                 break;
178             }
179             case PNG_COLOR_TYPE_GA:
180             {
181                 #ifdef BOOST_GIL_IO_ENABLE_GRAY_ALPHA
182                 switch( this->_info._bit_depth )
183                 {
184                     case  8: read_rows< gray_alpha8_pixel_t > ( view ); break;
185                     case 16: read_rows< gray_alpha16_pixel_t >( view ); break;
186                     default: io_error( "png_reader::read_data(): unknown combination of color type and bit depth" );
187                 }
188                 #else
189                     io_error( "gray_alpha isn't enabled. Define BOOST_GIL_IO_ENABLE_GRAY_ALPHA when building application." );
190                 #endif // BOOST_GIL_IO_ENABLE_GRAY_ALPHA
191
192
193                 break;
194             }
195             case PNG_COLOR_TYPE_RGB:
196             {
197                 switch( this->_info._bit_depth )
198                 {
199                     case 8:  read_rows< rgb8_pixel_t > ( view ); break;
200                     case 16: read_rows< rgb16_pixel_t >( view ); break;
201                     default: io_error( "png_reader::read_data(): unknown combination of color type and bit depth" );
202                 }
203
204                 break;
205             }
206             case PNG_COLOR_TYPE_RGBA:
207             {
208                 switch( this->_info._bit_depth )
209                 {
210                     case  8: read_rows< rgba8_pixel_t > ( view ); break;
211                     case 16: read_rows< rgba16_pixel_t >( view ); break;
212                     default: io_error( "png_reader_color_convert::read_data(): unknown combination of color type and bit depth" );
213                 }
214
215                 break;
216             }
217             default: io_error( "png_reader_color_convert::read_data(): unknown color type" );
218         }
219
220         // read rest of file, and get additional chunks in info_ptr
221         png_read_end( this->get_struct()
222                     , nullptr
223                     );
224     }
225
226 private:
227
228     template< typename ImagePixel
229             , typename View
230             >
231     void read_rows( const View& view )
232     {
233         using row_buffer_helper_t = detail::row_buffer_helper_view<ImagePixel>;
234
235         using it_t = typename row_buffer_helper_t::iterator_t;
236
237         using is_read_and_convert_t = typename std::is_same
238             <
239                 ConversionPolicy,
240                 detail::read_and_no_convert
241             >::type;
242
243         io_error_if( !detail::is_allowed< View >( this->_info
244                                                 , is_read_and_convert_t()
245                                                 )
246                    , "Image types aren't compatible."
247                    );
248
249         std::size_t rowbytes = png_get_rowbytes( this->get_struct()
250                                                , this->get_info()
251                                                );
252
253         row_buffer_helper_t buffer( rowbytes
254                                   , true
255                                   );
256
257         png_bytep row_ptr = (png_bytep)( &( buffer.data()[0]));
258
259         for( std::size_t pass = 0; pass < this->_number_passes; pass++ )
260         {
261             if( pass == this->_number_passes - 1 )
262             {
263                 // skip lines if necessary
264                 for( std::ptrdiff_t y = 0; y < this->_settings._top_left.y; ++y )
265                 {
266                     // Read the image using the "sparkle" effect.
267                     png_read_rows( this->get_struct()
268                                  , &row_ptr
269                                  , nullptr
270                                  , 1
271                                  );
272                 }
273
274                 for( std::ptrdiff_t y = 0
275                    ; y < this->_settings._dim.y
276                    ; ++y
277                    )
278                 {
279                     // Read the image using the "sparkle" effect.
280                     png_read_rows( this->get_struct()
281                                  , &row_ptr
282                                  , nullptr
283                                  , 1
284                                  );
285
286                     it_t first = buffer.begin() + this->_settings._top_left.x;
287                     it_t last  = first + this->_settings._dim.x; // one after last element
288
289                     this->_cc_policy.read( first
290                                          , last
291                                          , view.row_begin( y ));
292                 }
293
294                 // Read the rest of the image. libpng needs that.
295                 std::ptrdiff_t remaining_rows = static_cast< std::ptrdiff_t >( this->_info._height )
296                                               - this->_settings._top_left.y
297                                               - this->_settings._dim.y;
298                 for( std::ptrdiff_t y = 0
299                    ; y < remaining_rows
300                    ; ++y
301                    )
302                 {
303                     // Read the image using the "sparkle" effect.
304                     png_read_rows( this->get_struct()
305                                  , &row_ptr
306                                  , nullptr
307                                  , 1
308                                  );
309                 }
310             }
311             else
312             {
313                 for( int y = 0; y < view.height(); ++y )
314                 {
315                     // Read the image using the "sparkle" effect.
316                     png_read_rows( this->get_struct()
317                                  , &row_ptr
318                                  , nullptr
319                                  , 1
320                                  );
321                 }
322             }
323         }
324     }
325 };
326
327 namespace detail {
328
329 struct png_type_format_checker
330 {
331     png_type_format_checker( png_bitdepth::type   bit_depth
332                            , png_color_type::type color_type
333                            )
334     : _bit_depth ( bit_depth  )
335     , _color_type( color_type )
336     {}
337
338     template< typename Image >
339     bool apply()
340     {
341         using is_supported_t = is_read_supported
342             <
343                 typename get_pixel_type<typename Image::view_t>::type,
344                 png_tag
345             >;
346
347         return is_supported_t::_bit_depth  == _bit_depth
348             && is_supported_t::_color_type == _color_type;
349     }
350
351 private:
352
353     png_bitdepth::type   _bit_depth;
354     png_color_type::type _color_type;
355 };
356
357 struct png_read_is_supported
358 {
359     template< typename View >
360     struct apply : public is_read_supported< typename get_pixel_type< View >::type
361                                            , png_tag
362                                            >
363     {};
364 };
365
366 } // namespace detail
367
368
369 ///
370 /// PNG Dynamic Image Reader
371 ///
372 template< typename Device
373         >
374 class dynamic_image_reader< Device
375                           , png_tag
376                           >
377     : public reader< Device
378                    , png_tag
379                    , detail::read_and_no_convert
380                    >
381 {
382     using parent_t = reader
383         <
384             Device,
385             png_tag,
386             detail::read_and_no_convert
387         >;
388
389 public:
390
391     dynamic_image_reader( const Device&                         io_dev
392                         , const image_read_settings< png_tag >& settings
393                         )
394     : parent_t( io_dev
395               , settings
396               )
397     {}
398
399     template< typename Images >
400     void apply( any_image< Images >& images )
401     {
402         detail::png_type_format_checker format_checker( this->_info._bit_depth
403                                                       , this->_info._color_type
404                                                       );
405
406         if( !construct_matched( images
407                               , format_checker
408                               ))
409         {
410             io_error( "No matching image type between those of the given any_image and that of the file" );
411         }
412         else
413         {
414             this->init_image( images
415                             , this->_settings
416                             );
417
418             detail::dynamic_io_fnobj< detail::png_read_is_supported
419                                     , parent_t
420                                     > op( this );
421
422             apply_operation( view( images )
423                            , op
424                            );
425         }
426     }
427 };
428
429 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
430 #pragma warning(pop)
431 #endif
432
433 } // namespace gil
434 } // namespace boost
435
436 #endif