1 // 2 // Copyright 2007-2012 Christian Henning, Andreas Pokorny 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_IO_DEVICE_HPP 9 #define BOOST_GIL_IO_DEVICE_HPP 10 11 #include <boost/gil/detail/mp11.hpp> 12 #include <boost/gil/io/base.hpp> 13 14 #include <cstdio> 15 #include <memory> 16 #include <type_traits> 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 #endif 24 25 namespace detail { 26 27 template < typename T > struct buff_item 28 { 29 static const unsigned int size = sizeof( T ); 30 }; 31 32 template <> struct buff_item< void > 33 { 34 static const unsigned int size = 1; 35 }; 36 37 /*! 38 * Implements the IODevice concept c.f. to \ref IODevice required by Image libraries like 39 * libjpeg and libpng. 40 * 41 * \todo switch to a sane interface as soon as there is 42 * something good in boost. I.E. the IOChains library 43 * would fit very well here. 44 * 45 * This implementation is based on FILE*. 46 */ 47 template< typename FormatTag > 48 class file_stream_device 49 { 50 public: 51 52 using format_tag_t = FormatTag; 53 54 public: 55 56 /// Used to overload the constructor. 57 struct read_tag {}; 58 struct write_tag {}; 59 60 /// 61 /// Constructor 62 /// file_stream_device(const std::string & file_name,read_tag tag=read_tag ())63 file_stream_device( const std::string& file_name 64 , read_tag tag = read_tag() 65 ) 66 : file_stream_device(file_name.c_str(), tag) 67 {} 68 69 /// 70 /// Constructor 71 /// file_stream_device(const char * file_name,read_tag=read_tag ())72 file_stream_device( const char* file_name 73 , read_tag = read_tag() 74 ) 75 { 76 FILE* file = nullptr; 77 78 io_error_if( ( file = fopen( file_name, "rb" )) == nullptr 79 , "file_stream_device: failed to open file for reading" 80 ); 81 82 _file = file_ptr_t( file 83 , file_deleter 84 ); 85 } 86 87 /// 88 /// Constructor 89 /// file_stream_device(const std::string & file_name,write_tag tag)90 file_stream_device( const std::string& file_name 91 , write_tag tag 92 ) 93 : file_stream_device(file_name.c_str(), tag) 94 {} 95 96 /// 97 /// Constructor 98 /// file_stream_device(const char * file_name,write_tag)99 file_stream_device( const char* file_name 100 , write_tag 101 ) 102 { 103 FILE* file = nullptr; 104 105 io_error_if( ( file = fopen( file_name, "wb" )) == nullptr 106 , "file_stream_device: failed to open file for writing" 107 ); 108 109 _file = file_ptr_t( file 110 , file_deleter 111 ); 112 } 113 114 /// 115 /// Constructor 116 /// file_stream_device(FILE * file)117 file_stream_device( FILE* file ) 118 : _file( file 119 , file_deleter 120 ) 121 {} 122 get()123 FILE* get() { return _file.get(); } get() const124 const FILE* get() const { return _file.get(); } 125 getc_unchecked()126 int getc_unchecked() 127 { 128 return std::getc( get() ); 129 } 130 getc()131 char getc() 132 { 133 int ch; 134 135 io_error_if( ( ch = std::getc( get() )) == EOF 136 , "file_stream_device: unexpected EOF" 137 ); 138 139 return ( char ) ch; 140 } 141 142 ///@todo: change byte_t* to void* read(byte_t * data,std::size_t count)143 std::size_t read( byte_t* data 144 , std::size_t count 145 ) 146 { 147 std::size_t num_elements = fread( data 148 , 1 149 , static_cast<int>( count ) 150 , get() 151 ); 152 153 ///@todo: add compiler symbol to turn error checking on and off. 154 io_error_if( ferror( get() ) 155 , "file_stream_device: file read error" 156 ); 157 158 //libjpeg sometimes reads blocks in 4096 bytes even when the file is smaller than that. 159 //return value indicates how much was actually read 160 //returning less than "count" is not an error 161 return num_elements; 162 } 163 164 /// Reads array 165 template< typename T 166 , int N 167 > read(T (& buf)[N])168 void read( T (&buf)[N] ) 169 { 170 io_error_if( read( buf, N ) < N 171 , "file_stream_device: file read error" 172 ); 173 } 174 175 /// Reads byte read_uint8()176 uint8_t read_uint8() 177 { 178 byte_t m[1]; 179 180 read( m ); 181 return m[0]; 182 } 183 184 /// Reads 16 bit little endian integer read_uint16()185 uint16_t read_uint16() 186 { 187 byte_t m[2]; 188 189 read( m ); 190 return (m[1] << 8) | m[0]; 191 } 192 193 /// Reads 32 bit little endian integer read_uint32()194 uint32_t read_uint32() 195 { 196 byte_t m[4]; 197 198 read( m ); 199 return (m[3] << 24) | (m[2] << 16) | (m[1] << 8) | m[0]; 200 } 201 202 /// Writes number of elements from a buffer 203 template < typename T > write(const T * buf,std::size_t count)204 std::size_t write( const T* buf 205 , std::size_t count 206 ) 207 { 208 std::size_t num_elements = fwrite( buf 209 , buff_item<T>::size 210 , count 211 , get() 212 ); 213 214 //return value indicates how much was actually written 215 //returning less than "count" is not an error 216 return num_elements; 217 } 218 219 /// Writes array 220 template < typename T 221 , std::size_t N 222 > write(const T (& buf)[N])223 void write( const T (&buf)[N] ) 224 { 225 io_error_if( write( buf, N ) < N 226 , "file_stream_device: file write error" 227 ); 228 return ; 229 } 230 231 /// Writes byte write_uint8(uint8_t x)232 void write_uint8( uint8_t x ) 233 { 234 byte_t m[1] = { x }; 235 write(m); 236 } 237 238 /// Writes 16 bit little endian integer write_uint16(uint16_t x)239 void write_uint16( uint16_t x ) 240 { 241 byte_t m[2]; 242 243 m[0] = byte_t( x >> 0 ); 244 m[1] = byte_t( x >> 8 ); 245 246 write( m ); 247 } 248 249 /// Writes 32 bit little endian integer write_uint32(uint32_t x)250 void write_uint32( uint32_t x ) 251 { 252 byte_t m[4]; 253 254 m[0] = byte_t( x >> 0 ); 255 m[1] = byte_t( x >> 8 ); 256 m[2] = byte_t( x >> 16 ); 257 m[3] = byte_t( x >> 24 ); 258 259 write( m ); 260 } 261 seek(long count,int whence=SEEK_SET)262 void seek( long count, int whence = SEEK_SET ) 263 { 264 io_error_if( fseek( get() 265 , count 266 , whence 267 ) != 0 268 , "file_stream_device: file seek error" 269 ); 270 } 271 tell()272 long int tell() 273 { 274 long int pos = ftell( get() ); 275 276 io_error_if( pos == -1L 277 , "file_stream_device: file position error" 278 ); 279 280 return pos; 281 } 282 flush()283 void flush() 284 { 285 fflush( get() ); 286 } 287 288 /// Prints formatted ASCII text print_line(const std::string & line)289 void print_line( const std::string& line ) 290 { 291 std::size_t num_elements = fwrite( line.c_str() 292 , sizeof( char ) 293 , line.size() 294 , get() 295 ); 296 297 io_error_if( num_elements < line.size() 298 , "file_stream_device: line print error" 299 ); 300 } 301 error()302 int error() 303 { 304 return ferror( get() ); 305 } 306 307 private: 308 file_deleter(FILE * file)309 static void file_deleter( FILE* file ) 310 { 311 if( file ) 312 { 313 fclose( file ); 314 } 315 } 316 317 private: 318 319 using file_ptr_t = std::shared_ptr<FILE> ; 320 file_ptr_t _file; 321 }; 322 323 /** 324 * Input stream device 325 */ 326 template< typename FormatTag > 327 class istream_device 328 { 329 public: istream_device(std::istream & in)330 istream_device( std::istream& in ) 331 : _in( in ) 332 { 333 // does the file exists? 334 io_error_if( !in 335 , "istream_device: Stream is not valid." 336 ); 337 } 338 getc_unchecked()339 int getc_unchecked() 340 { 341 return _in.get(); 342 } 343 getc()344 char getc() 345 { 346 int ch; 347 348 io_error_if( ( ch = _in.get() ) == EOF 349 , "istream_device: unexpected EOF" 350 ); 351 352 return ( char ) ch; 353 } 354 read(byte_t * data,std::size_t count)355 std::size_t read( byte_t* data 356 , std::size_t count ) 357 { 358 std::streamsize cr = 0; 359 360 do 361 { 362 _in.peek(); 363 std::streamsize c = _in.readsome( reinterpret_cast< char* >( data ) 364 , static_cast< std::streamsize >( count )); 365 366 count -= static_cast< std::size_t >( c ); 367 data += c; 368 cr += c; 369 370 } while( count && _in ); 371 372 return static_cast< std::size_t >( cr ); 373 } 374 375 /// Reads array 376 template<typename T, int N> read(T (& buf)[N])377 void read(T (&buf)[N]) 378 { 379 read(buf, N); 380 } 381 382 /// Reads byte read_uint8()383 uint8_t read_uint8() 384 { 385 byte_t m[1]; 386 387 read( m ); 388 return m[0]; 389 } 390 391 /// Reads 16 bit little endian integer read_uint16()392 uint16_t read_uint16() 393 { 394 byte_t m[2]; 395 396 read( m ); 397 return (m[1] << 8) | m[0]; 398 } 399 400 /// Reads 32 bit little endian integer read_uint32()401 uint32_t read_uint32() 402 { 403 byte_t m[4]; 404 405 read( m ); 406 return (m[3] << 24) | (m[2] << 16) | (m[1] << 8) | m[0]; 407 } 408 seek(long count,int whence=SEEK_SET)409 void seek( long count, int whence = SEEK_SET ) 410 { 411 _in.seekg( count 412 , whence == SEEK_SET ? std::ios::beg 413 :( whence == SEEK_CUR ? std::ios::cur 414 : std::ios::end ) 415 ); 416 } 417 write(const byte_t *,std::size_t)418 void write(const byte_t*, std::size_t) 419 { 420 io_error( "istream_device: Bad io error." ); 421 } 422 flush()423 void flush() {} 424 425 private: 426 427 std::istream& _in; 428 }; 429 430 /** 431 * Output stream device 432 */ 433 template< typename FormatTag > 434 class ostream_device 435 { 436 public: ostream_device(std::ostream & out)437 ostream_device( std::ostream & out ) 438 : _out( out ) 439 { 440 } 441 read(byte_t *,std::size_t)442 std::size_t read(byte_t *, std::size_t) 443 { 444 io_error( "ostream_device: Bad io error." ); 445 return 0; 446 } 447 seek(long count,int whence)448 void seek( long count, int whence ) 449 { 450 _out.seekp( count 451 , whence == SEEK_SET 452 ? std::ios::beg 453 : ( whence == SEEK_CUR 454 ?std::ios::cur 455 :std::ios::end ) 456 ); 457 } 458 write(const byte_t * data,std::size_t count)459 void write( const byte_t* data 460 , std::size_t count ) 461 { 462 _out.write( reinterpret_cast<char const*>( data ) 463 , static_cast<std::streamsize>( count ) 464 ); 465 } 466 467 /// Writes array 468 template < typename T 469 , std::size_t N 470 > write(const T (& buf)[N])471 void write( const T (&buf)[N] ) 472 { 473 write( buf, N ); 474 } 475 476 /// Writes byte write_uint8(uint8_t x)477 void write_uint8( uint8_t x ) 478 { 479 byte_t m[1] = { x }; 480 write(m); 481 } 482 483 /// Writes 16 bit little endian integer write_uint16(uint16_t x)484 void write_uint16( uint16_t x ) 485 { 486 byte_t m[2]; 487 488 m[0] = byte_t( x >> 0 ); 489 m[1] = byte_t( x >> 8 ); 490 491 write( m ); 492 } 493 494 /// Writes 32 bit little endian integer write_uint32(uint32_t x)495 void write_uint32( uint32_t x ) 496 { 497 byte_t m[4]; 498 499 m[0] = byte_t( x >> 0 ); 500 m[1] = byte_t( x >> 8 ); 501 m[2] = byte_t( x >> 16 ); 502 m[3] = byte_t( x >> 24 ); 503 504 write( m ); 505 } 506 flush()507 void flush() 508 { 509 _out << std::flush; 510 } 511 512 /// Prints formatted ASCII text print_line(const std::string & line)513 void print_line( const std::string& line ) 514 { 515 _out << line; 516 } 517 518 519 520 private: 521 522 std::ostream& _out; 523 }; 524 525 526 /** 527 * Metafunction to detect input devices. 528 * Should be replaced by an external facility in the future. 529 */ 530 template< typename IODevice > struct is_input_device : std::false_type{}; 531 template< typename FormatTag > struct is_input_device< file_stream_device< FormatTag > > : std::true_type{}; 532 template< typename FormatTag > struct is_input_device< istream_device< FormatTag > > : std::true_type{}; 533 534 template< typename FormatTag 535 , typename T 536 , typename D = void 537 > 538 struct is_adaptable_input_device : std::false_type{}; 539 540 template <typename FormatTag, typename T> 541 struct is_adaptable_input_device 542 < 543 FormatTag, 544 T, 545 typename std::enable_if 546 < 547 mp11::mp_or 548 < 549 std::is_base_of<std::istream, T>, 550 std::is_same<std::istream, T> 551 >::value 552 >::type 553 > : std::true_type 554 { 555 using device_type = istream_device<FormatTag>; 556 }; 557 558 template< typename FormatTag > 559 struct is_adaptable_input_device< FormatTag 560 , FILE* 561 , void 562 > 563 : std::true_type 564 { 565 using device_type = file_stream_device<FormatTag>; 566 }; 567 568 /// 569 /// Metafunction to decide if a given type is an acceptable read device type. 570 /// 571 template< typename FormatTag 572 , typename T 573 , typename D = void 574 > 575 struct is_read_device : std::false_type 576 {}; 577 578 template <typename FormatTag, typename T> 579 struct is_read_device 580 < 581 FormatTag, 582 T, 583 typename std::enable_if 584 < 585 mp11::mp_or 586 < 587 is_input_device<FormatTag>, 588 is_adaptable_input_device<FormatTag, T> 589 >::value 590 >::type 591 > : std::true_type 592 { 593 }; 594 595 596 /** 597 * Metafunction to detect output devices. 598 * Should be replaced by an external facility in the future. 599 */ 600 template<typename IODevice> struct is_output_device : std::false_type{}; 601 602 template< typename FormatTag > struct is_output_device< file_stream_device< FormatTag > > : std::true_type{}; 603 template< typename FormatTag > struct is_output_device< ostream_device < FormatTag > > : std::true_type{}; 604 605 template< typename FormatTag 606 , typename IODevice 607 , typename D = void 608 > 609 struct is_adaptable_output_device : std::false_type {}; 610 611 template <typename FormatTag, typename T> 612 struct is_adaptable_output_device 613 < 614 FormatTag, 615 T, 616 typename std::enable_if 617 < 618 mp11::mp_or 619 < 620 std::is_base_of<std::ostream, T>, 621 std::is_same<std::ostream, T> 622 >::value 623 >::type 624 > : std::true_type 625 { 626 using device_type = ostream_device<FormatTag>; 627 }; 628 629 template<typename FormatTag> struct is_adaptable_output_device<FormatTag,FILE*,void> 630 : std::true_type 631 { 632 using device_type = file_stream_device<FormatTag>; 633 }; 634 635 636 /// 637 /// Metafunction to decide if a given type is an acceptable read device type. 638 /// 639 template< typename FormatTag 640 , typename T 641 , typename D = void 642 > 643 struct is_write_device : std::false_type 644 {}; 645 646 template <typename FormatTag, typename T> 647 struct is_write_device 648 < 649 FormatTag, 650 T, 651 typename std::enable_if 652 < 653 mp11::mp_or 654 < 655 is_output_device<FormatTag>, 656 is_adaptable_output_device<FormatTag, T> 657 >::value 658 >::type 659 > : std::true_type 660 { 661 }; 662 663 } // namespace detail 664 665 template< typename Device, typename FormatTag > class scanline_reader; 666 template< typename Device, typename FormatTag, typename ConversionPolicy > class reader; 667 668 template< typename Device, typename FormatTag, typename Log = no_log > class writer; 669 670 template< typename Device, typename FormatTag > class dynamic_image_reader; 671 template< typename Device, typename FormatTag, typename Log = no_log > class dynamic_image_writer; 672 673 674 namespace detail { 675 676 template< typename T > 677 struct is_reader : std::false_type 678 {}; 679 680 template< typename Device 681 , typename FormatTag 682 , typename ConversionPolicy 683 > 684 struct is_reader< reader< Device 685 , FormatTag 686 , ConversionPolicy 687 > 688 > : std::true_type 689 {}; 690 691 template< typename T > 692 struct is_dynamic_image_reader : std::false_type 693 {}; 694 695 template< typename Device 696 , typename FormatTag 697 > 698 struct is_dynamic_image_reader< dynamic_image_reader< Device 699 , FormatTag 700 > 701 > : std::true_type 702 {}; 703 704 template< typename T > 705 struct is_writer : std::false_type 706 {}; 707 708 template< typename Device 709 , typename FormatTag 710 > 711 struct is_writer< writer< Device 712 , FormatTag 713 > 714 > : std::true_type 715 {}; 716 717 template< typename T > 718 struct is_dynamic_image_writer : std::false_type 719 {}; 720 721 template< typename Device 722 , typename FormatTag 723 > 724 struct is_dynamic_image_writer< dynamic_image_writer< Device 725 , FormatTag 726 > 727 > : std::true_type 728 {}; 729 730 } // namespace detail 731 732 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 733 #pragma warning(pop) 734 #endif 735 736 } // namespace gil 737 } // namespace boost 738 739 #endif 740