1 // 2 // Copyright 2007-2012 Christian Henning, Lubomir Bourdev 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_WRITE_HPP 9 #define BOOST_GIL_EXTENSION_IO_TIFF_DETAIL_WRITE_HPP 10 11 #include <boost/gil/extension/io/tiff/tags.hpp> 12 #include <boost/gil/extension/io/tiff/detail/writer_backend.hpp> 13 #include <boost/gil/extension/io/tiff/detail/device.hpp> 14 15 #include <boost/gil/premultiply.hpp> 16 #include <boost/gil/io/base.hpp> 17 #include <boost/gil/io/device.hpp> 18 #include <boost/gil/io/dynamic_io_new.hpp> 19 20 #include <algorithm> 21 #include <string> 22 #include <type_traits> 23 #include <vector> 24 25 extern "C" { 26 #include "tiff.h" 27 #include "tiffio.h" 28 } 29 30 namespace boost { namespace gil { 31 32 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 33 #pragma warning(push) 34 #pragma warning(disable:4512) //assignment operator could not be generated 35 #endif 36 37 namespace detail { 38 39 template <typename PixelReference> 40 struct my_interleaved_pixel_iterator_type_from_pixel_reference 41 { 42 private: 43 using pixel_t = typename std::remove_reference<PixelReference>::type::value_type; 44 45 public: 46 using type = typename iterator_type_from_pixel 47 < 48 pixel_t, 49 false, 50 false, 51 true 52 >::type; 53 }; 54 55 56 template< typename Channel 57 , typename Layout 58 , bool Mutable 59 > 60 struct my_interleaved_pixel_iterator_type_from_pixel_reference< const bit_aligned_pixel_reference< byte_t 61 , Channel 62 , Layout 63 , Mutable 64 > 65 > 66 : public iterator_type_from_pixel< const bit_aligned_pixel_reference< uint8_t 67 , Channel 68 , Layout 69 , Mutable 70 > 71 ,false 72 ,false 73 ,true 74 > {}; 75 76 struct tiff_write_is_supported 77 { 78 template< typename View > 79 struct apply 80 : public is_write_supported< typename get_pixel_type< View >::type 81 , tiff_tag 82 > 83 {}; 84 }; 85 86 } // namespace detail 87 88 /// 89 /// TIFF Writer 90 /// 91 template < typename Device, typename Log > 92 class writer< Device 93 , tiff_tag 94 , Log 95 > 96 : public writer_backend< Device 97 , tiff_tag 98 > 99 { 100 private: 101 using backend_t = writer_backend<Device, tiff_tag>; 102 103 public: 104 writer(const Device & io_dev,const image_write_info<tiff_tag> & info)105 writer( const Device& io_dev 106 , const image_write_info< tiff_tag >& info 107 ) 108 : backend_t( io_dev 109 , info 110 ) 111 {} 112 113 template<typename View> apply(const View & view)114 void apply( const View& view ) 115 { 116 write_view( view ); 117 } 118 119 private: 120 121 template< typename View > write_view(const View & view)122 void write_view( const View& view ) 123 { 124 using pixel_t = typename View::value_type; 125 // get the type of the first channel (heterogeneous pixels might be broken for now!) 126 using channel_t = typename channel_traits<typename element_type<pixel_t>::type>::value_type; 127 tiff_bits_per_sample::type bits_per_sample = detail::unsigned_integral_num_bits< channel_t >::value; 128 129 tiff_samples_per_pixel::type samples_per_pixel = num_channels< pixel_t >::value; 130 131 this->write_header( view ); 132 133 if( this->_info._is_tiled == false ) 134 { 135 write_data( view 136 , (view.width() * samples_per_pixel * bits_per_sample + 7) / 8 137 , typename is_bit_aligned< pixel_t >::type() 138 ); 139 } 140 else 141 { 142 tiff_tile_width::type tw = this->_info._tile_width; 143 tiff_tile_length::type th = this->_info._tile_length; 144 145 if(!this->_io_dev.check_tile_size( tw, th )) 146 { 147 io_error( "Tile sizes need to be multiples of 16." ); 148 } 149 150 // tile related tags 151 this->_io_dev.template set_property<tiff_tile_width> ( tw ); 152 this->_io_dev.template set_property<tiff_tile_length>( th ); 153 154 write_tiled_data( view 155 , tw 156 , th 157 , typename is_bit_aligned< pixel_t >::type() 158 ); 159 } 160 } 161 162 ////////////////////////////// 163 164 template<typename View> write_bit_aligned_view_to_dev(const View & view,const std::size_t row_size_in_bytes,const std::true_type &)165 void write_bit_aligned_view_to_dev( const View& view 166 , const std::size_t row_size_in_bytes 167 , const std::true_type& // has_alpha 168 ) 169 { 170 byte_vector_t row( row_size_in_bytes ); 171 172 using x_it_t = typename View::x_iterator; 173 x_it_t row_it = x_it_t( &(*row.begin())); 174 175 auto pm_view = premultiply_view <typename View:: value_type> (view); 176 177 for( typename View::y_coord_t y = 0; y < pm_view.height(); ++y ) 178 { 179 std::copy( pm_view.row_begin( y ) 180 , pm_view.row_end( y ) 181 , row_it 182 ); 183 184 185 this->_io_dev.write_scaline( row 186 , (uint32) y 187 , 0 188 ); 189 190 // @todo: do optional bit swapping here if you need to... 191 } 192 } 193 194 template<typename View> write_bit_aligned_view_to_dev(const View & view,const std::size_t row_size_in_bytes,const std::false_type &)195 void write_bit_aligned_view_to_dev( const View& view 196 , const std::size_t row_size_in_bytes 197 , const std::false_type& // has_alpha 198 ) 199 { 200 byte_vector_t row( row_size_in_bytes ); 201 202 using x_it_t = typename View::x_iterator; 203 x_it_t row_it = x_it_t( &(*row.begin())); 204 205 for( typename View::y_coord_t y = 0; y < view.height(); ++y ) 206 { 207 std::copy( view.row_begin( y ) 208 , view.row_end( y ) 209 , row_it 210 ); 211 212 213 this->_io_dev.write_scaline( row 214 , (uint32) y 215 , 0 216 ); 217 218 // @todo: do optional bit swapping here if you need to... 219 } 220 } 221 222 ///////////////////////////// 223 224 template< typename View > write_data(const View & view,std::size_t row_size_in_bytes,const std::true_type &)225 void write_data( const View& view 226 , std::size_t row_size_in_bytes 227 , const std::true_type& // bit_aligned 228 ) 229 { 230 using colour_space_t = typename color_space_type<typename View::value_type>::type; 231 using has_alpha_t = mp11::mp_contains<colour_space_t, alpha_t>; 232 233 write_bit_aligned_view_to_dev(view, row_size_in_bytes, has_alpha_t()); 234 } 235 236 template< typename View> write_tiled_data(const View & view,tiff_tile_width::type tw,tiff_tile_length::type th,const std::true_type &)237 void write_tiled_data( const View& view 238 , tiff_tile_width::type tw 239 , tiff_tile_length::type th 240 , const std::true_type& // bit_aligned 241 ) 242 { 243 byte_vector_t row( this->_io_dev.get_tile_size() ); 244 245 using x_it_t = typename View::x_iterator; 246 x_it_t row_it = x_it_t( &(*row.begin())); 247 248 internal_write_tiled_data(view, tw, th, row, row_it); 249 } 250 251 template< typename View > write_data(const View & view,std::size_t,const std::false_type &)252 void write_data( const View& view 253 , std::size_t 254 , const std::false_type& // bit_aligned 255 ) 256 { 257 std::vector< pixel< typename channel_type< View >::type 258 , layout<typename color_space_type< View >::type > 259 > 260 > row( view.size() ); 261 262 byte_t* row_addr = reinterpret_cast< byte_t* >( &row.front() ); 263 264 // @todo: is there an overhead to doing this when there's no 265 // alpha to premultiply by? I'd hope it's optimised out. 266 auto pm_view = premultiply_view <typename View:: value_type> (view); 267 268 for( typename View::y_coord_t y = 0; y < pm_view.height(); ++y ) 269 { 270 std::copy( pm_view.row_begin( y ) 271 , pm_view.row_end( y ) 272 , row.begin() 273 ); 274 275 this->_io_dev.write_scaline( row_addr 276 , (uint32) y 277 , 0 278 ); 279 280 // @todo: do optional bit swapping here if you need to... 281 } 282 } 283 284 template< typename View > write_tiled_data(const View & view,tiff_tile_width::type tw,tiff_tile_length::type th,const std::false_type &)285 void write_tiled_data( const View& view 286 , tiff_tile_width::type tw 287 , tiff_tile_length::type th 288 , const std::false_type& // bit_aligned 289 ) 290 { 291 byte_vector_t row( this->_io_dev.get_tile_size() ); 292 293 using x_iterator = typename detail::my_interleaved_pixel_iterator_type_from_pixel_reference<typename View::reference>::type; 294 x_iterator row_it = x_iterator( &(*row.begin())); 295 296 internal_write_tiled_data(view, tw, th, row, row_it); 297 } 298 299 300 ////////////////////////////// 301 302 template< typename View 303 , typename IteratorType 304 > write_tiled_view_to_dev(const View & view,IteratorType it,const std::true_type &)305 void write_tiled_view_to_dev( const View& view 306 , IteratorType it 307 , const std::true_type& // has_alpha 308 ) 309 { 310 auto pm_view = premultiply_view <typename View:: value_type>( view ); 311 312 std::copy( pm_view.begin() 313 , pm_view.end() 314 , it 315 ); 316 } 317 318 319 template< typename View 320 , typename IteratorType 321 > write_tiled_view_to_dev(const View & view,IteratorType it,const std::false_type &)322 void write_tiled_view_to_dev( const View& view 323 , IteratorType it 324 , const std::false_type& // has_alpha 325 ) 326 { 327 std::copy( view.begin() 328 , view.end() 329 , it 330 ); 331 } 332 333 ///////////////////////////// 334 335 336 337 template< typename View, 338 typename IteratorType 339 > internal_write_tiled_data(const View & view,tiff_tile_width::type tw,tiff_tile_length::type th,byte_vector_t & row,IteratorType it)340 void internal_write_tiled_data( const View& view 341 , tiff_tile_width::type tw 342 , tiff_tile_length::type th 343 , byte_vector_t& row 344 , IteratorType it 345 ) 346 { 347 std::ptrdiff_t i = 0, j = 0; 348 View tile_subimage_view; 349 while( i < view.height() ) 350 { 351 while( j < view.width() ) 352 { 353 if( j + tw < view.width() && i + th < view.height() ) 354 { 355 // a tile is fully included in the image: just copy values 356 tile_subimage_view = subimage_view( view 357 , static_cast< int >( j ) 358 , static_cast< int >( i ) 359 , static_cast< int >( tw ) 360 , static_cast< int >( th ) 361 ); 362 363 using colour_space_t = typename color_space_type<typename View::value_type>::type; 364 using has_alpha_t = mp11::mp_contains<colour_space_t, alpha_t>; 365 366 write_tiled_view_to_dev(tile_subimage_view, it, has_alpha_t()); 367 } 368 else 369 { 370 std::ptrdiff_t width = view.width(); 371 std::ptrdiff_t height = view.height(); 372 373 std::ptrdiff_t current_tile_width = ( j + tw < width ) ? tw : width - j; 374 std::ptrdiff_t current_tile_length = ( i + th < height) ? th : height - i; 375 376 tile_subimage_view = subimage_view( view 377 , static_cast< int >( j ) 378 , static_cast< int >( i ) 379 , static_cast< int >( current_tile_width ) 380 , static_cast< int >( current_tile_length ) 381 ); 382 383 for( typename View::y_coord_t y = 0; y < tile_subimage_view.height(); ++y ) 384 { 385 std::copy( tile_subimage_view.row_begin( y ) 386 , tile_subimage_view.row_end( y ) 387 , it 388 ); 389 std::advance(it, tw); 390 } 391 392 it = IteratorType( &(*row.begin())); 393 } 394 395 this->_io_dev.write_tile( row 396 , static_cast< uint32 >( j ) 397 , static_cast< uint32 >( i ) 398 , 0 399 , 0 400 ); 401 j += tw; 402 } 403 j = 0; 404 i += th; 405 } 406 // @todo: do optional bit swapping here if you need to... 407 } 408 }; 409 410 /// 411 /// TIFF Dynamic Image Writer 412 /// 413 template< typename Device > 414 class dynamic_image_writer< Device 415 , tiff_tag 416 > 417 : public writer< Device 418 , tiff_tag 419 > 420 { 421 using parent_t = writer<Device, tiff_tag>; 422 423 public: 424 dynamic_image_writer(const Device & io_dev,const image_write_info<tiff_tag> & info)425 dynamic_image_writer( const Device& io_dev 426 , const image_write_info< tiff_tag >& info 427 ) 428 : parent_t( io_dev 429 , info 430 ) 431 {} 432 433 template< typename ...Views > apply(const any_image_view<Views...> & views)434 void apply( const any_image_view< Views... >& views ) 435 { 436 detail::dynamic_io_fnobj< detail::tiff_write_is_supported 437 , parent_t 438 > op( this ); 439 440 apply_operation( views, op ); 441 } 442 }; 443 444 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 445 #pragma warning(pop) 446 #endif 447 448 } // namespace gil 449 } // namespace boost 450 451 #endif 452