1 //
2 // Copyright 2012 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_READ_HPP
9 #define BOOST_GIL_EXTENSION_IO_BMP_DETAIL_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/dynamic_io_new.hpp>
19 #include <boost/gil/io/reader_base.hpp>
20 #include <boost/gil/io/row_buffer_helper.hpp>
21 #include <boost/gil/io/typedefs.hpp>
22 
23 #include <boost/assert.hpp>
24 
25 #include <type_traits>
26 #include <vector>
27 
28 namespace boost { namespace gil {
29 
30 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
31 #pragma warning(push)
32 #pragma warning(disable:4512) //assignment operator could not be generated
33 #endif
34 
35 ///
36 /// BMP Reader
37 ///
38 template< typename Device
39         , typename ConversionPolicy
40         >
41 class reader< Device
42             , bmp_tag
43             , ConversionPolicy
44             >
45     : public reader_base< bmp_tag
46                         , ConversionPolicy
47                         >
48     , public reader_backend< Device
49                            , bmp_tag
50                            >
51 {
52 private:
53 
54     using this_t = reader<Device, bmp_tag, ConversionPolicy>;
55     using cc_t = typename ConversionPolicy::color_converter_type;
56 
57 public:
58 
59     using backend_t = reader_backend< Device, bmp_tag>;
60 
61 public:
62 
63     //
64     // Constructor
65     //
reader(const Device & io_dev,const image_read_settings<bmp_tag> & settings)66     reader( const Device&                         io_dev
67           , const image_read_settings< bmp_tag >& settings
68           )
69     : backend_t( io_dev
70                , settings
71                )
72     , _pitch( 0 )
73     {}
74 
75     //
76     // Constructor
77     //
reader(const Device & io_dev,const ConversionPolicy & cc,const image_read_settings<bmp_tag> & settings)78     reader( const Device&                         io_dev
79           , const ConversionPolicy&               cc
80           , const image_read_settings< bmp_tag >& settings
81           )
82     : reader_base< bmp_tag
83                  , ConversionPolicy
84                  >( cc )
85     , backend_t( io_dev
86                , settings
87                )
88     , _pitch( 0 )
89     {}
90 
91 
92     /// Read image.
93     template< typename View >
apply(const View & dst_view)94     void apply( const View& dst_view )
95     {
96         if( this->_info._valid == false )
97         {
98             io_error( "Image header was not read." );
99         }
100 
101         using is_read_and_convert_t = typename std::is_same
102             <
103                 ConversionPolicy,
104                 detail::read_and_no_convert
105             >::type;
106 
107         io_error_if( !detail::is_allowed< View >( this->_info
108                                                 , is_read_and_convert_t()
109                                                 )
110                    , "Image types aren't compatible."
111                    );
112 
113         // the row pitch must be multiple 4 bytes
114         if( this->_info._bits_per_pixel < 8 )
115         {
116             _pitch = static_cast<long>((( this->_info._width * this->_info._bits_per_pixel ) + 7 ) >> 3 );
117         }
118         else
119         {
120             _pitch = static_cast<long>( this->_info._width * (( this->_info._bits_per_pixel + 7 ) >> 3 ));
121         }
122 
123         _pitch = (_pitch + 3) & ~3;
124 
125         switch( this->_info._bits_per_pixel )
126         {
127             case 1:
128             {
129                 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3;
130 
131                 read_palette_image
132                     <
133                         gray1_image_t::view_t,
134                         detail::mirror_bits<byte_vector_t, std::true_type>
135                     >(dst_view);
136                 break;
137             }
138 
139             case 4:
140             {
141                 switch ( this->_info._compression )
142                 {
143                     case bmp_compression::_rle4:
144                     {
145                         ///@todo How can we determine that?
146                         this->_scanline_length = 0;
147 
148                         read_palette_image_rle( dst_view );
149 
150                         break;
151                     }
152 
153                     case bmp_compression::_rgb:
154                     {
155                         this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3;
156 
157                         read_palette_image
158                             <
159                                 gray4_image_t::view_t,
160                                 detail::swap_half_bytes<byte_vector_t, std::true_type>
161                             >(dst_view);
162                         break;
163                     }
164 
165                     default:
166                     {
167                         io_error( "Unsupported compression mode in BMP file." );
168                         break;
169                     }
170                 }
171                 break;
172             }
173 
174             case 8:
175             {
176                 switch ( this->_info._compression )
177                 {
178                     case bmp_compression::_rle8:
179                     {
180                         ///@todo How can we determine that?
181                         this->_scanline_length = 0;
182 
183                         read_palette_image_rle( dst_view );
184                         break;
185                     }
186 
187                     case bmp_compression::_rgb:
188                     {
189                         this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3;
190 
191                         read_palette_image< gray8_image_t::view_t
192                                           , detail::do_nothing< std::vector< gray8_pixel_t > >
193                                           > ( dst_view );
194                         break;
195                     }
196 
197                     default:
198                     {
199                         io_error( "Unsupported compression mode in BMP file." );
200                         break;
201                     }
202                 }
203 
204                 break;
205             }
206 
207             case 15: case 16:
208             {
209                 this->_scanline_length = ( this->_info._width * num_channels< rgb8_view_t >::value + 3 ) & ~3;
210 
211                 read_data_15( dst_view );
212 
213                 break;
214             }
215 
216             case 24:
217             {
218                 this->_scanline_length = ( this->_info._width * num_channels< rgb8_view_t >::value + 3 ) & ~3;
219 
220                 read_data< bgr8_view_t  >( dst_view );
221 
222                 break;
223             }
224 
225             case 32:
226             {
227                 this->_scanline_length = ( this->_info._width * num_channels< rgba8_view_t >::value + 3 ) & ~3;
228 
229                 read_data< bgra8_view_t >( dst_view );
230 
231                 break;
232             }
233         }
234     }
235 
236 private:
237 
get_offset(std::ptrdiff_t pos)238     long get_offset( std::ptrdiff_t pos )
239     {
240         if( this->_info._height > 0 )
241         {
242             // the image is upside down
243             return static_cast<long>( ( this->_info._offset
244                                       + ( this->_info._height - 1 - pos ) * _pitch
245                                     ));
246         }
247         else
248         {
249             return static_cast<long>( ( this->_info._offset
250                                       + pos * _pitch
251                                     ));
252         }
253     }
254 
255     template< typename View_Src
256             , typename Byte_Manipulator
257             , typename View_Dst
258             >
read_palette_image(const View_Dst & view)259     void read_palette_image( const View_Dst& view )
260     {
261         this->read_palette();
262 
263         using rh_t = detail::row_buffer_helper_view<View_Src>;
264         using it_t = typename rh_t::iterator_t;
265 
266         rh_t rh( _pitch, true );
267 
268         // we have to swap bits
269         Byte_Manipulator byte_manipulator;
270 
271         for( std::ptrdiff_t y = 0
272            ; y < this->_settings._dim.y
273            ; ++y
274            )
275         {
276             this->_io_dev.seek( get_offset( y + this->_settings._top_left.y ));
277 
278             this->_io_dev.read( reinterpret_cast< byte_t* >( rh.data() )
279                         , _pitch
280                         );
281 
282             byte_manipulator( rh.buffer() );
283 
284             typename View_Dst::x_iterator dst_it = view.row_begin( y );
285 
286             it_t it  = rh.begin() + this->_settings._top_left.x;
287             it_t end = it + this->_settings._dim.x;
288 
289             for( ; it != end; ++it, ++dst_it )
290             {
291                 unsigned char c = get_color( *it, gray_color_t() );
292                 *dst_it = this->_palette[ c ];
293             }
294         }
295     }
296 
297     template< typename View >
read_data_15(const View & view)298     void read_data_15( const View& view )
299     {
300         byte_vector_t row( _pitch );
301 
302         // read the color masks
303         if( this->_info._compression == bmp_compression::_bitfield )
304         {
305             this->_mask.red.mask    = this->_io_dev.read_uint32();
306             this->_mask.green.mask  = this->_io_dev.read_uint32();
307             this->_mask.blue.mask   = this->_io_dev.read_uint32();
308 
309             this->_mask.red.width   = detail::count_ones( this->_mask.red.mask   );
310             this->_mask.green.width = detail::count_ones( this->_mask.green.mask );
311             this->_mask.blue.width  = detail::count_ones( this->_mask.blue.mask  );
312 
313             this->_mask.red.shift   = detail::trailing_zeros( this->_mask.red.mask   );
314             this->_mask.green.shift = detail::trailing_zeros( this->_mask.green.mask );
315             this->_mask.blue.shift  = detail::trailing_zeros( this->_mask.blue.mask  );
316         }
317         else if( this->_info._compression == bmp_compression::_rgb )
318         {
319             switch( this->_info._bits_per_pixel )
320             {
321                 case 15:
322                 case 16:
323                 {
324                     this->_mask.red.mask   = 0x007C00; this->_mask.red.width   = 5; this->_mask.red.shift   = 10;
325                     this->_mask.green.mask = 0x0003E0; this->_mask.green.width = 5; this->_mask.green.shift =  5;
326                     this->_mask.blue.mask  = 0x00001F; this->_mask.blue.width  = 5; this->_mask.blue.shift  =  0;
327 
328                     break;
329                 }
330 
331                 case 24:
332                 case 32:
333                 {
334                     this->_mask.red.mask   = 0xFF0000; this->_mask.red.width   = 8; this->_mask.red.shift   = 16;
335                     this->_mask.green.mask = 0x00FF00; this->_mask.green.width = 8; this->_mask.green.shift =  8;
336                     this->_mask.blue.mask  = 0x0000FF; this->_mask.blue.width  = 8; this->_mask.blue.shift  =  0;
337 
338                     break;
339                 }
340             }
341         }
342         else
343         {
344             io_error( "bmp_reader::apply(): unsupported BMP compression" );
345         }
346 
347         using image_t = rgb8_image_t;
348         using it_t = typename image_t::view_t::x_iterator;
349 
350         for( std::ptrdiff_t y = 0
351            ; y < this->_settings._dim.y
352            ; ++y
353            )
354         {
355             this->_io_dev.seek( get_offset( y + this->_settings._top_left.y ));
356 
357             this->_io_dev.read( &row.front()
358                         , row.size()
359                         );
360 
361             image_t img_row( this->_info._width, 1 );
362             image_t::view_t v = gil::view( img_row );
363             it_t it = v.row_begin( 0 );
364 
365             it_t beg = v.row_begin( 0 ) + this->_settings._top_left.x;
366             it_t end = beg + this->_settings._dim.x;
367 
368             byte_t* src = &row.front();
369             for( int32_t i = 0 ; i < this->_info._width; ++i, src += 2 )
370             {
371                 int p = ( src[1] << 8 ) | src[0];
372 
373                 int r = ((p & this->_mask.red.mask)   >> this->_mask.red.shift)   << (8 - this->_mask.red.width);
374                 int g = ((p & this->_mask.green.mask) >> this->_mask.green.shift) << (8 - this->_mask.green.width);
375                 int b = ((p & this->_mask.blue.mask)  >> this->_mask.blue.shift)  << (8 - this->_mask.blue.width);
376 
377                 get_color( it[i], red_t()   ) = static_cast< byte_t >( r );
378                 get_color( it[i], green_t() ) = static_cast< byte_t >( g );
379                 get_color( it[i], blue_t()  ) = static_cast< byte_t >( b );
380             }
381 
382             this->_cc_policy.read( beg
383                                  , end
384                                  , view.row_begin( y )
385                                  );
386         }
387     }
388 
389 
390     // 8-8-8 BGR
391     // 8-8-8-8 BGRA
392     template< typename View_Src
393             , typename View_Dst
394             >
read_data(const View_Dst & view)395     void read_data( const View_Dst& view )
396     {
397         byte_vector_t row( _pitch );
398 
399         View_Src v = interleaved_view( this->_info._width
400                                      , 1
401                                      , (typename View_Src::value_type*) &row.front()
402                                      , this->_info._width * num_channels< View_Src >::value
403                                      );
404 
405         typename View_Src::x_iterator beg = v.row_begin( 0 ) + this->_settings._top_left.x;
406         typename View_Src::x_iterator end = beg + this->_settings._dim.x;
407 
408         for( std::ptrdiff_t y = 0
409            ; y < this->_settings._dim.y
410            ; ++y
411            )
412         {
413             this->_io_dev.seek( get_offset( y + this->_settings._top_left.y ));
414 
415             this->_io_dev.read( &row.front()
416                         , row.size()
417                         );
418 
419             this->_cc_policy.read( beg
420                                  , end
421                                  , view.row_begin( y )
422                                  );
423         }
424     }
425 
426     template< typename Buffer
427             , typename View
428             >
copy_row_if_needed(const Buffer & buf,const View & view,std::ptrdiff_t y)429     void copy_row_if_needed( const Buffer&  buf
430                            , const View&    view
431                            , std::ptrdiff_t y
432                            )
433     {
434         if(  y >= this->_settings._top_left.y
435           && y <  this->_settings._dim.y
436           )
437         {
438             typename Buffer::const_iterator beg = buf.begin() + this->_settings._top_left.x;
439             typename Buffer::const_iterator end = beg + this->_settings._dim.x;
440 
441             std::copy( beg
442                      , end
443                      , view.row_begin( y )
444                      );
445         }
446     }
447 
448     template< typename View_Dst >
read_palette_image_rle(const View_Dst & view)449     void read_palette_image_rle( const View_Dst& view )
450     {
451         BOOST_ASSERT(
452             this->_info._compression == bmp_compression::_rle4 ||
453             this->_info._compression == bmp_compression::_rle8);
454 
455         this->read_palette();
456 
457         // jump to start of rle4 data
458         this->_io_dev.seek( this->_info._offset );
459 
460         // we need to know the stream position for padding purposes
461         std::size_t stream_pos = this->_info._offset;
462 
463         using Buf_type = std::vector<rgba8_pixel_t>;
464         Buf_type buf( this->_settings._dim.x );
465         Buf_type::iterator dst_it  = buf.begin();
466         Buf_type::iterator dst_end = buf.end();
467 
468         // If height is positive, the bitmap is a bottom-up DIB.
469         // If height is negative, the bitmap is a top-down DIB.
470         // The origin of a bottom-up DIB is the bottom left corner of the bitmap image,
471         // which is the first pixel of the first row of bitmap data.
472         // The origin of a top-down DIB is also the bottom left corner of the bitmap image,
473         // but in this case the bottom left corner is the first pixel of the last row of bitmap data.
474         // - "Programming Windows", 5th Ed. by Charles Petzold explains Windows docs ambiguities.
475         std::ptrdiff_t ybeg = 0;
476         std::ptrdiff_t yend = this->_settings._dim.y;
477         std::ptrdiff_t yinc = 1;
478         if( this->_info._height > 0 )
479         {
480             ybeg = this->_settings._dim.y - 1;
481             yend = -1;
482             yinc = -1;
483         }
484 
485         std::ptrdiff_t y = ybeg;
486         bool finished = false;
487 
488         while ( !finished )
489         {
490             std::ptrdiff_t count  = this->_io_dev.read_uint8();
491             std::ptrdiff_t second = this->_io_dev.read_uint8();
492             stream_pos += 2;
493 
494             if ( count )
495             {
496                 // encoded mode
497 
498                 // clamp to boundary
499                 if( count > dst_end - dst_it )
500                 {
501                     count = dst_end - dst_it;
502                 }
503 
504                 if( this->_info._compression == bmp_compression::_rle4 )
505                 {
506                     std::ptrdiff_t cs[2] = { second >> 4, second & 0x0f };
507 
508                     for( int i = 0; i < count; ++i )
509                     {
510                         *dst_it++ = this->_palette[ cs[i & 1] ];
511                     }
512                 }
513                 else
514                 {
515                     for( int i = 0; i < count; ++i )
516                     {
517                         *dst_it++ = this->_palette[ second ];
518                     }
519                 }
520             }
521             else
522             {
523                 switch( second )
524                 {
525                     case 0:  // end of row
526                     {
527                         copy_row_if_needed( buf, view, y );
528 
529                         y += yinc;
530                         if( y == yend )
531                         {
532                             finished = true;
533                         }
534                         else
535                         {
536                             dst_it = buf.begin();
537                             dst_end = buf.end();
538                         }
539 
540                         break;
541                     }
542 
543                     case 1:  // end of bitmap
544                     {
545                         copy_row_if_needed( buf, view, y );
546                         finished = true;
547 
548                         break;
549                     }
550 
551                     case 2:  // offset coordinates
552                     {
553                         std::ptrdiff_t dx = this->_io_dev.read_uint8();
554                         std::ptrdiff_t dy = this->_io_dev.read_uint8() * yinc;
555                         stream_pos += 2;
556 
557                         if( dy )
558                         {
559                             copy_row_if_needed( buf, view, y );
560                         }
561 
562                         std::ptrdiff_t x = dst_it - buf.begin();
563                         x += dx;
564 
565                         if( x > this->_info._width )
566                         {
567                             io_error( "Mangled BMP file." );
568                         }
569 
570                         y += dy;
571                         if( yinc > 0 ? y > yend : y < yend )
572                         {
573                             io_error( "Mangled BMP file." );
574                         }
575 
576                         dst_it = buf.begin() + x;
577                         dst_end = buf.end();
578 
579                         break;
580                     }
581 
582                     default:  // absolute mode
583                     {
584                         count = second;
585 
586                         // clamp to boundary
587                         if( count > dst_end - dst_it )
588                         {
589                             count = dst_end - dst_it;
590                         }
591 
592                         if ( this->_info._compression == bmp_compression::_rle4 )
593                         {
594                             for( int i = 0; i < count; ++i )
595                             {
596                                 uint8_t packed_indices = this->_io_dev.read_uint8();
597                                 ++stream_pos;
598 
599                                 *dst_it++ = this->_palette[ packed_indices >> 4 ];
600                                 if( ++i == second )
601                                     break;
602 
603                                 *dst_it++ = this->_palette[ packed_indices & 0x0f ];
604                             }
605                         }
606                         else
607                         {
608                             for( int i = 0; i < count; ++i )
609                             {
610                                 uint8_t c = this->_io_dev.read_uint8();
611                                 ++stream_pos;
612                                 *dst_it++ = this->_palette[ c ];
613                              }
614                         }
615 
616                         // pad to word boundary
617                         if( ( stream_pos - get_offset( 0 )) & 1 )
618                         {
619                             this->_io_dev.seek( 1, SEEK_CUR );
620                             ++stream_pos;
621                         }
622 
623                         break;
624                     }
625                 }
626             }
627         }
628     }
629 
630 private:
631 
632     std::size_t _pitch;
633 };
634 
635 namespace detail {
636 
637 class bmp_type_format_checker
638 {
639 public:
640 
bmp_type_format_checker(const bmp_bits_per_pixel::type & bpp)641     bmp_type_format_checker( const bmp_bits_per_pixel::type& bpp )
642     : _bpp( bpp )
643     {}
644 
645     template< typename Image >
apply()646     bool apply()
647     {
648         if( _bpp < 32 )
649         {
650             return pixels_are_compatible< typename Image::value_type, rgb8_pixel_t >::value
651                    ? true
652                    : false;
653         }
654         else
655         {
656             return pixels_are_compatible< typename Image::value_type, rgba8_pixel_t >::value
657                    ? true
658                    : false;
659         }
660     }
661 
662 private:
663 
664     // to avoid C4512
operator =(const bmp_type_format_checker &)665     bmp_type_format_checker& operator=( const bmp_type_format_checker& ) { return *this; }
666 
667 private:
668 
669     const bmp_bits_per_pixel::type _bpp;
670 };
671 
672 struct bmp_read_is_supported
673 {
674     template< typename View >
675     struct apply : public is_read_supported< typename get_pixel_type< View >::type
676                                            , bmp_tag
677                                            >
678     {};
679 };
680 
681 } // namespace detail
682 
683 ///
684 /// BMP Dynamic Reader
685 ///
686 template< typename Device >
687 class dynamic_image_reader< Device
688                           , bmp_tag
689                           >
690     : public reader< Device
691                    , bmp_tag
692                    , detail::read_and_no_convert
693                    >
694 {
695     using parent_t = reader<Device, bmp_tag, detail::read_and_no_convert>;
696 
697 public:
698 
dynamic_image_reader(const Device & io_dev,const image_read_settings<bmp_tag> & settings)699     dynamic_image_reader( const Device&                         io_dev
700                         , const image_read_settings< bmp_tag >& settings
701                         )
702     : parent_t( io_dev
703               , settings
704               )
705     {}
706 
707     template< typename ...Images >
apply(any_image<Images...> & images)708     void apply( any_image< Images... >& images )
709     {
710         detail::bmp_type_format_checker format_checker( this->_info._bits_per_pixel );
711 
712         if( !construct_matched( images
713                               , format_checker
714                               ))
715         {
716             io_error( "No matching image type between those of the given any_image and that of the file" );
717         }
718         else
719         {
720             this->init_image( images
721                             , this->_settings
722                             );
723 
724             detail::dynamic_io_fnobj< detail::bmp_read_is_supported
725                                     , parent_t
726                                     > op( this );
727 
728             apply_operation( view( images )
729                            , op
730                            );
731         }
732     }
733 };
734 
735 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
736 #pragma warning(pop)
737 #endif
738 
739 } // gil
740 } // boost
741 
742 #endif
743