WIP. Scale cover art option. Seems to work

This commit is contained in:
emeric
2014-06-14 20:44:03 +02:00
parent 1d67a548c9
commit 9bada5ee2a
92 changed files with 15866 additions and 1890 deletions
@@ -0,0 +1,92 @@
/*
Copyright 2009 Christian Henning
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_BMP_IO_IS_ALLOWED_HPP
#define BOOST_GIL_EXTENSION_IO_BMP_IO_IS_ALLOWED_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
namespace boost { namespace gil { namespace detail {
template< typename View >
bool is_allowed( const image_read_info< bmp_tag >& info
, mpl::true_ // is read_and_no_convert
)
{
bmp_bits_per_pixel::type src_bits_per_pixel = 0;
switch( info._bits_per_pixel )
{
case 1:
case 4:
case 8:
{
if( info._header_size == bmp_header_size::_win32_info_size
&& info._compression != bmp_compression::_rle8
&& info._compression != bmp_compression::_rle4
)
{
src_bits_per_pixel = 32;
}
else
{
src_bits_per_pixel = 24;
}
break;
}
case 15:
case 16:
{
src_bits_per_pixel = 24;
break;
}
case 24:
case 32:
{
src_bits_per_pixel = info._bits_per_pixel;
break;
}
default:
{
io_error( "Pixel size not supported." );
}
}
typedef typename channel_traits< typename element_type< typename View::value_type >::type >::value_type channel_t;
bmp_bits_per_pixel::type dst_bits_per_pixel = detail::unsigned_integral_num_bits< channel_t >::value
* num_channels< View >::value;
return ( dst_bits_per_pixel == src_bits_per_pixel );
}
template< typename View >
bool is_allowed( const image_read_info< bmp_tag >& /* info */
, mpl::false_ // is read_and_convert
)
{
return true;
}
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_BMP_IO_IS_ALLOWED_HPP
@@ -0,0 +1,865 @@
/*
Copyright 2008 Christian Henning
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_BMP_IO_READ_HPP
#define BOOST_GIL_EXTENSION_IO_BMP_IO_READ_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <vector>
#include <boost/gil/extension/io_new/bmp_tags.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/bit_operations.hpp>
#include <boost/gil/extension/io_new/detail/conversion_policies.hpp>
#include <boost/gil/extension/io_new/detail/row_buffer_helper.hpp>
#include <boost/gil/extension/io_new/detail/reader_base.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
#include <boost/gil/extension/io_new/detail/typedefs.hpp>
#include "is_allowed.hpp"
namespace boost { namespace gil { namespace detail {
/// Color channel mask
struct bit_field
{
unsigned int mask; // Bit mask at corresponding position
unsigned int width; // Bit width of the mask
unsigned int shift; // Bit position from right to left
};
/// BMP color masks
struct color_mask
{
bit_field red; // Red bits
bit_field green; // Green bits
bit_field blue; // Blue bits
};
template< typename Device
, typename ConversionPolicy
>
class reader< Device
, bmp_tag
, ConversionPolicy
>
: public reader_base< bmp_tag
, ConversionPolicy
>
{
private:
typedef typename ConversionPolicy::color_converter_type cc_t;
public:
reader( Device& device
, const image_read_settings< bmp_tag >& settings
)
: reader_base< bmp_tag
, ConversionPolicy >( settings )
, _io_dev( device )
{}
reader( Device& device
, const cc_t& cc
, const image_read_settings< bmp_tag >& settings
)
: reader_base< bmp_tag
, ConversionPolicy
>( cc
, settings
)
, _io_dev( device )
{}
image_read_info< bmp_tag > get_info()
{
// read file header
// the magic number used to identify the BMP file:
// 0x42 0x4D (ASCII code points for B and M)
if( _io_dev.read_int16() == 0x424D )
{
io_error( "Wrong magic number for bmp file." );
}
// the size of the BMP file in bytes
_io_dev.read_int32();
// reserved; actual value depends on the application that creates the image
_io_dev.read_int16();
// reserved; actual value depends on the application that creates the image
_io_dev.read_int16();
_info._offset = _io_dev.read_int32();
// bitmap information
// the size of this header ( 40 bytes )
_info._header_size = _io_dev.read_int32();
if( _info._header_size == bmp_header_size::_win32_info_size )
{
_info._width = _io_dev.read_int32();
_info._height = _io_dev.read_int32();
// the number of color planes being used. Must be set to 1.
_io_dev.read_int16();
_info._bits_per_pixel = _io_dev.read_int16();
_info._compression = _io_dev.read_int32();
_info._image_size = _io_dev.read_int32();
_info._horizontal_resolution = _io_dev.read_int32();
_info._vertical_resolution = _io_dev.read_int32();
_info._num_colors = _io_dev.read_int32();
_info._num_important_colors = _io_dev.read_int32();
}
else if( _info._header_size == bmp_header_size::_os2_info_size )
{
_info._width = static_cast< bmp_image_width::type >( _io_dev.read_int16() );
_info._height = static_cast< bmp_image_height::type >( _io_dev.read_int16() );
// the number of color planes being used. Must be set to 1.
_io_dev.read_int16();
_info._bits_per_pixel = _io_dev.read_int16();
_info._compression = bmp_compression::_rgb;
// not used
_info._image_size = 0;
_info._horizontal_resolution = 0;
_info._vertical_resolution = 0;
_info._num_colors = 0;
_info._num_important_colors = 0;
}
else
{
io_error( "Invalid BMP info header." );
}
_info._valid = true;
return _info;
}
template< typename View >
void apply( const View& dst_view )
{
if( !_info._valid )
{
get_info();
}
typedef typename is_same< ConversionPolicy
, read_and_no_convert
>::type is_read_and_convert_t;
io_error_if( !is_allowed< View >( this->_info
, is_read_and_convert_t()
)
, "Image types aren't compatible."
);
// the row pitch must be multiple 4 bytes
int pitch;
if( _info._bits_per_pixel < 8 )
{
pitch = (( this->_info._width * this->_info._bits_per_pixel ) + 7 ) >> 3;
}
else
{
pitch = _info._width * (( this->_info._bits_per_pixel + 7 ) >> 3);
}
pitch = (pitch + 3) & ~3;
std::ptrdiff_t ybeg = 0;
std::ptrdiff_t yend = this->_settings._dim.y;
std::ptrdiff_t yinc = 1;
// offset to first scanline
std::ptrdiff_t offset = 0;
if( _info._height > 0 )
{
// the image is upside down
ybeg = this->_settings._dim.y - 1;
yend = -1;
yinc = -1;
offset = _info._offset
+ ( this->_info._height
- this->_settings._top_left.y
- this->_settings._dim.y
) * pitch;
}
else
{
offset = _info._offset
+ this->_settings._top_left.y * pitch;
}
switch( _info._bits_per_pixel )
{
case 1:
{
read_palette_image< gray1_image_t::view_t
, mirror_bits< byte_vector_t
, mpl::true_
>
> ( dst_view
, pitch
, ybeg
, yend
, yinc
, offset
);
break;
}
case 4:
{
switch ( _info._compression )
{
case bmp_compression::_rle4:
{
read_palette_image_rle( dst_view
, ybeg
, yend
, yinc
, offset
);
break;
}
case bmp_compression::_rgb:
{
read_palette_image< gray4_image_t::view_t
, swap_half_bytes< byte_vector_t
, mpl::true_
>
> ( dst_view
, pitch
, ybeg
, yend
, yinc
, offset
);
break;
}
default:
{
io_error( "Unsupported compression mode in BMP file." );
break;
}
}
break;
}
case 8:
{
switch ( _info._compression )
{
case bmp_compression::_rle8:
{
read_palette_image_rle( dst_view
, ybeg
, yend
, yinc
, offset
);
break;
}
case bmp_compression::_rgb:
{
read_palette_image< gray8_image_t::view_t
, do_nothing< std::vector< gray8_pixel_t > >
> ( dst_view
, pitch
, ybeg
, yend
, yinc
, offset
);
break;
}
default:
{
io_error( "Unsupported compression mode in BMP file." );
break;
}
}
break;
}
case 15: case 16:
{
read_data_15( dst_view
, pitch
, ybeg
, yend
, yinc
, offset
);
break;
}
case 24: { read_data< bgr8_view_t >( dst_view, pitch, ybeg, yend, yinc, offset ); break; }
case 32: { read_data< bgra8_view_t >( dst_view, pitch, ybeg, yend, yinc, offset ); break; }
}
}
private:
void read_palette( std::vector< rgba8_pixel_t >& palette )
{
int entries = _info._num_colors;
if( entries == 0 )
{
entries = 1 << _info._bits_per_pixel;
}
palette.resize( entries );
for( int i = 0; i < entries; ++i )
{
get_color( palette[i], blue_t() ) = _io_dev.read_int8();
get_color( palette[i], green_t() ) = _io_dev.read_int8();
get_color( palette[i], red_t() ) = _io_dev.read_int8();
// there are 4 entries when windows header
// but 3 for os2 header
if( _info._header_size == bmp_header_size::_win32_info_size )
{
_io_dev.read_int8();
}
} // for
}
template< typename View_Src
, typename Byte_Manipulator
, typename View_Dst
>
void read_palette_image( const View_Dst& view
, int pitch
, std::ptrdiff_t ybeg
, std::ptrdiff_t yend
, std::ptrdiff_t yinc
, std::ptrdiff_t offset
)
{
std::vector< rgba8_pixel_t > pal;
read_palette( pal );
// jump to first scanline
_io_dev.seek( static_cast< long >( offset ));
typedef row_buffer_helper_view< View_Src > rh_t;
typedef typename rh_t::iterator_t it_t;
rh_t rh( pitch, true );
// we have to swap bits
Byte_Manipulator byte_manipulator;
for( std::ptrdiff_t y = ybeg; y != yend; y += yinc )
{
// @todo: For now we're reading the whole scanline which is
// slightly inefficient. Later versions should try to read
// only the bytes which are necessary.
_io_dev.read( reinterpret_cast< byte_t* >( rh.data() )
, pitch
);
byte_manipulator( rh.buffer() );
typename View_Dst::x_iterator dst_it = view.row_begin( y );
it_t it = rh.begin() + this->_settings._top_left.x;
it_t end = it + this->_settings._dim.x;
for( ; it != end; ++it, ++dst_it )
{
unsigned char c = get_color( *it, gray_color_t() );
*dst_it = pal[ c ];
}
}
}
template< typename View >
void read_data_15( const View& view
, int pitch
, std::ptrdiff_t ybeg
, std::ptrdiff_t yend
, std::ptrdiff_t yinc
, std::ptrdiff_t offset
)
{
byte_vector_t row( pitch );
// read the color masks
color_mask mask = { {0} };
if( _info._compression == bmp_compression::_bitfield )
{
mask.red.mask = _io_dev.read_int32();
mask.green.mask = _io_dev.read_int32();
mask.blue.mask = _io_dev.read_int32();
mask.red.width = count_ones( mask.red.mask );
mask.green.width = count_ones( mask.green.mask );
mask.blue.width = count_ones( mask.blue.mask );
mask.red.shift = trailing_zeros( mask.red.mask );
mask.green.shift = trailing_zeros( mask.green.mask );
mask.blue.shift = trailing_zeros( mask.blue.mask );
}
else if( _info._compression == bmp_compression::_rgb )
{
switch( _info._bits_per_pixel )
{
case 15:
case 16:
{
mask.red.mask = 0x007C00; mask.red.width = 5; mask.red.shift = 10;
mask.green.mask = 0x0003E0; mask.green.width = 5; mask.green.shift = 5;
mask.blue.mask = 0x00001F; mask.blue.width = 5; mask.blue.shift = 0;
break;
}
case 24:
case 32:
{
mask.red.mask = 0xFF0000; mask.red.width = 8; mask.red.shift = 16;
mask.green.mask = 0x00FF00; mask.green.width = 8; mask.green.shift = 8;
mask.blue.mask = 0x0000FF; mask.blue.width = 8; mask.blue.shift = 0;
break;
}
}
}
else
{
io_error( "bmp_reader::apply(): unsupported BMP compression" );
}
// jump to first scanline
_io_dev.seek( static_cast< long >( offset ));
typedef rgb8_image_t image_t;
typedef image_t::view_t::x_iterator it_t;
for( std::ptrdiff_t y = ybeg; y != yend; y += yinc )
{
// @todo: For now we're reading the whole scanline which is
// slightly inefficient. Later versions should try to read
// only the bytes which are necessary.
_io_dev.read( &row.front(), row.size() );
image_t img_row( _info._width, 1 );
image_t::view_t v = gil::view( img_row );
it_t it = v.row_begin( 0 );
it_t beg = v.row_begin( 0 ) + this->_settings._top_left.x;
it_t end = beg + this->_settings._dim.x;
byte_t* src = &row.front();
for( int32_t i = 0 ; i < _info._width; ++i, src += 2 )
{
int p = ( src[1] << 8 ) | src[0];
int r = ((p & mask.red.mask) >> mask.red.shift) << (8 - mask.red.width);
int g = ((p & mask.green.mask) >> mask.green.shift) << (8 - mask.green.width);
int b = ((p & mask.blue.mask) >> mask.blue.shift) << (8 - mask.blue.width);
get_color( it[i], red_t() ) = static_cast< byte_t >( r );
get_color( it[i], green_t() ) = static_cast< byte_t >( g );
get_color( it[i], blue_t() ) = static_cast< byte_t >( b );
}
this->_cc_policy.read( beg
, end
, view.row_begin( y )
);
}
}
// 8-8-8 BGR
// 8-8-8-8 BGRA
template< typename View_Src
, typename View_Dst
>
void read_data( const View_Dst& view
, int pitch
, std::ptrdiff_t ybeg
, std::ptrdiff_t yend
, std::ptrdiff_t yinc
, std::ptrdiff_t offset
)
{
byte_vector_t row( pitch );
// jump to first scanline
_io_dev.seek( static_cast< long >( offset ));
View_Src v = interleaved_view( _info._width
, 1
, (typename View_Src::value_type*) &row.front()
, _info._width * num_channels< View_Src >::value
);
typename View_Src::x_iterator beg = v.row_begin( 0 ) + this->_settings._top_left.x;
typename View_Src::x_iterator end = beg + this->_settings._dim.x;
for( std::ptrdiff_t y = ybeg; y != yend; y += yinc )
{
// @todo: For now we're reading the whole scanline which is
// slightly inefficient. Later versions should try to read
// only the bytes which are necessary.
_io_dev.read( &row.front(), row.size() );
this->_cc_policy.read( beg
, end
, view.row_begin( y )
);
}
}
template< typename Buffer
, typename View
>
void copy_row_if_needed( const Buffer& buf
, const View& view
, std::ptrdiff_t y
)
{
if( y >= this->_settings._top_left.y
&& y < this->_settings._dim.y
)
{
typename Buffer::const_iterator beg = buf.begin() + this->_settings._top_left.x;
typename Buffer::const_iterator end = beg + this->_settings._dim.x;
std::copy( beg
, end
, view.row_begin( y )
);
}
}
template< typename View_Dst >
void read_palette_image_rle( const View_Dst& view
, std::ptrdiff_t ybeg
, std::ptrdiff_t yend
, std::ptrdiff_t yinc
, std::ptrdiff_t offset
)
{
assert( _info._compression == bmp_compression::_rle4
|| _info._compression == bmp_compression::_rle8
);
std::vector< rgba8_pixel_t > pal;
read_palette( pal );
// jump to start of rle4 data
_io_dev.seek( static_cast< long >( offset ));
// we need to know the stream position for padding purposes
std::size_t stream_pos = offset;
typedef std::vector< rgba8_pixel_t > Buf_type;
Buf_type buf( this->_settings._dim.x );
Buf_type::iterator dst_it = buf.begin();
Buf_type::iterator dst_end = buf.end();
std::ptrdiff_t y = ybeg;
bool finished = false;
while ( !finished )
{
std::ptrdiff_t count = _io_dev.read_int8();
std::ptrdiff_t second = _io_dev.read_int8();
stream_pos += 2;
if ( count )
{
// encoded mode
// clamp to boundary
if( count > dst_end - dst_it )
{
count = dst_end - dst_it;
}
if( _info._compression == bmp_compression::_rle4 )
{
std::ptrdiff_t cs[2] = { second >> 4, second & 0x0f };
for( int i = 0; i < count; ++i )
{
*dst_it++ = pal[ cs[i & 1] ];
}
}
else
{
for( int i = 0; i < count; ++i )
{
*dst_it++ = pal[ second ];
}
}
}
else
{
switch( second )
{
case 0: // end of row
{
copy_row_if_needed( buf, view, y );
y += yinc;
if( y == yend )
{
finished = true;
}
else
{
dst_it = buf.begin();
dst_end = buf.end();
}
break;
}
case 1: // end of bitmap
{
copy_row_if_needed( buf, view, y );
finished = true;
break;
}
case 2: // offset coordinates
{
std::ptrdiff_t dx = _io_dev.read_int8();
std::ptrdiff_t dy = _io_dev.read_int8() * yinc;
stream_pos += 2;
if( dy )
{
copy_row_if_needed( buf, view, y );
}
std::ptrdiff_t x = dst_it - buf.begin();
x += dx;
if( x > _info._width )
{
io_error( "Mangled BMP file." );
}
y += dy;
if( yinc > 0 ? y > yend : y < yend )
{
io_error( "Mangled BMP file." );
}
dst_it = buf.begin() + x;
dst_end = buf.end();
break;
}
default: // absolute mode
{
count = second;
// clamp to boundary
if( count > dst_end - dst_it )
{
count = dst_end - dst_it;
}
if ( _info._compression == bmp_compression::_rle4 )
{
for( int i = 0; i < count; ++i )
{
uint8_t packed_indices = _io_dev.read_int8();
++stream_pos;
*dst_it++ = pal[ packed_indices >> 4 ];
if( ++i == second )
break;
*dst_it++ = pal[ packed_indices & 0x0f ];
}
}
else
{
for( int i = 0; i < count; ++i )
{
uint8_t c = _io_dev.read_int8();
++stream_pos;
*dst_it++ = pal[ c ];
}
}
// pad to word boundary
if( ( stream_pos - offset ) & 1 )
{
_io_dev.seek( 1, SEEK_CUR );
++stream_pos;
}
break;
}
}
}
}
}
protected:
Device& _io_dev;
image_read_info< bmp_tag > _info;
};
/////////////////////////////////// dynamic image
class bmp_type_format_checker
{
public:
bmp_type_format_checker( const bmp_bits_per_pixel::type& bpp )
: _bpp( bpp )
{}
template< typename Image >
bool apply()
{
if( _bpp < 32 )
{
return pixels_are_compatible< typename Image::value_type, rgb8_pixel_t >::value
? true
: false;
}
else
{
return pixels_are_compatible< typename Image::value_type, rgba8_pixel_t >::value
? true
: false;
}
}
private:
const bmp_bits_per_pixel::type& _bpp;
};
struct bmp_read_is_supported
{
template< typename View >
struct apply : public is_read_supported< typename get_pixel_type< View >::type
, bmp_tag
>
{};
};
template< typename Device
>
class dynamic_image_reader< Device
, bmp_tag
>
: public reader< Device
, bmp_tag
, detail::read_and_no_convert
>
{
typedef reader< Device
, bmp_tag
, detail::read_and_no_convert
> parent_t;
public:
dynamic_image_reader( Device& device
, const image_read_settings< bmp_tag >& settings
)
: parent_t( device
, settings
)
{}
template< typename Images >
void apply( any_image< Images >& images )
{
if( !this->_info._valid )
{
parent_t::get_info();
}
bmp_type_format_checker format_checker( this->_info._bits_per_pixel );
if( !construct_matched( images
, format_checker
))
{
io_error( "No matching image type between those of the given any_image and that of the file" );
}
else
{
init_image( images
, this->_info
);
dynamic_io_fnobj< bmp_read_is_supported
, parent_t
> op( this );
apply_operation( view( images )
, op
);
}
}
};
} // detail
} // gil
} // boost
#endif // BOOST_GIL_EXTENSION_IO_BMP_IO_READ_HPP
@@ -0,0 +1,147 @@
/*
Copyright 2008 Christian Henning
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_BMP_SUPPORTED_TYPES_HPP
#define BOOST_GIL_EXTENSION_IO_BMP_SUPPORTED_TYPES_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/mpl/not.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/gil/channel.hpp>
#include <boost/gil/color_base.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
namespace boost { namespace gil { namespace detail {
// Read support
template< typename Channel
, typename ColorSpace
>
struct bmp_read_support : read_support_false
{
static const bmp_bits_per_pixel::type bpp = 0;
};
template< typename BitField
, bool Mutable
>
struct bmp_read_support< packed_dynamic_channel_reference< BitField
, 1
, Mutable
>
, gray_t
> : read_support_true
{
static const bmp_bits_per_pixel::type bpp = 1;
};
template< typename BitField
, bool Mutable
>
struct bmp_read_support< packed_dynamic_channel_reference< BitField
, 4
, Mutable
>
, gray_t
> : read_support_true
{
static const bmp_bits_per_pixel::type bpp = 4;
};
template<>
struct bmp_read_support< bits8
, gray_t
> : read_support_true
{
static const bmp_bits_per_pixel::type bpp = 8;
};
template<>
struct bmp_read_support< bits8
, rgb_t
> : read_support_true
{
static const bmp_bits_per_pixel::type bpp = 24;
};
template<>
struct bmp_read_support< bits8
, rgba_t
> : read_support_true
{
static const bmp_bits_per_pixel::type bpp = 32;
};
// Write support
template< typename Channel
, typename ColorSpace
>
struct bmp_write_support : write_support_false
{};
template<>
struct bmp_write_support< bits8
, rgb_t
> : write_support_true {};
template<>
struct bmp_write_support< bits8
, rgba_t
> : write_support_true {};
} // namespace detail
template< typename Pixel >
struct is_read_supported< Pixel
, bmp_tag
>
: mpl::bool_< detail::bmp_read_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
>::is_supported
>
{
typedef detail::bmp_read_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
> parent_t;
static const typename bmp_bits_per_pixel::type bpp = parent_t::bpp;
};
template< typename Pixel >
struct is_write_supported< Pixel
, bmp_tag
>
: mpl::bool_< detail::bmp_write_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
>::is_supported
> {};
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_BMP_SUPPORTED_TYPES_HPP
@@ -0,0 +1,215 @@
/*
Copyright 2008 Christian Henning
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_BMP_IO_WRITE_HPP
#define BOOST_GIL_EXTENSION_IO_BMP_IO_WRITE_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <vector>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
#include <boost/gil/extension/io_new/bmp_tags.hpp>
namespace boost { namespace gil { namespace detail {
template < int N > struct get_bgr_cs {};
template <> struct get_bgr_cs< 1 > { typedef gray8_view_t type; };
template <> struct get_bgr_cs< 3 > { typedef bgr8_view_t type; };
template <> struct get_bgr_cs< 4 > { typedef bgra8_view_t type; };
template< typename Device >
class writer< Device
, bmp_tag
>
{
public:
writer( Device& file )
: _out( file )
{
}
~writer()
{
}
template<typename View>
void apply( const View& view )
{
write( view );
}
template<typename View>
void apply( const View& view
, const image_write_info< bmp_tag >& /* info */
)
{
// Add code here, once image_write_info< bmp_tag > isn't empty anymore.
write( view );
}
private:
template< typename View >
void write( const View& view )
{
typedef typename channel_type<
typename get_pixel_type< View >::type >::type channel_t;
typedef typename color_space_type< View >::type color_space_t;
// check if supported
/*
/// todo
if( bmp_read_write_support_private<channel_t, color_space_t>::channel != 8)
{
io_error("Input view type is incompatible with the image type");
}
*/
// compute the file size
int bpp = num_channels< View >::value * 8;
int entries = 0;
/*
/// @todo: Not supported for now. bit_aligned_images refer to indexed images
/// in this context.
if( bpp <= 8 )
{
entries = 1 << bpp;
}
*/
std::size_t spn = ( view.width() * num_channels< View >::value + 3 ) & ~3;
std::size_t ofs = bmp_header_size::_size
+ bmp_header_size::_win32_info_size
+ entries * 4;
std::size_t siz = ofs + spn * view.height();
// write the BMP file header
_out.write_int16( bmp_signature );
_out.write_int32( (uint32_t) siz );
_out.write_int16( 0 );
_out.write_int16( 0 );
_out.write_int32( (uint32_t) ofs );
// writes Windows information header
_out.write_int32( bmp_header_size::_win32_info_size );
_out.write_int32( static_cast< uint32_t >( view.width() ));
_out.write_int32( static_cast< uint32_t >( view.height() ));
_out.write_int16( 1 );
_out.write_int16( static_cast< uint16_t >( bpp ));
_out.write_int32( bmp_compression::_rgb );
_out.write_int32( 0 );
_out.write_int32( 0 );
_out.write_int32( 0 );
_out.write_int32( entries );
_out.write_int32( 0 );
write_image< View
, typename get_bgr_cs< num_channels< View >::value >::type
>( view, spn );
}
template< typename View
, typename BMP_View
>
void write_image( const View& view
, const std::size_t spn
)
{
byte_vector_t buffer( spn );
std::fill( buffer.begin(), buffer.end(), 0 );
BMP_View row = interleaved_view( view.width()
, 1
, (typename BMP_View::value_type*) &buffer.front()
, spn
);
for( typename View::y_coord_t y = view.height() - 1; y > -1; --y )
{
copy_pixels( subimage_view( view
, 0
, (int) y
, (int) view.width()
, 1
)
, row
);
_out.write( &buffer.front(), spn );
}
}
private:
Device& _out;
};
struct bmp_write_is_supported
{
template< typename View >
struct apply
: public is_write_supported< typename get_pixel_type< View >::type
, bmp_tag
>
{};
};
template< typename Device >
class dynamic_image_writer< Device
, bmp_tag
>
: public writer< Device
, bmp_tag
>
{
typedef writer< Device
, bmp_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< bmp_write_is_supported
, parent_t
> op( this );
apply_operation( views, op );
}
};
} // detail
} // gil
} // boost
#endif // BOOST_GIL_EXTENSION_IO_BMP_IO_WRITE_HPP
@@ -0,0 +1,39 @@
/*
Copyright 2010 Christian Henning
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_DETAIL_JPEG_IO_BASE_HPP
#define BOOST_GIL_EXTENSION_IO_DETAIL_JPEG_IO_BASE_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2010 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/gil/extension/io_new/jpeg_tags.hpp>
namespace boost { namespace gil { namespace detail {
class jpeg_io_base
{
protected:
jpeg_error_mgr _jerr;
jmp_buf _mark;
};
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_DETAIL_JPEG_IO_BASE_HPP
@@ -0,0 +1,54 @@
/*
Copyright 2009 Christian Henning
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_JPEG_IO_IS_ALLOWED_HPP
#define BOOST_GIL_EXTENSION_IO_JPEG_IO_IS_ALLOWED_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
namespace boost { namespace gil { namespace detail {
template< typename View >
bool is_allowed( const image_read_info< jpeg_tag >& info
, mpl::true_ // is read_and_no_convert
)
{
if( info._color_space == JCS_YCbCr )
{
// We read JCS_YCbCr files as rgb.
return ( is_read_supported< typename View::value_type
, jpeg_tag
>::_color_space == JCS_RGB );
}
return ( is_read_supported< typename View::value_type
, jpeg_tag
>::_color_space == info._color_space );
}
template< typename View >
bool is_allowed( const image_read_info< jpeg_tag >& /* info */
, mpl::false_ // is read_and_convert
)
{
return true;
}
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_JPEG_IO_IS_ALLOWED_HPP
@@ -0,0 +1,418 @@
/*
Copyright 2007-2008 Christian Henning, Andreas Pokorny, 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_JPEG_IO_READ_HPP
#define BOOST_GIL_EXTENSION_IO_JPEG_IO_READ_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning, Andreas Pokorny, Lubomir Bourdev \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <csetjmp>
#include <vector>
#include <boost/gil/extension/io_new/jpeg_tags.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/conversion_policies.hpp>
#include <boost/gil/extension/io_new/detail/reader_base.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
#include <boost/gil/extension/io_new/detail/typedefs.hpp>
#include "base.hpp"
#include "is_allowed.hpp"
namespace boost { namespace gil { namespace detail {
template<typename Device>
struct jpeg_decompress_mgr : public jpeg_io_base
{
jpeg_decompress_mgr( Device& file )
: in(file)
{
_cinfo.err = jpeg_std_error( &_jerr );
_cinfo.client_data = this;
// Error exit handler: does not return to caller.
_jerr.error_exit = &jpeg_decompress_mgr::error_exit;
if( setjmp( _mark )) { raise_error(); }
_src._jsrc.bytes_in_buffer = 0;
_src._jsrc.next_input_byte = buffer;
_src._jsrc.init_source = reinterpret_cast< void(*) ( j_decompress_ptr )>( &jpeg_decompress_mgr< Device >::init_device );
_src._jsrc.fill_input_buffer = reinterpret_cast< boolean(*)( j_decompress_ptr )>( &jpeg_decompress_mgr< Device >::fill_buffer );
_src._jsrc.skip_input_data = reinterpret_cast< void(*) ( j_decompress_ptr
, long num_bytes
) >( &jpeg_decompress_mgr< Device >::skip_input_data );
_src._jsrc.term_source = reinterpret_cast< void(*) ( j_decompress_ptr ) >( &jpeg_decompress_mgr< Device >::close_device );
_src._jsrc.resync_to_restart = jpeg_resync_to_restart;
_src._this = this;
jpeg_create_decompress( &_cinfo );
_cinfo.src = &_src._jsrc;
jpeg_read_header( &_cinfo
, TRUE );
io_error_if( _cinfo.data_precision != 8
, "Image file is not supported."
);
}
~jpeg_decompress_mgr()
{
jpeg_destroy_decompress( &_cinfo );
}
protected:
// Taken from jerror.c
/*
* Error exit handler: must not return to caller.
*
* Applications may override this if they want to get control back after
* an error. Typically one would longjmp somewhere instead of exiting.
* The setjmp buffer can be made a private field within an expanded error
* handler object. Note that the info needed to generate an error message
* is stored in the error object, so you can generate the message now or
* later, at your convenience.
* You should make sure that the JPEG object is cleaned up (with jpeg_abort
* or jpeg_destroy) at some point.
*/
static void error_exit( j_common_ptr cinfo )
{
jpeg_decompress_mgr< Device >* mgr = reinterpret_cast< jpeg_decompress_mgr< Device >* >( cinfo->client_data );
longjmp( mgr->_mark, 1 );
}
void raise_error()
{
// we clean up in the destructor
io_error( "jpeg is invalid." );
}
private:
// See jdatasrc.c for default implementation for the following static member functions.
static void init_device( jpeg_decompress_struct * cinfo )
{
gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src );
src->_jsrc.bytes_in_buffer = 0;
src->_jsrc.next_input_byte = src->_this->buffer;
}
static boolean fill_buffer( jpeg_decompress_struct * cinfo )
{
gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src );
size_t count= src->_this->in.read(src->_this->buffer, sizeof(src->_this->buffer) );
if( count <= 0 )
{
// libjpeg does that: adding an EOF marker
src->_this->buffer[0] = (JOCTET) 0xFF;
src->_this->buffer[1] = (JOCTET) JPEG_EOI;
count = 2;
}
src->_jsrc.next_input_byte = src->_this->buffer;
src->_jsrc.bytes_in_buffer = count;
return TRUE;
}
static void skip_input_data( jpeg_decompress_struct * cinfo, long num_bytes )
{
gil_jpeg_source_mgr* src = reinterpret_cast< gil_jpeg_source_mgr* >( cinfo->src );
if( num_bytes > 0 )
{
while( num_bytes > long( src->_jsrc.bytes_in_buffer ))
{
num_bytes -= (long) src->_jsrc.bytes_in_buffer;
fill_buffer( cinfo );
}
src->_jsrc.next_input_byte += num_bytes;
src->_jsrc.bytes_in_buffer -= num_bytes;
}
}
static void close_device( jpeg_decompress_struct* ) {}
protected:
jpeg_decompress_struct _cinfo;
private:
Device &in;
struct gil_jpeg_source_mgr
{
jpeg_source_mgr _jsrc;
jpeg_decompress_mgr* _this;
};
gil_jpeg_source_mgr _src;
// libjpeg default is 4096 - see jdatasrc.c
JOCTET buffer[4096];
};
template< typename Device
, typename ConversionPolicy
>
class reader< Device
, jpeg_tag
, ConversionPolicy
>
: public jpeg_decompress_mgr< Device >
, public reader_base< jpeg_tag
, ConversionPolicy >
{
public:
reader( Device& device
, const image_read_settings< jpeg_tag >& settings
)
: jpeg_decompress_mgr<Device>( device )
, reader_base< jpeg_tag
, ConversionPolicy >( settings )
{}
reader( Device& device
, const typename ConversionPolicy::color_converter_type& cc
, const image_read_settings< jpeg_tag >& settings
)
: jpeg_decompress_mgr< Device >( device )
, reader_base< jpeg_tag
, ConversionPolicy >( cc
, settings
)
{}
image_read_info< jpeg_tag > get_info()
{
image_read_info<jpeg_tag> ret;
ret._width = this->_cinfo.image_width;
ret._height = this->_cinfo.image_height;
ret._num_components = this->_cinfo.num_components;
ret._color_space = this->_cinfo.jpeg_color_space;
ret._data_precision = this->_cinfo.data_precision;
return ret;
}
template<typename View>
void apply( const View& view )
{
// Fire exception in case of error.
if( setjmp( this->_mark )) { this->raise_error(); }
jpeg_decompress_struct& cinfo = this->_cinfo;
cinfo.dct_method = this->_settings._dct_method;
typedef typename is_same< ConversionPolicy
, read_and_no_convert
>::type is_read_and_convert_t;
io_error_if( !is_allowed< View >( this->_info
, is_read_and_convert_t()
)
, "Image types aren't compatible."
);
if( jpeg_start_decompress( &this->_cinfo ) == false )
{
io_error( "Cannot start decompression." );
}
switch( this->_info._color_space )
{
case JCS_GRAYSCALE: { read_rows< gray8_pixel_t >( view ); break; }
case JCS_RGB: { read_rows< rgb8_pixel_t >( view ); break; }
//!\todo add Y'CbCr? We loose image quality when reading JCS_YCbCr as JCS_RGB
case JCS_YCbCr: { read_rows< rgb8_pixel_t >( view ); break; }
case JCS_CMYK: { read_rows< cmyk8_pixel_t >( view ); break; }
//!\todo add Y'CbCrK? We loose image quality when reading JCS_YCCK as JCS_CMYK
case JCS_YCCK:
{
this->_cinfo.out_color_space = JCS_CMYK;
read_rows< cmyk8_pixel_t >( view );
break;
}
default: { io_error( "Unsupported jpeg color space." ); }
}
jpeg_finish_decompress ( &this->_cinfo );
}
private:
template< typename ImagePixel
, typename View
>
void read_rows( const View& view )
{
typedef std::vector<ImagePixel> buffer_t;
buffer_t buffer( this->_info._width );
// In case of an error we'll jump back to here and fire an exception.
// @todo Is the buffer above cleaned up when the exception is thrown?
// The strategy right now is to allocate necessary memory before
// the setjmp.
if( setjmp( this->_mark )) { this->raise_error(); }
JSAMPLE *row_adr = reinterpret_cast< JSAMPLE* >( &buffer[0] );
//Skip scanlines if necessary.
for( int y = 0; y < this->_settings._top_left.y; ++y )
{
io_error_if( jpeg_read_scanlines( &this->_cinfo
, &row_adr
, 1
) !=1
, "jpeg_read_scanlines: fail to read JPEG file"
);
}
// Read data.
for( int y = 0; y < view.height(); ++y )
{
io_error_if( jpeg_read_scanlines( &this->_cinfo
, &row_adr
, 1
) !=1
, "jpeg_read_scanlines: fail to read JPEG file"
);
typename buffer_t::iterator beg = buffer.begin() + this->_settings._top_left.x;
typename buffer_t::iterator end = beg + this->_settings._dim.x;
this->_cc_policy.read( beg
, end
, view.row_begin( y )
);
}
//@todo: There might be a better way to do that.
while( this->_cinfo.output_scanline < this->_cinfo.image_height )
{
io_error_if( jpeg_read_scanlines( &this->_cinfo
, &row_adr
, 1
) !=1
, "jpeg_read_scanlines: fail to read JPEG file"
);
}
}
};
struct jpeg_type_format_checker
{
jpeg_type_format_checker( jpeg_color_space::type color_space )
: _color_space( color_space )
{}
template< typename Image >
bool apply()
{
return is_read_supported< typename get_pixel_type< typename Image::view_t >::type
, jpeg_tag
>::_color_space == _color_space;
}
private:
jpeg_color_space::type _color_space;
};
struct jpeg_read_is_supported
{
template< typename View >
struct apply : public is_read_supported< typename get_pixel_type< View >::type
, jpeg_tag
>
{};
};
template< typename Device
>
class dynamic_image_reader< Device
, jpeg_tag
>
: public reader< Device
, jpeg_tag
, detail::read_and_no_convert
>
{
typedef reader< Device
, jpeg_tag
, detail::read_and_no_convert
> parent_t;
public:
dynamic_image_reader( Device& device
, const image_read_settings< jpeg_tag >& settings
)
: parent_t( device
, settings
)
{}
template< typename Images >
void apply( any_image< Images >& images )
{
jpeg_type_format_checker format_checker( this->_info._color_space != JCS_YCbCr
? this->_info._color_space
: JCS_RGB
);
if( !construct_matched( images
, format_checker
))
{
io_error( "No matching image type between those of the given any_image and that of the file" );
}
else
{
init_image( images
, this->_info
);
dynamic_io_fnobj< jpeg_read_is_supported
, parent_t
> op( this );
apply_operation( view( images )
, op
);
}
}
};
} // detail
} // gil
} // boost
#endif // BOOST_GIL_EXTENSION_IO_JPEG_IO_READ_HPP
@@ -0,0 +1,121 @@
/*
Copyright 2007-2008 Christian Henning, Andreas Pokorny, 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_JPEG_SUPPORTED_TYPES_HPP
#define BOOST_GIL_EXTENSION_IO_JPEG_SUPPORTED_TYPES_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning, Andreas Pokorny, Lubomir Bourdev \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/mpl/not.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/gil/channel.hpp>
#include <boost/gil/color_base.hpp>
namespace boost { namespace gil { namespace detail {
// Read support
template< jpeg_color_space::type ColorSpace >
struct jpeg_rw_support_base
{
static const jpeg_color_space::type _color_space = ColorSpace;
};
template< typename Channel
, typename ColorSpace
>
struct jpeg_read_support : read_support_false
, jpeg_rw_support_base< JCS_UNKNOWN > {};
template<>
struct jpeg_read_support< bits8
, rgb_t
> : read_support_true
, jpeg_rw_support_base< JCS_RGB > {};
template<>
struct jpeg_read_support< bits8
, cmyk_t
> : read_support_true
, jpeg_rw_support_base< JCS_CMYK > {};
template<>
struct jpeg_read_support< bits8
, gray_t
> : read_support_true
, jpeg_rw_support_base< JCS_GRAYSCALE > {};
// Write support
template< typename Channel
, typename ColorSpace
>
struct jpeg_write_support : write_support_false
, jpeg_rw_support_base< JCS_UNKNOWN > {};
template<>
struct jpeg_write_support< bits8
, gray_t
> : write_support_true
, jpeg_rw_support_base< JCS_GRAYSCALE > {};
template<>
struct jpeg_write_support< bits8
, rgb_t
> : write_support_true
, jpeg_rw_support_base< JCS_RGB > {};
template<>
struct jpeg_write_support< bits8
, cmyk_t
> : write_support_true
, jpeg_rw_support_base< JCS_CMYK > {};
} // namespace detail
template< typename Pixel >
struct is_read_supported< Pixel
, jpeg_tag
>
: mpl::bool_< detail::jpeg_read_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
>::is_supported
>
{
typedef detail::jpeg_read_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
> parent_t;
static const typename jpeg_color_space::type _color_space = parent_t::_color_space;
};
template< typename Pixel >
struct is_write_supported< Pixel
, jpeg_tag
>
: mpl::bool_< detail::jpeg_write_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
>::is_supported
>
{};
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_JPEG_SUPPORTED_TYPES_HPP
@@ -0,0 +1,317 @@
/*
Copyright 2007-2008 Christian Henning, Andreas Pokorny, 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_JPEG_IO_WRITE_HPP
#define BOOST_GIL_EXTENSION_IO_JPEG_IO_WRITE_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning, Andreas Pokorny, Lubomir Bourdev \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <vector>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
#include <boost/gil/extension/io_new/jpeg_tags.hpp>
#include "base.hpp"
#include "supported_types.hpp"
namespace boost { namespace gil { namespace detail {
template< typename Device >
class writer< Device
, jpeg_tag
> : public jpeg_io_base
{
public:
writer( Device& file )
: out( file )
{
_cinfo.err = jpeg_std_error( &_jerr );
_cinfo.client_data = this;
// Error exit handler: does not return to caller.
_jerr.error_exit = &writer< Device, jpeg_tag >::error_exit;
// Fire exception in case of error.
if( setjmp( _mark )) { raise_error(); }
_dest._jdest.free_in_buffer = sizeof( buffer );
_dest._jdest.next_output_byte = buffer;
_dest._jdest.init_destination = reinterpret_cast< void(*) ( j_compress_ptr ) >( &writer< Device, jpeg_tag >::init_device );
_dest._jdest.empty_output_buffer = reinterpret_cast< boolean(*)( j_compress_ptr ) >( &writer< Device, jpeg_tag >::empty_buffer );
_dest._jdest.term_destination = reinterpret_cast< void(*) ( j_compress_ptr ) >( &writer< Device, jpeg_tag >::close_device );
_dest._this = this;
jpeg_create_compress( &_cinfo );
_cinfo.dest = &_dest._jdest;
}
~writer()
{
jpeg_finish_compress ( &_cinfo );
jpeg_destroy_compress( &_cinfo );
}
template<typename View>
void apply( const View& view )
{
write_rows( view
, jpeg_quality::default_value
, jpeg_dct_method::default_value
);
}
template< typename View >
void apply( const View& view
, const image_write_info< jpeg_tag >& info )
{
write_rows( view
, info._quality
, info._dct_method
);
}
private:
template<typename View>
void write_rows( const View& view
, const jpeg_quality::type quality
, const jpeg_dct_method::type dct_method
)
{
std::vector< typename View::value_type > row_buffer( view.width() );
// In case of an error we'll jump back to here and fire an exception.
// @todo Is the buffer above cleaned up when the exception is thrown?
// The strategy right now is to allocate necessary memory before
// the setjmp.
if( setjmp( _mark )) { raise_error(); }
typedef typename channel_type< typename View::value_type >::type channel_t;
_cinfo.image_width = JDIMENSION( view.width() );
_cinfo.image_height = JDIMENSION( view.height() );
_cinfo.input_components = num_channels<View>::value;
_cinfo.in_color_space = detail::jpeg_write_support< channel_t
, typename color_space_type< View >::type
>::_color_space;
jpeg_set_defaults( &_cinfo );
jpeg_set_quality ( &_cinfo
, quality
, TRUE
);
// Needs to be done after jpeg_set_defaults() since it's overridding this value back to slow.
_cinfo.dct_method = dct_method;
jpeg_start_compress( &_cinfo
, TRUE
);
JSAMPLE* row_addr = reinterpret_cast< JSAMPLE* >( &row_buffer[0] );
for( int y =0; y != view.height(); ++ y )
{
std::copy( view.row_begin( y )
, view.row_end ( y )
, row_buffer.begin()
);
jpeg_write_scanlines( &this->_cinfo
, &row_addr
, 1
);
}
}
struct gil_jpeg_destination_mgr
{
jpeg_destination_mgr _jdest;
writer<Device,jpeg_tag> * _this;
};
static void init_device( jpeg_compress_struct* cinfo )
{
gil_jpeg_destination_mgr* dest = reinterpret_cast< gil_jpeg_destination_mgr* >( cinfo->dest );
dest->_jdest.free_in_buffer = sizeof( dest->_this->buffer );
dest->_jdest.next_output_byte = dest->_this->buffer;
}
static boolean empty_buffer( jpeg_compress_struct* cinfo )
{
gil_jpeg_destination_mgr* dest = reinterpret_cast< gil_jpeg_destination_mgr* >( cinfo->dest );
dest->_this->out.write( dest->_this->buffer
, buffer_size
);
writer<Device,jpeg_tag>::init_device( cinfo );
return 1;
}
static void close_device( jpeg_compress_struct* cinfo )
{
writer<Device,jpeg_tag>::empty_buffer( cinfo );
gil_jpeg_destination_mgr* dest = reinterpret_cast< gil_jpeg_destination_mgr* >( cinfo->dest );
dest->_this->out.flush();
}
void raise_error()
{
io_error( "Cannot write jpeg file." );
}
static void error_exit( j_common_ptr cinfo )
{
writer< Device, jpeg_tag >* mgr = reinterpret_cast< writer< Device, jpeg_tag >* >( cinfo->client_data );
longjmp( mgr->_mark, 1 );
}
private:
Device &out;
jpeg_compress_struct _cinfo;
gil_jpeg_destination_mgr _dest;
static const unsigned int buffer_size = 1024;
JOCTET buffer[buffer_size];
};
struct jpeg_write_is_supported
{
template< typename View >
struct apply
: public is_write_supported< typename get_pixel_type< View >::type
, jpeg_tag
>
{};
};
// unary application
template <typename Types, typename Tag, typename Bits, typename Op>
typename Op::result_type GIL_FORCEINLINE apply_operation_basec( const Bits& bits
, std::size_t index
, const image_write_info< Tag >& info
, Op op
)
{
return detail::apply_operation_fwd_fn<mpl::size<Types>::value>().template applyc<Types>( bits
, index
, op
);
}
// unary application
template< typename Types
, typename Info
, typename Bits
, typename Op
>
typename Op::result_type GIL_FORCEINLINE apply_operation_base( Bits& bits
, std::size_t index
, const Info& info
, Op op
)
{
return detail::apply_operation_fwd_fn< mpl::size< Types >::value>().template apply< Types
>( bits
, index
, info
, op
);
}
/// \ingroup Variant
/// \brief Invokes a generic constant operation (represented as a binary function object) on two variants
template< typename Types1
, typename Info
, typename BinaryOp
>
GIL_FORCEINLINE
typename BinaryOp::result_type apply_operation( const variant< Types1 >& arg1
, const Info& info
, BinaryOp op
)
{
typename variant< Types1 >::base_t bits = arg1.bits();
return apply_operation_base< Types1
, image_write_info< jpeg_tag >
>( bits
, arg1.index()
, info
, op
);
}
template< typename Device >
class dynamic_image_writer< Device
, jpeg_tag
>
: public writer< Device
, jpeg_tag
>
{
typedef writer< Device
, jpeg_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< jpeg_write_is_supported
, parent_t
> op( this );
apply_operation( views, op );
}
template< typename Views >
void apply( const any_image_view < Views >& views
, const image_write_info< jpeg_tag >& info
)
{
dynamic_io_fnobj< jpeg_write_is_supported
, parent_t
> op( this );
apply_operation( views, info, op );
}
};
} // detail
} // gil
} // boost
#endif // BOOST_GIL_EXTENSION_IO_JPEG_IO_WRITE_HPP
@@ -0,0 +1,99 @@
/*
Copyright 2007-2008 Christian Henning, Andreas Pokorny, 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_DETAIL_PNG_IO_BASE_HPP
#define BOOST_GIL_EXTENSION_IO_DETAIL_PNG_IO_BASE_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning, Andreas Pokorny, Lubomir Bourdev \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/gil/extension/io_new/png_tags.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/reader_base.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
#include <boost/gil/extension/io_new/detail/typedefs.hpp>
namespace boost { namespace gil { namespace detail {
template<typename Device>
class png_io_base
{
public:
png_io_base( Device & io_dev )
: _io_dev(io_dev)
{}
protected:
Device & _io_dev;
void check() const
{
byte_t buf[PNG_BYTES_TO_CHECK];
io_error_if(_io_dev.read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK,
"png_check_validity: failed to read image");
io_error_if(png_sig_cmp(png_bytep(buf), png_size_t(0), PNG_BYTES_TO_CHECK)!=0,
"png_check_validity: invalid png image");
}
static void read_data( png_structp png_ptr
, png_bytep data
, png_size_t length
)
{
static_cast<Device*>(png_get_io_ptr(png_ptr) )->read( data
, length );
}
static void write_data( png_structp png_ptr
, png_bytep data
, png_size_t length
)
{
static_cast<Device*>( png_get_io_ptr( png_ptr ))->write( data
, length );
}
static void flush( png_structp png_ptr )
{
static_cast<Device*>(png_get_io_ptr(png_ptr) )->flush();
}
static int read_user_chunk_callback( png_struct* /* png_ptr */
, png_unknown_chunkp /* chunk */
)
{
// @todo
return 0;
}
static void read_row_callback( png_structp /* png_ptr */
, png_uint_32 /* row_number */
, int /* pass */
)
{
// @todo
}
};
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_DETAIL_PNG_IO_BASE_HPP
@@ -0,0 +1,55 @@
/*
Copyright 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_PNG_IO_IS_ALLOWED_HPP
#define BOOST_GIL_EXTENSION_IO_PNG_IO_IS_ALLOWED_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/mpl/for_each.hpp>
namespace boost { namespace gil { namespace detail {
template< typename View >
bool is_allowed( const image_read_info< png_tag >& info
, mpl::true_ // is read_and_no_convert
)
{
typedef typename get_pixel_type< View >::type pixel_t;
typedef typename channel_traits<
typename element_type< pixel_t >::type >::value_type channel_t;
const png_num_channels::type dst_num_channels = num_channels< pixel_t >::value;
const png_bitdepth::type dst_bit_depth = detail::unsigned_integral_num_bits< channel_t >::value;
return dst_num_channels == info._num_channels
&& dst_bit_depth == info._bit_depth;
}
template< typename View >
bool is_allowed( const image_read_info< png_tag >& /* info */
, mpl::false_ // is read_and_convert
)
{
return true;
}
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_PNG_IO_IS_ALLOWED_HPP
@@ -0,0 +1,923 @@
/*
Copyright 2007-2008 Christian Henning, Andreas Pokorny, 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_DETAIL_PNG_IO_READ_HPP
#define BOOST_GIL_EXTENSION_IO_DETAIL_PNG_IO_READ_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning, Andreas Pokorny, Lubomir Bourdev \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/gil/extension/io_new/png_tags.hpp>
#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/reader_base.hpp>
#include <boost/gil/extension/io_new/detail/conversion_policies.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
#include <boost/gil/extension/io_new/detail/typedefs.hpp>
#include <boost/gil/extension/io_new/detail/row_buffer_helper.hpp>
#include "base.hpp"
#include "is_allowed.hpp"
namespace boost { namespace gil { namespace detail {
template< typename Device
, typename ConversionPolicy
>
class reader< Device
, png_tag
, ConversionPolicy
>
: public png_io_base< Device >
, public reader_base< png_tag
, ConversionPolicy >
{
public:
reader( Device& io_dev
, const image_read_settings< png_tag >& settings
)
: png_io_base< Device >( io_dev )
, reader_base< png_tag
, ConversionPolicy >( settings )
, _png_ptr ( NULL )
, _info_ptr( NULL )
{
this->check();
init_reader();
}
reader( Device& io_dev
, const typename ConversionPolicy::color_converter_type& cc
, const image_read_settings< png_tag >& settings
)
: png_io_base< Device >( io_dev )
, reader_base< png_tag
, ConversionPolicy >( cc
, settings
)
, _png_ptr ( 0 )
, _info_ptr( 0 )
{
this->check();
init_reader();
}
~reader()
{
png_destroy_read_struct( &_png_ptr
, &_info_ptr
, NULL
);
}
image_read_info<png_tag> get_info() const
{
image_read_info< png_tag > ret;
// get PNG_IHDR chunk information from png_info structure
png_get_IHDR( _png_ptr
, _info_ptr
, &ret._width
, &ret._height
, &ret._bit_depth
, &ret._color_type
, &ret._interlace_method
, &ret._compression_method
, &ret._filter_method
);
// get number of color channels in image
ret._num_channels = png_get_channels( _png_ptr
, _info_ptr
);
#ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
// Get CIE chromacities and referenced white point for given image
if( this->_settings._read_cie_chromacities )
{
ret._valid_cie_colors = png_get_cHRM( _png_ptr
, _info_ptr
, &ret._white_x, &ret._white_y
, &ret._red_x, &ret._red_y
, &ret._green_x, &ret._green_y
, &ret._blue_x, &ret._blue_y
);
}
// get the gamma value for given image
if( this->_settings._read_file_gamma )
{
ret._valid_file_gamma = png_get_gAMA( _png_ptr
, _info_ptr
, &ret._file_gamma
);
if( ret._valid_file_gamma == false )
{
ret._file_gamma = 1.0;
}
}
#else
// Get CIE chromacities and referenced white point for given image
if( this->_settings._read_cie_chromacities )
{
ret._valid_cie_colors = png_get_cHRM_fixed( _png_ptr
, _info_ptr
, &ret._white_x, &ret._white_y
, &ret._red_x, &ret._red_y
, &ret._green_x, &ret._green_y
, &ret._blue_x, &ret._blue_y
);
}
// get the gamma value for given image
if( this->_settings._read_file_gamma )
{
ret._valid_file_gamma = png_get_gAMA_fixed( _png_ptr
, _info_ptr
, &ret._file_gamma
);
if( ret._valid_file_gamma == false )
{
ret._file_gamma = 1;
}
}
#endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
// get the embedded ICC profile data for given image
if( this->_settings._read_icc_profile )
{
png_charp icc_name = png_charp( NULL );
png_charp profile = png_charp( NULL );
ret._valid_icc_profile = png_get_iCCP( _png_ptr
, _info_ptr
, &icc_name
, &ret._iccp_compression_type
, &profile
, &ret._profile_length
);
if( icc_name )
{
ret._icc_name.append( icc_name
, std::strlen( icc_name )
);
}
if( ret._profile_length > 0 )
{
ret._profile.append( profile
, ret._profile_length
);
}
}
// get the rendering intent for given image
if( this->_settings._read_intent )
{
ret._valid_intent = png_get_sRGB( _png_ptr
, _info_ptr
, &ret._intent
);
}
// get image palette information from png_info structure
if( this->_settings._read_palette )
{
png_colorp palette = png_colorp( NULL );
ret._valid_palette = png_get_PLTE( _png_ptr
, _info_ptr
, &palette
, &ret._num_palette
);
if( ret._num_palette > 0 )
{
ret._palette.resize( ret._num_palette );
std::copy( palette
, palette + ret._num_palette
, &ret._palette.front()
);
}
}
// get background color for given image
if( this->_settings._read_background )
{
png_color_16p background = png_color_16p( NULL );
ret._valid_background = png_get_bKGD( _png_ptr
, _info_ptr
, &background
);
if( background )
{
ret._background = *background;
}
}
// get the histogram for given image
if( this->_settings._read_histogram )
{
png_uint_16p histogram = png_uint_16p( NULL );
ret._valid_histogram = png_get_hIST( _png_ptr
, _info_ptr
, &histogram
);
if( histogram )
{
// the number of values is set by the number of colors inside
// the palette.
if( this->_settings._read_palette == false )
{
png_colorp palette = png_colorp( NULL );
png_get_PLTE( _png_ptr
, _info_ptr
, &palette
, &ret._num_palette
);
}
std::copy( histogram
, histogram + ret._num_palette
, &ret._histogram.front()
);
}
}
// get screen offsets for the given image
if( this->_settings._read_screen_offsets )
{
ret._valid_offset = png_get_oFFs( _png_ptr
, _info_ptr
, &ret._offset_x
, &ret._offset_y
, &ret._off_unit_type
);
}
// get pixel calibration settings
if( this->_settings._read_pixel_calibration )
{
png_charp purpose = png_charp ( NULL );
png_charp units = png_charp ( NULL );
png_charpp params = png_charpp( NULL );
ret._valid_pixel_calibration = png_get_pCAL( _png_ptr
, _info_ptr
, &purpose
, &ret._X0
, &ret._X1
, &ret._cal_type
, &ret._num_params
, &units
, &params
);
if( purpose )
{
ret._purpose.append( purpose
, std::strlen( purpose )
);
}
if( units )
{
ret._units.append( units
, std::strlen( units )
);
}
if( ret._num_params > 0 )
{
ret._params.resize( ret._num_params );
for( png_CAL_nparam::type i = 0
; i < ret._num_params
; ++i
)
{
ret._params[i].append( params[i]
, std::strlen( params[i] )
);
}
}
}
// get the physical resolution for given image
if( this->_settings._read_physical_resolution )
{
ret._valid_resolution = png_get_pHYs( _png_ptr
, _info_ptr
, &ret._res_x
, &ret._res_y
, &ret._phy_unit_type
);
}
// get number of significant bits for each color channel
if( this->_settings._read_number_of_significant_bits )
{
png_color_8p sig_bits = png_color_8p( NULL );
ret._valid_significant_bits = png_get_sBIT( _png_ptr
, _info_ptr
, &sig_bits
);
// @todo Is there one or more colors?
if( sig_bits )
{
ret._sig_bits = *sig_bits;
}
}
#ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
// get physical scale settings
if( this->_settings._read_scale_factors )
{
ret._valid_scale_factors = png_get_sCAL( _png_ptr
, _info_ptr
, &ret._scale_unit
, &ret._scale_width
, &ret._scale_height
);
}
#else
#ifdef BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED
if( this->_settings._read_scale_factors )
{
png_charp scale_width, scale_height;
if( ret._valid_scale_factors = png_get_sCAL_s( _png_ptr
, _info_ptr
, &ret._scale_unit
, &scale_width
, &scale_height
)
)
{
if( scale_width )
{
ret._scale_width.append( scale_width
, std::strlen( scale_width )
);
}
if( scale_height )
{
ret._scale_height.append( scale_height
, std::strlen( scale_height )
);
}
}
}
#endif // BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED
#endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
// get comments information from png_info structure
if( this->_settings._read_comments )
{
png_textp text = png_textp( NULL );
ret._valid_text = png_get_text( _png_ptr
, _info_ptr
, &text
, &ret._num_text
);
if( ret._num_text > 0 )
{
ret._text.resize( ret._num_text );
for( png_num_text::type i = 0
; i < ret._num_text
; ++i
)
{
ret._text[i]._compression = text[i].compression;
ret._text[i]._key.append( text[i].key
, std::strlen( text[i].key )
);
ret._text[i]._text.append( text[i].text
, std::strlen( text[i].text )
);
}
}
}
// get last modification time for the image
if( this->_settings._read_last_modification_time )
{
png_timep mod_time = png_timep( NULL );
ret._valid_modification_time = png_get_tIME( _png_ptr
, _info_ptr
, &mod_time
);
ret._mod_time = *mod_time;
}
// get transparency data for images
if( this->_settings._read_transparency_data )
{
png_bytep trans = png_bytep ( NULL );
png_color_16p trans_values = png_color_16p( NULL );
ret._valid_transparency_factors = png_get_tRNS( _png_ptr
, _info_ptr
, &trans
, &ret._num_trans
, &trans_values
);
if( trans )
{
//@todo What to do, here? How do I know the length of the "trans" array?
}
if( ret._num_trans )
{
ret._trans_values.resize( ret._num_trans );
std::copy( trans_values
, trans_values + ret._num_trans
, &ret._trans_values.front()
);
}
}
// @todo One day!
/*
if( false )
{
png_unknown_chunkp unknowns = png_unknown_chunkp( NULL );
int num_unknowns = static_cast< int >( png_get_unknown_chunks( _png_ptr
, _info_ptr
, &unknowns
)
);
}
*/
return ret;
}
template< typename View >
void apply( const View& view )
{
// The info structures are filled at this point.
// Now it's time for some transformations.
if( little_endian() )
{
if( this->_info._bit_depth == 16 )
{
// Swap bytes of 16 bit files to least significant byte first.
png_set_swap( _png_ptr );
}
if( this->_info._bit_depth < 8 )
{
// swap bits of 1, 2, 4 bit packed pixel formats
png_set_packswap( _png_ptr );
}
}
if( this->_info._color_type == PNG_COLOR_TYPE_PALETTE )
{
png_set_palette_to_rgb( _png_ptr );
}
if( this->_info._num_trans > 0 )
{
png_set_tRNS_to_alpha( _png_ptr );
}
// Tell libpng to handle the gamma conversion for you. The final call
// is a good guess for PC generated images, but it should be configurable
// by the user at run time by the user. It is strongly suggested that
// your application support gamma correction.
if( this->_settings._apply_screen_gamma )
{
// png_set_gamma will change the image data!
#ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
png_set_gamma( _png_ptr
, this->_settings._screen_gamma
, this->_info._file_gamma
);
#else
png_set_gamma( _png_ptr
, this->_settings._screen_gamma
, this->_info._file_gamma
);
#endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
}
// Turn on interlace handling. REQUIRED if you are not using
// png_read_image(). To see how to handle interlacing passes,
// see the png_read_row() method below:
_number_passes = png_set_interlace_handling( _png_ptr );
// The above transformation might have changed the bit_depth and color type.
png_read_update_info( _png_ptr
, _info_ptr
);
this->_info._bit_depth = png_get_bit_depth( _png_ptr
, _info_ptr
);
this->_info._num_channels = png_get_channels( _png_ptr
, _info_ptr
);
this->_info._color_type = png_get_color_type( _png_ptr
, _info_ptr
);
switch( this->_info._color_type )
{
case PNG_COLOR_TYPE_GRAY:
{
switch( this->_info._bit_depth )
{
case 1: read_rows< gray1_image_t::view_t::reference >( view ); break;
case 2: read_rows< gray2_image_t::view_t::reference >( view ); break;
case 4: read_rows< gray4_image_t::view_t::reference >( view ); break;
case 8: read_rows< gray8_pixel_t >( view ); break;
case 16: read_rows< gray16_pixel_t >( view ); break;
default: io_error( "png_reader::read_data(): unknown combination of color type and bit depth" );
}
break;
}
case PNG_COLOR_TYPE_GA:
{
#ifdef BOOST_GIL_IO_ENABLE_GRAY_ALPHA
switch( this->_info._bit_depth )
{
case 8: read_rows< gray_alpha8_pixel_t > ( view ); break;
case 16: read_rows< gray_alpha16_pixel_t >( view ); break;
default: io_error( "png_reader::read_data(): unknown combination of color type and bit depth" );
}
#else
io_error( "gray_alpha isn't enabled. Use ENABLE_GRAY_ALPHA when building application." );
#endif // BOOST_GIL_IO_ENABLE_GRAY_ALPHA
break;
}
case PNG_COLOR_TYPE_RGB:
{
switch( this->_info._bit_depth )
{
case 8: read_rows< rgb8_pixel_t > ( view ); break;
case 16: read_rows< rgb16_pixel_t >( view ); break;
default: io_error( "png_reader::read_data(): unknown combination of color type and bit depth" );
}
break;
}
case PNG_COLOR_TYPE_RGBA:
{
switch( this->_info._bit_depth )
{
case 8: read_rows< rgba8_pixel_t > ( view ); break;
case 16: read_rows< rgba16_pixel_t >( view ); break;
default: io_error( "png_reader_color_convert::read_data(): unknown combination of color type and bit depth" );
}
break;
}
default: io_error( "png_reader_color_convert::read_data(): unknown color type" );
}
// read rest of file, and get additional chunks in info_ptr
png_read_end( _png_ptr
, NULL
);
}
private:
template< typename ImagePixel
, typename View
>
void read_rows( const View& view )
{
typedef row_buffer_helper_view< ImagePixel > row_buffer_helper_t;
typedef typename row_buffer_helper_t::buffer_t buffer_t;
typedef typename row_buffer_helper_t::iterator_t it_t;
typedef typename is_same< ConversionPolicy
, read_and_no_convert
>::type is_read_and_convert_t;
io_error_if( !is_allowed< View >( this->_info
, is_read_and_convert_t()
)
, "Image types aren't compatible."
);
std::size_t rowbytes = png_get_rowbytes( _png_ptr
, _info_ptr
);
row_buffer_helper_t buffer( rowbytes
, true
);
png_bytep row_ptr = (png_bytep)( &( buffer.data()[0]));
for( std::size_t pass = 0; pass < _number_passes; pass++ )
{
if( pass == _number_passes - 1 )
{
// skip lines if necessary
for( std::ptrdiff_t y = 0; y < this->_settings._top_left.y; ++y )
{
// Read the image using the "sparkle" effect.
png_read_rows( _png_ptr
, &row_ptr
, NULL
, 1
);
}
for( std::ptrdiff_t y = 0
; y < this->_settings._dim.y
; ++y
)
{
// Read the image using the "sparkle" effect.
png_read_rows( _png_ptr
, &row_ptr
, NULL
, 1
);
it_t first = buffer.begin() + this->_settings._top_left.x;
it_t last = first + this->_settings._dim.x; // one after last element
this->_cc_policy.read( first
, last
, view.row_begin( y ));
}
// Read the rest of the image. libpng needs that.
std::ptrdiff_t remaining_rows = static_cast< std::ptrdiff_t >( this->_info._height )
- this->_settings._top_left.y
- this->_settings._dim.y;
for( std::ptrdiff_t y = 0
; y < remaining_rows
; ++y
)
{
// Read the image using the "sparkle" effect.
png_read_rows( _png_ptr
, &row_ptr
, NULL
, 1
);
}
}
else
{
for( int y = 0; y < view.height(); ++y )
{
// Read the image using the "sparkle" effect.
png_read_rows( _png_ptr
, &row_ptr
, NULL
, 1
);
}
}
}
}
void init_reader()
{
// Create and initialize the png_struct with the desired error handler
// functions. If you want to use the default stderr and longjump method,
// you can supply NULL for the last three parameters. We also supply the
// the compiler header file version, so that we know if the application
// was compiled with a compatible version of the library. REQUIRED
_png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING
, NULL // user_error_ptr
, NULL // user_error_fn
, NULL // user_warning_fn
);
io_error_if( _png_ptr == NULL
, "png_reader: fail to call png_create_write_struct()"
);
png_uint_32 user_chunk_data[4];
user_chunk_data[0] = 0;
user_chunk_data[1] = 0;
user_chunk_data[2] = 0;
user_chunk_data[3] = 0;
png_set_read_user_chunk_fn( _png_ptr
, user_chunk_data
, png_io_base< Device >::read_user_chunk_callback
);
// Allocate/initialize the memory for image information. REQUIRED.
_info_ptr = png_create_info_struct( _png_ptr );
if( _info_ptr == NULL )
{
png_destroy_read_struct( &_png_ptr
, NULL
, NULL
);
io_error( "png_reader: fail to call png_create_info_struct()" );
}
// Set error handling if you are using the setjmp/longjmp method (this is
// the normal method of doing things with libpng). REQUIRED unless you
// set up your own error handlers in the png_create_read_struct() earlier.
if( setjmp( png_jmpbuf( _png_ptr )))
{
//free all of the memory associated with the png_ptr and info_ptr
png_destroy_read_struct( &_png_ptr
, &_info_ptr
, NULL
);
io_error( "png is invalid" );
}
png_set_read_fn( _png_ptr
, static_cast< png_voidp >( &this->_io_dev )
, png_io_base< Device >::read_data
);
// Set up a callback function that will be
// called after each row has been read, which you can use to control
// a progress meter or the like.
png_set_read_status_fn( _png_ptr
, png_io_base< Device >::read_row_callback
);
// Set up a callback which implements user defined transformation.
// @todo
png_set_read_user_transform_fn( _png_ptr
, png_user_transform_ptr( NULL )
);
png_set_keep_unknown_chunks( _png_ptr
, PNG_HANDLE_CHUNK_ALWAYS
, NULL
, 0
);
// Make sure we read the signature.
// @todo make it an option
png_set_sig_bytes( _png_ptr
, PNG_BYTES_TO_CHECK
);
// The call to png_read_info() gives us all of the information from the
// PNG file before the first IDAT (image data chunk). REQUIRED
png_read_info( _png_ptr
, _info_ptr
);
}
private:
png_structp _png_ptr;
png_infop _info_ptr;
std::size_t _number_passes;
};
struct png_type_format_checker
{
png_type_format_checker( png_bitdepth::type bit_depth
, png_color_type::type color_type
)
: _bit_depth ( bit_depth )
, _color_type( color_type )
{}
template< typename Image >
bool apply()
{
typedef is_read_supported< typename get_pixel_type< typename Image::view_t >::type
, png_tag
> is_supported_t;
return is_supported_t::_bit_depth == _bit_depth
&& is_supported_t::_color_type == _color_type;
}
private:
png_bitdepth::type _bit_depth;
png_color_type::type _color_type;
};
struct png_read_is_supported
{
template< typename View >
struct apply : public is_read_supported< typename get_pixel_type< View >::type
, png_tag
>
{};
};
template< typename Device
>
class dynamic_image_reader< Device
, png_tag
>
: public reader< Device
, png_tag
, detail::read_and_no_convert
>
{
typedef reader< Device
, png_tag
, detail::read_and_no_convert
> parent_t;
public:
dynamic_image_reader( Device& device
, const image_read_settings< png_tag >& settings
)
: parent_t( device
, settings
)
{}
template< typename Images >
void apply( any_image< Images >& images )
{
png_type_format_checker format_checker( this->_info._bit_depth
, this->_info._color_type
);
if( !construct_matched( images
, format_checker
))
{
io_error( "No matching image type between those of the given any_image and that of the file" );
}
else
{
init_image( images
, this->_info
);
dynamic_io_fnobj< png_read_is_supported
, parent_t
> op( this );
apply_operation( view( images )
, op
);
}
}
};
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_DETAIL_PNG_IO_READ_HPP
@@ -0,0 +1,366 @@
/*
Copyright 2007-2008 Christian Henning, Andreas Pokorny, 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_PNG_SUPPORTED_TYPES_HPP
#define BOOST_GIL_EXTENSION_IO_PNG_SUPPORTED_TYPES_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning, Andreas Pokorny, Lubomir Bourdev \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#ifdef BOOST_GIL_IO_ENABLE_GRAY_ALPHA
#include <boost/gil/extension/toolbox/gray_alpha.hpp>
#endif // BOOST_GIL_IO_ENABLE_GRAY_ALPHA
namespace boost { namespace gil { namespace detail {
static const size_t PNG_BYTES_TO_CHECK = 4;
// Read support
template< png_bitdepth::type BitDepth
, png_color_type::type ColorType
>
struct png_rw_support_base
{
static const png_bitdepth::type _bit_depth = BitDepth;
static const png_color_type::type _color_type = ColorType;
};
template< typename Channel
, typename ColorSpace
>
struct png_read_support : read_support_false
, png_rw_support_base< 1
, PNG_COLOR_TYPE_GRAY
> {};
template< typename BitField
, bool Mutable
>
struct png_read_support< packed_dynamic_channel_reference< BitField
, 1
, Mutable
>
, gray_t
> : read_support_true
, png_rw_support_base< 1
, PNG_COLOR_TYPE_GRAY
> {};
template< typename BitField
, bool Mutable
>
struct png_read_support< packed_dynamic_channel_reference< BitField
, 2
, Mutable
>
, gray_t
> : read_support_true
, png_rw_support_base< 2
, PNG_COLOR_TYPE_GRAY
> {};
template< typename BitField
, bool Mutable
>
struct png_read_support< packed_dynamic_channel_reference< BitField
, 4
, Mutable
>
, gray_t
> : read_support_true
, png_rw_support_base< 4
, PNG_COLOR_TYPE_GRAY
> {};
template<>
struct png_read_support< bits8
, gray_t
> : read_support_true
, png_rw_support_base< 8
, PNG_COLOR_TYPE_GRAY
> {};
#ifdef BOOST_GIL_IO_ENABLE_GRAY_ALPHA
template<>
struct png_read_support< bits8
, gray_alpha_t
> : read_support_true
, png_rw_support_base< 8
, PNG_COLOR_TYPE_GA
> {};
#endif // BOOST_GIL_IO_ENABLE_GRAY_ALPHA
template<>
struct png_read_support< bits8
, rgb_t
> : read_support_true
, png_rw_support_base< 8
, PNG_COLOR_TYPE_RGB
> {};
template<>
struct png_read_support< bits8
, rgba_t
> : read_support_true
, png_rw_support_base< 8
, PNG_COLOR_TYPE_RGBA
> {};
template<>
struct png_read_support< bits16
, gray_t
> : read_support_true
, png_rw_support_base< 16
, PNG_COLOR_TYPE_GRAY
> {};
template<>
struct png_read_support< bits16
, rgb_t
> : read_support_true
, png_rw_support_base< 16
, PNG_COLOR_TYPE_RGB
> {};
template<>
struct png_read_support< bits16
, rgba_t
> : read_support_true
, png_rw_support_base< 16
, PNG_COLOR_TYPE_RGBA
> {};
#ifdef BOOST_GIL_IO_ENABLE_GRAY_ALPHA
template<>
struct png_read_support< bits16
, gray_alpha_t
> : read_support_true
, png_rw_support_base< 16
, PNG_COLOR_TYPE_GA
> {};
#endif // BOOST_GIL_IO_ENABLE_GRAY_ALPHA
// Write support
template< typename Channel
, typename ColorSpace
>
struct png_write_support : write_support_false
, png_rw_support_base< 1
, PNG_COLOR_TYPE_GRAY
> {};
template< typename BitField
, bool Mutable
>
struct png_write_support< packed_dynamic_channel_reference< BitField
, 1
, Mutable
>
, gray_t
> : write_support_true
, png_rw_support_base< 1
, PNG_COLOR_TYPE_GRAY
>
{};
template< typename BitField
, bool Mutable
>
struct png_write_support< packed_dynamic_channel_reference< BitField
, 1
, Mutable
> const
, gray_t
> : write_support_true
, png_rw_support_base< 1
, PNG_COLOR_TYPE_GRAY
>
{};
template< typename BitField
, bool Mutable
>
struct png_write_support< packed_dynamic_channel_reference< BitField
, 2
, Mutable
>
, gray_t
> : write_support_true
, png_rw_support_base< 2
, PNG_COLOR_TYPE_GRAY
>
{};
template< typename BitField
, bool Mutable
>
struct png_write_support< packed_dynamic_channel_reference< BitField
, 2
, Mutable
> const
, gray_t
> : write_support_true
, png_rw_support_base< 2
, PNG_COLOR_TYPE_GRAY
>
{};
template< typename BitField
, bool Mutable
>
struct png_write_support< packed_dynamic_channel_reference< BitField
, 4
, Mutable
>
, gray_t
> : write_support_true
, png_rw_support_base< 4
, PNG_COLOR_TYPE_GRAY
>
{};
template< typename BitField
, bool Mutable
>
struct png_write_support< packed_dynamic_channel_reference< BitField
, 4
, Mutable
> const
, gray_t
> : write_support_true
, png_rw_support_base< 4
, PNG_COLOR_TYPE_GRAY
>
{};
template<>
struct png_write_support< bits8
, gray_t
> : write_support_true
, png_rw_support_base< 8
, PNG_COLOR_TYPE_GRAY
>
{};
#ifdef BOOST_GIL_IO_ENABLE_GRAY_ALPHA
template<>
struct png_write_support< bits8
, gray_alpha_t
> : write_support_true
, png_rw_support_base< 8
, PNG_COLOR_TYPE_GA
>
{};
#endif // BOOST_GIL_IO_ENABLE_GRAY_ALPHA
template<>
struct png_write_support< bits8
, rgb_t
> : write_support_true
, png_rw_support_base< 8
, PNG_COLOR_TYPE_RGB
>
{};
template<>
struct png_write_support< bits8
, rgba_t
> : write_support_true
, png_rw_support_base< 8
, PNG_COLOR_TYPE_RGBA
>
{};
template<>
struct png_write_support< bits16
, gray_t
> : write_support_true
, png_rw_support_base< 16
, PNG_COLOR_TYPE_GRAY
>
{};
template<>
struct png_write_support< bits16
, rgb_t
> : write_support_true
, png_rw_support_base< 16
, PNG_COLOR_TYPE_RGB
>
{};
template<>
struct png_write_support< bits16
, rgba_t
> : write_support_true
, png_rw_support_base< 16
, PNG_COLOR_TYPE_RGBA
>
{};
#ifdef BOOST_GIL_IO_ENABLE_GRAY_ALPHA
template<>
struct png_write_support< bits16
, gray_alpha_t
> : write_support_true
, png_rw_support_base< 16
, PNG_COLOR_TYPE_GA
>
{};
#endif // BOOST_GIL_IO_ENABLE_GRAY_ALPHA
} // namespace detail
template< typename Pixel >
struct is_read_supported< Pixel
, png_tag
>
: mpl::bool_< detail::png_read_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
>::is_supported
>
{
typedef detail::png_read_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
> parent_t;
static const png_bitdepth::type _bit_depth = parent_t::_bit_depth;
static const png_color_type::type _color_type = parent_t::_color_type;
};
template< typename Pixel >
struct is_write_supported< Pixel
, png_tag
>
: mpl::bool_< detail::png_write_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
>::is_supported
>
{
typedef detail::png_write_support< typename channel_type< Pixel >::type
, typename color_space_type< Pixel >::type
> parent_t;
static const png_bitdepth::type _bit_depth = parent_t::_bit_depth;
static const png_color_type::type _color_type = parent_t::_color_type;
};
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_PNG_SUPPORTED_TYPES_HPP
@@ -0,0 +1,521 @@
/*
Copyright 2007-2008 Christian Henning, Andreas Pokorny, 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_DETAIL_PNG_IO_WRITE_HPP
#define BOOST_GIL_EXTENSION_IO_DETAIL_PNG_IO_WRITE_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning, Andreas Pokorny, Lubomir Bourdev \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
extern "C" {
#include <png.h>
}
#include <boost/gil/extension/io_new/detail/typedefs.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/row_buffer_helper.hpp>
#include "base.hpp"
#include "supported_types.hpp"
namespace boost { namespace gil { namespace detail {
template<typename Device>
class writer< Device
, png_tag
> : png_io_base< Device >
{
public:
writer( Device& io_dev )
: png_io_base< Device >( io_dev )
, _png_ptr ( NULL )
, _info_ptr( NULL )
{
// Create and initialize the png_struct with the desired error handler
// functions. If you want to use the default stderr and longjump method,
// you can supply NULL for the last three parameters. We also check that
// the library version is compatible with the one used at compile time,
// in case we are using dynamically linked libraries. REQUIRED.
_png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING
, NULL // user_error_ptr
, NULL // user_error_fn
, NULL // user_warning_fn
);
io_error_if( _png_ptr == NULL
, "png_writer: fail to call png_create_write_struct()"
);
// Allocate/initialize the image information data. REQUIRED
_info_ptr = png_create_info_struct( _png_ptr );
if( _info_ptr == NULL )
{
png_destroy_write_struct( &_png_ptr
, NULL
);
io_error( "png_writer: fail to call png_create_info_struct()" );
}
// Set error handling. REQUIRED if you aren't supplying your own
// error handling functions in the png_create_write_struct() call.
if( setjmp( png_jmpbuf( _png_ptr )))
{
//free all of the memory associated with the png_ptr and info_ptr
png_destroy_write_struct( &_png_ptr
, &_info_ptr
);
io_error( "png_writer: fail to call setjmp()" );
}
this->init_io( _png_ptr );
}
~writer()
{
png_destroy_write_struct( &_png_ptr
, &_info_ptr
);
}
template< typename View >
void apply( const View& view )
{
apply( view, image_write_info< png_tag >() );
}
template <typename View>
void apply( const View& view
, const image_write_info< png_tag >& info
)
{
typedef png_write_support< typename channel_type < typename get_pixel_type< View >::type >::type
, typename color_space_type< View >::type
> png_rw_info_t;
io_error_if( view.width() == 0 && view.height() == 0
, "png format cannot handle empty views."
);
// Set the image information here. Width and height are up to 2^31,
// bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
// the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
// PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
// or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
// PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
// currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
png_set_IHDR( _png_ptr
, _info_ptr
, static_cast< png_image_width::type >( view.width() )
, static_cast< png_image_height::type >( view.height() )
, static_cast< png_bitdepth::type >( png_rw_info_t::_bit_depth )
, static_cast< png_color_type::type >( png_rw_info_t::_color_type )
, info._interlace_method
, info._compression_method
, info._filter_method
);
#ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
if( info._valid_cie_colors )
{
png_set_cHRM( _png_ptr
, _info_ptr
, info._white_x
, info._white_y
, info._red_x
, info._red_y
, info._green_x
, info._green_y
, info._blue_x
, info._blue_y
);
}
if( info._valid_file_gamma )
{
png_set_gAMA( _png_ptr
, _info_ptr
, info._gamma
);
}
#else
if( info._valid_cie_colors )
{
png_set_cHRM_fixed( _png_ptr
, _info_ptr
, info._white_x
, info._white_y
, info._red_x
, info._red_y
, info._green_x
, info._green_y
, info._blue_x
, info._blue_y
);
}
if( info._valid_file_gamma )
{
png_set_gAMA_fixed( _png_ptr
, _info_ptr
, info._file_gamma
);
}
#endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
if( info._valid_icc_profile )
{
png_set_iCCP( _png_ptr
, _info_ptr
, const_cast< png_charp >( info._icc_name.c_str() )
, info._iccp_compression_type
, const_cast< png_charp >( info._profile.c_str() )
, info._profile_length
);
}
if( info._valid_intent )
{
png_set_sRGB( _png_ptr
, _info_ptr
, info._intent
);
}
if( info._valid_palette )
{
png_set_PLTE( _png_ptr
, _info_ptr
, const_cast< png_colorp >( &info._palette.front() )
, info._num_palette
);
}
if( info._valid_background )
{
png_set_bKGD( _png_ptr
, _info_ptr
, const_cast< png_color_16p >( &info._background )
);
}
if( info._valid_histogram )
{
png_set_hIST( _png_ptr
, _info_ptr
, const_cast< png_uint_16p >( &info._histogram.front() )
);
}
if( info._valid_offset )
{
png_set_oFFs( _png_ptr
, _info_ptr
, info._offset_x
, info._offset_y
, info._off_unit_type
);
}
if( info._valid_pixel_calibration )
{
std::vector< const char* > params( info._num_params );
for( std::size_t i = 0; i < params.size(); ++i )
{
params[i] = info._params[ i ].c_str();
}
png_set_pCAL( _png_ptr
, _info_ptr
, const_cast< png_charp >( info._purpose.c_str() )
, info._X0
, info._X1
, info._cal_type
, info._num_params
, const_cast< png_charp >( info._units.c_str() )
, const_cast< png_charpp >( &params.front() )
);
}
if( info._valid_resolution )
{
png_set_pHYs( _png_ptr
, _info_ptr
, info._res_x
, info._res_y
, info._phy_unit_type
);
}
if( info._valid_significant_bits )
{
png_set_sBIT( _png_ptr
, _info_ptr
, const_cast< png_color_8p >( &info._sig_bits )
);
}
#ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
if( info._valid_scale_factors )
{
png_set_sCAL( _png_ptr
, _info_ptr
, info._scale_unit
, info._scale_width
, info._scale_height
);
}
#else
#ifdef BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED
if( info._valid_scale_factors )
{
png_set_sCAL_s( _png_ptr
, _info_ptr
, _scale_unit
, const_cast< png_charp >( _scale_width.c_str() )
, const_cast< png_charp >( _scale_height.c_str() )
);
}
#endif // BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED
#endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED
if( info._valid_text )
{
std::vector< png_text > texts( info._num_text );
for( std::size_t i = 0; i < texts.size(); ++i )
{
png_text pt;
pt.compression = info._text[i]._compression;
pt.key = const_cast< png_charp >( info._text[i]._key.c_str() );
pt.text = const_cast< png_charp >( info._text[i]._text.c_str() );
pt.text_length = info._text[i]._text.length();
texts[i] = pt;
}
png_set_text( _png_ptr
, _info_ptr
, &texts.front()
, info._num_text
);
}
if( info._valid_modification_time )
{
png_set_tIME( _png_ptr
, _info_ptr
, const_cast< png_timep >( &info._mod_time )
);
}
if( info._valid_transparency_factors )
{
int sample_max = ( 1 << info._bit_depth );
/* libpng doesn't reject a tRNS chunk with out-of-range samples */
if( !( ( info._color_type == PNG_COLOR_TYPE_GRAY
&& (int) info._trans_values[0].gray > sample_max
)
|| ( info._color_type == PNG_COLOR_TYPE_RGB
&&( (int) info._trans_values[0].red > sample_max
|| (int) info._trans_values[0].green > sample_max
|| (int) info._trans_values[0].blue > sample_max
)
)
)
)
{
//@todo Fix that once reading transparency values works
/*
png_set_tRNS( _png_ptr
, _info_ptr
, trans
, num_trans
, trans_values
);
*/
}
}
png_write_info( _png_ptr
,_info_ptr
);
write_view( view
, typename is_bit_aligned< View >::type()
);
}
private:
template<typename View>
void write_view( const View& view
, mpl::false_ // is bit aligned
)
{
typedef typename get_pixel_type< View >::type pixel_t;
typedef png_write_support< typename channel_type < pixel_t >::type
, typename color_space_type< pixel_t >::type
> png_rw_info;
if( little_endian() )
{
if( png_rw_info::_bit_depth == 16 )
{
png_set_swap( _png_ptr );
}
if( png_rw_info::_bit_depth < 8 )
{
png_set_packswap( _png_ptr );
}
}
row_buffer_helper_view< View > row_buffer( view.width()
, false
);
for( int y = 0; y != view.height(); ++ y)
{
std::copy( view.row_begin( y )
, view.row_end ( y )
, row_buffer.begin()
);
png_write_row( _png_ptr
, reinterpret_cast< png_bytep >( row_buffer.data() )
);
}
png_write_end( _png_ptr
, _info_ptr
);
}
template<typename View>
void write_view( const View& view
, mpl::true_ // is bit aligned
)
{
typedef png_write_support< typename kth_semantic_element_type< typename View::value_type
, 0
>::type
, typename color_space_type<View>::type
> png_rw_info;
if (little_endian() )
{
if( png_rw_info::_bit_depth == 16 )
{
png_set_swap( _png_ptr );
}
if( png_rw_info::_bit_depth < 8 )
{
png_set_packswap( _png_ptr );
}
}
row_buffer_helper_view< View > row_buffer( view.width()
, false
);
for( int y = 0; y != view.height(); ++y )
{
std::copy( view.row_begin( y )
, view.row_end ( y )
, row_buffer.begin()
);
png_write_row( _png_ptr
, reinterpret_cast< png_bytep >( row_buffer.data() )
);
}
png_free_data( _png_ptr
, _info_ptr
, PNG_FREE_UNKN
, -1
);
png_write_end( _png_ptr
, _info_ptr
);
}
void init_io( png_structp png_ptr )
{
png_set_write_fn( png_ptr
, static_cast< void* > ( &this->_io_dev )
, static_cast< png_rw_ptr > ( &png_io_base<Device>::write_data )
, static_cast< png_flush_ptr >( &png_io_base<Device>::flush )
);
}
png_structp _png_ptr;
png_infop _info_ptr;
};
struct png_write_is_supported
{
template< typename View >
struct apply
: public is_write_supported< typename get_pixel_type< View >::type
, png_tag
>
{};
};
template< typename Device >
class dynamic_image_writer< Device
, png_tag
>
: public writer< Device
, png_tag
>
{
typedef writer< Device
, png_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< png_write_is_supported
, parent_t
> op( this );
apply_operation( views, op );
}
};
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_DETAIL_PNG_IO_WRITE_HPP
@@ -0,0 +1,60 @@
/*
Copyright 2009 Christian Henning
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_PNM_IO_IS_ALLOWED_HPP
#define BOOST_GIL_EXTENSION_IO_PNM_IO_IS_ALLOWED_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
namespace boost { namespace gil { namespace detail {
template< typename View >
bool is_allowed( const image_read_info< pnm_tag >& info
, mpl::true_ // is read_and_no_convert
)
{
pnm_image_type::type asc_type = is_read_supported< typename get_pixel_type< View >::type
, pnm_tag
>::_asc_type;
pnm_image_type::type bin_type = is_read_supported< typename get_pixel_type< View >::type
, pnm_tag
>::_bin_type;
if( info._type == pnm_image_type::mono_asc_t::value )
{
// ascii mono images are read gray8_image_t
return ( asc_type == pnm_image_type::gray_asc_t::value );
}
return ( asc_type == info._type
|| bin_type == info._type
);
}
template< typename View >
bool is_allowed( const image_read_info< pnm_tag >& /* info */
, mpl::false_ // is read_and_convert
)
{
return true;
}
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_PNM_IO_IS_ALLOWED_HPP
@@ -0,0 +1,498 @@
/*
Copyright 2008 Christian Henning
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_PNM_IO_READ_HPP
#define BOOST_GIL_EXTENSION_IO_PNM_IO_READ_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <vector>
#include <boost/bind.hpp>
#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/io_new/pnm_tags.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/conversion_policies.hpp>
#include <boost/gil/extension/io_new/detail/row_buffer_helper.hpp>
#include <boost/gil/extension/io_new/detail/bit_operations.hpp>
#include <boost/gil/extension/io_new/detail/reader_base.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
#include <boost/gil/extension/io_new/detail/typedefs.hpp>
#include "is_allowed.hpp"
namespace boost { namespace gil { namespace detail {
template< typename View, typename T >
struct calc_pitch {};
template< typename View >
struct calc_pitch< View, mpl::false_ >
{
static uint32_t do_it( uint32_t width ) { return width * num_channels< View >::value; }
};
template< typename View >
struct calc_pitch< View, mpl::true_ >
{
static uint32_t do_it( uint32_t width ) { return ( width + 7 ) >> 3; }
};
template< typename Device
, typename ConversionPolicy
>
class reader< Device
, pnm_tag
, ConversionPolicy
>
: public reader_base< pnm_tag
, ConversionPolicy
>
{
private:
typedef typename ConversionPolicy::color_converter_type cc_t;
public:
reader( Device& device
, const image_read_settings< pnm_tag >& settings
)
: reader_base< pnm_tag
, ConversionPolicy >( settings )
, _io_dev( device )
{}
reader( Device& device
, const cc_t& cc
, const image_read_settings< pnm_tag >& settings
)
: reader_base< pnm_tag
, ConversionPolicy >( cc
, settings
)
, _io_dev( device )
{}
image_read_info<pnm_tag> get_info()
{
image_read_info<pnm_tag> ret;
// read signature
io_error_if( read_char() != 'P', "Invalid PNM signature" );
ret._type = read_char() - '0';
io_error_if( ret._type < pnm_image_type::mono_asc_t::value || ret._type > pnm_image_type::color_bin_t::value
, "Invalid PNM file (supports P1 to P6)"
);
ret._width = read_int();
ret._height = read_int();
if( ret._type == pnm_image_type::mono_asc_t::value || ret._type == pnm_image_type::mono_bin_t::value )
{
ret._max_value = 1;
}
else
{
ret._max_value = read_int();
io_error_if( ret._max_value > 255
, "Unsupported PNM format (supports maximum value 255)"
);
}
return ret;
}
template<typename View>
void apply( const View& view )
{
typedef typename is_same< ConversionPolicy
, read_and_no_convert
>::type is_read_and_convert_t;
io_error_if( !is_allowed< View >( this->_info
, is_read_and_convert_t()
)
, "Image types aren't compatible."
);
switch( this->_info._type )
{
// reading mono text is reading grayscale but with only two values
case pnm_image_type::mono_asc_t::value: { read_text_data< gray8_view_t >( view ); break; }
case pnm_image_type::gray_asc_t::value: { read_text_data< gray8_view_t >( view ); break; }
case pnm_image_type::color_asc_t::value: { read_text_data< rgb8_view_t >( view ); break; }
case pnm_image_type::mono_bin_t::value: { read_bin_data< gray1_image_t::view_t >( view ); break; }
case pnm_image_type::gray_bin_t::value: { read_bin_data< gray8_view_t >( view ); break; }
case pnm_image_type::color_bin_t::value: { read_bin_data< rgb8_view_t >( view ); break; }
}
}
private:
template< typename View_Src
, typename View_Dst
>
void read_text_data( const View_Dst& dst )
{
typedef typename View_Dst::y_coord_t y_t;
uint32_t pitch = this->_info._width * num_channels< View_Src >::value;
byte_vector_t row( pitch );
//Skip scanlines if necessary.
for( int y = 0; y < this->_settings._top_left.y; ++y )
{
read_text_row< View_Src >( dst, row, pitch, y, false );
}
for( y_t y = 0; y < dst.height(); ++y )
{
read_text_row< View_Src >( dst, row, pitch, y, true );
}
}
template< typename View_Src
, typename View_Dst
>
void read_text_row( const View_Dst& dst
, byte_vector_t& row
, uint32_t pitch
, typename View_Dst::y_coord_t y
, bool process
)
{
static char buf[16];
View_Src src = interleaved_view( this->_info._width
, 1
, (typename View_Src::value_type*) &row.front()
, pitch
);
for( uint32_t x = 0; x < pitch; ++x )
{
for( uint32_t k = 0; ; )
{
int ch = _io_dev.getc_unchecked();
if( isdigit( ch ))
{
buf[ k++ ] = static_cast< char >( ch );
}
else if( k )
{
buf[ k ] = 0;
break;
}
else if( ch == EOF || !isspace( ch ))
{
return;
}
}
if( process )
{
int value = atoi( buf );
if( this->_info._max_value == 1 )
{
typedef typename channel_type< typename get_pixel_type< View_Dst >::type >::type channel_t;
// for pnm format 0 is white
row[x] = ( value != 0 )
? typename channel_traits< channel_t >::value_type( 0 )
: channel_traits< channel_t >::max_value();
}
else
{
row[x] = static_cast< byte_t >( value );
}
}
}
if( process )
{
// We are reading a gray1_image like a gray8_image but the two pixel_t
// aren't compatible. Though, read_and_no_convert::read(...) wont work.
copy_data< View_Dst
, View_Src >( dst
, src
, y
, typename is_same< View_Dst
, gray1_image_t::view_t
>::type()
);
}
}
template< typename View_Dst
, typename View_Src
>
void copy_data( const View_Dst& dst
, const View_Src& src
, typename View_Dst::y_coord_t y
, mpl::true_ // is gray1_view
)
{
if( this->_info._max_value == 1 )
{
typename View_Dst::x_iterator it = dst.row_begin( y );
for( typename View_Dst::x_coord_t x = 0
; x < dst.width()
; ++x
)
{
it[x] = src[x];
}
}
else
{
copy_data( dst
, src
, y
, mpl::false_()
);
}
}
template< typename View_Dst
, typename View_Src
>
void copy_data( const View_Dst& view
, const View_Src& src
, typename View_Dst::y_coord_t y
, mpl::false_ // is gray1_view
)
{
typename View_Src::x_iterator beg = src.row_begin( 0 ) + this->_settings._top_left.x;
typename View_Src::x_iterator end = beg + this->_settings._dim.x;
this->_cc_policy.read( beg
, end
, view.row_begin( y )
);
}
template< typename View_Src
, typename View_Dst
>
void read_bin_data( const View_Dst& view )
{
typedef typename View_Dst::y_coord_t y_t;
typedef typename is_bit_aligned<
typename View_Src::value_type >::type is_bit_aligned_t;
uint32_t pitch = calc_pitch< View_Src, is_bit_aligned_t >::do_it( this->_info._width );
typedef row_buffer_helper_view< View_Src > rh_t;
rh_t rh( pitch, true );
typename rh_t::iterator_t beg = rh.begin() + this->_settings._top_left.x;
typename rh_t::iterator_t end = beg + this->_settings._dim.x;
// For bit_aligned images we need to negate all bytes in the row_buffer
// to make sure that 0 is black and 255 is white.
negate_bits< typename rh_t::buffer_t
, is_bit_aligned_t
> neg;
swap_half_bytes< typename rh_t::buffer_t
, is_bit_aligned_t
> swhb;
//Skip scanlines if necessary.
for( y_t y = 0; y < this->_settings._top_left.y; ++y )
{
_io_dev.read( reinterpret_cast< byte_t* >( rh.data() )
, pitch
);
}
for( y_t y = 0; y < view.height(); ++y )
{
_io_dev.read( reinterpret_cast< byte_t* >( rh.data() )
, pitch
);
neg( rh.buffer() );
swhb( rh.buffer() );
this->_cc_policy.read( beg
, end
, view.row_begin( y )
);
}
}
private:
// Read a character and skip a comment if necessary.
char read_char()
{
char ch;
if(( ch = _io_dev.getc() ) == '#' )
{
// skip comment to EOL
do
{
ch = _io_dev.getc();
}
while (ch != '\n' && ch != '\r');
}
return ch;
}
unsigned int read_int()
{
char ch;
// skip whitespaces, tabs, and new lines
do
{
ch = read_char();
}
while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
if( ch < '0' || ch > '9' )
{
io_error( "Unexpected characters reading decimal digits" );
}
unsigned val = 0;
do
{
unsigned dig = ch - '0';
if( val > INT_MAX / 10 - dig )
{
io_error( "Integer too large" );
}
val = val * 10 + dig;
ch = read_char();
}
while( '0' <= ch && ch <= '9' );
return val;
}
private:
Device& _io_dev;
};
struct pnm_type_format_checker
{
pnm_type_format_checker( pnm_image_type::type type )
: _type( type )
{}
template< typename Image >
bool apply()
{
typedef is_read_supported< typename get_pixel_type< typename Image::view_t >::type
, pnm_tag
> is_supported_t;
return is_supported_t::_asc_type == _type
|| is_supported_t::_bin_type == _type;
}
private:
pnm_image_type::type _type;
};
struct pnm_read_is_supported
{
template< typename View >
struct apply : public is_read_supported< typename get_pixel_type< View >::type
, pnm_tag
>
{};
};
template< typename Device
>
class dynamic_image_reader< Device
, pnm_tag
>
: public reader< Device
, pnm_tag
, detail::read_and_no_convert
>
{
typedef reader< Device
, pnm_tag
, detail::read_and_no_convert
> parent_t;
public:
dynamic_image_reader( Device& device
, const image_read_settings< pnm_tag >& settings
)
: parent_t( device
, settings
)
{}
template< typename Images >
void apply( any_image< Images >& images )
{
pnm_type_format_checker format_checker( this->_info._type );
if( !construct_matched( images
, format_checker
))
{
io_error( "No matching image type between those of the given any_image and that of the file" );
}
else
{
init_image( images
, this->_info
);
dynamic_io_fnobj< pnm_read_is_supported
, parent_t
> op( this );
apply_operation( view( images )
, op
);
}
}
};
} // detail
} // gil
} // boost
#endif // BOOST_GIL_EXTENSION_IO_PNM_IO_READ_HPP
@@ -0,0 +1,146 @@
/*
Copyright 2008 Christian Henning
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_PNM_SUPPORTED_TYPES_HPP
#define BOOST_GIL_EXTENSION_IO_PNM_SUPPORTED_TYPES_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/mpl/not.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/gil/channel.hpp>
#include <boost/gil/color_base.hpp>
namespace boost { namespace gil { namespace detail {
// Read Support
template< pnm_image_type::type ASCII_Type
, pnm_image_type::type Binary_Type
>
struct pnm_rw_support_base
{
static const pnm_image_type::type _asc_type = ASCII_Type;
static const pnm_image_type::type _bin_type = Binary_Type;
};
template< typename Channel
, typename ColorSpace
>
struct pnm_read_support : read_support_false
, pnm_rw_support_base< 0
, 0
> {};
template< typename BitField, bool Mutable >
struct pnm_read_support< packed_dynamic_channel_reference< BitField
, 1
, Mutable
>
, gray_t
> : read_support_true
, pnm_rw_support_base< pnm_image_type::mono_asc_t::value
, pnm_image_type::mono_bin_t::value
> {};
template<>
struct pnm_read_support< bits8
, gray_t
> : read_support_true
, pnm_rw_support_base< pnm_image_type::gray_asc_t::value
, pnm_image_type::gray_bin_t::value
> {};
template<>
struct pnm_read_support< bits8
, rgb_t
> : read_support_true
, pnm_rw_support_base< pnm_image_type::color_asc_t::value
, pnm_image_type::color_bin_t::value
> {};
// Write support
template< typename Channel
, typename ColorSpace
>
struct pnm_write_support : write_support_false
{};
template< typename BitField, bool Mutable >
struct pnm_write_support< packed_dynamic_channel_reference< BitField
, 1
, Mutable
>
, gray_t
> : write_support_true
, pnm_rw_support_base< pnm_image_type::mono_asc_t::value
, pnm_image_type::mono_bin_t::value
> {};
template<>
struct pnm_write_support< bits8
, gray_t
> : write_support_true
, pnm_rw_support_base< pnm_image_type::gray_asc_t::value
, pnm_image_type::gray_bin_t::value
> {};
template<>
struct pnm_write_support< bits8
, rgb_t
> : write_support_true
, pnm_rw_support_base< pnm_image_type::color_asc_t::value
, pnm_image_type::color_bin_t::value
> {};
} // namespace detail
template< typename Pixel >
struct is_read_supported< Pixel
, pnm_tag
>
: mpl::bool_< detail::pnm_read_support< typename channel_type < Pixel >::type
, typename color_space_type< Pixel >::type
>::is_supported
>
{
typedef detail::pnm_read_support< typename channel_type < Pixel >::type
, typename color_space_type< Pixel >::type
> parent_t;
static const pnm_image_type::type _asc_type = parent_t::_asc_type;
static const pnm_image_type::type _bin_type = parent_t::_bin_type;
};
template< typename Pixel >
struct is_write_supported< Pixel
, pnm_tag
>
: mpl::bool_< detail::pnm_write_support< typename channel_type < Pixel >::type
, typename color_space_type< Pixel >::type
>::is_supported
> {};
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_PNM_SUPPORTED_TYPES_HPP
@@ -0,0 +1,242 @@
/*
Copyright 2008 Christian Henning
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_PNM_IO_WRITE_HPP
#define BOOST_GIL_EXTENSION_IO_PNM_IO_WRITE_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <boost/lexical_cast.hpp>
#include <boost/gil/extension/io_new/pnm_tags.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
namespace boost { namespace gil { namespace detail {
template< typename Device >
class writer< Device
, pnm_tag
>
{
typedef image_write_info< pnm_tag > info_t;
public:
writer( Device & file )
: _out( file )
{
}
~writer()
{
}
template<typename View>
void apply( const View& view )
{
info_t info;
apply( view
, info );
}
template<typename View>
void apply( const View& view
, const info_t& /* info */
)
{
typedef typename get_pixel_type< View >::type pixel_t;
std::size_t width = view.width();
std::size_t height = view.height();
std::size_t chn = num_channels< View >::value;
std::size_t pitch = chn * width;
unsigned int type;
if( num_channels< View >::value == 1 )
{
if( is_bit_aligned< pixel_t >::value )
{
type = pnm_image_type::mono_bin_t::value;
}
else
{
type = pnm_image_type::gray_bin_t::value;
}
}
else
{
type = pnm_image_type::color_bin_t::value;
}
// write header
// Add a white space at each string so read_int() can decide when a numbers ends.
std::string str( "P" );
str += lexical_cast< std::string >( type ) + std::string( " " );
_out.print_line( str );
str.clear();
str += lexical_cast< std::string >( width ) + std::string( " " );
_out.print_line( str );
str.clear();
str += lexical_cast< std::string >( height ) + std::string( " " );
_out.print_line( str );
if( type != pnm_image_type::mono_bin_t::value )
{
_out.print_line( "255 ");
}
// write data
write_data( view
, pitch
, typename is_bit_aligned< pixel_t >::type()
);
}
template< typename View >
void write_data( const View& src
, std::size_t pitch
, const mpl::true_& // bit_aligned
)
{
BOOST_STATIC_ASSERT(( is_same< View
, typename gray1_image_t::view_t
>::value
));
byte_vector_t row( pitch / 8 );
typedef typename View::x_iterator x_it_t;
x_it_t row_it = x_it_t( &( *row.begin() ));
negate_bits< byte_vector_t
, mpl::true_
> neg;
mirror_bits< byte_vector_t
, mpl::true_
> mirror;
for( typename View::y_coord_t y = 0; y < src.height(); ++y )
{
std::copy( src.row_begin( y )
, src.row_end( y )
, row_it
);
mirror( row );
neg ( row );
_out.write( &row.front()
, pitch / 8
);
}
}
template< typename View >
void write_data( const View& src
, std::size_t pitch
, const mpl::false_& // bit_aligned
)
{
byte_vector_t buf( pitch );
typedef typename View::value_type pixel_t;
typedef typename view_type_from_pixel< pixel_t >::type view_t;
view_t row = interleaved_view( src.width()
, 1
, reinterpret_cast< pixel_t* >( &buf.front() )
, pitch
);
for( typename View::y_coord_t y = 0
; y < src.height()
; ++y
)
{
copy_pixels( subimage_view( src
, 0
, (int) y
, (int) src.width()
, 1
)
, row
);
_out.write( &buf.front(), pitch );
}
}
private:
Device& _out;
};
struct pnm_write_is_supported
{
template< typename View >
struct apply
: public is_write_supported< typename get_pixel_type< View >::type
, pnm_tag
>
{};
};
template< typename Device >
class dynamic_image_writer< Device
, pnm_tag
>
: public writer< Device
, pnm_tag
>
{
typedef writer< Device
, pnm_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< pnm_write_is_supported
, parent_t
> op( this );
apply_operation( views, op );
}
};
} // detail
} // gil
} // boost
#endif // BOOST_GIL_EXTENSION_IO_JPEG_IO_WRITE_HPP
@@ -0,0 +1,360 @@
/*
Copyright 2007-2008 Andreas Pokorny, Christian Henning
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_DETAIL_TIFF_IO_DEVICE_HPP
#define BOOST_GIL_EXTENSION_IO_DETAIL_TIFF_IO_DEVICE_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Andreas Pokorny, Christian Henning \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
extern "C" {
#include "tiff.h"
#include "tiffio.h"
}
#include "tiffio.hxx"
#include <boost/shared_ptr.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
namespace boost { namespace gil { namespace detail {
class tiff_device_base
{
public:
tiff_device_base()
{}
tiff_device_base( TIFF* tiff_file )
: _tiff_file( tiff_file
, TIFFClose )
{}
template <typename Property>
bool get_property( typename Property::type& value )
{
if( TIFFGetFieldDefaulted( _tiff_file.get()
, Property::tag
, &value ) == 1 )
{
return true;
}
return false;
}
template <typename Property>
inline
bool set_property( const typename Property::type& value )
{
if( TIFFSetField( _tiff_file.get()
, Property::tag
, value ) == 1 )
{
return true;
}
return false;
}
// TIFFIsByteSwapped returns a non-zero value if the image data was in a different
// byte-order than the host machine. Zero is returned if the TIFF file and local
// host byte-orders are the same.
bool are_bytes_swapped()
{
return ( TIFFIsByteSwapped( _tiff_file.get() )) ? true : false;
}
bool is_tiled() const
{
return ( TIFFIsTiled( _tiff_file.get() )) ? true : false;
}
unsigned int get_default_strip_size()
{
return TIFFDefaultStripSize( _tiff_file.get()
, 0 );
}
std::size_t get_scanline_size()
{
return TIFFScanlineSize( _tiff_file.get() );
}
std::size_t get_tile_size()
{
return TIFFTileSize( _tiff_file.get() );
}
int get_field_defaulted( uint16_t*& red
, uint16_t*& green
, uint16_t*& blue
)
{
return TIFFGetFieldDefaulted( _tiff_file.get()
, TIFFTAG_COLORMAP
, &red
, &green
, &blue
);
}
template< typename Buffer >
void read_scanline( Buffer& buffer
, std::ptrdiff_t row
, tsample_t plane
)
{
io_error_if( TIFFReadScanline( _tiff_file.get()
, reinterpret_cast< tdata_t >( &buffer.front() )
, (uint32) row
, plane ) == -1
, "Read error."
);
}
template< typename Buffer >
void read_tile( Buffer& buffer
, std::ptrdiff_t x
, std::ptrdiff_t y
, std::ptrdiff_t z
, tsample_t plane
)
{
if( TIFFReadTile( _tiff_file.get()
, reinterpret_cast< tdata_t >( &buffer.front() )
, (uint32) x
, (uint32) y
, (uint32) z
, plane
) == -1 )
{
std::ostringstream oss;
oss << "Read tile error (" << x << "," << y << "," << z << "," << plane << ").";
io_error(oss.str());
}
}
template< typename Buffer >
void write_scaline( Buffer& buffer
, uint32 row
, tsample_t plane
)
{
io_error_if( TIFFWriteScanline( _tiff_file.get()
, &buffer.front()
, row
, plane
) == -1
, "Write error"
);
}
template< typename Buffer >
void write_tile( Buffer& buffer
, uint32 x
, uint32 y
, uint32 z
, tsample_t plane
)
{
if( TIFFWriteTile( _tiff_file.get()
, &buffer.front()
, x
, y
, z
, plane
) == -1 )
{
std::ostringstream oss;
oss << "Write tile error (" << x << "," << y << "," << z << "," << plane << ").";
io_error(oss.str());
}
}
void set_directory( tdir_t directory )
{
io_error_if( TIFFSetDirectory( _tiff_file.get()
, directory
) != 1
, "Failing to set directory"
);
}
// return false if the given tile width or height is not TIFF compliant (multiple of 16) or larger than image size, true otherwise
bool check_tile_size( tiff_tile_width::type& width
, tiff_tile_length::type& height
)
{
bool result = true;
uint32 tw = static_cast< uint32 >( width );
uint32 th = static_cast< uint32 >( height );
TIFFDefaultTileSize( _tiff_file.get()
, &tw
, &th
);
if(width==0 || width%16!=0)
{
width = tw;
result = false;
}
if(height==0 || height%16!=0)
{
height = th;
result = false;
}
return result;
}
protected:
typedef shared_ptr<TIFF> tiff_file_t;
tiff_file_t _tiff_file;
};
/*!
*
* file_stream_device specialization for tiff images, which are based on TIFF*.
*/
template<>
class file_stream_device< tiff_tag > : public tiff_device_base
{
public:
struct read_tag {};
struct write_tag {};
file_stream_device( std::string const& file_name, read_tag )
{
TIFF* tiff;
io_error_if( ( tiff = TIFFOpen( file_name.c_str(), "r" )) == NULL
, "file_stream_device: failed to open file" );
_tiff_file = tiff_file_t( tiff, TIFFClose );
}
file_stream_device( std::string const& file_name, write_tag )
{
TIFF* tiff;
io_error_if( ( tiff = TIFFOpen( file_name.c_str(), "w" )) == NULL
, "file_stream_device: failed to open file" );
_tiff_file = tiff_file_t( tiff, TIFFClose );
}
file_stream_device( TIFF* tiff_file )
: tiff_device_base( tiff_file )
{}
};
/*!
*
* ostream_device specialization for tiff images.
*/
template<>
class ostream_device< tiff_tag > : public tiff_device_base
{
public:
ostream_device( std::ostream & out )
: _out( out )
{
TIFF* tiff;
io_error_if( ( tiff = TIFFStreamOpen( ""
, &_out
)
) == NULL
, "ostream_device: failed to stream"
);
_tiff_file = tiff_file_t( tiff, TIFFClose );
}
private:
std::ostream& _out;
};
/*!
*
* ostream_device specialization for tiff images.
*/
template<>
class istream_device< tiff_tag > : public tiff_device_base
{
public:
istream_device( std::istream & in )
: _in( in )
{
TIFF* tiff;
io_error_if( ( tiff = TIFFStreamOpen( ""
, &_in
)
) == NULL
, "istream_device: failed to stream"
);
_tiff_file = tiff_file_t( tiff, TIFFClose );
}
private:
std::istream& _in;
};
/*
template< typename T, typename D >
struct is_adaptable_input_device< tiff_tag, T, D > : mpl::false_{};
*/
template< typename FormatTag >
struct is_adaptable_input_device< FormatTag
, TIFF*
, void
>
: mpl::true_
{
typedef file_stream_device< FormatTag > device_type;
};
template< typename FormatTag >
struct is_adaptable_output_device< FormatTag
, TIFF*
, void
>
: mpl::true_
{
typedef file_stream_device< FormatTag > device_type;
};
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_DETAIL_TIFF_IO_DEVICE_HPP
@@ -0,0 +1,174 @@
/*
Copyright 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_IS_ALLOWED_HPP
#define BOOST_GIL_EXTENSION_IO_TIFF_IO_IS_ALLOWED_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/mpl/for_each.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
namespace boost { namespace gil { namespace detail {
typedef std::vector< tiff_bits_per_sample::type > channel_sizes_t;
template< typename Channel >
int format_value( mpl::true_ ) // is_bit_aligned
{
return SAMPLEFORMAT_UINT;
}
template< typename Channel >
int format_value( mpl::false_ ) // is_bit_aligned
{
if( is_unsigned< Channel >::value )
{
return SAMPLEFORMAT_UINT;
}
if( is_signed< Channel >::value )
{
return SAMPLEFORMAT_INT;
}
else if( is_floating_point< Channel >::value )
{
return SAMPLEFORMAT_IEEEFP;
}
io_error( "Unkown channel format." );
}
// The following two functions look the same but are different since one is using
// a pixel_t as template parameter whereas the other is using reference_t.
template< typename View >
bool compare_channel_sizes( const channel_sizes_t& channel_sizes // in bits
, mpl::false_ // is_bit_aligned
, mpl::true_ // is_homogeneous
)
{
typedef typename View::value_type pixel_t;
typedef typename channel_traits<
typename element_type< pixel_t >::type >::value_type channel_t;
unsigned int s = detail::unsigned_integral_num_bits< channel_t >::value;
return ( s == channel_sizes[0] );
}
template< typename View >
bool compare_channel_sizes( const channel_sizes_t& channel_sizes // in bits
, mpl::true_ // is_bit_aligned
, mpl::true_ // is_homogeneous
)
{
typedef typename View::reference ref_t;
typedef typename channel_traits< typename element_type< ref_t >::type >::value_type channel_t;
channel_t c;
unsigned int s = detail::unsigned_integral_num_bits< channel_t >::value;
return ( s == channel_sizes[0] );
}
struct compare_channel_sizes_fn
{
compare_channel_sizes_fn( uint16_t* a )
: _a( a )
, _b( true )
{}
template< typename ChannelSize >
void operator()( ChannelSize x)
{
if( x != *_a++ )
{
_b = false;
}
}
uint16_t* _a;
bool _b;
};
template< typename T >
struct channel_sizes_type {};
template< typename B, typename C, typename L, bool M >
struct channel_sizes_type< bit_aligned_pixel_reference< B, C, L, M > > { typedef C type; };
template< typename B, typename C, typename L, bool M >
struct channel_sizes_type< const bit_aligned_pixel_reference< B, C, L, M > > { typedef C type; };
template< typename View >
bool compare_channel_sizes( channel_sizes_t& channel_sizes // in bits
, mpl::true_ // is_bit_aligned
, mpl::false_ // is_homogeneous
)
{
// loop through all channels and compare
typedef typename View::reference ref_t;
typedef typename channel_sizes_type< ref_t >::type cs_t;
compare_channel_sizes_fn fn( &channel_sizes.front() );
mpl::for_each< cs_t >( fn );
return fn._b;
}
template< typename View >
bool is_allowed( const image_read_info< tiff_tag >& info
, mpl::true_ // is read_and_no_convert
)
{
channel_sizes_t channel_sizes( info._samples_per_pixel
, info._bits_per_sample
);
typedef typename get_pixel_type< View >::type pixel_t;
typedef typename channel_traits<
typename element_type< pixel_t >::type >::value_type channel_t;
typedef typename num_channels< pixel_t >::value_type num_channel_t;
const num_channel_t dst_samples_per_pixel = num_channels< pixel_t >::value;
const num_channel_t dst_sample_format = format_value< channel_t >( typename is_bit_aligned< pixel_t >::type() );
return ( dst_samples_per_pixel == info._samples_per_pixel
&& compare_channel_sizes< View >( channel_sizes
, typename is_bit_aligned< pixel_t >::type()
, typename is_homogeneous< pixel_t >::type()
)
&& dst_sample_format == info._sample_format
);
}
template< typename View >
bool is_allowed( const image_read_info< tiff_tag >& /* info */
, mpl::false_ // is read_and_no_convert
)
{
return true;
}
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_TIFF_IO_IS_ALLOWED_HPP
@@ -0,0 +1,66 @@
/*
Copyright 2009 Christian Henning
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_LOG_HPP
#define BOOST_GIL_EXTENSION_IO_TIFF_IO_LOG_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning \n
///
/// \date 2009 \n
///
////////////////////////////////////////////////////////////////////////////////////////
extern "C" {
#include "tiffio.h"
}
#include <iostream>
namespace boost { namespace gil {
class console_log
{
public:
console_log()
{
TIFFSetErrorHandler ( console_log::error );
TIFFSetWarningHandler( console_log::warning );
}
private:
static void error( const char* /* module */
, const char* fmt
, va_list ap
)
{
char buf[1000];
sprintf(buf, fmt, ap);
std::cout << "error: " << buf << std::endl;
}
static void warning( char const* /* module */
, char const* fmt
, va_list ap
)
{
char buf[1000];
sprintf(buf, fmt, ap);
std::cout << "warning: " << fmt << std::endl;
}
};
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_TIFF_IO_LOG_HPP
@@ -0,0 +1,782 @@
/*
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_READ_HPP
#define BOOST_GIL_EXTENSION_IO_TIFF_IO_READ_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning, Lubomir Bourdev \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
extern "C" {
#include "tiff.h"
#include "tiffio.h"
}
#include <algorithm>
#include <string>
#include <vector>
#include <boost/static_assert.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/conversion_policies.hpp>
#include <boost/gil/extension/io_new/detail/bit_operations.hpp>
#include <boost/gil/extension/io_new/detail/row_buffer_helper.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
#include <boost/gil/extension/io_new/detail/reader_base.hpp>
#include "device.hpp"
#include "is_allowed.hpp"
namespace boost { namespace gil { namespace detail {
template < int K >
struct plane_recursion
{
template< typename View
, typename Device
, typename ConversionPolicy
>
static
void read_plane( const View& dst_view
, reader< Device
, tiff_tag
, ConversionPolicy >* p
)
{
typedef typename kth_channel_view_type< K, View >::type plane_t;
plane_t plane = kth_channel_view<K>( dst_view );
p->template read_data< row_buffer_helper_view< plane_t > >( plane, K );
plane_recursion< K - 1 >::read_plane( dst_view, p );
}
};
template <>
struct plane_recursion< -1 >
{
template< typename View
, typename Device
, typename ConversionPolicy
>
static
void read_plane( const View& /* dst_view */
, reader< Device
, tiff_tag
, ConversionPolicy
>* /* p */
)
{}
};
template< typename Device
, typename ConversionPolicy
>
class reader< Device
, tiff_tag
, ConversionPolicy
>
: public reader_base< tiff_tag
, ConversionPolicy >
{
public:
reader( Device& device
, const image_read_settings< tiff_tag >& settings
)
: reader_base< tiff_tag
, ConversionPolicy
>( settings )
, _io_dev( device )
{
init_multipage_read( settings );
}
reader( Device& device
, const typename ConversionPolicy::color_converter_type& cc
, const image_read_settings< tiff_tag >& settings
)
: reader_base< tiff_tag
, ConversionPolicy
>( cc
, settings
)
, _io_dev( device )
{
init_multipage_read( settings );
}
image_read_info<tiff_tag> get_info() const
{
image_read_info<tiff_tag> info;
io_error_if( _io_dev.template get_property<tiff_image_width> ( info._width ) == false
, "cannot read tiff tag." );
io_error_if( _io_dev.template get_property<tiff_image_height> ( info._height ) == false
, "cannot read tiff tag." );
io_error_if( _io_dev.template get_property<tiff_compression> ( info._compression ) == false
, "cannot read tiff tag." );
io_error_if( _io_dev.template get_property<tiff_samples_per_pixel> ( info._samples_per_pixel ) == false
, "cannot read tiff tag." );
io_error_if( _io_dev.template get_property<tiff_bits_per_sample> ( info._bits_per_sample ) == false
, "cannot read tiff tag." );
io_error_if( _io_dev.template get_property<tiff_sample_format> ( info._sample_format ) == false
, "cannot read tiff tag." );
io_error_if( _io_dev.template get_property<tiff_planar_configuration> ( info._planar_configuration ) == false
, "cannot read tiff tag." );
io_error_if( _io_dev.template get_property<tiff_photometric_interpretation>( info._photometric_interpretation ) == false
, "cannot read tiff tag." );
info._is_tiled = false;
// Tile tags
if( _io_dev.is_tiled() )
{
info._is_tiled = true;
io_error_if( !_io_dev.template get_property< tiff_tile_width >( info._tile_width )
, "cannot read tiff_tile_width tag." );
io_error_if( !_io_dev.template get_property< tiff_tile_length >( info._tile_length )
, "cannot read tiff_tile_length tag." );
}
return info;
}
// only works for homogeneous image types
template< typename View >
void apply( View& dst_view )
{
if( this->_info._photometric_interpretation == PHOTOMETRIC_PALETTE )
{
// Steps:
// 1. Read indices. It's an array of grayX_pixel_t.
// 2. Read palette. It's an array of rgb16_pixel_t.
// 3. ??? Create virtual image or transform the two arrays
// into a rgb16_image_t object. The latter might
// be a good first solution.
switch( this->_info._bits_per_sample )
{
case 1: { read_palette_image< gray1_image_t >( dst_view ); break; }
case 2: { read_palette_image< gray2_image_t >( dst_view ); break; }
case 4: { read_palette_image< gray4_image_t >( dst_view ); break; }
case 8: { read_palette_image< gray8_image_t >( dst_view ); break; }
case 16: { read_palette_image< gray16_image_t >( dst_view ); break; }
default: { io_error( "Not supported palette " ); }
}
return;
}
else
{
// In case we only read the image the user's type and
// the tiff type need to compatible. Which means:
// color_spaces_are_compatible && channels_are_pairwise_compatible
typedef typename is_same< ConversionPolicy
, read_and_no_convert
>::type is_read_only;
io_error_if( !is_allowed< View >( this->_info
, is_read_only()
)
, "Image types aren't compatible."
);
if( this->_info._planar_configuration == PLANARCONFIG_SEPARATE )
{
plane_recursion< num_channels< View >::value - 1 >::read_plane( dst_view
, this
);
}
else if( this->_info._planar_configuration == PLANARCONFIG_CONTIG )
{
if( is_read_only::value == false )
{
// the read_data function needs to know what gil type the source image is
// to have the default color converter function correctly
switch( this->_info._photometric_interpretation )
{
case PHOTOMETRIC_MINISWHITE:
case PHOTOMETRIC_MINISBLACK:
{
switch( this->_info._bits_per_sample )
{
case 1: { read_data< row_buffer_helper_view< gray1_image_t::view_t > >( dst_view, 0 ); break; }
case 2: { read_data< row_buffer_helper_view< gray2_image_t::view_t > >( dst_view, 0 ); break; }
case 4: { read_data< row_buffer_helper_view< gray4_image_t::view_t > >( dst_view, 0 ); break; }
case 8: { read_data< row_buffer_helper_view< gray8_view_t > >( dst_view, 0 ); break; }
case 16: { read_data< row_buffer_helper_view< gray16_view_t > >( dst_view, 0 ); break; }
case 32: { read_data< row_buffer_helper_view< gray32_view_t > >( dst_view, 0 ); break; }
}
break;
}
case PHOTOMETRIC_RGB:
{
switch( this->_info._bits_per_sample )
{
case 8: { read_data< row_buffer_helper_view< rgb8_view_t > >( dst_view, 0 ); break; }
case 16: { read_data< row_buffer_helper_view< rgb16_view_t > >( dst_view, 0 ); break; }
case 32: { read_data< row_buffer_helper_view< rgb32_view_t > >( dst_view, 0 ); break; }
}
break;
}
case PHOTOMETRIC_SEPARATED: // CYMK
{
switch( this->_info._bits_per_sample )
{
case 8: { read_data< row_buffer_helper_view< cmyk8_view_t > >( dst_view, 0 ); break; }
case 16: { read_data< row_buffer_helper_view< cmyk16_view_t > >( dst_view, 0 ); break; }
case 32: { read_data< row_buffer_helper_view< cmyk32_view_t > >( dst_view, 0 ); break; }
}
break;
}
default: { io_error( "Not supported colorspace " ); }
}
}
else
{
read_data< row_buffer_helper_view< View > >( dst_view, 0 );
}
}
else
{
io_error( "Wrong planar configuration setting." );
}
}
}
private:
void init_multipage_read( const image_read_settings< tiff_tag >& settings )
{
if( settings._directory > 0 )
{
_io_dev.set_directory( settings._directory );
}
}
template< typename PaletteImage
, typename View
>
void read_palette_image( const View& dst_view )
{
PaletteImage indices( this->_info._width - this->_settings._top_left.x
, this->_info._height - this->_settings._top_left.y );
// read the palette first
read_data< row_buffer_helper_view< typename PaletteImage::view_t > >( view( indices ), 0 );
read_palette_image( dst_view
, view( indices )
, typename is_same< View, rgb16_view_t >::type() );
}
template< typename View
, typename Indices_View
>
void read_palette_image( const View& dst_view
, const Indices_View& indices_view
, mpl::true_ // is View rgb16_view_t
)
{
tiff_color_map::red_t red = NULL;
tiff_color_map::green_t green = NULL;
tiff_color_map::blue_t blue = NULL;
_io_dev.get_field_defaulted( red, green, blue );
typedef typename channel_traits<
typename element_type<
typename Indices_View::value_type >::type >::value_type channel_t;
int num_colors = channel_traits< channel_t >::max_value();
rgb16_planar_view_t palette = planar_rgb_view( num_colors
, 1
, red
, green
, blue
, sizeof( bits16 ) * num_colors );
typename rgb16_planar_view_t::x_iterator palette_it = palette.row_begin( 0 );
for( typename rgb16_view_t::y_coord_t y = 0; y < dst_view.height(); ++y )
{
typename rgb16_view_t::x_iterator it = dst_view.row_begin( y );
typename rgb16_view_t::x_iterator end = dst_view.row_end( y );
typename Indices_View::x_iterator indices_it = indices_view.row_begin( y );
for( ; it != end; ++it, ++indices_it )
{
bits16 i = gil::at_c<0>( *indices_it );
*it = palette[i];
}
}
}
template< typename View
, typename Indices_View
>
inline
void read_palette_image( const View& /* dst_view */
, const Indices_View& /* indices_view */
, mpl::false_ // is View rgb16_view_t
)
{
io_error( "User supplied image type must be rgb16_image_t." );
}
template< typename Buffer >
void skip_over_rows( Buffer& buffer
, int plane
)
{
if( this->_info._compression != COMPRESSION_NONE )
{
// Skipping over rows is not possible for compressed images( no random access ). See man
// page ( diagnostics section ) for more information.
for( std::ptrdiff_t row = 0; row < this->_settings._top_left.y; ++row )
{
_io_dev.read_scanline( buffer
, row
, static_cast< tsample_t >( plane ));
}
}
}
template< typename Buffer
, typename View
>
void read_data( const View& dst_view
, int plane )
{
if( _io_dev.is_tiled() )
{
read_tiled_data< Buffer >( dst_view, 0 );
}
else
{
read_stripped_data< Buffer >( dst_view, 0 );
}
}
template< typename Buffer
, typename View
>
void read_tiled_data( const View& dst_view
, int plane
)
{
if( dst_view.width() != this->_info._width
|| dst_view.height() != this->_info._height
)
{
// read a subimage
read_tiled_data_subimage< Buffer >( dst_view, plane );
}
else
{
// read full image
read_tiled_data_full< Buffer >( dst_view, plane );
}
}
template< typename Buffer
, typename View
>
void read_tiled_data_subimage( const View& dst_view
, int plane
)
{
///@todo: why is
/// typedef Buffer row_buffer_helper_t;
/// not working? I get compiler error with MSVC10.
/// read_stripped_data IS working.
typedef row_buffer_helper_view< View > row_buffer_helper_t;
typedef typename row_buffer_helper_t::buffer_t buffer_t;
typedef typename row_buffer_helper_t::iterator_t it_t;
tiff_image_width::type image_width = this->_info._width;
tiff_image_height::type image_height = this->_info._height;
tiff_tile_width::type tile_width = this->_info._tile_width;
tiff_tile_length::type tile_height = this->_info._tile_length;
std::ptrdiff_t subimage_x = this->_settings._top_left.x;
std::ptrdiff_t subimage_y = this->_settings._top_left.y;
std::ptrdiff_t subimage_width = this->_settings._dim.x;
std::ptrdiff_t subimage_height = this->_settings._dim.y;
row_buffer_helper_t row_buffer_helper( _io_dev.get_tile_size(), true );
mirror_bits< buffer_t
, typename is_bit_aligned< typename View::value_type >::type
> mirror_bits( _io_dev.are_bytes_swapped() );
for( unsigned int y = 0; y < image_height; y += tile_height )
{
for( unsigned int x = 0; x < image_width; x += tile_width )
{
uint32_t current_tile_width = ( x + tile_width < image_width ) ? tile_width : image_width - x;
uint32_t current_tile_length = ( y + tile_height < image_height ) ? tile_height : image_height - y;
_io_dev.read_tile( row_buffer_helper.buffer()
, x
, y
, 0
, static_cast< tsample_t >( plane )
);
mirror_bits( row_buffer_helper.buffer() );
// these are all whole image coordinates
point_t tile_top_left ( x, y );
point_t tile_lower_right( x + current_tile_width - 1, y + current_tile_length - 1 );
point_t view_top_left ( subimage_x, subimage_y );
point_t view_lower_right( subimage_x + subimage_width - 1
, subimage_y + subimage_height - 1 );
if( tile_top_left.x > view_lower_right.x
|| tile_top_left.y > view_lower_right.y
|| tile_lower_right.x < view_top_left.x
|| tile_lower_right.y < view_top_left.y
)
{
// current tile and dst_view do not overlap
continue;
}
else
{
// dst_view is overlapping the current tile
// next is to define the portion in the tile that needs to be copied
// get the whole image coordinates
std::ptrdiff_t img_x0 = ( tile_top_left.x >= view_top_left.x ) ? tile_top_left.x : view_top_left.x;
std::ptrdiff_t img_y0 = ( tile_top_left.y >= view_top_left.y ) ? tile_top_left.y : view_top_left.y;
std::ptrdiff_t img_x1 = ( tile_lower_right.x <= view_lower_right.x ) ? tile_lower_right.x : view_lower_right.x;
std::ptrdiff_t img_y1 = ( tile_lower_right.y <= view_lower_right.y ) ? tile_lower_right.y : view_lower_right.y;
// convert to tile coordinates
std::ptrdiff_t tile_x0 = img_x0 - x;
std::ptrdiff_t tile_y0 = img_y0 - y;
std::ptrdiff_t tile_x1 = img_x1 - x;
std::ptrdiff_t tile_y1 = img_y1 - y;
assert( tile_x0 >= 0 && tile_y0 >= 0 && tile_x1 >= 0 && tile_y1 >= 0 );
assert( tile_x0 <= img_x1 && tile_y0 <= img_y1 );
assert( tile_x0 < tile_width && tile_y0 < tile_height && tile_x1 < tile_width && tile_y1 < tile_height );
std::ptrdiff_t tile_subimage_view_width = tile_x1 - tile_x0 + 1;
std::ptrdiff_t tile_subimage_view_height = tile_y1 - tile_y0 + 1;
// convert to dst_view coordinates
std::ptrdiff_t dst_x0 = img_x0 - subimage_x;
std::ptrdiff_t dst_y0 = img_y0 - subimage_y;
assert( dst_x0 >= 0 && dst_y0 >= 0 );
View dst_subimage_view = subimage_view( dst_view
, (int) dst_x0
, (int) dst_y0
, (int) tile_subimage_view_width
, (int) tile_subimage_view_height
);
// the row_buffer is a 1D array which represents a 2D image. We cannot
// use interleaved_view here, since row_buffer could be bit_aligned.
// Interleaved_view's fourth parameter "rowsize_in_bytes" doesn't work
// for bit_aligned pixels.
for( std::ptrdiff_t dst_row = 0; dst_row < dst_subimage_view.height(); ++dst_row )
{
std::ptrdiff_t tile_row = dst_row + tile_y0;
// jump to the beginning of the current tile row
it_t begin = row_buffer_helper.begin() + tile_row * tile_width;
begin += tile_x0;
it_t end = begin + dst_subimage_view.width();
this->_cc_policy.read( begin
, end
, dst_subimage_view.row_begin( dst_row )
);
} //for
}
} // for
} // for
}
template< typename Buffer
, typename View
>
void read_tiled_data_full( const View& dst_view
, int plane
)
{
///@todo: why is
/// typedef Buffer row_buffer_helper_t;
/// not working? I get compiler error with MSVC10.
/// read_stripped_data IS working.
typedef row_buffer_helper_view< View > row_buffer_helper_t;
typedef typename row_buffer_helper_t::buffer_t buffer_t;
typedef typename row_buffer_helper_t::iterator_t it_t;
tiff_image_width::type image_width = this->_info._width;
tiff_image_height::type image_height = this->_info._height;
tiff_tile_width::type tile_width = this->_info._tile_width;
tiff_tile_length::type tile_height = this->_info._tile_length;
row_buffer_helper_t row_buffer_helper( _io_dev.get_tile_size(), true );
mirror_bits< buffer_t
, typename is_bit_aligned< typename View::value_type >::type
> mirror_bits( _io_dev.are_bytes_swapped() );
for( unsigned int y = 0; y < image_height; y += tile_height )
{
for( unsigned int x = 0; x < image_width; x += tile_width )
{
uint32_t current_tile_width = ( x + tile_width < image_width ) ? tile_width : image_width - x;
uint32_t current_tile_length = ( y + tile_height < image_height ) ? tile_height : image_height - y;
_io_dev.read_tile( row_buffer_helper.buffer()
, x
, y
, 0
, static_cast< tsample_t >( plane )
);
mirror_bits( row_buffer_helper.buffer() );
View dst_subimage_view = subimage_view( dst_view
, x
, y
, current_tile_width
, current_tile_length
);
// the row_buffer is a 1D array which represents a 2D image. We cannot
// use interleaved_view here, since row_buffer could be bit_aligned.
// Interleaved_view's fourth parameter "rowsize_in_bytes" doesn't work
// for bit_aligned pixels.
for( int row = 0; row < dst_subimage_view.height(); ++row )
{
it_t begin = row_buffer_helper.begin() + row * tile_width;
it_t end = begin + dst_subimage_view.width();
this->_cc_policy.read( begin
, end
, dst_subimage_view.row_begin( row )
);
} //for
} // for
} // for
}
template< typename Buffer
, typename View
>
void read_stripped_data( const View& dst_view
, int plane )
{
typedef typename is_bit_aligned< typename View::value_type >::type is_view_bit_aligned_t;
//typedef row_buffer_helper_view< View > row_buffer_helper_t;
typedef Buffer row_buffer_helper_t;
typedef typename row_buffer_helper_t::buffer_t buffer_t;
typedef typename row_buffer_helper_t::iterator_t it_t;
std::size_t size_to_allocate = buffer_size< typename View::value_type >( dst_view.width()
, is_view_bit_aligned_t() );
row_buffer_helper_t row_buffer_helper( size_to_allocate, true );
it_t begin = row_buffer_helper.begin();
it_t first = begin + this->_settings._top_left.x;
it_t last = first + this->_settings._dim.x; // one after last element
// I don't think tiff allows for random access of row, that's why we need
// to read and discard rows when reading subimages.
skip_over_rows( row_buffer_helper.buffer()
, plane
);
mirror_bits< buffer_t
, typename is_bit_aligned< View >::type
> mirror_bits( _io_dev.are_bytes_swapped() );
std::ptrdiff_t row = this->_settings._top_left.y;
std::ptrdiff_t row_end = row + this->_settings._dim.y;
std::ptrdiff_t dst_row = 0;
for(
; row < row_end
; ++row, ++dst_row
)
{
_io_dev.read_scanline( row_buffer_helper.buffer()
, row
, static_cast< tsample_t >( plane )
);
mirror_bits( row_buffer_helper.buffer() );
this->_cc_policy.read( first
, last
, dst_view.row_begin( dst_row ));
}
}
template< typename Pixel >
std::size_t buffer_size( std::size_t width
, mpl::false_ // is_bit_aligned
)
{
std::size_t scanline_size_in_bytes = _io_dev.get_scanline_size();
std::size_t element_size = sizeof( Pixel );
return std::max( width
, (( scanline_size_in_bytes + element_size - 1 ) / element_size ));
}
template< typename Pixel >
std::size_t buffer_size( std::size_t /* width */
, mpl::true_ // is_bit_aligned
)
{
return _io_dev.get_scanline_size();
}
private:
Device& _io_dev;
template < int K > friend struct plane_recursion;
};
struct tiff_type_format_checker
{
tiff_type_format_checker( const image_read_info< tiff_tag >& info )
: _info( info )
{}
template< typename Image >
bool apply()
{
typedef typename Image::view_t view_t;
return is_allowed< view_t >( _info
, mpl::true_()
);
}
private:
const image_read_info< tiff_tag >& _info;
};
struct tiff_read_is_supported
{
template< typename View >
struct apply : public is_read_supported< typename get_pixel_type< View >::type
, tiff_tag
>
{};
};
template< typename Device
>
class dynamic_image_reader< Device
, tiff_tag
>
: public reader< Device
, tiff_tag
, detail::read_and_no_convert
>
{
typedef reader< Device
, tiff_tag
, detail::read_and_no_convert
> parent_t;
public:
dynamic_image_reader( Device& device
, const image_read_settings< tiff_tag >& settings
)
: parent_t( device
, settings
)
{}
template< typename Images >
void apply( any_image< Images >& images )
{
tiff_type_format_checker format_checker( this->_info );
if( !construct_matched( images
, format_checker
))
{
io_error( "No matching image type between those of the given any_image and that of the file" );
}
else
{
init_image( images
, this->_info
);
dynamic_io_fnobj< tiff_read_is_supported
, parent_t
> op( this );
apply_operation( view( images )
, op
);
}
}
};
} // namespace detail
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_TIFF_IO_READ_HPP
@@ -0,0 +1,62 @@
/*
Copyright 2007-2008 Christian Henning, Andreas Pokorny, 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_HPP
#define BOOST_GIL_EXTENSION_IO_TIFF_IO_HPP
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief
/// \author Christian Henning, Andreas Pokorny, Lubomir Bourdev \n
///
/// \date 2007-2008 \n
///
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/mpl/not.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/gil/channel.hpp>
#include <boost/gil/color_base.hpp>
namespace boost{ namespace gil {
namespace detail {
// Read support
// TIFF virtually supports everything
struct tiff_read_support : read_support_true
{};
// Write support
struct tiff_write_support : write_support_true
{};
} // namespace detail
template< typename Pixel >
struct is_read_supported< Pixel
, tiff_tag
>
: mpl::bool_< detail::tiff_read_support::is_supported > {};
template< typename Pixel >
struct is_write_supported< Pixel
, tiff_tag
>
: mpl::bool_< detail::tiff_write_support::is_supported >
{};
} // namespace gil
} // namespace boost
#endif // BOOST_GIL_EXTENSION_IO_TIFF_IO_HPP
@@ -0,0 +1,422 @@
/*
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 <algorithm>
#include <string>
#include <vector>
#include <boost/static_assert.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/gil/extension/io_new/tiff_tags.hpp>
#include <boost/gil/extension/io_new/detail/base.hpp>
#include <boost/gil/extension/io_new/detail/io_device.hpp>
#include "device.hpp"
namespace boost { namespace gil { namespace detail {
template <typename PixelReference>
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_<SAMPLEFORMAT_UINT> {};
template<> struct sample_format<bits8> : public mpl::int_<SAMPLEFORMAT_UINT> {};
template<> struct sample_format<bits16> : public mpl::int_<SAMPLEFORMAT_UINT> {};
template<> struct sample_format<bits32> : public mpl::int_<SAMPLEFORMAT_UINT> {};
template<> struct sample_format<bits32f> : public mpl::int_<SAMPLEFORMAT_IEEEFP> {};
template<> struct sample_format<double> : public mpl::int_<SAMPLEFORMAT_IEEEFP> {};
template<> struct sample_format<bits8s> : public mpl::int_<SAMPLEFORMAT_INT> {};
template<> struct sample_format<bits16s> : public mpl::int_<SAMPLEFORMAT_INT> {};
template<> struct sample_format<bits32s> : public mpl::int_<SAMPLEFORMAT_INT> {};
template <typename Channel> 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<tiff_tag, Log> info_t;
writer( Device& dev )
: _io_dev( dev ) {}
template<typename View>
void apply( const View& view )
{
typedef typename color_space_type<View>::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<typename View>
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<tiff_planar_configuration>( 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<tiff_samples_per_pixel>( 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<channel_t>::value;
_io_dev.template set_property<tiff_bits_per_sample>( bits_per_sample );
// write sample format
tiff_sample_format::type sampl_format = sample_format< channel_t >::type::value;
_io_dev.template set_property<tiff_sample_format>( sampl_format );
// write photometric format
_io_dev.template set_property<tiff_photometric_interpretation>( info._photometric_interpretation );
// write compression
_io_dev.template set_property<tiff_compression>( info._compression );
// write orientation
_io_dev.template set_property<tiff_orientation>( info._orientation );
// write rows per strip
_io_dev.template set_property<tiff_rows_per_strip>( _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<tiff_tile_width> ( tw );
_io_dev.template set_property<tiff_tile_length>( 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