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_PNG_DETAIL_WRITER_BACKEND_HPP
9 #define BOOST_GIL_EXTENSION_IO_PNG_DETAIL_WRITER_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 #include <boost/gil/io/base.hpp>
16 #include <boost/gil/io/typedefs.hpp>
17 
18 namespace boost { namespace gil {
19 
20 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
21 #pragma warning(push)
22 #pragma warning(disable:4512) //assignment operator could not be generated
23 #pragma warning(disable:4611) //interaction between '_setjmp' and C++ object destruction is non-portable
24 #endif
25 
26 ///
27 /// PNG Writer Backend
28 ///
29 template< typename Device >
30 struct writer_backend< Device
31                      , png_tag
32                      >
33     : public detail::png_struct_info_wrapper
34 {
35 
36 private:
37 
38     using this_t = writer_backend<Device, png_tag>;
39 
40 public:
41 
42     using format_tag_t = png_tag;
43 
44     ///
45     /// Constructor
46     ///
writer_backendboost::gil::writer_backend47     writer_backend( const Device&                      io_dev
48                   , const image_write_info< png_tag >& info
49                   )
50     : png_struct_info_wrapper( false )
51     , _io_dev( io_dev )
52     , _info( info )
53     {
54         // Create and initialize the png_struct with the desired error handler
55         // functions.  If you want to use the default stderr and longjump method,
56         // you can supply NULL for the last three parameters.  We also check that
57         // the library version is compatible with the one used at compile time,
58         // in case we are using dynamically linked libraries.  REQUIRED.
59         get()->_struct = png_create_write_struct( PNG_LIBPNG_VER_STRING
60                                                 , nullptr  // user_error_ptr
61                                                 , nullptr  // user_error_fn
62                                                 , nullptr  // user_warning_fn
63                                                 );
64 
65         io_error_if( get_struct() == nullptr
66                    , "png_writer: fail to call png_create_write_struct()"
67                    );
68 
69         // Allocate/initialize the image information data.  REQUIRED
70         get()->_info = png_create_info_struct( get_struct() );
71 
72         if( get_info() == nullptr )
73         {
74             png_destroy_write_struct( &get()->_struct
75                                     , nullptr
76                                     );
77 
78             io_error( "png_writer: fail to call png_create_info_struct()" );
79         }
80 
81         // Set error handling.  REQUIRED if you aren't supplying your own
82         // error handling functions in the png_create_write_struct() call.
83         if( setjmp( png_jmpbuf( get_struct() )))
84         {
85             //free all of the memory associated with the png_ptr and info_ptr
86             png_destroy_write_struct( &get()->_struct
87                                     , &get()->_info
88                                     );
89 
90             io_error( "png_writer: fail to call setjmp()" );
91         }
92 
93         init_io( get_struct() );
94     }
95 
96 protected:
97 
98     template< typename View >
write_headerboost::gil::writer_backend99     void write_header( const View& view )
100     {
101         using png_rw_info_t = detail::png_write_support
102             <
103                 typename channel_type<typename get_pixel_type<View>::type>::type,
104                 typename color_space_type<View>::type
105             >;
106 
107         // Set the image information here.  Width and height are up to 2^31,
108         // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
109         // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
110         // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
111         // or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
112         // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
113         // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
114         png_set_IHDR( get_struct()
115                     , get_info()
116                     , static_cast< png_image_width::type  >( view.width()  )
117                     , static_cast< png_image_height::type >( view.height() )
118                     , static_cast< png_bitdepth::type     >( png_rw_info_t::_bit_depth )
119                     , static_cast< png_color_type::type   >( png_rw_info_t::_color_type )
120                     , _info._interlace_method
121                     , _info._compression_type
122                     , _info._filter_method
123                     );
124 
125 #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
126         if( _info._valid_cie_colors )
127         {
128             png_set_cHRM( get_struct()
129                         , get_info()
130                         , _info._white_x
131                         , _info._white_y
132                         , _info._red_x
133                         , _info._red_y
134                         , _info._green_x
135                         , _info._green_y
136                         , _info._blue_x
137                         , _info._blue_y
138                         );
139         }
140 
141         if( _info._valid_file_gamma )
142         {
143             png_set_gAMA( get_struct()
144                         , get_info()
145                         , _info._file_gamma
146                         );
147         }
148 #else
149         if( _info._valid_cie_colors )
150         {
151             png_set_cHRM_fixed( get_struct()
152                               , get_info()
153                               , _info._white_x
154                               , _info._white_y
155                               , _info._red_x
156                               , _info._red_y
157                               , _info._green_x
158                               , _info._green_y
159                               , _info._blue_x
160                               , _info._blue_y
161                               );
162         }
163 
164         if( _info._valid_file_gamma )
165         {
166             png_set_gAMA_fixed( get_struct()
167                               , get_info()
168                               , _info._file_gamma
169                               );
170         }
171 #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
172 
173         if( _info._valid_icc_profile )
174         {
175 #if PNG_LIBPNG_VER_MINOR >= 5
176             png_set_iCCP( get_struct()
177                         , get_info()
178                         , const_cast< png_charp >( _info._icc_name.c_str() )
179                         , _info._iccp_compression_type
180                         , reinterpret_cast< png_const_bytep >( & (_info._profile.front ()) )
181                         , _info._profile_length
182                         );
183 #else
184             png_set_iCCP( get_struct()
185                         , get_info()
186                         , const_cast< png_charp >( _info._icc_name.c_str() )
187                         , _info._iccp_compression_type
188                         , const_cast< png_charp >( & (_info._profile.front()) )
189                         , _info._profile_length
190                         );
191 #endif
192         }
193 
194         if( _info._valid_intent )
195         {
196             png_set_sRGB( get_struct()
197                         , get_info()
198                         , _info._intent
199                         );
200         }
201 
202         if( _info._valid_palette )
203         {
204             png_set_PLTE( get_struct()
205                         , get_info()
206                         , const_cast< png_colorp >( &_info._palette.front() )
207                         , _info._num_palette
208                         );
209         }
210 
211         if( _info._valid_background )
212         {
213             png_set_bKGD( get_struct()
214                         , get_info()
215                         , const_cast< png_color_16p >( &_info._background )
216                         );
217         }
218 
219         if( _info._valid_histogram )
220         {
221             png_set_hIST( get_struct()
222                         , get_info()
223                         , const_cast< png_uint_16p >( &_info._histogram.front() )
224                         );
225         }
226 
227         if( _info._valid_offset )
228         {
229             png_set_oFFs( get_struct()
230                         , get_info()
231                         , _info._offset_x
232                         , _info._offset_y
233                         , _info._off_unit_type
234                         );
235         }
236 
237         if( _info._valid_pixel_calibration )
238         {
239             std::vector< const char* > params( _info._num_params );
240             for( std::size_t i = 0; i < params.size(); ++i )
241             {
242                 params[i] = _info._params[ i ].c_str();
243             }
244 
245             png_set_pCAL( get_struct()
246                         , get_info()
247                         , const_cast< png_charp >( _info._purpose.c_str() )
248                         , _info._X0
249                         , _info._X1
250                         , _info._cal_type
251                         , _info._num_params
252                         , const_cast< png_charp  >( _info._units.c_str() )
253                         , const_cast< png_charpp >( &params.front()     )
254                         );
255         }
256 
257         if( _info._valid_resolution )
258         {
259             png_set_pHYs( get_struct()
260                         , get_info()
261                         , _info._res_x
262                         , _info._res_y
263                         , _info._phy_unit_type
264                         );
265         }
266 
267         if( _info._valid_significant_bits )
268         {
269             png_set_sBIT( get_struct()
270                         , get_info()
271                         , const_cast< png_color_8p >( &_info._sig_bits )
272                         );
273         }
274 
275 #ifndef BOOST_GIL_IO_PNG_1_4_OR_LOWER
276 
277 #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
278         if( _info._valid_scale_factors )
279         {
280             png_set_sCAL( get_struct()
281                         , get_info()
282                         , this->_info._scale_unit
283                         , this->_info._scale_width
284                         , this->_info._scale_height
285                         );
286         }
287 #else
288 #ifdef BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED
289         if( _info._valid_scale_factors )
290         {
291             png_set_sCAL_fixed( get_struct()
292                               , get_info()
293                               , this->_info._scale_unit
294                               , this->_info._scale_width
295                               , this->_info._scale_height
296                               );
297         }
298 #else
299         if( _info._valid_scale_factors )
300         {
301             png_set_sCAL_s( get_struct()
302                           , get_info()
303                           , this->_info._scale_unit
304                           , const_cast< png_charp >( this->_info._scale_width.c_str()  )
305                           , const_cast< png_charp >( this->_info._scale_height.c_str() )
306                           );
307         }
308 
309 #endif // BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED
310 #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
311 #endif // BOOST_GIL_IO_PNG_1_4_OR_LOWER
312 
313         if( _info._valid_text )
314         {
315             std::vector< png_text > texts( _info._num_text );
316             for( std::size_t i = 0; i < texts.size(); ++i )
317             {
318                 png_text pt;
319                 pt.compression = _info._text[i]._compression;
320                 pt.key         = const_cast< png_charp >( this->_info._text[i]._key.c_str()  );
321                 pt.text        = const_cast< png_charp >( this->_info._text[i]._text.c_str() );
322                 pt.text_length = _info._text[i]._text.length();
323 
324                 texts[i] = pt;
325             }
326 
327             png_set_text( get_struct()
328                         , get_info()
329                         , &texts.front()
330                         , _info._num_text
331                         );
332         }
333 
334         if( _info._valid_modification_time )
335         {
336             png_set_tIME( get_struct()
337                         , get_info()
338                         , const_cast< png_timep >( &_info._mod_time )
339                         );
340         }
341 
342         if( _info._valid_transparency_factors )
343         {
344             int sample_max = ( 1u << _info._bit_depth );
345 
346             /* libpng doesn't reject a tRNS chunk with out-of-range samples */
347             if( !(  (  _info._color_type == PNG_COLOR_TYPE_GRAY
348                     && (int) _info._trans_values[0].gray > sample_max
349                     )
350                  || (  _info._color_type == PNG_COLOR_TYPE_RGB
351                     &&(  (int) _info._trans_values[0].red   > sample_max
352                       || (int) _info._trans_values[0].green > sample_max
353                       || (int) _info._trans_values[0].blue  > sample_max
354                       )
355                     )
356                  )
357               )
358             {
359                 //@todo Fix that once reading transparency values works
360 /*
361                 png_set_tRNS( get_struct()
362                             , get_info()
363                             , trans
364                             , num_trans
365                             , trans_values
366                             );
367 */
368             }
369         }
370 
371         // Compression Levels - valid values are [0,9]
372         png_set_compression_level( get_struct()
373                                  , _info._compression_level
374                                  );
375 
376         png_set_compression_mem_level( get_struct()
377                                      , _info._compression_mem_level
378                                      );
379 
380         png_set_compression_strategy( get_struct()
381                                     , _info._compression_strategy
382                                     );
383 
384         png_set_compression_window_bits( get_struct()
385                                        , _info._compression_window_bits
386                                        );
387 
388         png_set_compression_method( get_struct()
389                                   , _info._compression_method
390                                   );
391 
392         png_set_compression_buffer_size( get_struct()
393                                        , _info._compression_buffer_size
394                                        );
395 
396 #ifdef BOOST_GIL_IO_PNG_DITHERING_SUPPORTED
397         // Dithering
398         if( _info._set_dithering )
399         {
400             png_set_dither( get_struct()
401                           , &_info._dithering_palette.front()
402                           , _info._dithering_num_palette
403                           , _info._dithering_maximum_colors
404                           , &_info._dithering_histogram.front()
405                           , _info._full_dither
406                           );
407         }
408 #endif // BOOST_GIL_IO_PNG_DITHERING_SUPPORTED
409 
410         // Filter
411         if( _info._set_filter )
412         {
413             png_set_filter( get_struct()
414                           , 0
415                           , _info._filter
416                           );
417         }
418 
419         // Invert Mono
420         if( _info._invert_mono )
421         {
422             png_set_invert_mono( get_struct() );
423         }
424 
425         // True Bits
426         if( _info._set_true_bits )
427         {
428             png_set_sBIT( get_struct()
429                         , get_info()
430                         , &_info._true_bits.front()
431                         );
432         }
433 
434         // sRGB Intent
435         if( _info._set_srgb_intent )
436         {
437             png_set_sRGB( get_struct()
438                         , get_info()
439                         , _info._srgb_intent
440                         );
441         }
442 
443         // Strip Alpha
444         if( _info._strip_alpha )
445         {
446             png_set_strip_alpha( get_struct() );
447         }
448 
449         // Swap Alpha
450         if( _info._swap_alpha )
451         {
452             png_set_swap_alpha( get_struct() );
453         }
454 
455 
456         png_write_info( get_struct()
457                       , get_info()
458                       );
459     }
460 
461 protected:
462 
write_databoost::gil::writer_backend463     static void write_data( png_structp png_ptr
464                           , png_bytep   data
465                           , png_size_t  length
466                           )
467     {
468         static_cast< Device* >( png_get_io_ptr( png_ptr ))->write( data
469                                                                  , length );
470     }
471 
flushboost::gil::writer_backend472     static void flush( png_structp png_ptr )
473     {
474         static_cast< Device* >(png_get_io_ptr(png_ptr) )->flush();
475     }
476 
477 private:
478 
init_ioboost::gil::writer_backend479     void init_io( png_structp png_ptr )
480     {
481         png_set_write_fn( png_ptr
482                         , static_cast< void* >        ( &this->_io_dev      )
483                         , static_cast< png_rw_ptr >   ( &this_t::write_data )
484                         , static_cast< png_flush_ptr >( &this_t::flush      )
485                         );
486     }
487 
488 public:
489 
490     Device _io_dev;
491 
492     image_write_info< png_tag > _info;
493 };
494 
495 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
496 #pragma warning(pop)
497 #endif
498 
499 } // namespace gil
500 } // namespace boost
501 
502 #endif
503