Update to 2.0.0 tree from current Fremantle build
[opencv] / src / highgui / grfmt_bmp.cpp
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 "_highgui.h"
44 #include "grfmt_bmp.h"
45
46 namespace cv
47 {
48
49 static const char* fmtSignBmp = "BM";
50
51 /************************ BMP decoder *****************************/
52
53 BmpDecoder::BmpDecoder()
54 {
55     m_signature = fmtSignBmp;
56     m_offset = -1;
57     m_buf_supported = true;
58 }
59
60
61 BmpDecoder::~BmpDecoder()
62 {
63 }
64
65
66 void  BmpDecoder::close()
67 {
68     m_strm.close();
69 }
70
71 ImageDecoder BmpDecoder::newDecoder() const
72 {
73     return new BmpDecoder;
74 }
75
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     if( !result )
172     {
173         m_offset = -1;
174         m_width = m_height = -1;
175         m_strm.close();
176     }
177     return result;
178 }
179
180
181 bool  BmpDecoder::readData( Mat& img )
182 {
183     uchar* data = img.data;
184     int step = img.step;
185     bool color = img.channels() > 1;
186     uchar  gray_palette[256];
187     bool   result = false;
188     int  src_pitch = ((m_width*(m_bpp != 15 ? m_bpp : 16) + 7)/8 + 3) & -4;
189     int  nch = color ? 3 : 1;
190     int  y, width3 = m_width*nch;
191
192     if( m_offset < 0 || !m_strm.isOpened())
193         return false;
194
195     data += (m_height - 1)*step;
196     step = -step;
197
198     AutoBuffer<uchar> _src, _bgr;
199     if( (m_bpp != 24 || !color) )
200         _src.allocate(src_pitch + 32);
201
202     if( !color )
203     {
204         if( m_bpp <= 8 )
205         {
206             CvtPaletteToGray( m_palette, gray_palette, 1 << m_bpp );
207         }
208         _bgr.allocate(m_width*3 + 32);
209     }
210     uchar *src = _src, *bgr = _bgr;
211
212     try
213     {
214         m_strm.setPos( m_offset );
215
216         switch( m_bpp )
217         {
218         /************************* 1 BPP ************************/
219         case 1:
220             for( y = 0; y < m_height; y++, data += step )
221             {
222                 m_strm.getBytes( src, src_pitch );
223                 FillColorRow1( color ? data : bgr, src, m_width, m_palette );
224                 if( !color )
225                     icvCvt_BGR2Gray_8u_C3C1R( bgr, 0, data, 0, cvSize(m_width,1) );
226             }
227             result = true;
228             break;
229
230         /************************* 4 BPP ************************/
231         case 4:
232             if( m_rle_code == BMP_RGB )
233             {
234                 for( y = 0; y < m_height; y++, data += step )
235                 {
236                     m_strm.getBytes( src, src_pitch );
237                     if( color )
238                         FillColorRow4( data, src, m_width, m_palette );
239                     else
240                         FillGrayRow4( data, src, m_width, gray_palette );
241                 }
242                 result = true;
243             }
244             else if( m_rle_code == BMP_RLE4 ) // rle4 compression
245             {
246                 uchar* line_end = data + width3;
247                 y = 0;
248
249                 for(;;)
250                 {
251                     int code = m_strm.getWord();
252                     int len = code & 255;
253                     code >>= 8;
254                     if( len != 0 ) // encoded mode
255                     {
256                         PaletteEntry clr[2];
257                         uchar gray_clr[2];
258                         int t = 0;
259
260                         clr[0] = m_palette[code >> 4];
261                         clr[1] = m_palette[code & 15];
262                         gray_clr[0] = gray_palette[code >> 4];
263                         gray_clr[1] = gray_palette[code & 15];
264
265                         uchar* end = data + len*nch;
266                         if( end > line_end ) goto decode_rle4_bad;
267                         do
268                         {
269                             if( color )
270                                 WRITE_PIX( data, clr[t] );
271                             else
272                                 *data = gray_clr[t];
273                             t ^= 1;
274                         }
275                         while( (data += nch) < end );
276                     }
277                     else if( code > 2 ) // absolute mode
278                     {
279                         if( data + code*nch > line_end ) goto decode_rle4_bad;
280                         m_strm.getBytes( src, (((code + 1)>>1) + 1) & -2 );
281                         if( color )
282                             data = FillColorRow4( data, src, code, m_palette );
283                         else
284                             data = FillGrayRow4( data, src, code, gray_palette );
285                     }
286                     else
287                     {
288                         int x_shift3 = (int)(line_end - data);
289                         int y_shift = m_height - y;
290
291                         if( code == 2 )
292                         {
293                             x_shift3 = m_strm.getByte()*nch;
294                             y_shift = m_strm.getByte();
295                         }
296
297                         len = x_shift3 + ((y_shift * width3) & ((code == 0) - 1));
298
299                         if( color )
300                             data = FillUniColor( data, line_end, step, width3,
301                                                  y, m_height, x_shift3,
302                                                  m_palette[0] );
303                         else
304                             data = FillUniGray( data, line_end, step, width3,
305                                                 y, m_height, x_shift3,
306                                                 gray_palette[0] );
307
308                         if( y >= m_height )
309                             break;
310                     }
311                 }
312
313                 result = true;
314 decode_rle4_bad: ;
315             }
316             break;
317
318         /************************* 8 BPP ************************/
319         case 8:
320             if( m_rle_code == BMP_RGB )
321             {
322                 for( y = 0; y < m_height; y++, data += step )
323                 {
324                     m_strm.getBytes( src, src_pitch );
325                     if( color )
326                         FillColorRow8( data, src, m_width, m_palette );
327                     else
328                         FillGrayRow8( data, src, m_width, gray_palette );
329                 }
330                 result = true;
331             }
332             else if( m_rle_code == BMP_RLE8 ) // rle8 compression
333             {
334                 uchar* line_end = data + width3;
335                 int line_end_flag = 0;
336                 y = 0;
337
338                 for(;;)
339                 {
340                     int code = m_strm.getWord();
341                     int len = code & 255;
342                     code >>= 8;
343                     if( len != 0 ) // encoded mode
344                     {
345                         int prev_y = y;
346                         len *= nch;
347
348                         if( data + len > line_end )
349                             goto decode_rle8_bad;
350
351                         if( color )
352                             data = FillUniColor( data, line_end, step, width3,
353                                                  y, m_height, len,
354                                                  m_palette[code] );
355                         else
356                             data = FillUniGray( data, line_end, step, width3,
357                                                 y, m_height, len,
358                                                 gray_palette[code] );
359
360                         line_end_flag = y - prev_y;
361                     }
362                     else if( code > 2 ) // absolute mode
363                     {
364                         int prev_y = y;
365                         int code3 = code*nch;
366
367                         if( data + code3 > line_end )
368                             goto decode_rle8_bad;
369                         m_strm.getBytes( src, (code + 1) & -2 );
370                         if( color )
371                             data = FillColorRow8( data, src, code, m_palette );
372                         else
373                             data = FillGrayRow8( data, src, code, gray_palette );
374
375                         line_end_flag = y - prev_y;
376                     }
377                     else
378                     {
379                         int x_shift3 = (int)(line_end - data);
380                         int y_shift = m_height - y;
381
382                         if( code || !line_end_flag || x_shift3 < width3 )
383                         {
384                             if( code == 2 )
385                             {
386                                 x_shift3 = m_strm.getByte()*nch;
387                                 y_shift = m_strm.getByte();
388                             }
389
390                             x_shift3 += (y_shift * width3) & ((code == 0) - 1);
391
392                             if( y >= m_height )
393                                 break;
394
395                             if( color )
396                                 data = FillUniColor( data, line_end, step, width3,
397                                                      y, m_height, x_shift3,
398                                                      m_palette[0] );
399                             else
400                                 data = FillUniGray( data, line_end, step, width3,
401                                                     y, m_height, x_shift3,
402                                                     gray_palette[0] );
403
404                             if( y >= m_height )
405                                 break;
406                         }
407
408                         line_end_flag = 0;
409                     }
410                 }
411
412                 result = true;
413 decode_rle8_bad: ;
414             }
415             break;
416         /************************* 15 BPP ************************/
417         case 15:
418             for( y = 0; y < m_height; y++, data += step )
419             {
420                 m_strm.getBytes( src, src_pitch );
421                 if( !color )
422                     icvCvt_BGR5552Gray_8u_C2C1R( src, 0, data, 0, cvSize(m_width,1) );
423                 else
424                     icvCvt_BGR5552BGR_8u_C2C3R( src, 0, data, 0, cvSize(m_width,1) );
425             }
426             result = true;
427             break;
428         /************************* 16 BPP ************************/
429         case 16:
430             for( y = 0; y < m_height; y++, data += step )
431             {
432                 m_strm.getBytes( src, src_pitch );
433                 if( !color )
434                     icvCvt_BGR5652Gray_8u_C2C1R( src, 0, data, 0, cvSize(m_width,1) );
435                 else
436                     icvCvt_BGR5652BGR_8u_C2C3R( src, 0, data, 0, cvSize(m_width,1) );
437             }
438             result = true;
439             break;
440         /************************* 24 BPP ************************/
441         case 24:
442             for( y = 0; y < m_height; y++, data += step )
443             {
444                 m_strm.getBytes( color ? data : src, src_pitch );
445                 if( !color )
446                     icvCvt_BGR2Gray_8u_C3C1R( src, 0, data, 0, cvSize(m_width,1) );
447             }
448             result = true;
449             break;
450         /************************* 32 BPP ************************/
451         case 32:
452             for( y = 0; y < m_height; y++, data += step )
453             {
454                 m_strm.getBytes( src, src_pitch );
455
456                 if( !color )
457                     icvCvt_BGRA2Gray_8u_C4C1R( src, 0, data, 0, cvSize(m_width,1) );
458                 else
459                     icvCvt_BGRA2BGR_8u_C4C3R( src, 0, data, 0, cvSize(m_width,1) );
460             }
461             result = true;
462             break;
463         default:
464             assert(0);
465         }
466     }
467     catch(...)
468     {
469     }
470
471     return result;
472 }
473
474
475 //////////////////////////////////////////////////////////////////////////////////////////
476
477 BmpEncoder::BmpEncoder()
478 {
479     m_description = "Windows bitmap (*.bmp;*.dib)";
480     m_buf_supported = true;
481 }
482
483
484 BmpEncoder::~BmpEncoder()
485 {
486 }
487
488 ImageEncoder BmpEncoder::newEncoder() const
489 {
490     return new BmpEncoder;
491 }
492
493 bool  BmpEncoder::write( const Mat& img, const vector<int>& )
494 {
495     int width = img.cols, height = img.rows, channels = img.channels();
496     int fileStep = (width*channels + 3) & -4;
497     uchar zeropad[] = "\0\0\0\0";
498     WLByteStream strm;
499
500     if( m_buf )
501     {
502         if( !strm.open( *m_buf ) )
503             return false;
504     }
505     else if( !strm.open( m_filename ))
506         return false;
507
508     int  bitmapHeaderSize = 40;
509     int  paletteSize = channels > 1 ? 0 : 1024;
510     int  headerSize = 14 /* fileheader */ + bitmapHeaderSize + paletteSize;
511     int  fileSize = fileStep*height + headerSize;
512     PaletteEntry palette[256];
513
514     if( m_buf )
515         m_buf->reserve( alignSize(fileSize + 16, 256) );
516
517     // write signature 'BM'
518     strm.putBytes( fmtSignBmp, (int)strlen(fmtSignBmp) );
519
520     // write file header
521     strm.putDWord( fileSize ); // file size
522     strm.putDWord( 0 );
523     strm.putDWord( headerSize );
524
525     // write bitmap header
526     strm.putDWord( bitmapHeaderSize );
527     strm.putDWord( width );
528     strm.putDWord( height );
529     strm.putWord( 1 );
530     strm.putWord( channels << 3 );
531     strm.putDWord( BMP_RGB );
532     strm.putDWord( 0 );
533     strm.putDWord( 0 );
534     strm.putDWord( 0 );
535     strm.putDWord( 0 );
536     strm.putDWord( 0 );
537
538     if( channels == 1 )
539     {
540         FillGrayPalette( palette, 8 );
541         strm.putBytes( palette, sizeof(palette));
542     }
543
544     width *= channels;
545     for( int y = height - 1; y >= 0; y-- )
546     {
547         strm.putBytes( img.data + img.step*y, width );
548         if( fileStep > width )
549             strm.putBytes( zeropad, fileStep - width );
550     }
551
552     strm.close();
553     return true;
554 }
555
556 }
557