diff options
Diffstat (limited to 'boost/gil/extension/io/bmp/detail/scanline_read.hpp')
-rw-r--r-- | boost/gil/extension/io/bmp/detail/scanline_read.hpp | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/boost/gil/extension/io/bmp/detail/scanline_read.hpp b/boost/gil/extension/io/bmp/detail/scanline_read.hpp new file mode 100644 index 0000000000..fdc1e54f68 --- /dev/null +++ b/boost/gil/extension/io/bmp/detail/scanline_read.hpp @@ -0,0 +1,425 @@ +/* + 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_DETAIL_SCANLINE_READ_HPP +#define BOOST_GIL_EXTENSION_IO_BMP_DETAIL_SCANLINE_READ_HPP + +//////////////////////////////////////////////////////////////////////////////////////// +/// \file +/// \brief +/// \author Christian Henning \n +/// +/// \date 2008 - 2012 \n +/// +//////////////////////////////////////////////////////////////////////////////////////// + +#include <vector> + +#include <boost/function.hpp> + +#include <boost/gil/io/base.hpp> +#include <boost/gil/io/bit_operations.hpp> +#include <boost/gil/io/conversion_policies.hpp> +#include <boost/gil/io/row_buffer_helper.hpp> +#include <boost/gil/io/reader_base.hpp> +#include <boost/gil/io/device.hpp> +#include <boost/gil/io/scanline_read_iterator.hpp> +#include <boost/gil/io/typedefs.hpp> + +#include <boost/gil/extension/io/bmp/detail/reader_backend.hpp> +#include <boost/gil/extension/io/bmp/detail/is_allowed.hpp> + +namespace boost { namespace gil { + +/// +/// BMP Scanline Reader +/// +template< typename Device > +class scanline_reader< Device + , bmp_tag + > + : public reader_backend< Device + , bmp_tag + > +{ +public: + + typedef bmp_tag tag_t; + typedef reader_backend < Device, tag_t > backend_t; + typedef scanline_reader< Device, tag_t > this_t; + typedef scanline_read_iterator< this_t > iterator_t; + +public: + + // + // Constructor + // + scanline_reader( Device& device + , const image_read_settings< bmp_tag >& settings + ) + : backend_t( device + , settings + ) + + , _pitch( 0 ) + { + initialize(); + } + + /// Read part of image defined by View and return the data. + void read( byte_t* dst, int pos ) + { + // jump to scanline + long offset = 0; + + if( this->_info._height > 0 ) + { + // the image is upside down + offset = this->_info._offset + + ( this->_info._height - 1 - pos ) * this->_pitch; + } + else + { + offset = this->_info._offset + + pos * _pitch; + } + + this->_io_dev.seek( offset ); + + + // read data + _read_function(this, dst); + } + + /// Skip over a scanline. + void skip( byte_t*, int ) + { + // nothing to do. + } + + iterator_t begin() { return iterator_t( *this ); } + iterator_t end() { return iterator_t( *this, this->_info._height ); } + +private: + + void initialize() + { + if( this->_info._bits_per_pixel < 8 ) + { + _pitch = (( this->_info._width * this->_info._bits_per_pixel ) + 7 ) >> 3; + } + else + { + _pitch = this->_info._width * (( this->_info._bits_per_pixel + 7 ) >> 3); + } + + _pitch = (_pitch + 3) & ~3; + + // + + switch( this->_info._bits_per_pixel ) + { + case 1: + { + this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; + + read_palette(); + _buffer.resize( _pitch ); + + _read_function = boost::mem_fn( &this_t::read_1_bit_row ); + + break; + } + + case 4: + { + switch( this->_info._compression ) + { + case bmp_compression::_rle4: + { + io_error( "Cannot read run-length encoded images in iterator mode. Try to read as whole image." ); + + break; + } + + case bmp_compression::_rgb : + { + this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; + + read_palette(); + _buffer.resize( _pitch ); + + _read_function = boost::mem_fn( &this_t::read_4_bits_row ); + + break; + } + + default: + { + io_error( "Unsupported compression mode in BMP file." ); + } + } + + break; + } + + case 8: + { + switch( this->_info._compression ) + { + case bmp_compression::_rle8: + { + io_error( "Cannot read run-length encoded images in iterator mode. Try to read as whole image." ); + + break; + } + case bmp_compression::_rgb: + { + this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; + + read_palette(); + _buffer.resize( _pitch ); + + _read_function = boost::mem_fn( &this_t::read_8_bits_row ); + + break; + } + + default: { io_error( "Unsupported compression mode in BMP file." ); break; } + } + + break; + } + + case 15: + case 16: + { + this->_scanline_length = ( this->_info._width * num_channels< rgb8_view_t >::value + 3 ) & ~3; + + _buffer.resize( _pitch ); + + if( this->_info._compression == bmp_compression::_bitfield ) + { + this->_mask.red.mask = this->_io_dev.read_uint32(); + this->_mask.green.mask = this->_io_dev.read_uint32(); + this->_mask.blue.mask = this->_io_dev.read_uint32(); + + this->_mask.red.width = detail::count_ones( this->_mask.red.mask ); + this->_mask.green.width = detail::count_ones( this->_mask.green.mask ); + this->_mask.blue.width = detail::count_ones( this->_mask.blue.mask ); + + this->_mask.red.shift = detail::trailing_zeros( this->_mask.red.mask ); + this->_mask.green.shift = detail::trailing_zeros( this->_mask.green.mask ); + this->_mask.blue.shift = detail::trailing_zeros( this->_mask.blue.mask ); + } + else if( this->_info._compression == bmp_compression::_rgb ) + { + switch( this->_info._bits_per_pixel ) + { + case 15: + case 16: + { + this->_mask.red.mask = 0x007C00; this->_mask.red.width = 5; this->_mask.red.shift = 10; + this->_mask.green.mask = 0x0003E0; this->_mask.green.width = 5; this->_mask.green.shift = 5; + this->_mask.blue.mask = 0x00001F; this->_mask.blue.width = 5; this->_mask.blue.shift = 0; + + break; + } + + case 24: + case 32: + { + this->_mask.red.mask = 0xFF0000; this->_mask.red.width = 8; this->_mask.red.shift = 16; + this->_mask.green.mask = 0x00FF00; this->_mask.green.width = 8; this->_mask.green.shift = 8; + this->_mask.blue.mask = 0x0000FF; this->_mask.blue.width = 8; this->_mask.blue.shift = 0; + + break; + } + } + } + else + { + io_error( "Unsupported BMP compression." ); + } + + + _read_function = boost::mem_fn( &this_t::read_15_bits_row ); + + break; + } + + case 24: + { + this->_scanline_length = ( this->_info._width * num_channels< rgb8_view_t >::value + 3 ) & ~3; + _read_function = boost::mem_fn( &this_t::read_row ); + + break; + } + + case 32: + { + this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; + _read_function = boost::mem_fn( &this_t::read_row ); + + break; + } + + default: + { + io_error( "Unsupported bits per pixel." ); + } + } + } + + void read_palette() + { + if( this->_palette.size() > 0 ) + { + // palette has been read already. + return; + } + + int entries = this->_info._num_colors; + + if( entries == 0 ) + { + entries = 1u << this->_info._bits_per_pixel; + } + + this->_palette.resize( entries, rgba8_pixel_t(0,0,0,0) ); + + for( int i = 0; i < entries; ++i ) + { + get_color( this->_palette[i], blue_t() ) = this->_io_dev.read_uint8(); + get_color( this->_palette[i], green_t() ) = this->_io_dev.read_uint8(); + get_color( this->_palette[i], red_t() ) = this->_io_dev.read_uint8(); + + // there are 4 entries when windows header + // but 3 for os2 header + if( this->_info._header_size == bmp_header_size::_win32_info_size ) + { + this->_io_dev.read_uint8(); + } + + } // for + } + + template< typename View > + void read_bit_row( byte_t* dst ) + { + typedef View src_view_t; + typedef rgba8_image_t::view_t dst_view_t; + + src_view_t src_view = interleaved_view( this->_info._width + , 1 + , (typename src_view_t::x_iterator) &_buffer.front() + , this->_pitch + ); + + dst_view_t dst_view = interleaved_view( this->_info._width + , 1 + , (typename dst_view_t::value_type*) dst + , num_channels< dst_view_t >::value * this->_info._width + ); + + + typename src_view_t::x_iterator src_it = src_view.row_begin( 0 ); + typename dst_view_t::x_iterator dst_it = dst_view.row_begin( 0 ); + + for( dst_view_t::x_coord_t i = 0 + ; i < this->_info._width + ; ++i, src_it++, dst_it++ + ) + { + unsigned char c = get_color( *src_it, gray_color_t() ); + *dst_it = this->_palette[c]; + } + } + + // Read 1 bit image. The colors are encoded by an index. + void read_1_bit_row( byte_t* dst ) + { + this->_io_dev.read( &_buffer.front(), _pitch ); + _mirror_bits( _buffer ); + + read_bit_row< gray1_image_t::view_t >( dst ); + } + + // Read 4 bits image. The colors are encoded by an index. + void read_4_bits_row( byte_t* dst ) + { + this->_io_dev.read( &_buffer.front(), _pitch ); + _swap_half_bytes( _buffer ); + + read_bit_row< gray4_image_t::view_t >( dst ); + } + + /// Read 8 bits image. The colors are encoded by an index. + void read_8_bits_row( byte_t* dst ) + { + this->_io_dev.read( &_buffer.front(), _pitch ); + + read_bit_row< gray8_image_t::view_t >( dst ); + } + + /// Read 15 or 16 bits image. + void read_15_bits_row( byte_t* dst ) + { + typedef rgb8_view_t dst_view_t; + + dst_view_t dst_view = interleaved_view( this->_info._width + , 1 + , (typename dst_view_t::value_type*) dst + , this->_pitch + ); + + typename dst_view_t::x_iterator dst_it = dst_view.row_begin( 0 ); + + // + byte_t* src = &_buffer.front(); + this->_io_dev.read( src, _pitch ); + + for( dst_view_t::x_coord_t i = 0 + ; i < this->_info._width + ; ++i, src += 2 + ) + { + int p = ( src[1] << 8 ) | src[0]; + + int r = ((p & this->_mask.red.mask) >> this->_mask.red.shift) << (8 - this->_mask.red.width); + int g = ((p & this->_mask.green.mask) >> this->_mask.green.shift) << (8 - this->_mask.green.width); + int b = ((p & this->_mask.blue.mask) >> this->_mask.blue.shift) << (8 - this->_mask.blue.width); + + get_color( dst_it[i], red_t() ) = static_cast< byte_t >( r ); + get_color( dst_it[i], green_t() ) = static_cast< byte_t >( g ); + get_color( dst_it[i], blue_t() ) = static_cast< byte_t >( b ); + } + } + + void read_row( byte_t* dst ) + { + this->_io_dev.read( dst, _pitch ); + } + +private: + + // the row pitch must be multiple of 4 bytes + int _pitch; + + std::vector< byte_t > _buffer; + detail::mirror_bits < std::vector< byte_t >, mpl::true_ > _mirror_bits; + detail::swap_half_bytes< std::vector< byte_t >, mpl::true_ > _swap_half_bytes; + + boost::function< void ( this_t*, byte_t* ) > _read_function; +}; + +} // namespace gil +} // namespace boost + +#endif |