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