imgcodecs: fix 4 reading channel bmp images
[platform/upstream/opencv.git] / modules / imgcodecs / src / 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 "precomp.hpp"
44 #include "grfmt_bmp.hpp"
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     m_origin = 0;
59     m_bpp = 0;
60     m_rle_code = BMP_RGB;
61 }
62
63
64 BmpDecoder::~BmpDecoder()
65 {
66 }
67
68
69 void  BmpDecoder::close()
70 {
71     m_strm.close();
72 }
73
74 ImageDecoder BmpDecoder::newDecoder() const
75 {
76     return makePtr<BmpDecoder>();
77 }
78
79 bool  BmpDecoder::readHeader()
80 {
81     bool result = false;
82     bool iscolor = false;
83
84     if( !m_buf.empty() )
85     {
86         if( !m_strm.open( m_buf ) )
87             return false;
88     }
89     else if( !m_strm.open( m_filename ))
90         return false;
91
92     try
93     {
94         m_strm.skip( 10 );
95         m_offset = m_strm.getDWord();
96
97         int  size = m_strm.getDWord();
98
99         if( size >= 36 )
100         {
101             m_width  = m_strm.getDWord();
102             m_height = m_strm.getDWord();
103             m_bpp    = m_strm.getDWord() >> 16;
104             m_rle_code = (BmpCompression)m_strm.getDWord();
105             m_strm.skip(12);
106             int clrused = m_strm.getDWord();
107             m_strm.skip( size - 36 );
108
109             if( m_width > 0 && m_height != 0 &&
110              (((m_bpp == 1 || m_bpp == 4 || m_bpp == 8 ||
111                 m_bpp == 24 || m_bpp == 32 ) && m_rle_code == BMP_RGB) ||
112                ((m_bpp == 16 || m_bpp == 32) && (m_rle_code == BMP_RGB || m_rle_code == BMP_BITFIELDS)) ||
113                (m_bpp == 4 && m_rle_code == BMP_RLE4) ||
114                (m_bpp == 8 && m_rle_code == BMP_RLE8)))
115             {
116                 iscolor = true;
117                 result = true;
118
119                 if( m_bpp <= 8 )
120                 {
121                     CV_Assert(clrused <= 256);
122                     memset(m_palette, 0, sizeof(m_palette));
123                     m_strm.getBytes(m_palette, (clrused == 0? 1<<m_bpp : clrused)*4 );
124                     iscolor = IsColorPalette( m_palette, m_bpp );
125                 }
126                 else if( m_bpp == 16 && m_rle_code == BMP_BITFIELDS )
127                 {
128                     int redmask = m_strm.getDWord();
129                     int greenmask = m_strm.getDWord();
130                     int bluemask = m_strm.getDWord();
131
132                     if( bluemask == 0x1f && greenmask == 0x3e0 && redmask == 0x7c00 )
133                         m_bpp = 15;
134                     else if( bluemask == 0x1f && greenmask == 0x7e0 && redmask == 0xf800 )
135                         ;
136                     else
137                         result = false;
138                 }
139                 else if (m_bpp == 32 && m_rle_code == BMP_BITFIELDS)
140                 {
141                     // 32bit BMP not require to check something - we can simply allow it to use
142                     ;
143                 }
144                 else if( m_bpp == 16 && m_rle_code == BMP_RGB )
145                     m_bpp = 15;
146             }
147         }
148         else if( size == 12 )
149         {
150             m_width  = m_strm.getWord();
151             m_height = m_strm.getWord();
152             m_bpp    = m_strm.getDWord() >> 16;
153             m_rle_code = BMP_RGB;
154
155             if( m_width > 0 && m_height != 0 &&
156                (m_bpp == 1 || m_bpp == 4 || m_bpp == 8 ||
157                 m_bpp == 24 || m_bpp == 32 ))
158             {
159                 if( m_bpp <= 8 )
160                 {
161                     uchar buffer[256*3];
162                     int j, clrused = 1 << m_bpp;
163                     m_strm.getBytes( buffer, clrused*3 );
164                     for( j = 0; j < clrused; j++ )
165                     {
166                         m_palette[j].b = buffer[3*j+0];
167                         m_palette[j].g = buffer[3*j+1];
168                         m_palette[j].r = buffer[3*j+2];
169                     }
170                 }
171                 result = true;
172             }
173         }
174     }
175     catch(...)
176     {
177         throw;
178     }
179     // in 32 bit case alpha channel is used - so require CV_8UC4 type
180     m_type = iscolor ? (m_bpp == 32 ? CV_8UC4 : CV_8UC3 ) : CV_8UC1;
181     m_origin = m_height > 0 ? IPL_ORIGIN_BL : IPL_ORIGIN_TL;
182     m_height = std::abs(m_height);
183
184     if( !result )
185     {
186         m_offset = -1;
187         m_width = m_height = -1;
188         m_strm.close();
189     }
190     return result;
191 }
192
193
194 bool  BmpDecoder::readData( Mat& img )
195 {
196     uchar* data = img.ptr();
197     int step = validateToInt(img.step);
198     bool color = img.channels() > 1;
199     uchar  gray_palette[256] = {0};
200     bool   result = false;
201     int  src_pitch = ((m_width*(m_bpp != 15 ? m_bpp : 16) + 7)/8 + 3) & -4;
202     int  nch = color ? 3 : 1;
203     int  y, width3 = m_width*nch;
204
205     if( m_offset < 0 || !m_strm.isOpened())
206         return false;
207
208     if( m_origin == IPL_ORIGIN_BL )
209     {
210         data += (m_height - 1)*(size_t)step;
211         step = -step;
212     }
213
214     AutoBuffer<uchar> _src, _bgr;
215     _src.allocate(src_pitch + 32);
216
217     if( !color )
218     {
219         if( m_bpp <= 8 )
220         {
221             CvtPaletteToGray( m_palette, gray_palette, 1 << m_bpp );
222         }
223         _bgr.allocate(m_width*3 + 32);
224     }
225     uchar *src = _src, *bgr = _bgr;
226
227     try
228     {
229         m_strm.setPos( m_offset );
230
231         switch( m_bpp )
232         {
233         /************************* 1 BPP ************************/
234         case 1:
235             for( y = 0; y < m_height; y++, data += step )
236             {
237                 m_strm.getBytes( src, src_pitch );
238                 FillColorRow1( color ? data : bgr, src, m_width, m_palette );
239                 if( !color )
240                     icvCvt_BGR2Gray_8u_C3C1R( bgr, 0, data, 0, cvSize(m_width,1) );
241             }
242             result = true;
243             break;
244
245         /************************* 4 BPP ************************/
246         case 4:
247             if( m_rle_code == BMP_RGB )
248             {
249                 for( y = 0; y < m_height; y++, data += step )
250                 {
251                     m_strm.getBytes( src, src_pitch );
252                     if( color )
253                         FillColorRow4( data, src, m_width, m_palette );
254                     else
255                         FillGrayRow4( data, src, m_width, gray_palette );
256                 }
257                 result = true;
258             }
259             else if( m_rle_code == BMP_RLE4 ) // rle4 compression
260             {
261                 uchar* line_end = data + width3;
262                 y = 0;
263
264                 for(;;)
265                 {
266                     int code = m_strm.getWord();
267                     int len = code & 255;
268                     code >>= 8;
269                     if( len != 0 ) // encoded mode
270                     {
271                         PaletteEntry clr[2];
272                         uchar gray_clr[2];
273                         int t = 0;
274
275                         clr[0] = m_palette[code >> 4];
276                         clr[1] = m_palette[code & 15];
277                         gray_clr[0] = gray_palette[code >> 4];
278                         gray_clr[1] = gray_palette[code & 15];
279
280                         uchar* end = data + len*nch;
281                         if( end > line_end ) goto decode_rle4_bad;
282                         do
283                         {
284                             if( color )
285                                 WRITE_PIX( data, clr[t] );
286                             else
287                                 *data = gray_clr[t];
288                             t ^= 1;
289                         }
290                         while( (data += nch) < end );
291                     }
292                     else if( code > 2 ) // absolute mode
293                     {
294                         if( data + code*nch > line_end ) goto decode_rle4_bad;
295                         int sz = (((code + 1)>>1) + 1) & (~1);
296                         CV_Assert((size_t)sz < _src.size());
297                         m_strm.getBytes(src, sz);
298                         if( color )
299                             data = FillColorRow4( data, src, code, m_palette );
300                         else
301                             data = FillGrayRow4( data, src, code, gray_palette );
302                     }
303                     else
304                     {
305                         int x_shift3 = (int)(line_end - data);
306                         int y_shift = m_height - y;
307
308                         if( code == 2 )
309                         {
310                             x_shift3 = m_strm.getByte()*nch;
311                             y_shift = m_strm.getByte();
312                         }
313
314                         len = x_shift3 + ((y_shift * width3) & ((code == 0) - 1));
315
316                         if( color )
317                             data = FillUniColor( data, line_end, step, width3,
318                                                  y, m_height, x_shift3,
319                                                  m_palette[0] );
320                         else
321                             data = FillUniGray( data, line_end, step, width3,
322                                                 y, m_height, x_shift3,
323                                                 gray_palette[0] );
324
325                         if( y >= m_height )
326                             break;
327                     }
328                 }
329
330                 result = true;
331 decode_rle4_bad: ;
332             }
333             break;
334
335         /************************* 8 BPP ************************/
336         case 8:
337             if( m_rle_code == BMP_RGB )
338             {
339                 for( y = 0; y < m_height; y++, data += step )
340                 {
341                     m_strm.getBytes( src, src_pitch );
342                     if( color )
343                         FillColorRow8( data, src, m_width, m_palette );
344                     else
345                         FillGrayRow8( data, src, m_width, gray_palette );
346                 }
347                 result = true;
348             }
349             else if( m_rle_code == BMP_RLE8 ) // rle8 compression
350             {
351                 uchar* line_end = data + width3;
352                 int line_end_flag = 0;
353                 y = 0;
354
355                 for(;;)
356                 {
357                     int code = m_strm.getWord();
358                     int len = code & 255;
359                     code >>= 8;
360                     if( len != 0 ) // encoded mode
361                     {
362                         int prev_y = y;
363                         len *= nch;
364
365                         if( data + len > line_end )
366                             goto decode_rle8_bad;
367
368                         if( color )
369                             data = FillUniColor( data, line_end, step, width3,
370                                                  y, m_height, len,
371                                                  m_palette[code] );
372                         else
373                             data = FillUniGray( data, line_end, step, width3,
374                                                 y, m_height, len,
375                                                 gray_palette[code] );
376
377                         line_end_flag = y - prev_y;
378
379                         if( y >= m_height )
380                             break;
381                     }
382                     else if( code > 2 ) // absolute mode
383                     {
384                         int prev_y = y;
385                         int code3 = code*nch;
386
387                         if( data + code3 > line_end )
388                             goto decode_rle8_bad;
389                         int sz = (code + 1) & (~1);
390                         CV_Assert((size_t)sz < _src.size());
391                         m_strm.getBytes(src, sz);
392                         if( color )
393                             data = FillColorRow8( data, src, code, m_palette );
394                         else
395                             data = FillGrayRow8( data, src, code, gray_palette );
396
397                         line_end_flag = y - prev_y;
398                     }
399                     else
400                     {
401                         int x_shift3 = (int)(line_end - data);
402                         int y_shift = m_height - y;
403
404                         if( code || !line_end_flag || x_shift3 < width3 )
405                         {
406                             if( code == 2 )
407                             {
408                                 x_shift3 = m_strm.getByte()*nch;
409                                 y_shift = m_strm.getByte();
410                             }
411
412                             x_shift3 += (y_shift * width3) & ((code == 0) - 1);
413
414                             if( y >= m_height )
415                                 break;
416
417                             if( color )
418                                 data = FillUniColor( data, line_end, step, width3,
419                                                      y, m_height, x_shift3,
420                                                      m_palette[0] );
421                             else
422                                 data = FillUniGray( data, line_end, step, width3,
423                                                     y, m_height, x_shift3,
424                                                     gray_palette[0] );
425
426                             if( y >= m_height )
427                                 break;
428                         }
429
430                         line_end_flag = 0;
431                         if( y >= m_height )
432                             break;
433                     }
434                 }
435
436                 result = true;
437 decode_rle8_bad: ;
438             }
439             break;
440         /************************* 15 BPP ************************/
441         case 15:
442             for( y = 0; y < m_height; y++, data += step )
443             {
444                 m_strm.getBytes( src, src_pitch );
445                 if( !color )
446                     icvCvt_BGR5552Gray_8u_C2C1R( src, 0, data, 0, cvSize(m_width,1) );
447                 else
448                     icvCvt_BGR5552BGR_8u_C2C3R( src, 0, data, 0, cvSize(m_width,1) );
449             }
450             result = true;
451             break;
452         /************************* 16 BPP ************************/
453         case 16:
454             for( y = 0; y < m_height; y++, data += step )
455             {
456                 m_strm.getBytes( src, src_pitch );
457                 if( !color )
458                     icvCvt_BGR5652Gray_8u_C2C1R( src, 0, data, 0, cvSize(m_width,1) );
459                 else
460                     icvCvt_BGR5652BGR_8u_C2C3R( src, 0, data, 0, cvSize(m_width,1) );
461             }
462             result = true;
463             break;
464         /************************* 24 BPP ************************/
465         case 24:
466             for( y = 0; y < m_height; y++, data += step )
467             {
468                 m_strm.getBytes( src, src_pitch );
469                 if(!color)
470                     icvCvt_BGR2Gray_8u_C3C1R( src, 0, data, 0, cvSize(m_width,1) );
471                 else
472                     memcpy( data, src, m_width*3 );
473             }
474             result = true;
475             break;
476         /************************* 32 BPP ************************/
477         case 32:
478             for( y = 0; y < m_height; y++, data += step )
479             {
480                 m_strm.getBytes( src, src_pitch );
481
482                 if( !color )
483                     icvCvt_BGRA2Gray_8u_C4C1R( src, 0, data, 0, cvSize(m_width,1) );
484                 else if( img.channels() == 3 )
485                     icvCvt_BGRA2BGR_8u_C4C3R(src, 0, data, 0, cvSize(m_width, 1));
486                 else if( img.channels() == 4 )
487                     memcpy(data, src, m_width * 4);
488             }
489             result = true;
490             break;
491         default:
492             CV_ErrorNoReturn(cv::Error::StsError, "Invalid/unsupported mode");
493         }
494     }
495     catch(...)
496     {
497         throw;
498     }
499
500     return result;
501 }
502
503
504 //////////////////////////////////////////////////////////////////////////////////////////
505
506 BmpEncoder::BmpEncoder()
507 {
508     m_description = "Windows bitmap (*.bmp;*.dib)";
509     m_buf_supported = true;
510 }
511
512
513 BmpEncoder::~BmpEncoder()
514 {
515 }
516
517 ImageEncoder BmpEncoder::newEncoder() const
518 {
519     return makePtr<BmpEncoder>();
520 }
521
522 bool  BmpEncoder::write( const Mat& img, const std::vector<int>& )
523 {
524     int width = img.cols, height = img.rows, channels = img.channels();
525     int fileStep = (width*channels + 3) & -4;
526     uchar zeropad[] = "\0\0\0\0";
527     WLByteStream strm;
528
529     if( m_buf )
530     {
531         if( !strm.open( *m_buf ) )
532             return false;
533     }
534     else if( !strm.open( m_filename ))
535         return false;
536
537     int  bitmapHeaderSize = 40;
538     int  paletteSize = channels > 1 ? 0 : 1024;
539     int  headerSize = 14 /* fileheader */ + bitmapHeaderSize + paletteSize;
540     size_t fileSize = (size_t)fileStep*height + headerSize;
541     PaletteEntry palette[256];
542
543     if( m_buf )
544         m_buf->reserve( alignSize(fileSize + 16, 256) );
545
546     // write signature 'BM'
547     strm.putBytes( fmtSignBmp, (int)strlen(fmtSignBmp) );
548
549     // write file header
550     strm.putDWord( validateToInt(fileSize) ); // file size
551     strm.putDWord( 0 );
552     strm.putDWord( headerSize );
553
554     // write bitmap header
555     strm.putDWord( bitmapHeaderSize );
556     strm.putDWord( width );
557     strm.putDWord( height );
558     strm.putWord( 1 );
559     strm.putWord( channels << 3 );
560     strm.putDWord( BMP_RGB );
561     strm.putDWord( 0 );
562     strm.putDWord( 0 );
563     strm.putDWord( 0 );
564     strm.putDWord( 0 );
565     strm.putDWord( 0 );
566
567     if( channels == 1 )
568     {
569         FillGrayPalette( palette, 8 );
570         strm.putBytes( palette, sizeof(palette));
571     }
572
573     width *= channels;
574     for( int y = height - 1; y >= 0; y-- )
575     {
576         strm.putBytes( img.ptr(y), width );
577         if( fileStep > width )
578             strm.putBytes( zeropad, fileStep - width );
579     }
580
581     strm.close();
582     return true;
583 }
584
585 }