/* Copyright 2007-2008 Christian Henning, Lubomir Bourdev Use, modification and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt). */ /*************************************************************************************************/ #ifndef BOOST_GIL_EXTENSION_IO_TIFF_IO_WRITE_HPP #define BOOST_GIL_EXTENSION_IO_TIFF_IO_WRITE_HPP //////////////////////////////////////////////////////////////////////////////////////// /// \file /// \brief /// \author Christian Henning, Lubomir Bourdev \n /// /// \date 2007-2008 \n /// //////////////////////////////////////////////////////////////////////////////////////// extern "C" { #include "tiff.h" #include "tiffio.h" } #include #include #include #include #include #include #include #include #include "device.hpp" namespace boost { namespace gil { namespace detail { template struct my_interleaved_pixel_iterator_type_from_pixel_reference { private: typedef typename remove_reference< PixelReference >::type::value_type pixel_t; public: typedef typename iterator_type_from_pixel< pixel_t , false , false , true >::type type; }; template< typename Channel , typename Layout , bool Mutable > struct my_interleaved_pixel_iterator_type_from_pixel_reference< const bit_aligned_pixel_reference< byte_t , Channel , Layout , Mutable > > : public iterator_type_from_pixel< const bit_aligned_pixel_reference< uint8_t , Channel , Layout , Mutable > ,false ,false ,true > {}; template < typename Channel > struct sample_format : public mpl::int_ {}; template<> struct sample_format : public mpl::int_ {}; template<> struct sample_format : public mpl::int_ {}; template<> struct sample_format : public mpl::int_ {}; template<> struct sample_format : public mpl::int_ {}; template<> struct sample_format : public mpl::int_ {}; template<> struct sample_format : public mpl::int_ {}; template<> struct sample_format : public mpl::int_ {}; template<> struct sample_format : public mpl::int_ {}; template struct photometric_interpretation {}; template<> struct photometric_interpretation< gray_t > : public mpl::int_< PHOTOMETRIC_MINISBLACK > {}; template<> struct photometric_interpretation< rgb_t > : public mpl::int_< PHOTOMETRIC_RGB > {}; template<> struct photometric_interpretation< rgba_t > : public mpl::int_< PHOTOMETRIC_RGB > {}; template<> struct photometric_interpretation< cmyk_t > : public mpl::int_< PHOTOMETRIC_SEPARATED > {}; template < typename Device, typename Log > class writer< Device , tiff_tag , Log > { public: typedef image_write_info info_t; writer( Device& dev ) : _io_dev( dev ) {} template void apply( const View& view ) { typedef typename color_space_type::type color_space_t; info_t info; // write photometric interpretion - Warning: This value is rather subjective. // The user should better set this value itself. There is no way to decide if // a image is PHOTOMETRIC_MINISWHITE or PHOTOMETRIC_MINISBLACK. This writer // will assume PHOTOMETRIC_MINISBLACK for gray_t images and PHOTOMETRIC_RGB // for rgb_t images. info._photometric_interpretation = photometric_interpretation< color_space_t >::value; write_view( view , info ); } template void apply( const View& view , const info_t& info ) { write_view( view , info ); } private: template< typename View > void write_view( const View& src_view , const info_t& info ) { typedef typename View::value_type pixel_t; // get the type of the first channel (heterogeneous pixels might be broken for now!) typedef typename channel_traits< typename element_type< pixel_t >::type >::value_type channel_t; // write dimensions tiff_image_width::type width = (tiff_image_width::type) src_view.width(); tiff_image_height::type height = (tiff_image_height::type) src_view.height(); _io_dev.template set_property< tiff_image_width >( width ); _io_dev.template set_property< tiff_image_height >( height ); // write planar configuration if( is_bit_aligned< View >::value == false ) { _io_dev.template set_property( info._planar_configuration ); } // write samples per pixel tiff_samples_per_pixel::type samples_per_pixel = num_channels< pixel_t >::value; _io_dev.template set_property( samples_per_pixel ); // write bits per sample // @todo: Settings this value usually requires to write for each sample the bit // value seperately in case they are different, like rgb556. tiff_bits_per_sample::type bits_per_sample = unsigned_integral_num_bits::value; _io_dev.template set_property( bits_per_sample ); // write sample format tiff_sample_format::type sampl_format = sample_format< channel_t >::type::value; _io_dev.template set_property( sampl_format ); // write photometric format _io_dev.template set_property( info._photometric_interpretation ); // write compression _io_dev.template set_property( info._compression ); // write orientation _io_dev.template set_property( info._orientation ); // write rows per strip _io_dev.template set_property( _io_dev.get_default_strip_size() ); if(!info._is_tiled) { write_data( src_view , info , (src_view.width() * samples_per_pixel * bits_per_sample + 7) / 8 , typename is_bit_aligned< pixel_t >::type() ); } else { tiff_tile_width::type tw = info._tile_width; tiff_tile_length::type th = info._tile_length; if(!_io_dev.check_tile_size( tw, th )) { io_error( "Tile sizes need to be multiples of 16." ); } // tile related tags _io_dev.template set_property ( tw ); _io_dev.template set_property( th ); write_tiled_data( src_view , tw , th , typename is_bit_aligned< pixel_t >::type() ); } } template< typename View > void write_data( const View& src_view , const info_t& /* info */ , std::size_t row_size_in_bytes , const mpl::true_& // bit_aligned ) { byte_vector_t row( row_size_in_bytes ); typedef typename View::x_iterator x_it_t; x_it_t row_it = x_it_t( &(*row.begin())); for( typename View::y_coord_t y = 0; y < src_view.height(); ++y ) { std::copy( src_view.row_begin( y ) , src_view.row_end( y ) , row_it ); _io_dev.write_scaline( row , (uint32) y , 0 ); // @todo: do optional bit swapping here if you need to... } } template< typename View > void write_tiled_data( const View& src_view , tiff_tile_width::type tw , tiff_tile_length::type th , const mpl::true_& // bit_aligned ) { byte_vector_t row( _io_dev.get_tile_size() ); typedef typename View::x_iterator x_it_t; x_it_t row_it = x_it_t( &(*row.begin())); internal_write_tiled_data(src_view, tw, th, row, row_it); } template< typename View > void write_data( const View& src_view , const info_t& info , std::size_t row_size_in_bytes , const mpl::false_& // bit_aligned ) { byte_vector_t row( row_size_in_bytes ); typedef typename my_interleaved_pixel_iterator_type_from_pixel_reference< typename View::reference >::type x_iterator; x_iterator row_it = x_iterator( &(*row.begin())); for( typename View::y_coord_t y = 0; y < src_view.height(); ++y ) { std::copy( src_view.row_begin( y ) , src_view.row_end( y ) , row_it ); _io_dev.write_scaline( row , (uint32) y , 0 ); // @todo: do optional bit swapping here if you need to... } } template< typename View > void write_tiled_data( const View& src_view , tiff_tile_width::type tw , tiff_tile_length::type th , const mpl::false_& // bit_aligned ) { byte_vector_t row( _io_dev.get_tile_size() ); typedef typename my_interleaved_pixel_iterator_type_from_pixel_reference< typename View::reference >::type x_iterator; x_iterator row_it = x_iterator( &(*row.begin())); internal_write_tiled_data(src_view, tw, th, row, row_it); } template< typename View, typename IteratorType > void internal_write_tiled_data( const View& src_view , tiff_tile_width::type tw , tiff_tile_length::type th , byte_vector_t& row , IteratorType it ) { std::ptrdiff_t i = 0, j = 0; View tile_subimage_view; while( i < src_view.height() ) { while( j < src_view.width() ) { if( j + tw < src_view.width() && i + th < src_view.height() ) { // a tile is fully included in the image: just copy values tile_subimage_view = subimage_view( src_view , static_cast< int >( j ) , static_cast< int >( i ) , static_cast< int >( tw ) , static_cast< int >( th ) ); std::copy( tile_subimage_view.begin() , tile_subimage_view.end() , it ); } else { std::ptrdiff_t width = src_view.width(); std::ptrdiff_t height = src_view.height(); std::ptrdiff_t current_tile_width = ( j + tw < width ) ? tw : width - j; std::ptrdiff_t current_tile_length = ( i + th < height) ? th : height - i; tile_subimage_view = subimage_view( src_view , static_cast< int >( j ) , static_cast< int >( i ) , static_cast< int >( current_tile_width ) , static_cast< int >( current_tile_length ) ); for( typename View::y_coord_t y = 0; y < tile_subimage_view.height(); ++y ) { std::copy( tile_subimage_view.row_begin( y ) , tile_subimage_view.row_end( y ) , it ); std::advance(it, tw); } it = IteratorType( &(*row.begin())); } _io_dev.write_tile( row , static_cast< uint32 >( j ) , static_cast< uint32 >( i ) , 0 , 0 ); j += tw; } j = 0; i += th; } // @todo: do optional bit swapping here if you need to... } Device& _io_dev; }; struct tiff_write_is_supported { template< typename View > struct apply : public is_write_supported< typename get_pixel_type< View >::type , tiff_tag > {}; }; template< typename Device > class dynamic_image_writer< Device , tiff_tag > : public writer< Device , tiff_tag > { typedef writer< Device , tiff_tag > parent_t; public: dynamic_image_writer( Device& file ) : parent_t( file ) {} template< typename Views > void apply( const any_image_view< Views >& views ) { dynamic_io_fnobj< tiff_write_is_supported , parent_t > op( this ); apply_operation( views, op ); } }; } // namespace detail } // namespace gil } // namespace boost #endif // BOOST_GIL_EXTENSION_IO_TIFF_IO_WRITE_HPP