1 //
2 // Copyright 2007-2008 Andreas Pokorny, 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_TIFF_DETAIL_DEVICE_HPP
9 #define BOOST_GIL_EXTENSION_IO_TIFF_DETAIL_DEVICE_HPP
10 
11 #include <boost/gil/extension/io/tiff/tags.hpp>
12 #include <boost/gil/extension/io/tiff/detail/log.hpp>
13 #include <boost/gil/detail/mp11.hpp>
14 #include <boost/gil/io/base.hpp>
15 #include <boost/gil/io/device.hpp>
16 
17 #include <algorithm>
18 #include <memory>
19 #include <sstream>
20 #include <type_traits>
21 
22 // taken from jpegxx - https://bitbucket.org/edd/jpegxx/src/ea2492a1a4a6/src/ijg_headers.hpp
23 #ifndef BOOST_GIL_EXTENSION_IO_TIFF_C_LIB_COMPILED_AS_CPLUSPLUS
24     extern "C" {
25 #endif
26 
27 #include <tiff.h>
28 #include <tiffio.h>
29 
30 #ifndef BOOST_GIL_EXTENSION_IO_TIFF_C_LIB_COMPILED_AS_CPLUSPLUS
31     }
32 #endif
33 
34 #include <tiffio.hxx>
35 
36 namespace boost { namespace gil { namespace detail {
37 
38 template <int n_args>
39 struct get_property_f {
40 	template <typename Property>
41 	bool call_me(typename Property:: type& value, std::shared_ptr<TIFF>& file);
42 };
43 
44 template <int n_args>
45 struct set_property_f {
46 	template <typename Property>
47 	bool call_me(const typename Property:: type& value, std::shared_ptr<TIFF>& file) const;
48 };
49 
50 template <> struct get_property_f <1>
51 {
52 	// For single-valued properties
53 	template <typename Property>
call_meboost::gil::detail::get_property_f54 	bool call_me(typename Property::type & value, std::shared_ptr<TIFF>& file) const
55 	{
56 		// @todo: defaulted, really?
57 		return (1 == TIFFGetFieldDefaulted( file.get()
58 				, Property:: tag
59 				, & value));
60 	}
61 };
62 
63 template <> struct get_property_f <2>
64 {
65 	// Specialisation for multi-valued properties. @todo: add one of
66 	// these for the three-parameter fields too.
67 	template <typename Property>
call_meboost::gil::detail::get_property_f68 	bool call_me(typename Property::type& vs, std::shared_ptr<TIFF>& file) const
69 	{
70         mp11::mp_at<typename Property::arg_types, std::integral_constant<int, 0>> length;
71         mp11::mp_at<typename Property::arg_types, std::integral_constant<int, 1>> pointer;
72 		if (1 == TIFFGetFieldDefaulted(file.get(), Property:: tag, & length, & pointer))
73         {
74 			std:: copy_n(static_cast<typename Property::type::const_pointer>(pointer), length, std:: back_inserter(vs));
75 			return true;
76 		} else
77 			return false;
78 	}
79 };
80 
81 template <> struct set_property_f <1>
82 {
83 	// For single-valued properties
84 	template <typename Property>
85 	inline
call_meboost::gil::detail::set_property_f86 	bool call_me(typename Property:: type const & value, std::shared_ptr<TIFF>& file) const
87 	{
88 		return (1 == TIFFSetField( file.get()
89 				, Property:: tag
90 				, value));
91 	}
92 };
93 
94 template <> struct set_property_f <2>
95 {
96 	// Specialisation for multi-valued properties. @todo: add one
97 	// of these for the three-parameter fields too. Actually we
98 	// will need further templation / specialisation for the
99 	// two-element fields which aren't a length and a data buffer
100 	// (e.g. http://www.awaresystems.be/imaging/tiff/tifftags/dotrange.html
101 	// )
102 	template <typename Property>
103 	inline
call_meboost::gil::detail::set_property_f104 	bool call_me(typename Property:: type const & values, std::shared_ptr<TIFF>& file) const
105 	{
106         using length_t = mp11::mp_at_c<typename Property::arg_types, 0>;
107 		auto const length = static_cast<length_t>(values.size());
108 
109         using pointer_t = mp11::mp_at_c<typename Property::arg_types, 1>;
110 		auto const pointer = static_cast<pointer_t>(&(values.front()));
111 		return (1 == TIFFSetField( file.get(), Property:: tag, length, pointer));
112 	}
113 };
114 
115 template< typename Log >
116 class tiff_device_base
117 {
118 public:
119     using tiff_file_t = std::shared_ptr<TIFF>;
120 
tiff_device_base()121     tiff_device_base()
122     {}
123 
tiff_device_base(TIFF * tiff_file)124     tiff_device_base( TIFF* tiff_file )
125     : _tiff_file( tiff_file
126                 , TIFFClose )
127     {}
128 
129 	template <typename Property>
get_property(typename Property::type & value)130     bool get_property( typename Property::type& value  )
131     {
132 		return get_property_f<mp11::mp_size<typename Property::arg_types>::value>().template call_me<Property>(value, _tiff_file);
133 	}
134 
135     template <typename Property>
136     inline
set_property(const typename Property::type & value)137     bool set_property( const typename Property::type& value )
138     {
139       // http://www.remotesensing.org/libtiff/man/TIFFSetField.3tiff.html
140       return set_property_f<mp11::mp_size<typename Property::arg_types>::value>().template call_me<Property>(value, _tiff_file);
141     }
142 
143     // TIFFIsByteSwapped returns a non-zero value if the image data was in a different
144     // byte-order than the host machine. Zero is returned if the TIFF file and local
145     // host byte-orders are the same. Note that TIFFReadTile(), TIFFReadStrip() and TIFFReadScanline()
146     // functions already normally perform byte swapping to local host order if needed.
are_bytes_swapped()147     bool are_bytes_swapped()
148     {
149         return ( TIFFIsByteSwapped( _tiff_file.get() )) ? true : false;
150     }
151 
is_tiled() const152     bool is_tiled() const
153     {
154         return ( TIFFIsTiled( _tiff_file.get() )) ? true : false;
155     }
156 
get_default_strip_size()157     unsigned int get_default_strip_size()
158     {
159         return TIFFDefaultStripSize( _tiff_file.get()
160                                    , 0 );
161     }
162 
get_scanline_size()163     std::size_t get_scanline_size()
164     {
165       return TIFFScanlineSize( _tiff_file.get() );
166     }
167 
get_tile_size()168     std::size_t get_tile_size()
169     {
170       return TIFFTileSize( _tiff_file.get() );
171     }
172 
173 
get_field_defaulted(uint16_t * & red,uint16_t * & green,uint16_t * & blue)174     int get_field_defaulted( uint16_t*& red
175                            , uint16_t*& green
176                            , uint16_t*& blue
177                            )
178     {
179         return TIFFGetFieldDefaulted( _tiff_file.get()
180                                     , TIFFTAG_COLORMAP
181                                     , &red
182                                     , &green
183                                     , &blue
184                                     );
185     }
186 
187     template< typename Buffer >
read_scanline(Buffer & buffer,std::ptrdiff_t row,tsample_t plane)188     void read_scanline( Buffer&        buffer
189                       , std::ptrdiff_t row
190                       , tsample_t      plane
191                       )
192     {
193         io_error_if( TIFFReadScanline( _tiff_file.get()
194                                      , reinterpret_cast< tdata_t >( &buffer.front() )
195                                      , (uint32) row
196                                      , plane           ) == -1
197                    , "Read error."
198                    );
199     }
200 
read_scanline(byte_t * buffer,std::ptrdiff_t row,tsample_t plane)201     void read_scanline( byte_t*        buffer
202                       , std::ptrdiff_t row
203                       , tsample_t      plane
204                       )
205     {
206         io_error_if( TIFFReadScanline( _tiff_file.get()
207                                      , reinterpret_cast< tdata_t >( buffer )
208                                      , (uint32) row
209                                      , plane           ) == -1
210                    , "Read error."
211                    );
212     }
213 
214     template< typename Buffer >
read_tile(Buffer & buffer,std::ptrdiff_t x,std::ptrdiff_t y,std::ptrdiff_t z,tsample_t plane)215     void read_tile( Buffer&        buffer
216                   , std::ptrdiff_t x
217                   , std::ptrdiff_t y
218                   , std::ptrdiff_t z
219                   , tsample_t      plane
220                   )
221     {
222         if( TIFFReadTile( _tiff_file.get()
223                         , reinterpret_cast< tdata_t >( &buffer.front() )
224                         , (uint32) x
225                         , (uint32) y
226                         , (uint32) z
227                         , plane
228                         ) == -1 )
229         {
230             std::ostringstream oss;
231             oss << "Read tile error (" << x << "," << y << "," << z << "," << plane << ").";
232             io_error(oss.str().c_str());
233         }
234     }
235 
236     template< typename Buffer >
write_scaline(Buffer & buffer,uint32 row,tsample_t plane)237     void write_scaline( Buffer&     buffer
238                       , uint32      row
239                       , tsample_t   plane
240                       )
241     {
242        io_error_if( TIFFWriteScanline( _tiff_file.get()
243                                      , &buffer.front()
244                                      , row
245                                      , plane
246                                      ) == -1
247                    , "Write error"
248                    );
249     }
250 
write_scaline(byte_t * buffer,uint32 row,tsample_t plane)251     void write_scaline( byte_t*     buffer
252                       , uint32      row
253                       , tsample_t   plane
254                       )
255     {
256        io_error_if( TIFFWriteScanline( _tiff_file.get()
257                                      , buffer
258                                      , row
259                                      , plane
260                                      ) == -1
261                    , "Write error"
262                    );
263     }
264 
265     template< typename Buffer >
write_tile(Buffer & buffer,uint32 x,uint32 y,uint32 z,tsample_t plane)266     void write_tile( Buffer&     buffer
267                    , uint32      x
268                    , uint32      y
269                    , uint32      z
270                    , tsample_t   plane
271                    )
272     {
273        if( TIFFWriteTile( _tiff_file.get()
274                         , &buffer.front()
275                         , x
276                         , y
277                         , z
278                         , plane
279                         ) == -1 )
280            {
281                std::ostringstream oss;
282                oss << "Write tile error (" << x << "," << y << "," << z << "," << plane << ").";
283                io_error(oss.str().c_str());
284            }
285     }
286 
set_directory(tdir_t directory)287     void set_directory( tdir_t directory )
288     {
289         io_error_if( TIFFSetDirectory( _tiff_file.get()
290                                      , directory
291                                      ) != 1
292                    , "Failing to set directory"
293                    );
294     }
295 
296     // return false if the given tile width or height is not TIFF compliant (multiple of 16) or larger than image size, true otherwise
check_tile_size(tiff_tile_width::type & width,tiff_tile_length::type & height)297     bool check_tile_size( tiff_tile_width::type&  width
298                         , tiff_tile_length::type& height
299 
300                         )
301     {
302         bool result = true;
303         uint32 tw = static_cast< uint32 >( width  );
304         uint32 th = static_cast< uint32 >( height );
305 
306         TIFFDefaultTileSize( _tiff_file.get()
307                            , &tw
308                            , &th
309                            );
310 
311         if(width==0 || width%16!=0)
312         {
313             width = tw;
314             result = false;
315         }
316         if(height==0 || height%16!=0)
317         {
318             height = th;
319             result = false;
320         }
321         return result;
322     }
323 
324 protected:
325 
326    tiff_file_t _tiff_file;
327 
328     Log _log;
329 };
330 
331 /*!
332  *
333  * file_stream_device specialization for tiff images, which are based on TIFF*.
334  */
335 template<>
336 class file_stream_device< tiff_tag > : public tiff_device_base< tiff_no_log >
337 {
338 public:
339 
340     struct read_tag {};
341     struct write_tag {};
342 
file_stream_device(std::string const & file_name,read_tag)343     file_stream_device( std::string const& file_name, read_tag )
344     {
345         TIFF* tiff;
346 
347         io_error_if( ( tiff = TIFFOpen( file_name.c_str(), "r" )) == nullptr
348                    , "file_stream_device: failed to open file" );
349 
350         _tiff_file = tiff_file_t( tiff, TIFFClose );
351     }
352 
file_stream_device(std::string const & file_name,write_tag)353     file_stream_device( std::string const& file_name, write_tag )
354     {
355         TIFF* tiff;
356 
357         io_error_if( ( tiff = TIFFOpen( file_name.c_str(), "w" )) == nullptr
358                    , "file_stream_device: failed to open file" );
359 
360         _tiff_file = tiff_file_t( tiff, TIFFClose );
361     }
362 
file_stream_device(TIFF * tiff_file)363     file_stream_device( TIFF* tiff_file )
364     : tiff_device_base( tiff_file )
365     {}
366 };
367 
368 /*!
369  *
370  * ostream_device specialization for tiff images.
371  */
372 template<>
373 class ostream_device< tiff_tag > : public tiff_device_base< tiff_no_log >
374 {
375 public:
ostream_device(std::ostream & out)376     ostream_device( std::ostream & out )
377     : _out( out )
378     {
379         TIFF* tiff;
380 
381         io_error_if( ( tiff = TIFFStreamOpen( ""
382                                             , &_out
383                                             )
384                       ) == nullptr
385                    , "ostream_device: failed to stream"
386                    );
387 
388         _tiff_file = tiff_file_t( tiff, TIFFClose );
389     }
390 
391 private:
operator =(const ostream_device &)392     ostream_device& operator=( const ostream_device& ) { return *this; }
393 
394 private:
395 
396     std::ostream& _out;
397 };
398 
399 /*!
400  *
401  * ostream_device specialization for tiff images.
402  */
403 template<>
404 class istream_device< tiff_tag > : public tiff_device_base< tiff_no_log >
405 {
406 public:
istream_device(std::istream & in)407     istream_device( std::istream & in )
408     : _in( in )
409     {
410         TIFF* tiff;
411 
412         io_error_if( ( tiff = TIFFStreamOpen( ""
413                                             , &_in
414                                             )
415                      ) == nullptr
416                    , "istream_device: failed to stream"
417                    );
418 
419         _tiff_file = tiff_file_t( tiff, TIFFClose );
420     }
421 
422 private:
operator =(const istream_device &)423     istream_device& operator=( const istream_device& ) { return *this; }
424 
425 private:
426 
427     std::istream& _in;
428 };
429 
430 /*
431 template< typename T, typename D >
432 struct is_adaptable_input_device< tiff_tag, T, D > : std::false_type {};
433 */
434 
435 template<typename FormatTag>
436 struct is_adaptable_input_device<FormatTag, TIFF*, void> : std::true_type
437 {
438     using device_type = file_stream_device<FormatTag>;
439 };
440 
441 template<typename FormatTag>
442 struct is_adaptable_output_device<FormatTag, TIFF*, void> : std::true_type
443 {
444     using device_type = file_stream_device<FormatTag>;
445 };
446 
447 
448 template <typename Channel>
449 struct sample_format : std::integral_constant<int, SAMPLEFORMAT_UINT> {};
450 template<>
451 struct sample_format<uint8_t> : std::integral_constant<int, SAMPLEFORMAT_UINT> {};
452 template<>
453 struct sample_format<uint16_t> : std::integral_constant<int, SAMPLEFORMAT_UINT> {};
454 template<>
455 struct sample_format<uint32_t> : std::integral_constant<int, SAMPLEFORMAT_UINT> {};
456 template<>
457 struct sample_format<float32_t> : std::integral_constant<int, SAMPLEFORMAT_IEEEFP> {};
458 template<>
459 struct sample_format<double> : std::integral_constant<int, SAMPLEFORMAT_IEEEFP> {};
460 template<>
461 struct sample_format<int8_t> : std::integral_constant<int, SAMPLEFORMAT_INT> {};
462 template<>
463 struct sample_format<int16_t> : std::integral_constant<int, SAMPLEFORMAT_INT> {};
464 template<>
465 struct sample_format<int32_t> : std::integral_constant<int, SAMPLEFORMAT_INT> {};
466 
467 template <typename Channel>
468 struct photometric_interpretation {};
469 template<>
470 struct photometric_interpretation<gray_t>
471     : std::integral_constant<int, PHOTOMETRIC_MINISBLACK> {};
472 template<>
473 struct photometric_interpretation<rgb_t>
474     : std::integral_constant<int, PHOTOMETRIC_RGB> {};
475 template<>
476 struct photometric_interpretation<rgba_t>
477     : std::integral_constant<int, PHOTOMETRIC_RGB> {};
478 template<>
479 struct photometric_interpretation<cmyk_t>
480     : std::integral_constant<int, PHOTOMETRIC_SEPARATED> {};
481 
482 } // namespace detail
483 } // namespace gil
484 } // namespace boost
485 
486 #endif
487