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_BMP_DETAIL_READ_HPP 9 #define BOOST_GIL_EXTENSION_IO_BMP_DETAIL_READ_HPP 10 11 #include <boost/gil/extension/io/bmp/detail/is_allowed.hpp> 12 #include <boost/gil/extension/io/bmp/detail/reader_backend.hpp> 13 14 #include <boost/gil/io/base.hpp> 15 #include <boost/gil/io/bit_operations.hpp> 16 #include <boost/gil/io/conversion_policies.hpp> 17 #include <boost/gil/io/device.hpp> 18 #include <boost/gil/io/dynamic_io_new.hpp> 19 #include <boost/gil/io/reader_base.hpp> 20 #include <boost/gil/io/row_buffer_helper.hpp> 21 #include <boost/gil/io/typedefs.hpp> 22 23 #include <boost/assert.hpp> 24 25 #include <type_traits> 26 #include <vector> 27 28 namespace boost { namespace gil { 29 30 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 31 #pragma warning(push) 32 #pragma warning(disable:4512) //assignment operator could not be generated 33 #endif 34 35 /// 36 /// BMP Reader 37 /// 38 template< typename Device 39 , typename ConversionPolicy 40 > 41 class reader< Device 42 , bmp_tag 43 , ConversionPolicy 44 > 45 : public reader_base< bmp_tag 46 , ConversionPolicy 47 > 48 , public reader_backend< Device 49 , bmp_tag 50 > 51 { 52 private: 53 54 using this_t = reader<Device, bmp_tag, ConversionPolicy>; 55 using cc_t = typename ConversionPolicy::color_converter_type; 56 57 public: 58 59 using backend_t = reader_backend< Device, bmp_tag>; 60 61 public: 62 63 // 64 // Constructor 65 // reader(const Device & io_dev,const image_read_settings<bmp_tag> & settings)66 reader( const Device& io_dev 67 , const image_read_settings< bmp_tag >& settings 68 ) 69 : backend_t( io_dev 70 , settings 71 ) 72 , _pitch( 0 ) 73 {} 74 75 // 76 // Constructor 77 // reader(const Device & io_dev,const ConversionPolicy & cc,const image_read_settings<bmp_tag> & settings)78 reader( const Device& io_dev 79 , const ConversionPolicy& cc 80 , const image_read_settings< bmp_tag >& settings 81 ) 82 : reader_base< bmp_tag 83 , ConversionPolicy 84 >( cc ) 85 , backend_t( io_dev 86 , settings 87 ) 88 , _pitch( 0 ) 89 {} 90 91 92 /// Read image. 93 template< typename View > apply(const View & dst_view)94 void apply( const View& dst_view ) 95 { 96 if( this->_info._valid == false ) 97 { 98 io_error( "Image header was not read." ); 99 } 100 101 using is_read_and_convert_t = typename std::is_same 102 < 103 ConversionPolicy, 104 detail::read_and_no_convert 105 >::type; 106 107 io_error_if( !detail::is_allowed< View >( this->_info 108 , is_read_and_convert_t() 109 ) 110 , "Image types aren't compatible." 111 ); 112 113 // the row pitch must be multiple 4 bytes 114 if( this->_info._bits_per_pixel < 8 ) 115 { 116 _pitch = static_cast<long>((( this->_info._width * this->_info._bits_per_pixel ) + 7 ) >> 3 ); 117 } 118 else 119 { 120 _pitch = static_cast<long>( this->_info._width * (( this->_info._bits_per_pixel + 7 ) >> 3 )); 121 } 122 123 _pitch = (_pitch + 3) & ~3; 124 125 switch( this->_info._bits_per_pixel ) 126 { 127 case 1: 128 { 129 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; 130 131 read_palette_image 132 < 133 gray1_image_t::view_t, 134 detail::mirror_bits<byte_vector_t, std::true_type> 135 >(dst_view); 136 break; 137 } 138 139 case 4: 140 { 141 switch ( this->_info._compression ) 142 { 143 case bmp_compression::_rle4: 144 { 145 ///@todo How can we determine that? 146 this->_scanline_length = 0; 147 148 read_palette_image_rle( dst_view ); 149 150 break; 151 } 152 153 case bmp_compression::_rgb: 154 { 155 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; 156 157 read_palette_image 158 < 159 gray4_image_t::view_t, 160 detail::swap_half_bytes<byte_vector_t, std::true_type> 161 >(dst_view); 162 break; 163 } 164 165 default: 166 { 167 io_error( "Unsupported compression mode in BMP file." ); 168 break; 169 } 170 } 171 break; 172 } 173 174 case 8: 175 { 176 switch ( this->_info._compression ) 177 { 178 case bmp_compression::_rle8: 179 { 180 ///@todo How can we determine that? 181 this->_scanline_length = 0; 182 183 read_palette_image_rle( dst_view ); 184 break; 185 } 186 187 case bmp_compression::_rgb: 188 { 189 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; 190 191 read_palette_image< gray8_image_t::view_t 192 , detail::do_nothing< std::vector< gray8_pixel_t > > 193 > ( dst_view ); 194 break; 195 } 196 197 default: 198 { 199 io_error( "Unsupported compression mode in BMP file." ); 200 break; 201 } 202 } 203 204 break; 205 } 206 207 case 15: case 16: 208 { 209 this->_scanline_length = ( this->_info._width * num_channels< rgb8_view_t >::value + 3 ) & ~3; 210 211 read_data_15( dst_view ); 212 213 break; 214 } 215 216 case 24: 217 { 218 this->_scanline_length = ( this->_info._width * num_channels< rgb8_view_t >::value + 3 ) & ~3; 219 220 read_data< bgr8_view_t >( dst_view ); 221 222 break; 223 } 224 225 case 32: 226 { 227 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; 228 229 read_data< bgra8_view_t >( dst_view ); 230 231 break; 232 } 233 } 234 } 235 236 private: 237 get_offset(std::ptrdiff_t pos)238 long get_offset( std::ptrdiff_t pos ) 239 { 240 if( this->_info._height > 0 ) 241 { 242 // the image is upside down 243 return static_cast<long>( ( this->_info._offset 244 + ( this->_info._height - 1 - pos ) * _pitch 245 )); 246 } 247 else 248 { 249 return static_cast<long>( ( this->_info._offset 250 + pos * _pitch 251 )); 252 } 253 } 254 255 template< typename View_Src 256 , typename Byte_Manipulator 257 , typename View_Dst 258 > read_palette_image(const View_Dst & view)259 void read_palette_image( const View_Dst& view ) 260 { 261 this->read_palette(); 262 263 using rh_t = detail::row_buffer_helper_view<View_Src>; 264 using it_t = typename rh_t::iterator_t; 265 266 rh_t rh( _pitch, true ); 267 268 // we have to swap bits 269 Byte_Manipulator byte_manipulator; 270 271 for( std::ptrdiff_t y = 0 272 ; y < this->_settings._dim.y 273 ; ++y 274 ) 275 { 276 this->_io_dev.seek( get_offset( y + this->_settings._top_left.y )); 277 278 this->_io_dev.read( reinterpret_cast< byte_t* >( rh.data() ) 279 , _pitch 280 ); 281 282 byte_manipulator( rh.buffer() ); 283 284 typename View_Dst::x_iterator dst_it = view.row_begin( y ); 285 286 it_t it = rh.begin() + this->_settings._top_left.x; 287 it_t end = it + this->_settings._dim.x; 288 289 for( ; it != end; ++it, ++dst_it ) 290 { 291 unsigned char c = get_color( *it, gray_color_t() ); 292 *dst_it = this->_palette[ c ]; 293 } 294 } 295 } 296 297 template< typename View > read_data_15(const View & view)298 void read_data_15( const View& view ) 299 { 300 byte_vector_t row( _pitch ); 301 302 // read the color masks 303 if( this->_info._compression == bmp_compression::_bitfield ) 304 { 305 this->_mask.red.mask = this->_io_dev.read_uint32(); 306 this->_mask.green.mask = this->_io_dev.read_uint32(); 307 this->_mask.blue.mask = this->_io_dev.read_uint32(); 308 309 this->_mask.red.width = detail::count_ones( this->_mask.red.mask ); 310 this->_mask.green.width = detail::count_ones( this->_mask.green.mask ); 311 this->_mask.blue.width = detail::count_ones( this->_mask.blue.mask ); 312 313 this->_mask.red.shift = detail::trailing_zeros( this->_mask.red.mask ); 314 this->_mask.green.shift = detail::trailing_zeros( this->_mask.green.mask ); 315 this->_mask.blue.shift = detail::trailing_zeros( this->_mask.blue.mask ); 316 } 317 else if( this->_info._compression == bmp_compression::_rgb ) 318 { 319 switch( this->_info._bits_per_pixel ) 320 { 321 case 15: 322 case 16: 323 { 324 this->_mask.red.mask = 0x007C00; this->_mask.red.width = 5; this->_mask.red.shift = 10; 325 this->_mask.green.mask = 0x0003E0; this->_mask.green.width = 5; this->_mask.green.shift = 5; 326 this->_mask.blue.mask = 0x00001F; this->_mask.blue.width = 5; this->_mask.blue.shift = 0; 327 328 break; 329 } 330 331 case 24: 332 case 32: 333 { 334 this->_mask.red.mask = 0xFF0000; this->_mask.red.width = 8; this->_mask.red.shift = 16; 335 this->_mask.green.mask = 0x00FF00; this->_mask.green.width = 8; this->_mask.green.shift = 8; 336 this->_mask.blue.mask = 0x0000FF; this->_mask.blue.width = 8; this->_mask.blue.shift = 0; 337 338 break; 339 } 340 } 341 } 342 else 343 { 344 io_error( "bmp_reader::apply(): unsupported BMP compression" ); 345 } 346 347 using image_t = rgb8_image_t; 348 using it_t = typename image_t::view_t::x_iterator; 349 350 for( std::ptrdiff_t y = 0 351 ; y < this->_settings._dim.y 352 ; ++y 353 ) 354 { 355 this->_io_dev.seek( get_offset( y + this->_settings._top_left.y )); 356 357 this->_io_dev.read( &row.front() 358 , row.size() 359 ); 360 361 image_t img_row( this->_info._width, 1 ); 362 image_t::view_t v = gil::view( img_row ); 363 it_t it = v.row_begin( 0 ); 364 365 it_t beg = v.row_begin( 0 ) + this->_settings._top_left.x; 366 it_t end = beg + this->_settings._dim.x; 367 368 byte_t* src = &row.front(); 369 for( int32_t i = 0 ; i < this->_info._width; ++i, src += 2 ) 370 { 371 int p = ( src[1] << 8 ) | src[0]; 372 373 int r = ((p & this->_mask.red.mask) >> this->_mask.red.shift) << (8 - this->_mask.red.width); 374 int g = ((p & this->_mask.green.mask) >> this->_mask.green.shift) << (8 - this->_mask.green.width); 375 int b = ((p & this->_mask.blue.mask) >> this->_mask.blue.shift) << (8 - this->_mask.blue.width); 376 377 get_color( it[i], red_t() ) = static_cast< byte_t >( r ); 378 get_color( it[i], green_t() ) = static_cast< byte_t >( g ); 379 get_color( it[i], blue_t() ) = static_cast< byte_t >( b ); 380 } 381 382 this->_cc_policy.read( beg 383 , end 384 , view.row_begin( y ) 385 ); 386 } 387 } 388 389 390 // 8-8-8 BGR 391 // 8-8-8-8 BGRA 392 template< typename View_Src 393 , typename View_Dst 394 > read_data(const View_Dst & view)395 void read_data( const View_Dst& view ) 396 { 397 byte_vector_t row( _pitch ); 398 399 View_Src v = interleaved_view( this->_info._width 400 , 1 401 , (typename View_Src::value_type*) &row.front() 402 , this->_info._width * num_channels< View_Src >::value 403 ); 404 405 typename View_Src::x_iterator beg = v.row_begin( 0 ) + this->_settings._top_left.x; 406 typename View_Src::x_iterator end = beg + this->_settings._dim.x; 407 408 for( std::ptrdiff_t y = 0 409 ; y < this->_settings._dim.y 410 ; ++y 411 ) 412 { 413 this->_io_dev.seek( get_offset( y + this->_settings._top_left.y )); 414 415 this->_io_dev.read( &row.front() 416 , row.size() 417 ); 418 419 this->_cc_policy.read( beg 420 , end 421 , view.row_begin( y ) 422 ); 423 } 424 } 425 426 template< typename Buffer 427 , typename View 428 > copy_row_if_needed(const Buffer & buf,const View & view,std::ptrdiff_t y)429 void copy_row_if_needed( const Buffer& buf 430 , const View& view 431 , std::ptrdiff_t y 432 ) 433 { 434 if( y >= this->_settings._top_left.y 435 && y < this->_settings._dim.y 436 ) 437 { 438 typename Buffer::const_iterator beg = buf.begin() + this->_settings._top_left.x; 439 typename Buffer::const_iterator end = beg + this->_settings._dim.x; 440 441 std::copy( beg 442 , end 443 , view.row_begin( y ) 444 ); 445 } 446 } 447 448 template< typename View_Dst > read_palette_image_rle(const View_Dst & view)449 void read_palette_image_rle( const View_Dst& view ) 450 { 451 BOOST_ASSERT( 452 this->_info._compression == bmp_compression::_rle4 || 453 this->_info._compression == bmp_compression::_rle8); 454 455 this->read_palette(); 456 457 // jump to start of rle4 data 458 this->_io_dev.seek( this->_info._offset ); 459 460 // we need to know the stream position for padding purposes 461 std::size_t stream_pos = this->_info._offset; 462 463 using Buf_type = std::vector<rgba8_pixel_t>; 464 Buf_type buf( this->_settings._dim.x ); 465 Buf_type::iterator dst_it = buf.begin(); 466 Buf_type::iterator dst_end = buf.end(); 467 468 // If height is positive, the bitmap is a bottom-up DIB. 469 // If height is negative, the bitmap is a top-down DIB. 470 // The origin of a bottom-up DIB is the bottom left corner of the bitmap image, 471 // which is the first pixel of the first row of bitmap data. 472 // The origin of a top-down DIB is also the bottom left corner of the bitmap image, 473 // but in this case the bottom left corner is the first pixel of the last row of bitmap data. 474 // - "Programming Windows", 5th Ed. by Charles Petzold explains Windows docs ambiguities. 475 std::ptrdiff_t ybeg = 0; 476 std::ptrdiff_t yend = this->_settings._dim.y; 477 std::ptrdiff_t yinc = 1; 478 if( this->_info._height > 0 ) 479 { 480 ybeg = this->_settings._dim.y - 1; 481 yend = -1; 482 yinc = -1; 483 } 484 485 std::ptrdiff_t y = ybeg; 486 bool finished = false; 487 488 while ( !finished ) 489 { 490 std::ptrdiff_t count = this->_io_dev.read_uint8(); 491 std::ptrdiff_t second = this->_io_dev.read_uint8(); 492 stream_pos += 2; 493 494 if ( count ) 495 { 496 // encoded mode 497 498 // clamp to boundary 499 if( count > dst_end - dst_it ) 500 { 501 count = dst_end - dst_it; 502 } 503 504 if( this->_info._compression == bmp_compression::_rle4 ) 505 { 506 std::ptrdiff_t cs[2] = { second >> 4, second & 0x0f }; 507 508 for( int i = 0; i < count; ++i ) 509 { 510 *dst_it++ = this->_palette[ cs[i & 1] ]; 511 } 512 } 513 else 514 { 515 for( int i = 0; i < count; ++i ) 516 { 517 *dst_it++ = this->_palette[ second ]; 518 } 519 } 520 } 521 else 522 { 523 switch( second ) 524 { 525 case 0: // end of row 526 { 527 copy_row_if_needed( buf, view, y ); 528 529 y += yinc; 530 if( y == yend ) 531 { 532 finished = true; 533 } 534 else 535 { 536 dst_it = buf.begin(); 537 dst_end = buf.end(); 538 } 539 540 break; 541 } 542 543 case 1: // end of bitmap 544 { 545 copy_row_if_needed( buf, view, y ); 546 finished = true; 547 548 break; 549 } 550 551 case 2: // offset coordinates 552 { 553 std::ptrdiff_t dx = this->_io_dev.read_uint8(); 554 std::ptrdiff_t dy = this->_io_dev.read_uint8() * yinc; 555 stream_pos += 2; 556 557 if( dy ) 558 { 559 copy_row_if_needed( buf, view, y ); 560 } 561 562 std::ptrdiff_t x = dst_it - buf.begin(); 563 x += dx; 564 565 if( x > this->_info._width ) 566 { 567 io_error( "Mangled BMP file." ); 568 } 569 570 y += dy; 571 if( yinc > 0 ? y > yend : y < yend ) 572 { 573 io_error( "Mangled BMP file." ); 574 } 575 576 dst_it = buf.begin() + x; 577 dst_end = buf.end(); 578 579 break; 580 } 581 582 default: // absolute mode 583 { 584 count = second; 585 586 // clamp to boundary 587 if( count > dst_end - dst_it ) 588 { 589 count = dst_end - dst_it; 590 } 591 592 if ( this->_info._compression == bmp_compression::_rle4 ) 593 { 594 for( int i = 0; i < count; ++i ) 595 { 596 uint8_t packed_indices = this->_io_dev.read_uint8(); 597 ++stream_pos; 598 599 *dst_it++ = this->_palette[ packed_indices >> 4 ]; 600 if( ++i == second ) 601 break; 602 603 *dst_it++ = this->_palette[ packed_indices & 0x0f ]; 604 } 605 } 606 else 607 { 608 for( int i = 0; i < count; ++i ) 609 { 610 uint8_t c = this->_io_dev.read_uint8(); 611 ++stream_pos; 612 *dst_it++ = this->_palette[ c ]; 613 } 614 } 615 616 // pad to word boundary 617 if( ( stream_pos - get_offset( 0 )) & 1 ) 618 { 619 this->_io_dev.seek( 1, SEEK_CUR ); 620 ++stream_pos; 621 } 622 623 break; 624 } 625 } 626 } 627 } 628 } 629 630 private: 631 632 std::size_t _pitch; 633 }; 634 635 namespace detail { 636 637 class bmp_type_format_checker 638 { 639 public: 640 bmp_type_format_checker(const bmp_bits_per_pixel::type & bpp)641 bmp_type_format_checker( const bmp_bits_per_pixel::type& bpp ) 642 : _bpp( bpp ) 643 {} 644 645 template< typename Image > apply()646 bool apply() 647 { 648 if( _bpp < 32 ) 649 { 650 return pixels_are_compatible< typename Image::value_type, rgb8_pixel_t >::value 651 ? true 652 : false; 653 } 654 else 655 { 656 return pixels_are_compatible< typename Image::value_type, rgba8_pixel_t >::value 657 ? true 658 : false; 659 } 660 } 661 662 private: 663 664 // to avoid C4512 operator =(const bmp_type_format_checker &)665 bmp_type_format_checker& operator=( const bmp_type_format_checker& ) { return *this; } 666 667 private: 668 669 const bmp_bits_per_pixel::type _bpp; 670 }; 671 672 struct bmp_read_is_supported 673 { 674 template< typename View > 675 struct apply : public is_read_supported< typename get_pixel_type< View >::type 676 , bmp_tag 677 > 678 {}; 679 }; 680 681 } // namespace detail 682 683 /// 684 /// BMP Dynamic Reader 685 /// 686 template< typename Device > 687 class dynamic_image_reader< Device 688 , bmp_tag 689 > 690 : public reader< Device 691 , bmp_tag 692 , detail::read_and_no_convert 693 > 694 { 695 using parent_t = reader<Device, bmp_tag, detail::read_and_no_convert>; 696 697 public: 698 dynamic_image_reader(const Device & io_dev,const image_read_settings<bmp_tag> & settings)699 dynamic_image_reader( const Device& io_dev 700 , const image_read_settings< bmp_tag >& settings 701 ) 702 : parent_t( io_dev 703 , settings 704 ) 705 {} 706 707 template< typename ...Images > apply(any_image<Images...> & images)708 void apply( any_image< Images... >& images ) 709 { 710 detail::bmp_type_format_checker format_checker( this->_info._bits_per_pixel ); 711 712 if( !construct_matched( images 713 , format_checker 714 )) 715 { 716 io_error( "No matching image type between those of the given any_image and that of the file" ); 717 } 718 else 719 { 720 this->init_image( images 721 , this->_settings 722 ); 723 724 detail::dynamic_io_fnobj< detail::bmp_read_is_supported 725 , parent_t 726 > op( this ); 727 728 apply_operation( view( images ) 729 , op 730 ); 731 } 732 } 733 }; 734 735 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 736 #pragma warning(pop) 737 #endif 738 739 } // gil 740 } // boost 741 742 #endif 743