1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 #include "itkBMPImageIO.h"
19 #include "itkByteSwapper.h"
20 #include "itksys/SystemTools.hxx"
21 #include <iostream>
22 
23 namespace itk
24 {
25 /** Constructor */
BMPImageIO()26 BMPImageIO::BMPImageIO() :
27   m_ColorPalette( 0 ) // palette has no element by default
28 {
29   this->SetNumberOfDimensions( 2 );
30 
31   m_ByteOrder = BigEndian;
32   m_ComponentType = UCHAR;
33   m_PixelType = SCALAR;
34 
35   m_Spacing[0] = 1.0;
36   m_Spacing[1] = 1.0;
37 
38   m_Origin[0] = 0.0;
39   m_Origin[1] = 0.0;
40 
41   const char *extensions[] =
42     {
43       ".bmp",".BMP",
44     };
45 
46   for(auto ext : extensions)
47     {
48     this->AddSupportedWriteExtension(ext);
49     this->AddSupportedReadExtension(ext);
50     }
51 }
52 
53 /** Destructor */
54 BMPImageIO::~BMPImageIO() = default;
55 
CanReadFile(const char * filename)56 bool BMPImageIO::CanReadFile(const char *filename)
57 {
58   // First check the filename
59   std::string fname = filename;
60 
61   if ( fname.empty() )
62     {
63     itkDebugMacro(<< "No filename specified.");
64     }
65 
66 
67   bool extensionFound = this->HasSupportedReadExtension(filename, false);
68 
69   if ( !extensionFound )
70     {
71     itkDebugMacro(<< "The filename extension is not recognized");
72     return false;
73     }
74 
75   // Now check the content
76   std::ifstream inputStream;
77   try
78     {
79     this->OpenFileForReading( inputStream, fname );
80     }
81   catch( ExceptionObject & )
82     {
83     return false;
84     }
85 
86   char magic_number1, magic_number2;
87   inputStream.read( (char *)&magic_number1, sizeof( char ) );
88   inputStream.read( (char *)&magic_number2, sizeof( char ) );
89 
90   if ( ( magic_number1 != 'B' ) || ( magic_number2 != 'M' ) )
91     {
92     std::cerr << "BMPImageIO : Magic Number Fails = " << magic_number1 << " : " << magic_number2 << std::endl;
93     inputStream.close();
94     return false;
95     }
96 
97   long tmp;
98   long infoSize;
99   int  iinfoSize;  // in case we are on a 64bit machine
100   int  itmp;       // in case we are on a 64bit machine
101 
102   // get the size of the file
103   ::size_t sizeLong = sizeof( long );
104   if ( sizeLong == 4 )
105     {
106     inputStream.read( (char *)&tmp, 4 );
107     // skip 4 bytes
108     inputStream.read( (char *)&tmp, 4 );
109     // read the offset
110     inputStream.read( (char *)&tmp, 4 );
111     }
112   else
113     {
114     inputStream.read( (char *)&itmp, 4 );
115     // skip 4 bytes
116     inputStream.read( (char *)&itmp, 4 );
117     // read the offset
118     inputStream.read( (char *)&itmp, 4 );
119     }
120 
121   // get size of header
122   if ( sizeLong == 4 )   // if we are on a 32 bit machine
123     {
124     inputStream.read( (char *)&infoSize, sizeof( long ) );
125     ByteSwapper< long >::SwapFromSystemToLittleEndian(&infoSize);
126     // error checking
127     if ( ( infoSize != 40 ) && ( infoSize != 12 ) )
128       {
129       inputStream.close();
130       return false;
131       }
132     }
133   else    // else we are on a 64bit machine
134     {
135     inputStream.read( (char *)&iinfoSize, 4 );
136     ByteSwapper< int >::SwapFromSystemToLittleEndian(&iinfoSize);
137     infoSize = iinfoSize;
138 
139     // error checking
140     if ( ( infoSize != 40 ) && ( infoSize != 12 ) )
141       {
142       inputStream.close();
143       return false;
144       }
145     }
146 
147   inputStream.close();
148   return true;
149 }
150 
CanWriteFile(const char * name)151 bool BMPImageIO::CanWriteFile(const char *name)
152 {
153   std::string filename = name;
154 
155   if ( filename.empty() )
156     {
157     itkDebugMacro(<< "No filename specified.");
158     }
159 
160   bool extensionFound = this->HasSupportedWriteExtension(name, false);
161 
162   if ( !extensionFound )
163     {
164     itkDebugMacro(<< "The filename extension is not recognized");
165     return false;
166     }
167 
168   return true;
169 }
170 
Read(void * buffer)171 void BMPImageIO::Read(void *buffer)
172 {
173   auto * p = static_cast< char * >( buffer );
174   unsigned long l = 0;
175   char *        value;
176 
177   this->OpenFileForReading( m_Ifstream, m_FileName );
178 
179   // If the file is RLE compressed
180   // RLE-compressed files are lower-left
181   // About the RLE compression algorithm:
182   // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183383%28v=vs.85%29.aspx
183   if ( m_BMPCompression == 1 && (this->GetNumberOfComponents() == 3 ||
184                                  this->GetIsReadAsScalarPlusPalette() )  )
185     {
186     value = new char[m_BMPDataSize + 1];
187     m_Ifstream.seekg(m_BitMapOffset, std::ios::beg);
188     m_Ifstream.read( (char *)value, m_BMPDataSize );
189 
190     SizeValueType posLine = 0;
191     SizeValueType line = m_Dimensions[1] - 1;
192     for ( unsigned int i = 0; i < m_BMPDataSize; i++ )
193       {
194       unsigned char byte1 = value[i];
195       i++;
196       unsigned char byte2 = value[i];
197       if(byte1 == 0)
198         {
199         if(byte2 == 0)
200           {
201           // End of line
202           line--;
203           posLine = 0;
204           continue;
205           }
206         else if(byte2 == 1)
207           {
208           // End of bitmap data
209           break;
210           }
211         else if(byte2 == 2)
212           {
213           // Delta
214           i++;
215           unsigned char dx = value[i];
216           i++;
217           unsigned char dy = value[i];
218           posLine += dx;
219           line -= dy;
220           continue;
221           }
222         else
223           {
224           // Unencoded run
225           if ( !this->GetIsReadAsScalarPlusPalette() )
226             {
227               for ( unsigned long j = 0; j < byte2; j++ )
228                 {
229                 i++;
230                 RGBPixelType rgb = this->GetColorPaletteEntry(value[i]);
231                 l = 3 * ( line * m_Dimensions[0] + posLine );
232                 p[l]     = rgb.GetBlue();
233                 p[l + 1] = rgb.GetGreen();
234                 p[l + 2] = rgb.GetRed();
235                 posLine++;
236                 }
237             }
238             else
239             {
240                for ( unsigned long j = 0; j < byte2; j++ )
241                  {
242                  i++;
243                  l = ( line * m_Dimensions[0] + posLine );
244                  p[l] = value[i];
245                  posLine++;
246                  }
247             }
248           // If a run's length is odd, the it is padded with 0
249           if(byte2 % 2)
250             {
251             i++;
252             }
253           }
254         }
255       else
256         {
257         // Encoded run
258         if ( !this->GetIsReadAsScalarPlusPalette() )
259           {
260           RGBPixelType rgb = this->GetColorPaletteEntry(byte2);
261           for ( unsigned long j = 0; j < byte1; j++ )
262             {
263             l = 3 * ( line * m_Dimensions[0] + posLine );
264             p[l]     = rgb.GetBlue();
265             p[l + 1] = rgb.GetGreen();
266             p[l + 2] = rgb.GetRed();
267             posLine++;
268             }
269           }
270         else
271           {
272           for ( unsigned long j = 0; j < byte1; j++ )
273             {
274             l = ( line * m_Dimensions[0] + posLine );
275             p[l] = byte2;
276             posLine++;
277             }
278           }
279         }
280       }
281     }
282   else
283     {
284     // File is not compressed
285     // Read one row at a time
286     long streamRead = m_Dimensions[0] * m_Depth / 8;
287     long paddedStreamRead = streamRead;
288     unsigned long step = this->GetNumberOfComponents();
289     if ( streamRead % 4 )
290       {
291       paddedStreamRead = ( ( streamRead / 4 ) + 1 ) * 4;
292       }
293     value = new char[paddedStreamRead + 1];
294 
295     for ( unsigned int id = 0; id < m_Dimensions[1]; id++ )
296       {
297       const unsigned int line_id = m_FileLowerLeft ? (m_Dimensions[1] - id - 1) : id;
298       m_Ifstream.seekg(m_BitMapOffset + paddedStreamRead * line_id, std::ios::beg);
299       m_Ifstream.read( (char *)value, paddedStreamRead );
300       for ( long i = 0; i < streamRead; i++ )
301         {
302         if ( this->GetNumberOfComponents() == 1 )
303           {
304           p[l++] = value[i];
305           }
306         else
307           {
308           if ( m_ColorPaletteSize == 0 )
309             {
310             if ( this->GetNumberOfComponents() == 3 )
311               {
312               p[l++] = value[i + 2];
313               p[l++] = value[i + 1];
314               p[l++] = value[i];
315               }
316             if ( this->GetNumberOfComponents() == 4 )
317               {
318               p[l++] = value[i + 3];
319               p[l++] = value[i + 2];
320               p[l++] = value[i + 1];
321               p[l++] = value[i];
322               }
323             i += step - 1;
324             }
325           else
326             {
327             RGBPixelType rgb = this->GetColorPaletteEntry(value[i]);
328             p[l++] = rgb.GetBlue();
329             p[l++] = rgb.GetGreen();
330             p[l++] = rgb.GetRed();
331             }
332           }
333         }
334       }
335     }
336   delete[] value;
337   m_Ifstream.close();
338 }
339 
340 /**
341  *  Read Information about the BMP file
342  *  and put the cursor of the stream just before the first data pixel
343  */
ReadImageInformation()344 void BMPImageIO::ReadImageInformation()
345 {
346   int   xsize, ysize;
347   long  tmp;
348   short stmp;
349   long  infoSize;
350   int   iinfoSize; // in case we are on a 64bit machine
351   int   itmp;      // in case we are on a 64bit machine
352 
353   // Now check the content
354   this->OpenFileForReading( m_Ifstream, m_FileName );
355 
356   char magic_number1, magic_number2;
357   m_Ifstream.read( (char *)&magic_number1, sizeof( char ) );
358   m_Ifstream.read( (char *)&magic_number2, sizeof( char ) );
359 
360   if ( ( magic_number1 != 'B' ) || ( magic_number2 != 'M' ) )
361     {
362     m_Ifstream.close();
363     itkExceptionMacro("BMPImageIO : Magic Number Fails = " << magic_number1 << " : " << magic_number2);
364     }
365 
366   // get the size of the file
367   ::size_t sizeLong = sizeof( long );
368   if ( sizeLong == 4 )
369     {
370     m_Ifstream.read( (char *)&tmp, 4 );
371     // skip 4 bytes
372     m_Ifstream.read( (char *)&tmp, 4 );
373     // read the offset
374     m_Ifstream.read( (char *)&tmp, 4 );
375     m_BitMapOffset = tmp;
376     ByteSwapper< long >::SwapFromSystemToLittleEndian(&m_BitMapOffset);
377     }
378   else
379     {
380     m_Ifstream.read( (char *)&itmp, 4 );
381     // skip 4 bytes
382     m_Ifstream.read( (char *)&itmp, 4 );
383     // read the offset
384     m_Ifstream.read( (char *)&itmp, 4 );
385     ByteSwapper< int >::SwapFromSystemToLittleEndian(&itmp);
386     m_BitMapOffset = static_cast< long >( itmp );
387     }
388 
389   // get size of header
390   if ( sizeLong == 4 )   // if we are on a 32 bit machine
391     {
392     m_Ifstream.read( (char *)&infoSize, 4 );
393     ByteSwapper< long >::SwapFromSystemToLittleEndian(&infoSize);
394     // error checking
395     if ( ( infoSize != 40 ) && ( infoSize != 12 ) )
396       {
397       itkExceptionMacro(<< "Unknown file type! " << m_FileName.c_str()
398                         << " is not a Windows BMP file!");
399       }
400 
401     // there are two different types of BMP files
402     if ( infoSize == 40 )
403       {
404       // now get the dimensions
405       m_Ifstream.read( (char *)&xsize, 4 );
406       ByteSwapper< int >::SwapFromSystemToLittleEndian(&xsize);
407       m_Ifstream.read( (char *)&ysize, 4 );
408       ByteSwapper< int >::SwapFromSystemToLittleEndian(&ysize);
409       }
410     else
411       {
412       m_Ifstream.read( (char *)&stmp, sizeof( short ) );
413       ByteSwapper< short >::SwapFromSystemToLittleEndian(&stmp);
414       xsize = stmp;
415       m_Ifstream.read( (char *)&stmp, sizeof( short ) );
416       ByteSwapper< short >::SwapFromSystemToLittleEndian(&stmp);
417       ysize = stmp;
418       }
419     }
420   else // else we are on a 64bit machine
421     {
422     m_Ifstream.read( (char *)&iinfoSize, sizeof( int ) );
423     ByteSwapper< int >::SwapFromSystemToLittleEndian(&iinfoSize);
424 
425     infoSize = iinfoSize;
426 
427     // error checking
428     if ( ( infoSize != 40 ) && ( infoSize != 12 ) )
429       {
430       itkExceptionMacro(<< "Unknown file type! " << m_FileName.c_str()
431                         << " is not a Windows BMP file!");
432       }
433 
434     // there are two different types of BMP files
435     if ( infoSize == 40 )
436       {
437       // now get the dimensions
438       m_Ifstream.read( (char *)&xsize, 4 );
439       ByteSwapper< int >::SwapFromSystemToLittleEndian(&xsize);
440       m_Ifstream.read( (char *)&ysize, 4 );
441       ByteSwapper< int >::SwapFromSystemToLittleEndian(&ysize);
442       }
443     else
444       {
445       stmp = 0;
446       m_Ifstream.read( (char *)&stmp, 2 );
447       ByteSwapper< short >::SwapFromSystemToLittleEndian(&stmp);
448       xsize = stmp;
449       m_Ifstream.read( (char *)&stmp, 2 );
450       ByteSwapper< short >::SwapFromSystemToLittleEndian(&stmp);
451       ysize = stmp;
452       }
453     }
454 
455   // is corner in upper left or lower left
456   if ( ysize < 0 )
457     {
458     ysize = -ysize;
459     m_FileLowerLeft = false;
460     }
461   else
462     {
463     m_FileLowerLeft = true;
464     }
465 
466   this->SetNumberOfDimensions(2);
467   m_Dimensions[0] = xsize;
468   m_Dimensions[1] = ysize;
469 
470   // ignore planes
471   m_Ifstream.read( (char *)&stmp, 2 );
472   // read depth
473   m_Ifstream.read( (char *)&m_Depth, 2 );
474   ByteSwapper< short >::SwapFromSystemToLittleEndian(&m_Depth);
475 
476   if ( ( m_Depth != 8 ) && ( m_Depth != 24 ) && ( m_Depth != 32 ) )
477     {
478     m_Ifstream.close();
479     itkExceptionMacro("Only BMP depths of (8,24,32) are supported. Not " << m_Depth);
480     }
481 
482   if ( infoSize == 40 )
483     {
484     if ( sizeLong == 4 )
485       {
486       // Compression
487       m_Ifstream.read( (char *)&m_BMPCompression, 4 );
488       ByteSwapper< long >::SwapFromSystemToLittleEndian(&m_BMPCompression);
489       // Image Data Size
490       m_Ifstream.read( (char *)&m_BMPDataSize, 4 );
491       ByteSwapper< unsigned long >::SwapFromSystemToLittleEndian(&m_BMPDataSize);
492       // Horizontal Resolution
493       m_Ifstream.read( (char *)&tmp, 4 );
494       // Vertical Resolution
495       m_Ifstream.read( (char *)&tmp, 4 );
496       // Number of colors
497       m_Ifstream.read( (char *)&tmp, 4 );
498       m_NumberOfColors = static_cast< unsigned short >( tmp );
499       // Number of important colors
500       m_Ifstream.read( (char *)&tmp, 4 );
501       }
502     else
503       {
504       // Compression
505       m_Ifstream.read( (char *)&itmp, 4 );
506       ByteSwapper< int >::SwapFromSystemToLittleEndian(&itmp);
507       m_BMPCompression = static_cast< long >( itmp );
508       // Image Data Size
509       m_Ifstream.read( (char *)&itmp, 4 );
510       ByteSwapper< int >::SwapFromSystemToLittleEndian(&itmp);
511       m_BMPDataSize = static_cast< unsigned long >( itmp );
512       // Horizontal Resolution
513       m_Ifstream.read( (char *)&itmp, 4 );
514       ByteSwapper< int >::SwapFromSystemToLittleEndian(&itmp);
515       // Vertical Resolution
516       m_Ifstream.read( (char *)&itmp, 4 );
517       ByteSwapper< int >::SwapFromSystemToLittleEndian(&itmp);
518       // Number of colors
519       m_Ifstream.read( (char *)&itmp, 4 );
520       ByteSwapper< int >::SwapFromSystemToLittleEndian(&itmp);
521       m_NumberOfColors = static_cast< unsigned short >( itmp );
522       // Number of important colors
523       m_Ifstream.read( (char *)&itmp, 4 );
524       }
525     }
526 
527   // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376%28v=vs.85%29.aspx
528   if(m_BMPCompression == 1 && !m_FileLowerLeft)
529     {
530     m_Ifstream.close();
531     itkExceptionMacro("Compressed BMP are not supposed to be upper-left.");
532     }
533 
534   // Read the color palette. Only used for 1,4 and 8 bit images.
535   if ( m_Depth <= 8 )
536     {
537     if ( m_NumberOfColors )
538       {
539       m_ColorPaletteSize = ( ( 1 << m_Depth ) < m_NumberOfColors ) ? ( 1 << m_Depth ) : m_NumberOfColors;
540       }
541     else
542       {
543       m_ColorPaletteSize = ( 1 << m_Depth );
544       }
545     }
546   else
547     {
548     m_ColorPaletteSize = 0;
549     }
550   unsigned char uctmp;
551   m_ColorPalette.resize(m_ColorPaletteSize);
552   for ( unsigned long i = 0; i < m_ColorPaletteSize; i++ )
553     {
554     RGBPixelType p;
555     m_Ifstream.read( (char *)&uctmp, 1 );
556     p.SetRed(uctmp);
557     m_Ifstream.read( (char *)&uctmp, 1 );
558     p.SetGreen(uctmp);
559     m_Ifstream.read( (char *)&uctmp, 1 );
560     p.SetBlue(uctmp);
561     m_Ifstream.read( (char *)&tmp, 1 );
562     m_ColorPalette[i] = p;
563     }
564 
565   m_IsReadAsScalarPlusPalette = false;
566   switch ( m_Depth )
567     {
568     case 1:
569     case 4:
570     case 8:
571       {
572       if ( this->GetExpandRGBPalette() )
573         {
574         this->SetNumberOfComponents(3);
575         m_PixelType = RGB;
576         }
577       else
578         {
579         this->SetNumberOfComponents(1);
580         m_PixelType = SCALAR;
581         m_IsReadAsScalarPlusPalette = true;
582         }
583       break;
584       }
585     case 24:
586       {
587       this->SetNumberOfComponents(3);
588       m_PixelType = RGB;
589       break;
590       }
591     case 32:
592       {
593       this->SetNumberOfComponents(4);
594       m_PixelType = RGBA;
595       break;
596       }
597     }
598 
599   m_Ifstream.close();
600 }
601 
602 void
603 BMPImageIO
SwapBytesIfNecessary(void * buffer,SizeValueType numberOfPixels)604 ::SwapBytesIfNecessary(void *buffer, SizeValueType numberOfPixels)
605 {
606   switch ( m_ComponentType )
607     {
608     case CHAR:
609       {
610       if ( m_ByteOrder == LittleEndian )
611         {
612         ByteSwapper< char >::SwapRangeFromSystemToLittleEndian(
613           (char *)buffer, numberOfPixels);
614         }
615       else if ( m_ByteOrder == BigEndian )
616         {
617         ByteSwapper< char >::SwapRangeFromSystemToBigEndian(
618           (char *)buffer, numberOfPixels);
619         }
620       break;
621       }
622     case UCHAR:
623       {
624       if ( m_ByteOrder == LittleEndian )
625         {
626         ByteSwapper< unsigned char >::SwapRangeFromSystemToLittleEndian(
627           (unsigned char *)buffer, numberOfPixels);
628         }
629       else if ( m_ByteOrder == BigEndian )
630         {
631         ByteSwapper< unsigned char >::SwapRangeFromSystemToBigEndian(
632           (unsigned char *)buffer, numberOfPixels);
633         }
634       break;
635       }
636     case SHORT:
637       {
638       if ( m_ByteOrder == LittleEndian )
639         {
640         ByteSwapper< short >::SwapRangeFromSystemToLittleEndian(
641           (short *)buffer, numberOfPixels);
642         }
643       else if ( m_ByteOrder == BigEndian )
644         {
645         ByteSwapper< short >::SwapRangeFromSystemToBigEndian(
646           (short *)buffer, numberOfPixels);
647         }
648       break;
649       }
650     case USHORT:
651       {
652       if ( m_ByteOrder == LittleEndian )
653         {
654         ByteSwapper< unsigned short >::SwapRangeFromSystemToLittleEndian(
655           (unsigned short *)buffer, numberOfPixels);
656         }
657       else if ( m_ByteOrder == BigEndian )
658         {
659         ByteSwapper< unsigned short >::SwapRangeFromSystemToBigEndian(
660           (unsigned short *)buffer, numberOfPixels);
661         }
662       break;
663       }
664     default:
665       itkExceptionMacro(<< "Pixel Type Unknown");
666     }
667 }
668 
669 void
670 BMPImageIO
Write32BitsInteger(unsigned int value)671 ::Write32BitsInteger(unsigned int value)
672 {
673   auto tmp = static_cast<char>(value % 256);
674   m_Ofstream.write( &tmp, sizeof( char ) );
675   tmp = static_cast< char >( ( value % 65536L ) / 256 );
676   m_Ofstream.write( &tmp, sizeof( char ) );
677   tmp = static_cast< char >( ( value / 65536L ) % 256 );
678   m_Ofstream.write( &tmp, sizeof( char ) );
679   tmp = static_cast< char >( ( value / 65536L ) / 256 );
680   m_Ofstream.write( &tmp, sizeof( char ) );
681 }
682 
683 void
684 BMPImageIO
Write16BitsInteger(unsigned short value)685 ::Write16BitsInteger(unsigned short value)
686 {
687   auto tmp = static_cast<char>(value % 256);
688   m_Ofstream.write( &tmp, sizeof( char ) );
689   tmp = static_cast< char >( ( value % 65536L ) / 256 );
690   m_Ofstream.write( &tmp, sizeof( char ) );
691 }
692 
693 BMPImageIO::RGBPixelType
694 BMPImageIO
GetColorPaletteEntry(const unsigned char entry) const695 ::GetColorPaletteEntry(const unsigned char entry) const
696 {
697   if ( entry < m_ColorPalette.size() )
698     {
699     return m_ColorPalette[entry];
700     }
701   else
702     {
703     RGBPixelType p;
704     p.SetRed(0);
705     p.SetGreen(0);
706     p.SetBlue(0);
707     return p;
708     }
709 }
710 
711 void
712 BMPImageIO
WriteImageInformation()713 ::WriteImageInformation()
714 {}
715 
716 /** The write function is not implemented */
717 void
718 BMPImageIO
Write(const void * buffer)719 ::Write(const void *buffer)
720 {
721   unsigned int nDims = this->GetNumberOfDimensions();
722 
723   if ( nDims != 2 )
724     {
725     itkExceptionMacro(<< "BMPImageIO cannot write images with a dimension != 2");
726     }
727 
728   if ( this->GetComponentType() != UCHAR )
729     {
730     itkExceptionMacro(<< "BMPImageIO supports unsigned char only");
731     }
732   if ( ( this->m_NumberOfComponents != 1 )
733        && ( this->m_NumberOfComponents != 3 )
734        && ( this->m_NumberOfComponents != 4 ) )
735     {
736     itkExceptionMacro(<< "BMPImageIO supports 1,3 or 4 components only");
737     }
738 
739   this->OpenFileForWriting( m_Ofstream, m_FileName );
740 
741   //
742   //
743   // A BMP file has four sections:
744   //
745   // * BMP Header                         14 bytes
746   // * Bitmap Information (DIB header)    40 bytes (Windows V3)
747   // * Color Palette
748   // * Bitmap Data
749   //
750   // For more details:
751   //
752   //             http://en.wikipedia.org/wiki/BMP_file_format
753   //
754   //
755 
756   // Write the BMP header
757   //
758   // Header structure is represented by first a 14 byte field, then the bitmap
759   // info header.
760   //
761   // The 14 byte field:
762   //
763   // Offset Length Description
764   //
765   //   0      2    Contain the string, "BM", (Hex: 42 4D)
766   //   2      4    The length of the entire file.
767   //   6      2    Reserved for application data. Usually zero.
768   //   8      2    Reserved for application data. Usually zero.
769   //  10      4    Provides an offset from the start of the file
770   //               to the first byte of image sample data. This
771   //               is normally 54 bytes (Hex: 36)
772   //
773   char tmp = 66;
774   m_Ofstream.write( &tmp, sizeof( char ) );
775   tmp = 77;
776   m_Ofstream.write( &tmp, sizeof( char ) );
777 
778   const unsigned int bpp = this->GetNumberOfComponents();
779   long               bytesPerRow = m_Dimensions[0] * bpp;
780   if ( bytesPerRow % 4 )
781     {
782     bytesPerRow = ( ( bytesPerRow / 4 ) + 1 ) * 4;
783     }
784   const unsigned long paddedBytes = bytesPerRow - ( m_Dimensions[0] * bpp );
785 
786   const auto rawImageDataSize = static_cast< unsigned int >( ( bytesPerRow * m_Dimensions[1] ) );
787   unsigned int       fileSize = ( rawImageDataSize ) + 54;
788   if ( bpp == 1 )
789     {
790     fileSize += 1024; // need colour LUT
791     }
792   this->Write32BitsInteger(fileSize);
793 
794   constexpr unsigned short applicationReservedValue = 0;
795   this->Write16BitsInteger(applicationReservedValue);
796   this->Write16BitsInteger(applicationReservedValue);
797 
798   unsigned int offsetToBinaryDataStart = 54;
799   if ( bpp == 1 ) // more space is needed for the LUT
800     {
801     offsetToBinaryDataStart += 1024;
802     }
803   this->Write32BitsInteger(offsetToBinaryDataStart);
804   //
805   // End of BMP header, 14 bytes written so far
806   //
807 
808   //
809   // Write the DIB header
810   //
811   // Offset Length Description
812   //
813   //  14      4    Size of the header (40 bytes)(Hex: 28)
814   //
815   //
816   //  Color Palette
817   //
818   //  If the bit_count is 1, 4 or 8, the structure must be followed by a colour
819   //  lookup table, with 4 bytes per entry, the first 3 of which identify the
820   //  blue, green and red intensities, respectively.
821   //
822   //  Finally the pixel data
823   //
824   constexpr unsigned int bitmapHeaderSize = 40;
825   this->Write32BitsInteger(bitmapHeaderSize);
826 
827   // image width
828   this->Write32BitsInteger(static_cast<unsigned int>(m_Dimensions[0]));
829 
830   // image height -ve means top to bottom
831   this->Write32BitsInteger(static_cast<unsigned int>(m_Dimensions[1]));
832 
833   // Set `planes'=1 (mandatory)
834   constexpr unsigned short numberOfColorPlanes = 1;
835   this->Write16BitsInteger(numberOfColorPlanes);
836 
837   // Set bits per pixel.
838   unsigned short numberOfBitsPerPixel = 0;
839   switch ( bpp )
840     {
841     case 4:
842       numberOfBitsPerPixel = 32;
843       break;
844     case 3:
845       numberOfBitsPerPixel = 24;
846       break;
847     case 1:
848       numberOfBitsPerPixel = 8;
849       break;
850     default:
851       itkExceptionMacro(<< "Number of components not supported.");
852     }
853   this->Write16BitsInteger(numberOfBitsPerPixel);
854 
855   constexpr unsigned int compressionMethod = 0;
856   this->Write32BitsInteger(compressionMethod);
857   this->Write32BitsInteger(rawImageDataSize);
858 
859   // Assuming spacing is in millimeters,
860   // the resolution is set here in pixel per meter.
861   // The specification calls for a signed integer, but
862   // here we force it to be an unsigned integer to avoid
863   // dealing with directions in a subterraneous way.
864   const auto horizontalResolution = Math::Round< unsigned int >(1000.0 / m_Spacing[0]);
865   const auto verticalResolution = Math::Round< unsigned int >(1000.0 / m_Spacing[1]);
866 
867   this->Write32BitsInteger(horizontalResolution);
868   this->Write32BitsInteger(verticalResolution);
869 
870   // zero here defaults to 2^n colors in the palette
871   constexpr unsigned int numberOfColorsInPalette = 0;
872   this->Write32BitsInteger(numberOfColorsInPalette);
873 
874   // zero here indicates that all colors in the palette are important.
875   constexpr unsigned int numberOfImportantColorsInPalette = 0;
876   this->Write32BitsInteger(numberOfImportantColorsInPalette);
877   //
878   // End of DIB header, 54 bytes written so far
879   //
880 
881   //
882   // Write down colour LUT
883   //
884   // only when using 1 byte per pixel
885   //
886   if ( bpp == 1 )
887     {
888     for ( unsigned int n = 0; n < 256; n++ )
889       {
890       char tmp2 = static_cast< unsigned char >( n );
891       m_Ofstream.write( &tmp2, sizeof( char ) );
892       m_Ofstream.write( &tmp2, sizeof( char ) );
893       m_Ofstream.write( &tmp2, sizeof( char ) );
894       m_Ofstream.write( &tmp, sizeof( char ) );
895       }
896     }
897 
898   //
899   // Write down the raw binary pixel data
900   //
901   unsigned int i;
902   for ( unsigned int h = 0; h < m_Dimensions[1]; h++ )
903     {
904     constexpr char paddingValue = 0;
905     const auto * ptr = static_cast< const char * >( buffer );
906     ptr += ( m_Dimensions[1] - ( h + 1 ) ) * m_Dimensions[0] * bpp;
907     if ( bpp == 1 )
908       {
909       for ( i = 0; i < m_Dimensions[0]; i++ )
910         {
911         m_Ofstream.write( ptr, sizeof( char ) );
912         ptr++;
913         }
914       for ( i = 0; i < paddedBytes; i++ )
915         {
916         m_Ofstream.write( &paddingValue, sizeof( char ) );
917         }
918       }
919     if ( bpp == 3 )
920       {
921       for ( i = 0; i < m_Dimensions[0]; i++ )
922         {
923         ptr += 2;
924         m_Ofstream.write( ptr, sizeof( char ) );
925         ptr--;
926         m_Ofstream.write( ptr, sizeof( char ) );
927         ptr--;
928         m_Ofstream.write( ptr, sizeof( char ) );
929         ptr += 3;
930         }
931       for ( i = 0; i < paddedBytes; i++ )
932         {
933         m_Ofstream.write( &paddingValue, sizeof( char ) );
934         }
935       }
936     if ( bpp == 4 )
937       {
938       for ( i = 0; i < m_Dimensions[0]; i++ )
939         {
940         ptr += 3;
941         m_Ofstream.write( ptr, sizeof( char ) );
942         ptr--;
943         m_Ofstream.write( ptr, sizeof( char ) );
944         ptr--;
945         m_Ofstream.write( ptr, sizeof( char ) );
946         ptr--;
947         m_Ofstream.write( ptr, sizeof( char ) );
948         ptr += 4;
949         }
950       for ( i = 0; i < paddedBytes; i++ )
951         {
952         m_Ofstream.write( &paddingValue, sizeof( char ) );
953         }
954       }
955     }
956 }
957 
958 /** Print Self Method */
PrintSelf(std::ostream & os,Indent indent) const959 void BMPImageIO::PrintSelf(std::ostream & os, Indent indent) const
960 {
961   Superclass::PrintSelf(os, indent);
962 
963   os << indent << "BitMapOffset: " << m_BitMapOffset << std::endl;
964   os << indent << "FileLowerLeft: " << m_FileLowerLeft << std::endl;
965   os << indent << "Depth: " << m_Depth << std::endl;
966   os << indent << "NumberOfColors: " << m_NumberOfColors << std::endl;
967   os << indent << "ColorPaletteSize: " << m_ColorPaletteSize << std::endl;
968   os << indent << "BMPCompression: " << m_BMPCompression << std::endl;
969   os << indent << "DataSize: " << m_BMPDataSize << std::endl;
970   if ( m_IsReadAsScalarPlusPalette )
971     {
972     os << "Read as Scalar Image plus palette" << "\n";
973     }
974   if( !m_ColorPalette.empty()  )
975     {
976     os << indent << "ColorPalette:" << std::endl;
977     for( unsigned int i = 0; i < m_ColorPalette.size(); ++i )
978       {
979       os << indent << "[" << i << "]"
980          << itk::NumericTraits< PaletteType::value_type >::PrintType( m_ColorPalette[i] ) << std::endl;
981       }
982     }
983 }
984 } // end namespace itk
985