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