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