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