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_DETAIL_READER_BACKEND_HPP
9 #define BOOST_GIL_EXTENSION_IO_DETAIL_READER_BACKEND_HPP
10 
11 #include <boost/gil/extension/io/png/tags.hpp>
12 #include <boost/gil/extension/io/png/detail/base.hpp>
13 #include <boost/gil/extension/io/png/detail/supported_types.hpp>
14 
15 namespace boost { namespace gil {
16 
17 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
18 #pragma warning(push)
19 #pragma warning(disable:4512) //assignment operator could not be generated
20 #pragma warning(disable:4611) //interaction between '_setjmp' and C++ object destruction is non-portable
21 #endif
22 
23 ///
24 /// PNG Backend
25 ///
26 template<typename Device >
27 struct reader_backend< Device
28                      , png_tag
29                      >
30     : public detail::png_struct_info_wrapper
31 {
32 public:
33 
34     using format_tag_t = png_tag;
35     using this_t = reader_backend<Device, png_tag>;
36 
37 public:
38 
reader_backendboost::gil::reader_backend39     reader_backend( const Device&                         io_dev
40                   , const image_read_settings< png_tag >& settings
41                   )
42     : _io_dev( io_dev )
43 
44     , _settings( settings )
45     , _info()
46     , _scanline_length( 0 )
47 
48     , _number_passes( 0 )
49     {
50         read_header();
51 
52         if( _settings._dim.x == 0 )
53         {
54             _settings._dim.x = _info._width;
55         }
56 
57         if( _settings._dim.y == 0 )
58         {
59             _settings._dim.y = _info._height;
60         }
61     }
62 
read_headerboost::gil::reader_backend63     void read_header()
64     {
65         using boost::gil::detail::PNG_BYTES_TO_CHECK;
66 
67         // check the file's first few bytes
68         byte_t buf[PNG_BYTES_TO_CHECK];
69 
70         io_error_if( _io_dev.read( buf
71                                 , PNG_BYTES_TO_CHECK
72                                 ) != PNG_BYTES_TO_CHECK
73                    , "png_check_validity: failed to read image"
74                    );
75 
76         io_error_if( png_sig_cmp( png_bytep(buf)
77                                 , png_size_t(0)
78                                 , PNG_BYTES_TO_CHECK
79                                 ) != 0
80                    , "png_check_validity: invalid png image"
81                    );
82 
83         // Create and initialize the png_struct with the desired error handler
84         // functions.  If you want to use the default stderr and longjump method,
85         // you can supply NULL for the last three parameters.  We also supply the
86         // the compiler header file version, so that we know if the application
87         // was compiled with a compatible version of the library.  REQUIRED
88         get()->_struct = png_create_read_struct( PNG_LIBPNG_VER_STRING
89                                              , nullptr  // user_error_ptr
90                                              , nullptr  // user_error_fn
91                                              , nullptr  // user_warning_fn
92                                              );
93 
94         io_error_if( get()->_struct == nullptr
95                    , "png_reader: fail to call png_create_write_struct()"
96                    );
97 
98         png_uint_32 user_chunk_data[4];
99         user_chunk_data[0] = 0;
100         user_chunk_data[1] = 0;
101         user_chunk_data[2] = 0;
102         user_chunk_data[3] = 0;
103         png_set_read_user_chunk_fn( get_struct()
104                                   , user_chunk_data
105                                   , this_t::read_user_chunk_callback
106                                   );
107 
108         // Allocate/initialize the memory for image information.  REQUIRED.
109         get()->_info = png_create_info_struct( get_struct() );
110 
111         if( get_info() == nullptr )
112         {
113             png_destroy_read_struct( &get()->_struct
114                                    , nullptr
115                                    , nullptr
116                                    );
117 
118             io_error( "png_reader: fail to call png_create_info_struct()" );
119         }
120 
121         // Set error handling if you are using the setjmp/longjmp method (this is
122         // the normal method of doing things with libpng).  REQUIRED unless you
123         // set up your own error handlers in the png_create_read_struct() earlier.
124         if( setjmp( png_jmpbuf( get_struct() )))
125         {
126             //free all of the memory associated with the png_ptr and info_ptr
127             png_destroy_read_struct( &get()->_struct
128                                    , &get()->_info
129                                    , nullptr
130                                    );
131 
132             io_error( "png is invalid" );
133         }
134 
135         png_set_read_fn( get_struct()
136                        , static_cast< png_voidp >( &this->_io_dev )
137                        , this_t::read_data
138                        );
139 
140         // Set up a callback function that will be
141         // called after each row has been read, which you can use to control
142         // a progress meter or the like.
143         png_set_read_status_fn( get_struct()
144                               , this_t::read_row_callback
145                               );
146 
147         // Set up a callback which implements user defined transformation.
148         // @todo
149         png_set_read_user_transform_fn( get_struct()
150                                       , png_user_transform_ptr( nullptr )
151                                       );
152 
153         png_set_keep_unknown_chunks( get_struct()
154                                    , PNG_HANDLE_CHUNK_ALWAYS
155                                    , nullptr
156                                    , 0
157                                    );
158 
159 
160         // Make sure we read the signature.
161         // @todo make it an option
162         png_set_sig_bytes( get_struct()
163                          , PNG_BYTES_TO_CHECK
164                          );
165 
166         // The call to png_read_info() gives us all of the information from the
167         // PNG file before the first IDAT (image data chunk).  REQUIRED
168         png_read_info( get_struct()
169                      , get_info()
170                      );
171 
172         ///
173         /// Start reading the image information
174         ///
175 
176         // get PNG_IHDR chunk information from png_info structure
177         png_get_IHDR( get_struct()
178                     , get_info()
179                     , &this->_info._width
180                     , &this->_info._height
181                     , &this->_info._bit_depth
182                     , &this->_info._color_type
183                     , &this->_info._interlace_method
184                     , &this->_info._compression_method
185                     , &this->_info._filter_method
186                     );
187 
188         // get number of color channels in image
189         this->_info._num_channels = png_get_channels( get_struct()
190                                               , get_info()
191                                               );
192 
193 #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
194 
195         // Get CIE chromacities and referenced white point
196         if( this->_settings._read_cie_chromacities )
197         {
198             this->_info._valid_cie_colors = png_get_cHRM( get_struct()
199                                                         , get_info()
200                                                         , &this->_info._white_x, &this->_info._white_y
201                                                         ,   &this->_info._red_x,   &this->_info._red_y
202                                                         , &this->_info._green_x, &this->_info._green_y
203                                                         ,  &this->_info._blue_x,  &this->_info._blue_y
204                                                         );
205         }
206 
207         // get the gamma value
208         if( this->_settings._read_file_gamma )
209         {
210             this->_info._valid_file_gamma = png_get_gAMA( get_struct()
211                                                         , get_info()
212                                                         , &this->_info._file_gamma
213                                                         );
214 
215             if( this->_info._valid_file_gamma == false )
216             {
217                 this->_info._file_gamma = 1.0;
218             }
219         }
220 #else
221 
222         // Get CIE chromacities and referenced white point
223         if( this->_settings._read_cie_chromacities )
224         {
225             this->_info._valid_cie_colors = png_get_cHRM_fixed( get_struct()
226                                                               , get_info()
227                                                               , &this->_info._white_x, &this->_info._white_y
228                                                               ,   &this->_info._red_x,   &this->_info._red_y
229                                                               , &this->_info._green_x, &this->_info._green_y
230                                                               ,  &this->_info._blue_x,  &this->_info._blue_y
231                                                               );
232         }
233 
234         // get the gamma value
235         if( this->_settings._read_file_gamma )
236         {
237             this->_info._valid_file_gamma = png_get_gAMA_fixed( get_struct()
238                                                               , get_info()
239                                                               , &this->_info._file_gamma
240                                                               );
241 
242             if( this->_info._valid_file_gamma == false )
243             {
244                 this->_info._file_gamma = 1;
245             }
246         }
247 #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
248 
249         // get the embedded ICC profile data
250         if( this->_settings._read_icc_profile )
251         {
252 #if PNG_LIBPNG_VER_MINOR >= 5
253             png_charp icc_name = png_charp( nullptr );
254             png_bytep profile  = png_bytep( nullptr );
255 
256             this->_info._valid_icc_profile = png_get_iCCP( get_struct()
257                                                          , get_info()
258                                                          , &icc_name
259                                                          , &this->_info._iccp_compression_type
260                                                          , &profile
261                                                          , &this->_info._profile_length
262                                                          );
263 #else
264             png_charp icc_name = png_charp( NULL );
265             png_charp profile  = png_charp( NULL );
266 
267             this->_info._valid_icc_profile = png_get_iCCP( get_struct()
268                                                          , get_info()
269                                                          , &icc_name
270                                                          , &this->_info._iccp_compression_type
271                                                          , &profile
272                                                          , &this->_info._profile_length
273                                                          );
274 #endif
275             if( icc_name )
276             {
277                 this->_info._icc_name.append( icc_name
278                                             , std::strlen( icc_name )
279                                             );
280             }
281 
282             if( this->_info._profile_length != 0 )
283             {
284                 std:: copy_n (profile, this->_info._profile_length, std:: back_inserter (this->_info._profile));
285             }
286         }
287 
288         // get the rendering intent
289         if( this->_settings._read_intent )
290         {
291             this->_info._valid_intent = png_get_sRGB( get_struct()
292                                                     , get_info()
293                                                     , &this->_info._intent
294                                                     );
295         }
296 
297         // get image palette information from png_info structure
298         if( this->_settings._read_palette )
299         {
300             png_colorp palette = png_colorp( nullptr );
301 
302             this->_info._valid_palette = png_get_PLTE( get_struct()
303                                                      , get_info()
304                                                      , &palette
305                                                      , &this->_info._num_palette
306                                                      );
307 
308             if( this->_info._num_palette > 0 )
309             {
310                 this->_info._palette.resize( this->_info._num_palette );
311                 std::copy( palette
312                          , palette + this->_info._num_palette
313                          , &this->_info._palette.front()
314                          );
315             }
316         }
317 
318         // get background color
319         if( this->_settings._read_background )
320         {
321             png_color_16p background = png_color_16p( nullptr );
322 
323             this->_info._valid_background = png_get_bKGD( get_struct()
324                                                         , get_info()
325                                                         , &background
326                                                         );
327             if( background )
328             {
329                 this->_info._background = *background;
330             }
331         }
332 
333         // get the histogram
334         if( this->_settings._read_histogram )
335         {
336             png_uint_16p histogram = png_uint_16p( nullptr );
337 
338             this->_info._valid_histogram = png_get_hIST( get_struct()
339                                                        , get_info()
340                                                        , &histogram
341                                                        );
342 
343             if( histogram )
344             {
345                 // the number of values is set by the number of colors inside
346                 // the palette.
347                 if( this->_settings._read_palette == false )
348                 {
349                     png_colorp palette = png_colorp( nullptr );
350                     png_get_PLTE( get_struct()
351                                 , get_info()
352                                 , &palette
353                                 , &this->_info._num_palette
354                                 );
355                 }
356 
357                 std::copy( histogram
358                          , histogram + this->_info._num_palette
359                          , &this->_info._histogram.front()
360                          );
361             }
362         }
363 
364         // get screen offsets for the given image
365         if( this->_settings._read_screen_offsets )
366         {
367             this->_info._valid_offset = png_get_oFFs( get_struct()
368                                                     , get_info()
369                                                     , &this->_info._offset_x
370                                                     , &this->_info._offset_y
371                                                     , &this->_info._off_unit_type
372                                                     );
373         }
374 
375 
376         // get pixel calibration settings
377         if( this->_settings._read_pixel_calibration )
378         {
379             png_charp purpose = png_charp ( nullptr );
380             png_charp units   = png_charp ( nullptr );
381             png_charpp params = png_charpp( nullptr );
382 
383             this->_info._valid_pixel_calibration = png_get_pCAL( get_struct()
384                                                                , get_info()
385                                                                , &purpose
386                                                                , &this->_info._X0
387                                                                , &this->_info._X1
388                                                                , &this->_info._cal_type
389                                                                , &this->_info._num_params
390                                                                , &units
391                                                                , &params
392                                                                );
393             if( purpose )
394             {
395                 this->_info._purpose.append( purpose
396                                            , std::strlen( purpose )
397                                            );
398             }
399 
400             if( units )
401             {
402                 this->_info._units.append( units
403                                          , std::strlen( units )
404                                          );
405             }
406 
407             if( this->_info._num_params > 0 )
408             {
409                 this->_info._params.resize( this->_info._num_params );
410 
411                 for( png_CAL_nparam::type i = 0
412                    ; i < this->_info._num_params
413                    ; ++i
414                    )
415                 {
416                     this->_info._params[i].append( params[i]
417                                                  , std::strlen( params[i] )
418                                                  );
419                 }
420             }
421         }
422 
423         // get the physical resolution
424         if( this->_settings._read_physical_resolution )
425         {
426             this->_info._valid_resolution = png_get_pHYs( get_struct()
427                                                         , get_info()
428                                                         , &this->_info._res_x
429                                                         , &this->_info._res_y
430                                                         , &this->_info._phy_unit_type
431                                                         );
432         }
433 
434         // get the image resolution in pixels per meter.
435         if( this->_settings._read_pixels_per_meter )
436         {
437             this->_info._pixels_per_meter = png_get_pixels_per_meter( get_struct()
438                                                                     , get_info()
439                                                                     );
440         }
441 
442 
443         // get number of significant bits for each color channel
444         if( this->_settings._read_number_of_significant_bits )
445         {
446             png_color_8p sig_bits = png_color_8p( nullptr );
447 
448             this->_info._valid_significant_bits = png_get_sBIT( get_struct()
449                                                               , get_info()
450                                                               , &sig_bits
451                                                               );
452 
453             // @todo Is there one or more colors?
454             if( sig_bits )
455             {
456                 this->_info._sig_bits = *sig_bits;
457             }
458         }
459 
460 #ifndef BOOST_GIL_IO_PNG_1_4_OR_LOWER
461 
462 #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
463 
464         // get physical scale settings
465         if( this->_settings._read_scale_factors )
466         {
467             this->_info._valid_scale_factors = png_get_sCAL( get_struct()
468                                                            , get_info()
469                                                            , &this->_info._scale_unit
470                                                            , &this->_info._scale_width
471                                                            , &this->_info._scale_height
472                                                            );
473         }
474 #else
475 #ifdef BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED
476         if( this->_settings._read_scale_factors )
477         {
478             this->_info._valid_scale_factors = png_get_sCAL_fixed( get_struct()
479                                                                  , get_info()
480                                                                  , &this->_info._scale_unit
481                                                                  , &this->_info._scale_width
482                                                                  , &this->_info._scale_height
483                                                                  );
484         }
485 #else
486         if( this->_settings._read_scale_factors )
487         {
488             png_charp scale_width  = nullptr;
489             png_charp scale_height = nullptr;
490 
491             this->_info._valid_scale_factors = png_get_sCAL_s(
492                 get_struct(), get_info(), &this->_info._scale_unit, &scale_width, &scale_height);
493 
494             if (this->_info._valid_scale_factors)
495             {
496                 if( scale_width )
497                 {
498                     this->_info._scale_width.append( scale_width
499                                                    , std::strlen( scale_width )
500                                                    );
501                 }
502 
503                 if( scale_height )
504                 {
505                     this->_info._scale_height.append( scale_height
506                                                     , std::strlen( scale_height )
507                                                     );
508                 }
509             }
510         }
511 #endif // BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED
512 #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
513 #endif // BOOST_GIL_IO_PNG_1_4_OR_LOWER
514 
515         // get comments information from png_info structure
516         if( this->_settings._read_comments )
517         {
518             png_textp text = png_textp( nullptr );
519 
520             this->_info._valid_text = png_get_text( get_struct()
521                                                   , get_info()
522                                                   , &text
523                                                   , &this->_info._num_text
524                                                   );
525 
526             if( this->_info._num_text > 0 )
527             {
528                 this->_info._text.resize( this->_info._num_text );
529 
530                 for( png_num_text::type i = 0
531                    ; i < this->_info._num_text
532                    ; ++i
533                    )
534                 {
535                     this->_info._text[i]._compression = text[i].compression;
536                     this->_info._text[i]._key.append( text[i].key
537                                                     , std::strlen( text[i].key )
538                                                     );
539 
540                     this->_info._text[i]._text.append( text[i].text
541                                                      , std::strlen( text[i].text )
542                                                      );
543                 }
544             }
545         }
546 
547         // get last modification time
548         if( this->_settings._read_last_modification_time )
549         {
550             png_timep mod_time = png_timep( nullptr );
551             this->_info._valid_modification_time = png_get_tIME( get_struct()
552                                                                , get_info()
553                                                                , &mod_time
554                                                                );
555             if( mod_time )
556             {
557                 this->_info._mod_time = *mod_time;
558             }
559         }
560 
561         // get transparency data
562         if( this->_settings._read_transparency_data )
563         {
564             png_bytep     trans        = png_bytep    ( nullptr );
565             png_color_16p trans_values = png_color_16p( nullptr );
566 
567             this->_info._valid_transparency_factors = png_get_tRNS( get_struct()
568                                                                   , get_info()
569                                                                   , &trans
570                                                                   , &this->_info._num_trans
571                                                                   , &trans_values
572                                                                   );
573 
574             if( trans )
575             {
576                 //@todo What to do, here? How do I know the length of the "trans" array?
577             }
578 
579             if( this->_info._num_trans )
580             {
581                 this->_info._trans_values.resize( this->_info._num_trans );
582                 std::copy( trans_values
583                          , trans_values + this->_info._num_trans
584                          , &this->_info._trans_values.front()
585                          );
586             }
587         }
588 
589         // @todo One day!
590 /*
591         if( false )
592         {
593             png_unknown_chunkp unknowns = png_unknown_chunkp( NULL );
594             int num_unknowns = static_cast< int >( png_get_unknown_chunks( get_struct()
595                                                                          , get_info()
596                                                                          , &unknowns
597                                                                          )
598                                                  );
599         }
600 */
601     }
602 
603     /// Check if image is large enough.
check_image_sizeboost::gil::reader_backend604     void check_image_size( const point_t& img_dim )
605     {
606         if( _settings._dim.x > 0 )
607         {
608             if( img_dim.x < _settings._dim.x ) { io_error( "Supplied image is too small" ); }
609         }
610         else
611         {
612             if( img_dim.x < _info._width ) { io_error( "Supplied image is too small" ); }
613         }
614 
615 
616         if( _settings._dim.y > 0 )
617         {
618             if( img_dim.y < _settings._dim.y ) { io_error( "Supplied image is too small" ); }
619         }
620         else
621         {
622             if( img_dim.y < _info._height ) { io_error( "Supplied image is too small" ); }
623         }
624     }
625 
626 protected:
627 
read_databoost::gil::reader_backend628     static void read_data( png_structp png_ptr
629                          , png_bytep   data
630                          , png_size_t length
631                          )
632     {
633         static_cast<Device*>(png_get_io_ptr(png_ptr) )->read( data
634                                                             , length );
635     }
636 
flushboost::gil::reader_backend637     static void flush( png_structp png_ptr )
638     {
639         static_cast<Device*>(png_get_io_ptr(png_ptr) )->flush();
640     }
641 
642 
read_user_chunk_callbackboost::gil::reader_backend643     static int read_user_chunk_callback( png_struct*        /* png_ptr */
644                                        , png_unknown_chunkp /* chunk */
645                                        )
646     {
647         // @todo
648         return 0;
649     }
650 
read_row_callbackboost::gil::reader_backend651     static void read_row_callback( png_structp /* png_ptr    */
652                                  , png_uint_32 /* row_number */
653                                  , int         /* pass       */
654                                  )
655     {
656         // @todo
657     }
658 
659 public:
660 
661     Device _io_dev;
662 
663     image_read_settings< png_tag > _settings;
664     image_read_info    < png_tag > _info;
665 
666     std::size_t _scanline_length;
667 
668     std::size_t _number_passes;
669 };
670 
671 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
672 #pragma warning(pop)
673 #endif
674 
675 } // namespace gil
676 } // namespace boost
677 
678 #endif
679