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 >( ¶ms.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