1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42 
43 #include "precomp.hpp"
44 #include "grfmt_bmp.hpp"
45 
46 namespace cv
47 {
48 
49 static const char* fmtSignBmp = "BM";
50 
51 /************************ BMP decoder *****************************/
52 
BmpDecoder()53 BmpDecoder::BmpDecoder()
54 {
55     m_signature = fmtSignBmp;
56     m_offset = -1;
57     m_buf_supported = true;
58 }
59 
60 
~BmpDecoder()61 BmpDecoder::~BmpDecoder()
62 {
63 }
64 
65 
close()66 void  BmpDecoder::close()
67 {
68     m_strm.close();
69 }
70 
newDecoder() const71 ImageDecoder BmpDecoder::newDecoder() const
72 {
73     return makePtr<BmpDecoder>();
74 }
75 
readHeader()76 bool  BmpDecoder::readHeader()
77 {
78     bool result = false;
79     bool iscolor = false;
80 
81     if( !m_buf.empty() )
82     {
83         if( !m_strm.open( m_buf ) )
84             return false;
85     }
86     else if( !m_strm.open( m_filename ))
87         return false;
88 
89     try
90     {
91         m_strm.skip( 10 );
92         m_offset = m_strm.getDWord();
93 
94         int  size = m_strm.getDWord();
95 
96         if( size >= 36 )
97         {
98             m_width  = m_strm.getDWord();
99             m_height = m_strm.getDWord();
100             m_bpp    = m_strm.getDWord() >> 16;
101             m_rle_code = (BmpCompression)m_strm.getDWord();
102             m_strm.skip(12);
103             int clrused = m_strm.getDWord();
104             m_strm.skip( size - 36 );
105 
106             if( m_width > 0 && m_height != 0 &&
107              (((m_bpp == 1 || m_bpp == 4 || m_bpp == 8 ||
108                 m_bpp == 24 || m_bpp == 32 ) && m_rle_code == BMP_RGB) ||
109                (m_bpp == 16 && (m_rle_code == BMP_RGB || m_rle_code == BMP_BITFIELDS)) ||
110                (m_bpp == 4 && m_rle_code == BMP_RLE4) ||
111                (m_bpp == 8 && m_rle_code == BMP_RLE8)))
112             {
113                 iscolor = true;
114                 result = true;
115 
116                 if( m_bpp <= 8 )
117                 {
118                     memset( m_palette, 0, sizeof(m_palette));
119                     m_strm.getBytes( m_palette, (clrused == 0? 1<<m_bpp : clrused)*4 );
120                     iscolor = IsColorPalette( m_palette, m_bpp );
121                 }
122                 else if( m_bpp == 16 && m_rle_code == BMP_BITFIELDS )
123                 {
124                     int redmask = m_strm.getDWord();
125                     int greenmask = m_strm.getDWord();
126                     int bluemask = m_strm.getDWord();
127 
128                     if( bluemask == 0x1f && greenmask == 0x3e0 && redmask == 0x7c00 )
129                         m_bpp = 15;
130                     else if( bluemask == 0x1f && greenmask == 0x7e0 && redmask == 0xf800 )
131                         ;
132                     else
133                         result = false;
134                 }
135                 else if( m_bpp == 16 && m_rle_code == BMP_RGB )
136                     m_bpp = 15;
137             }
138         }
139         else if( size == 12 )
140         {
141             m_width  = m_strm.getWord();
142             m_height = m_strm.getWord();
143             m_bpp    = m_strm.getDWord() >> 16;
144             m_rle_code = BMP_RGB;
145 
146             if( m_width > 0 && m_height != 0 &&
147                (m_bpp == 1 || m_bpp == 4 || m_bpp == 8 ||
148                 m_bpp == 24 || m_bpp == 32 ))
149             {
150                 if( m_bpp <= 8 )
151                 {
152                     uchar buffer[256*3];
153                     int j, clrused = 1 << m_bpp;
154                     m_strm.getBytes( buffer, clrused*3 );
155                     for( j = 0; j < clrused; j++ )
156                     {
157                         m_palette[j].b = buffer[3*j+0];
158                         m_palette[j].g = buffer[3*j+1];
159                         m_palette[j].r = buffer[3*j+2];
160                     }
161                 }
162                 result = true;
163             }
164         }
165     }
166     catch(...)
167     {
168     }
169 
170     m_type = iscolor ? CV_8UC3 : CV_8UC1;
171     m_origin = m_height > 0 ? IPL_ORIGIN_BL : IPL_ORIGIN_TL;
172     m_height = std::abs(m_height);
173 
174     if( !result )
175     {
176         m_offset = -1;
177         m_width = m_height = -1;
178         m_strm.close();
179     }
180     return result;
181 }
182 
183 
readData(Mat & img)184 bool  BmpDecoder::readData( Mat& img )
185 {
186     uchar* data = img.ptr();
187     int step = (int)img.step;
188     bool color = img.channels() > 1;
189     uchar  gray_palette[256];
190     bool   result = false;
191     int  src_pitch = ((m_width*(m_bpp != 15 ? m_bpp : 16) + 7)/8 + 3) & -4;
192     int  nch = color ? 3 : 1;
193     int  y, width3 = m_width*nch;
194 
195     if( m_offset < 0 || !m_strm.isOpened())
196         return false;
197 
198     if( m_origin == IPL_ORIGIN_BL )
199     {
200         data += (m_height - 1)*step;
201         step = -step;
202     }
203 
204     AutoBuffer<uchar> _src, _bgr;
205     _src.allocate(src_pitch + 32);
206 
207     if( !color )
208     {
209         if( m_bpp <= 8 )
210         {
211             CvtPaletteToGray( m_palette, gray_palette, 1 << m_bpp );
212         }
213         _bgr.allocate(m_width*3 + 32);
214     }
215     uchar *src = _src, *bgr = _bgr;
216 
217     try
218     {
219         m_strm.setPos( m_offset );
220 
221         switch( m_bpp )
222         {
223         /************************* 1 BPP ************************/
224         case 1:
225             for( y = 0; y < m_height; y++, data += step )
226             {
227                 m_strm.getBytes( src, src_pitch );
228                 FillColorRow1( color ? data : bgr, src, m_width, m_palette );
229                 if( !color )
230                     icvCvt_BGR2Gray_8u_C3C1R( bgr, 0, data, 0, cvSize(m_width,1) );
231             }
232             result = true;
233             break;
234 
235         /************************* 4 BPP ************************/
236         case 4:
237             if( m_rle_code == BMP_RGB )
238             {
239                 for( y = 0; y < m_height; y++, data += step )
240                 {
241                     m_strm.getBytes( src, src_pitch );
242                     if( color )
243                         FillColorRow4( data, src, m_width, m_palette );
244                     else
245                         FillGrayRow4( data, src, m_width, gray_palette );
246                 }
247                 result = true;
248             }
249             else if( m_rle_code == BMP_RLE4 ) // rle4 compression
250             {
251                 uchar* line_end = data + width3;
252                 y = 0;
253 
254                 for(;;)
255                 {
256                     int code = m_strm.getWord();
257                     int len = code & 255;
258                     code >>= 8;
259                     if( len != 0 ) // encoded mode
260                     {
261                         PaletteEntry clr[2];
262                         uchar gray_clr[2];
263                         int t = 0;
264 
265                         clr[0] = m_palette[code >> 4];
266                         clr[1] = m_palette[code & 15];
267                         gray_clr[0] = gray_palette[code >> 4];
268                         gray_clr[1] = gray_palette[code & 15];
269 
270                         uchar* end = data + len*nch;
271                         if( end > line_end ) goto decode_rle4_bad;
272                         do
273                         {
274                             if( color )
275                                 WRITE_PIX( data, clr[t] );
276                             else
277                                 *data = gray_clr[t];
278                             t ^= 1;
279                         }
280                         while( (data += nch) < end );
281                     }
282                     else if( code > 2 ) // absolute mode
283                     {
284                         if( data + code*nch > line_end ) goto decode_rle4_bad;
285                         m_strm.getBytes( src, (((code + 1)>>1) + 1) & -2 );
286                         if( color )
287                             data = FillColorRow4( data, src, code, m_palette );
288                         else
289                             data = FillGrayRow4( data, src, code, gray_palette );
290                     }
291                     else
292                     {
293                         int x_shift3 = (int)(line_end - data);
294                         int y_shift = m_height - y;
295 
296                         if( code == 2 )
297                         {
298                             x_shift3 = m_strm.getByte()*nch;
299                             y_shift = m_strm.getByte();
300                         }
301 
302                         len = x_shift3 + ((y_shift * width3) & ((code == 0) - 1));
303 
304                         if( color )
305                             data = FillUniColor( data, line_end, step, width3,
306                                                  y, m_height, x_shift3,
307                                                  m_palette[0] );
308                         else
309                             data = FillUniGray( data, line_end, step, width3,
310                                                 y, m_height, x_shift3,
311                                                 gray_palette[0] );
312 
313                         if( y >= m_height )
314                             break;
315                     }
316                 }
317 
318                 result = true;
319 decode_rle4_bad: ;
320             }
321             break;
322 
323         /************************* 8 BPP ************************/
324         case 8:
325             if( m_rle_code == BMP_RGB )
326             {
327                 for( y = 0; y < m_height; y++, data += step )
328                 {
329                     m_strm.getBytes( src, src_pitch );
330                     if( color )
331                         FillColorRow8( data, src, m_width, m_palette );
332                     else
333                         FillGrayRow8( data, src, m_width, gray_palette );
334                 }
335                 result = true;
336             }
337             else if( m_rle_code == BMP_RLE8 ) // rle8 compression
338             {
339                 uchar* line_end = data + width3;
340                 int line_end_flag = 0;
341                 y = 0;
342 
343                 for(;;)
344                 {
345                     int code = m_strm.getWord();
346                     int len = code & 255;
347                     code >>= 8;
348                     if( len != 0 ) // encoded mode
349                     {
350                         int prev_y = y;
351                         len *= nch;
352 
353                         if( data + len > line_end )
354                             goto decode_rle8_bad;
355 
356                         if( color )
357                             data = FillUniColor( data, line_end, step, width3,
358                                                  y, m_height, len,
359                                                  m_palette[code] );
360                         else
361                             data = FillUniGray( data, line_end, step, width3,
362                                                 y, m_height, len,
363                                                 gray_palette[code] );
364 
365                         line_end_flag = y - prev_y;
366                     }
367                     else if( code > 2 ) // absolute mode
368                     {
369                         int prev_y = y;
370                         int code3 = code*nch;
371 
372                         if( data + code3 > line_end )
373                             goto decode_rle8_bad;
374                         m_strm.getBytes( src, (code + 1) & -2 );
375                         if( color )
376                             data = FillColorRow8( data, src, code, m_palette );
377                         else
378                             data = FillGrayRow8( data, src, code, gray_palette );
379 
380                         line_end_flag = y - prev_y;
381                     }
382                     else
383                     {
384                         int x_shift3 = (int)(line_end - data);
385                         int y_shift = m_height - y;
386 
387                         if( code || !line_end_flag || x_shift3 < width3 )
388                         {
389                             if( code == 2 )
390                             {
391                                 x_shift3 = m_strm.getByte()*nch;
392                                 y_shift = m_strm.getByte();
393                             }
394 
395                             x_shift3 += (y_shift * width3) & ((code == 0) - 1);
396 
397                             if( y >= m_height )
398                                 break;
399 
400                             if( color )
401                                 data = FillUniColor( data, line_end, step, width3,
402                                                      y, m_height, x_shift3,
403                                                      m_palette[0] );
404                             else
405                                 data = FillUniGray( data, line_end, step, width3,
406                                                     y, m_height, x_shift3,
407                                                     gray_palette[0] );
408 
409                             if( y >= m_height )
410                                 break;
411                         }
412 
413                         line_end_flag = 0;
414                         if( y >= m_height )
415                             break;
416                     }
417                 }
418 
419                 result = true;
420 decode_rle8_bad: ;
421             }
422             break;
423         /************************* 15 BPP ************************/
424         case 15:
425             for( y = 0; y < m_height; y++, data += step )
426             {
427                 m_strm.getBytes( src, src_pitch );
428                 if( !color )
429                     icvCvt_BGR5552Gray_8u_C2C1R( src, 0, data, 0, cvSize(m_width,1) );
430                 else
431                     icvCvt_BGR5552BGR_8u_C2C3R( src, 0, data, 0, cvSize(m_width,1) );
432             }
433             result = true;
434             break;
435         /************************* 16 BPP ************************/
436         case 16:
437             for( y = 0; y < m_height; y++, data += step )
438             {
439                 m_strm.getBytes( src, src_pitch );
440                 if( !color )
441                     icvCvt_BGR5652Gray_8u_C2C1R( src, 0, data, 0, cvSize(m_width,1) );
442                 else
443                     icvCvt_BGR5652BGR_8u_C2C3R( src, 0, data, 0, cvSize(m_width,1) );
444             }
445             result = true;
446             break;
447         /************************* 24 BPP ************************/
448         case 24:
449             for( y = 0; y < m_height; y++, data += step )
450             {
451                 m_strm.getBytes( src, src_pitch );
452                 if(!color)
453                     icvCvt_BGR2Gray_8u_C3C1R( src, 0, data, 0, cvSize(m_width,1) );
454                 else
455                     memcpy( data, src, m_width*3 );
456             }
457             result = true;
458             break;
459         /************************* 32 BPP ************************/
460         case 32:
461             for( y = 0; y < m_height; y++, data += step )
462             {
463                 m_strm.getBytes( src, src_pitch );
464 
465                 if( !color )
466                     icvCvt_BGRA2Gray_8u_C4C1R( src, 0, data, 0, cvSize(m_width,1) );
467                 else
468                     icvCvt_BGRA2BGR_8u_C4C3R( src, 0, data, 0, cvSize(m_width,1) );
469             }
470             result = true;
471             break;
472         default:
473             assert(0);
474         }
475     }
476     catch(...)
477     {
478     }
479 
480     return result;
481 }
482 
483 
484 //////////////////////////////////////////////////////////////////////////////////////////
485 
BmpEncoder()486 BmpEncoder::BmpEncoder()
487 {
488     m_description = "Windows bitmap (*.bmp;*.dib)";
489     m_buf_supported = true;
490 }
491 
492 
~BmpEncoder()493 BmpEncoder::~BmpEncoder()
494 {
495 }
496 
newEncoder() const497 ImageEncoder BmpEncoder::newEncoder() const
498 {
499     return makePtr<BmpEncoder>();
500 }
501 
write(const Mat & img,const std::vector<int> &)502 bool  BmpEncoder::write( const Mat& img, const std::vector<int>& )
503 {
504     int width = img.cols, height = img.rows, channels = img.channels();
505     int fileStep = (width*channels + 3) & -4;
506     uchar zeropad[] = "\0\0\0\0";
507     WLByteStream strm;
508 
509     if( m_buf )
510     {
511         if( !strm.open( *m_buf ) )
512             return false;
513     }
514     else if( !strm.open( m_filename ))
515         return false;
516 
517     int  bitmapHeaderSize = 40;
518     int  paletteSize = channels > 1 ? 0 : 1024;
519     int  headerSize = 14 /* fileheader */ + bitmapHeaderSize + paletteSize;
520     int  fileSize = fileStep*height + headerSize;
521     PaletteEntry palette[256];
522 
523     if( m_buf )
524         m_buf->reserve( alignSize(fileSize + 16, 256) );
525 
526     // write signature 'BM'
527     strm.putBytes( fmtSignBmp, (int)strlen(fmtSignBmp) );
528 
529     // write file header
530     strm.putDWord( fileSize ); // file size
531     strm.putDWord( 0 );
532     strm.putDWord( headerSize );
533 
534     // write bitmap header
535     strm.putDWord( bitmapHeaderSize );
536     strm.putDWord( width );
537     strm.putDWord( height );
538     strm.putWord( 1 );
539     strm.putWord( channels << 3 );
540     strm.putDWord( BMP_RGB );
541     strm.putDWord( 0 );
542     strm.putDWord( 0 );
543     strm.putDWord( 0 );
544     strm.putDWord( 0 );
545     strm.putDWord( 0 );
546 
547     if( channels == 1 )
548     {
549         FillGrayPalette( palette, 8 );
550         strm.putBytes( palette, sizeof(palette));
551     }
552 
553     width *= channels;
554     for( int y = height - 1; y >= 0; y-- )
555     {
556         strm.putBytes( img.ptr(y), width );
557         if( fileStep > width )
558             strm.putBytes( zeropad, fileStep - width );
559     }
560 
561     strm.close();
562     return true;
563 }
564 
565 }
566