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