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,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