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 , ¶ms 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