1 // 2 // Copyright 2008 Christian Henning 3 // 4 // Distributed under the Boost Software License, Version 1.0 5 // See accompanying file LICENSE_1_0.txt or copy at 6 // http://www.boost.org/LICENSE_1_0.txt 7 // 8 #ifndef BOOST_GIL_EXTENSION_IO_BMP_DETAIL_SCANLINE_READ_HPP 9 #define BOOST_GIL_EXTENSION_IO_BMP_DETAIL_SCANLINE_READ_HPP 10 11 #include <boost/gil/extension/io/bmp/detail/is_allowed.hpp> 12 #include <boost/gil/extension/io/bmp/detail/reader_backend.hpp> 13 14 #include <boost/gil/io/base.hpp> 15 #include <boost/gil/io/bit_operations.hpp> 16 #include <boost/gil/io/conversion_policies.hpp> 17 #include <boost/gil/io/device.hpp> 18 #include <boost/gil/io/reader_base.hpp> 19 #include <boost/gil/io/row_buffer_helper.hpp> 20 #include <boost/gil/io/scanline_read_iterator.hpp> 21 #include <boost/gil/io/typedefs.hpp> 22 23 #include <functional> 24 #include <type_traits> 25 #include <vector> 26 27 namespace boost { namespace gil { 28 29 /// 30 /// BMP Scanline Reader 31 /// 32 template< typename Device > 33 class scanline_reader< Device 34 , bmp_tag 35 > 36 : public reader_backend< Device 37 , bmp_tag 38 > 39 { 40 public: 41 42 using tag_t = bmp_tag; 43 using backend_t = reader_backend<Device, tag_t>; 44 using this_t = scanline_reader<Device, tag_t>; 45 using iterator_t = scanline_read_iterator<this_t>; 46 47 public: 48 49 // 50 // Constructor 51 // scanline_reader(Device & device,const image_read_settings<bmp_tag> & settings)52 scanline_reader( Device& device 53 , const image_read_settings< bmp_tag >& settings 54 ) 55 : backend_t( device 56 , settings 57 ) 58 59 , _pitch( 0 ) 60 { 61 initialize(); 62 } 63 64 /// Read part of image defined by View and return the data. read(byte_t * dst,int pos)65 void read( byte_t* dst, int pos ) 66 { 67 // jump to scanline 68 long offset = 0; 69 70 if( this->_info._height > 0 ) 71 { 72 // the image is upside down 73 offset = this->_info._offset 74 + ( this->_info._height - 1 - pos ) * this->_pitch; 75 } 76 else 77 { 78 offset = this->_info._offset 79 + pos * _pitch; 80 } 81 82 this->_io_dev.seek( offset ); 83 84 85 // read data 86 _read_function(this, dst); 87 } 88 89 /// Skip over a scanline. skip(byte_t *,int)90 void skip( byte_t*, int ) 91 { 92 // nothing to do. 93 } 94 begin()95 iterator_t begin() { return iterator_t( *this ); } end()96 iterator_t end() { return iterator_t( *this, this->_info._height ); } 97 98 private: 99 initialize()100 void initialize() 101 { 102 if( this->_info._bits_per_pixel < 8 ) 103 { 104 _pitch = (( this->_info._width * this->_info._bits_per_pixel ) + 7 ) >> 3; 105 } 106 else 107 { 108 _pitch = this->_info._width * (( this->_info._bits_per_pixel + 7 ) >> 3); 109 } 110 111 _pitch = (_pitch + 3) & ~3; 112 113 // 114 115 switch( this->_info._bits_per_pixel ) 116 { 117 case 1: 118 { 119 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; 120 121 read_palette(); 122 _buffer.resize( _pitch ); 123 124 _read_function = std::mem_fn(&this_t::read_1_bit_row); 125 126 break; 127 } 128 129 case 4: 130 { 131 switch( this->_info._compression ) 132 { 133 case bmp_compression::_rle4: 134 { 135 io_error( "Cannot read run-length encoded images in iterator mode. Try to read as whole image." ); 136 137 break; 138 } 139 140 case bmp_compression::_rgb : 141 { 142 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; 143 144 read_palette(); 145 _buffer.resize( _pitch ); 146 147 _read_function = std::mem_fn(&this_t::read_4_bits_row); 148 149 break; 150 } 151 152 default: 153 { 154 io_error( "Unsupported compression mode in BMP file." ); 155 } 156 } 157 158 break; 159 } 160 161 case 8: 162 { 163 switch( this->_info._compression ) 164 { 165 case bmp_compression::_rle8: 166 { 167 io_error( "Cannot read run-length encoded images in iterator mode. Try to read as whole image." ); 168 169 break; 170 } 171 case bmp_compression::_rgb: 172 { 173 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; 174 175 read_palette(); 176 _buffer.resize( _pitch ); 177 178 _read_function = std::mem_fn(&this_t::read_8_bits_row); 179 180 break; 181 } 182 183 default: { io_error( "Unsupported compression mode in BMP file." ); break; } 184 } 185 186 break; 187 } 188 189 case 15: 190 case 16: 191 { 192 this->_scanline_length = ( this->_info._width * num_channels< rgb8_view_t >::value + 3 ) & ~3; 193 194 _buffer.resize( _pitch ); 195 196 if( this->_info._compression == bmp_compression::_bitfield ) 197 { 198 this->_mask.red.mask = this->_io_dev.read_uint32(); 199 this->_mask.green.mask = this->_io_dev.read_uint32(); 200 this->_mask.blue.mask = this->_io_dev.read_uint32(); 201 202 this->_mask.red.width = detail::count_ones( this->_mask.red.mask ); 203 this->_mask.green.width = detail::count_ones( this->_mask.green.mask ); 204 this->_mask.blue.width = detail::count_ones( this->_mask.blue.mask ); 205 206 this->_mask.red.shift = detail::trailing_zeros( this->_mask.red.mask ); 207 this->_mask.green.shift = detail::trailing_zeros( this->_mask.green.mask ); 208 this->_mask.blue.shift = detail::trailing_zeros( this->_mask.blue.mask ); 209 } 210 else if( this->_info._compression == bmp_compression::_rgb ) 211 { 212 switch( this->_info._bits_per_pixel ) 213 { 214 case 15: 215 case 16: 216 { 217 this->_mask.red.mask = 0x007C00; this->_mask.red.width = 5; this->_mask.red.shift = 10; 218 this->_mask.green.mask = 0x0003E0; this->_mask.green.width = 5; this->_mask.green.shift = 5; 219 this->_mask.blue.mask = 0x00001F; this->_mask.blue.width = 5; this->_mask.blue.shift = 0; 220 221 break; 222 } 223 224 case 24: 225 case 32: 226 { 227 this->_mask.red.mask = 0xFF0000; this->_mask.red.width = 8; this->_mask.red.shift = 16; 228 this->_mask.green.mask = 0x00FF00; this->_mask.green.width = 8; this->_mask.green.shift = 8; 229 this->_mask.blue.mask = 0x0000FF; this->_mask.blue.width = 8; this->_mask.blue.shift = 0; 230 231 break; 232 } 233 } 234 } 235 else 236 { 237 io_error( "Unsupported BMP compression." ); 238 } 239 240 241 _read_function = std::mem_fn(&this_t::read_15_bits_row); 242 243 break; 244 } 245 246 case 24: 247 { 248 this->_scanline_length = ( this->_info._width * num_channels< rgb8_view_t >::value + 3 ) & ~3; 249 _read_function = std::mem_fn(&this_t::read_row); 250 251 break; 252 } 253 254 case 32: 255 { 256 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3; 257 _read_function = std::mem_fn(&this_t::read_row); 258 259 break; 260 } 261 262 default: 263 { 264 io_error( "Unsupported bits per pixel." ); 265 } 266 } 267 } 268 read_palette()269 void read_palette() 270 { 271 if( this->_palette.size() > 0 ) 272 { 273 // palette has been read already. 274 return; 275 } 276 277 int entries = this->_info._num_colors; 278 279 if( entries == 0 ) 280 { 281 entries = 1u << this->_info._bits_per_pixel; 282 } 283 284 this->_palette.resize( entries, rgba8_pixel_t(0,0,0,0) ); 285 286 for( int i = 0; i < entries; ++i ) 287 { 288 get_color( this->_palette[i], blue_t() ) = this->_io_dev.read_uint8(); 289 get_color( this->_palette[i], green_t() ) = this->_io_dev.read_uint8(); 290 get_color( this->_palette[i], red_t() ) = this->_io_dev.read_uint8(); 291 292 // there are 4 entries when windows header 293 // but 3 for os2 header 294 if( this->_info._header_size == bmp_header_size::_win32_info_size ) 295 { 296 this->_io_dev.read_uint8(); 297 } 298 299 } // for 300 } 301 302 template< typename View > read_bit_row(byte_t * dst)303 void read_bit_row( byte_t* dst ) 304 { 305 using src_view_t = View; 306 using dst_view_t = rgba8_image_t::view_t; 307 308 src_view_t src_view = interleaved_view( this->_info._width 309 , 1 310 , (typename src_view_t::x_iterator) &_buffer.front() 311 , this->_pitch 312 ); 313 314 dst_view_t dst_view = interleaved_view( this->_info._width 315 , 1 316 , (typename dst_view_t::value_type*) dst 317 , num_channels< dst_view_t >::value * this->_info._width 318 ); 319 320 321 typename src_view_t::x_iterator src_it = src_view.row_begin( 0 ); 322 typename dst_view_t::x_iterator dst_it = dst_view.row_begin( 0 ); 323 324 for( dst_view_t::x_coord_t i = 0 325 ; i < this->_info._width 326 ; ++i, src_it++, dst_it++ 327 ) 328 { 329 unsigned char c = get_color( *src_it, gray_color_t() ); 330 *dst_it = this->_palette[c]; 331 } 332 } 333 334 // Read 1 bit image. The colors are encoded by an index. read_1_bit_row(byte_t * dst)335 void read_1_bit_row( byte_t* dst ) 336 { 337 this->_io_dev.read( &_buffer.front(), _pitch ); 338 _mirror_bits( _buffer ); 339 340 read_bit_row< gray1_image_t::view_t >( dst ); 341 } 342 343 // Read 4 bits image. The colors are encoded by an index. read_4_bits_row(byte_t * dst)344 void read_4_bits_row( byte_t* dst ) 345 { 346 this->_io_dev.read( &_buffer.front(), _pitch ); 347 _swap_half_bytes( _buffer ); 348 349 read_bit_row< gray4_image_t::view_t >( dst ); 350 } 351 352 /// Read 8 bits image. The colors are encoded by an index. read_8_bits_row(byte_t * dst)353 void read_8_bits_row( byte_t* dst ) 354 { 355 this->_io_dev.read( &_buffer.front(), _pitch ); 356 357 read_bit_row< gray8_image_t::view_t >( dst ); 358 } 359 360 /// Read 15 or 16 bits image. read_15_bits_row(byte_t * dst)361 void read_15_bits_row( byte_t* dst ) 362 { 363 using dst_view_t = rgb8_view_t; 364 365 dst_view_t dst_view = interleaved_view( this->_info._width 366 , 1 367 , (typename dst_view_t::value_type*) dst 368 , this->_pitch 369 ); 370 371 typename dst_view_t::x_iterator dst_it = dst_view.row_begin( 0 ); 372 373 // 374 byte_t* src = &_buffer.front(); 375 this->_io_dev.read( src, _pitch ); 376 377 for( dst_view_t::x_coord_t i = 0 378 ; i < this->_info._width 379 ; ++i, src += 2 380 ) 381 { 382 int p = ( src[1] << 8 ) | src[0]; 383 384 int r = ((p & this->_mask.red.mask) >> this->_mask.red.shift) << (8 - this->_mask.red.width); 385 int g = ((p & this->_mask.green.mask) >> this->_mask.green.shift) << (8 - this->_mask.green.width); 386 int b = ((p & this->_mask.blue.mask) >> this->_mask.blue.shift) << (8 - this->_mask.blue.width); 387 388 get_color( dst_it[i], red_t() ) = static_cast< byte_t >( r ); 389 get_color( dst_it[i], green_t() ) = static_cast< byte_t >( g ); 390 get_color( dst_it[i], blue_t() ) = static_cast< byte_t >( b ); 391 } 392 } 393 read_row(byte_t * dst)394 void read_row( byte_t* dst ) 395 { 396 this->_io_dev.read( dst, _pitch ); 397 } 398 399 private: 400 401 // the row pitch must be multiple of 4 bytes 402 int _pitch; 403 404 std::vector<byte_t> _buffer; 405 detail::mirror_bits <std::vector<byte_t>, std::true_type> _mirror_bits; 406 detail::swap_half_bytes<std::vector<byte_t>, std::true_type> _swap_half_bytes; 407 408 std::function<void(this_t*, byte_t*)> _read_function; 409 }; 410 411 } // namespace gil 412 } // namespace boost 413 414 #endif 415