522 lines
17 KiB
C++
522 lines
17 KiB
C++
/*
|
|
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 >( ¶ms.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
|