1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkBMPReader.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkBMPReader.h"
16 
17 #include "vtkByteSwap.h"
18 #include "vtkImageData.h"
19 #include "vtkLookupTable.h"
20 #include "vtkObjectFactory.h"
21 #include "vtkPointData.h"
22 #include <vtksys/SystemTools.hxx>
23 
24 vtkStandardNewMacro(vtkBMPReader);
25 
vtkBMPReader()26 vtkBMPReader::vtkBMPReader()
27 {
28   this->Colors = nullptr;
29   this->SetDataByteOrderToLittleEndian();
30   this->Depth = 0;
31   // we need to create it now in case its asked for later (pointer must be valid)
32   this->LookupTable = vtkLookupTable::New();
33   this->Allow8BitBMP = 0;
34 }
35 
36 //------------------------------------------------------------------------------
~vtkBMPReader()37 vtkBMPReader::~vtkBMPReader()
38 {
39   // free any old memory
40   delete[] this->Colors;
41   this->Colors = nullptr;
42 
43   if (this->LookupTable)
44   {
45     this->LookupTable->Delete();
46     this->LookupTable = nullptr;
47   }
48 }
49 
50 //------------------------------------------------------------------------------
ExecuteInformation()51 void vtkBMPReader::ExecuteInformation()
52 {
53   int xsize, ysize;
54   FILE* fp;
55   vtkTypeInt32 tmp;
56   vtkTypeInt32 offset;
57   vtkTypeInt32 infoSize;
58   vtkTypeInt16 stmp1, stmp2;
59 
60   // free any old memory
61   delete[] this->Colors;
62   this->Colors = nullptr;
63 
64   // if the user has not set the extent, but has set the VOI
65   // set the zaxis extent to the VOI z axis
66   if (this->DataExtent[4] == 0 && this->DataExtent[5] == 0 &&
67     (this->DataVOI[4] || this->DataVOI[5]))
68   {
69     this->DataExtent[4] = this->DataVOI[4];
70     this->DataExtent[5] = this->DataVOI[5];
71   }
72 
73   this->ComputeInternalFileName(this->DataExtent[4]);
74   if (this->InternalFileName == nullptr || this->InternalFileName[0] == '\0')
75   {
76     return;
77   }
78   // get the magic number by reading in a file
79   fp = vtksys::SystemTools::Fopen(this->InternalFileName, "rb");
80   if (!fp)
81   {
82     vtkErrorMacro("Unable to open file " << this->InternalFileName);
83     return;
84   }
85 
86   // compare magic number to determine file type
87   if ((fgetc(fp) != 'B') || (fgetc(fp) != 'M'))
88   {
89     vtkErrorMacro(<< "Unknown file type! " << this->InternalFileName
90                   << " is not a Windows BMP file!");
91     fclose(fp);
92     return;
93   }
94 
95   // error indicator
96   bool errorOccurred = false;
97 
98   // get the size of the file
99 
100   // skip 4 bytes
101   if (fread(&tmp, 4, 1, fp) != 1)
102   {
103     errorOccurred = true;
104   }
105   // skip 4 more bytes
106   else if (fread(&tmp, 4, 1, fp) != 1)
107   {
108     errorOccurred = true;
109   }
110   // read the offset
111   else if (fread(&offset, 4, 1, fp) != 1)
112   {
113     errorOccurred = true;
114   }
115   // get size of header
116   else if (fread(&infoSize, 4, 1, fp) != 1)
117   {
118     errorOccurred = true;
119   }
120 
121   if (errorOccurred)
122   {
123     vtkErrorMacro("Error reading file: " << this->InternalFileName << " Premature end of file.");
124     fclose(fp);
125     return;
126   }
127 
128   vtkByteSwap::Swap4LE(&infoSize);
129 
130   // error checking
131   if ((infoSize != 40) && (infoSize != 12))
132   {
133     vtkErrorMacro("Unknown file type! " << this->InternalFileName << " is not a Windows BMP file!");
134     fclose(fp);
135     return;
136   }
137 
138   // there are two different types of BMP files
139   if (infoSize == 40)
140   {
141     // now get the dimensions
142     if (fread(&xsize, 4, 1, fp) != 1)
143     {
144       errorOccurred = true;
145     }
146     else if (fread(&ysize, 4, 1, fp) != 1)
147     {
148       errorOccurred = true;
149     }
150     vtkByteSwap::Swap4LE(&xsize);
151     vtkByteSwap::Swap4LE(&ysize);
152   }
153   else
154   {
155     if (fread(&stmp1, 2, 1, fp) != 1)
156     {
157       errorOccurred = true;
158     }
159     else if (fread(&stmp2, 2, 1, fp) != 1)
160     {
161       errorOccurred = true;
162     }
163     vtkByteSwap::Swap2LE(&stmp1);
164     vtkByteSwap::Swap2LE(&stmp2);
165     xsize = stmp1;
166     ysize = stmp2;
167   }
168 
169   if (errorOccurred)
170   {
171     vtkErrorMacro("BMPReader error reading file: " << this->InternalFileName
172                                                    << " Premature EOF while reading size.");
173     fclose(fp);
174     return;
175   }
176 
177   // is corner in upper left or lower left
178   if (ysize < 0)
179   {
180     ysize = ysize * -1;
181     this->FileLowerLeft = 0;
182   }
183   else
184   {
185     this->FileLowerLeft = 1;
186   }
187 
188   // ignore planes
189   if (fread(&stmp1, 2, 1, fp) != 1)
190   {
191     errorOccurred = true;
192   }
193   // read depth
194   else if (fread(&stmp2, 2, 1, fp) != 1)
195   {
196     errorOccurred = true;
197   }
198 
199   if (errorOccurred)
200   {
201     vtkErrorMacro("BMPReader error reading file: " << this->InternalFileName
202                                                    << " Premature EOF while reading depth.");
203     fclose(fp);
204     return;
205   }
206 
207   vtkByteSwap::Swap2LE(&stmp2);
208   this->Depth = stmp2;
209 
210   if ((this->Depth != 8) && (this->Depth != 24))
211   {
212     vtkErrorMacro("Only BMP depths of (8,24) are supported. Not " << this->Depth);
213     fclose(fp);
214     return;
215   }
216 
217   // skip over rest of info for long format
218   if (infoSize == 40)
219   {
220     int skip[6];
221     if (fread(&skip, 4, 6, fp) != 6)
222     {
223       vtkErrorMacro("BMPReader error reading file: "
224         << this->InternalFileName << " Premature EOF skipping rest of info for long format.");
225       fclose(fp);
226       return;
227     }
228   }
229 
230   // read in color table if required
231   if (this->Depth < 24)
232   {
233     int numColors = 256;
234     this->Colors = new unsigned char[numColors * 3];
235     for (tmp = 0; tmp < numColors; tmp++)
236     {
237       this->Colors[tmp * 3 + 2] = fgetc(fp);
238       this->Colors[tmp * 3 + 1] = fgetc(fp);
239       this->Colors[tmp * 3] = fgetc(fp);
240       if (infoSize == 40)
241       {
242         fgetc(fp);
243       }
244     }
245     if (this->Allow8BitBMP)
246     {
247       if (!this->LookupTable)
248       {
249         this->LookupTable = vtkLookupTable::New();
250       }
251       this->LookupTable->SetNumberOfTableValues(numColors);
252       for (tmp = 0; tmp < numColors; tmp++)
253       {
254         this->LookupTable->SetTableValue(tmp, this->Colors[tmp * 3 + 0] / 255.0,
255           this->Colors[tmp * 3 + 1] / 255.0, this->Colors[tmp * 3 + 2] / 255.0, 1);
256       }
257       this->LookupTable->SetRange(0, 255);
258     }
259   }
260 
261   if (fclose(fp))
262   {
263     vtkWarningMacro("File close failed on " << this->InternalFileName);
264   }
265 
266   // Offset is the true header size. See bug 14397
267   vtkByteSwap::Swap4LE(&offset);
268   this->ManualHeaderSize = 1;
269   this->HeaderSize = offset;
270 
271   // if the user has set the VOI, just make sure its valid
272   if (this->DataVOI[0] || this->DataVOI[1] || this->DataVOI[2] || this->DataVOI[3] ||
273     this->DataVOI[4] || this->DataVOI[5])
274   {
275     if ((this->DataVOI[0] < 0) || (this->DataVOI[1] >= xsize) || (this->DataVOI[2] < 0) ||
276       (this->DataVOI[3] >= ysize))
277     {
278       vtkWarningMacro(
279         "The requested VOI is larger than the file's (" << this->InternalFileName << ") extent ");
280       this->DataVOI[0] = 0;
281       this->DataVOI[1] = xsize - 1;
282       this->DataVOI[2] = 0;
283       this->DataVOI[3] = ysize - 1;
284     }
285   }
286 
287   this->DataExtent[0] = 0;
288   this->DataExtent[1] = xsize - 1;
289   this->DataExtent[2] = 0;
290   this->DataExtent[3] = ysize - 1;
291 
292   this->SetDataScalarTypeToUnsignedChar();
293   if ((this->Depth == 8) && this->Allow8BitBMP)
294   {
295     this->SetNumberOfScalarComponents(1);
296   }
297   else
298   {
299     this->SetNumberOfScalarComponents(3);
300   }
301 
302   this->vtkImageReader::ExecuteInformation();
303 }
304 
305 //------------------------------------------------------------------------------
306 // This function opens a file to determine the file size, and to
307 // automatically determine the header size.
ComputeDataIncrements()308 void vtkBMPReader::ComputeDataIncrements()
309 {
310   int idx;
311   vtkIdType fileDataLength;
312 
313   // Determine the expected length of the data ...
314   switch (this->DataScalarType)
315   {
316     case VTK_FLOAT:
317       fileDataLength = sizeof(float);
318       break;
319     case VTK_INT:
320       fileDataLength = sizeof(int);
321       break;
322     case VTK_SHORT:
323       fileDataLength = sizeof(short);
324       break;
325     case VTK_UNSIGNED_SHORT:
326       fileDataLength = sizeof(unsigned short);
327       break;
328     case VTK_UNSIGNED_CHAR:
329       fileDataLength = sizeof(unsigned char);
330       break;
331     default:
332       vtkErrorMacro(<< "Unknown DataScalarType");
333       return;
334   }
335 
336   fileDataLength *= (this->Depth / 8);
337 
338   // a row must end on a 4 byte boundary
339   // so update the Increments[1]
340   this->DataIncrements[0] = fileDataLength;
341   fileDataLength = fileDataLength * (this->DataExtent[1] - this->DataExtent[0] + 1);
342   // move to 4 byte boundary
343   fileDataLength = fileDataLength + (4 - fileDataLength % 4) % 4;
344 
345   // compute the fileDataLength (in units of bytes)
346   for (idx = 1; idx < 3; ++idx)
347   {
348     this->DataIncrements[idx] = fileDataLength;
349     fileDataLength =
350       fileDataLength * (this->DataExtent[idx * 2 + 1] - this->DataExtent[idx * 2] + 1);
351   }
352 }
353 
354 //------------------------------------------------------------------------------
355 // This function reads in one data of data.
356 // templated to handle different data types.
357 template <class OT>
vtkBMPReaderUpdate2(vtkBMPReader * self,vtkImageData * data,OT * outPtr)358 void vtkBMPReaderUpdate2(vtkBMPReader* self, vtkImageData* data, OT* outPtr)
359 {
360   vtkIdType inIncr[3], outIncr[3];
361   OT *outPtr0, *outPtr1, *outPtr2;
362   vtkIdType streamSkip0, streamSkip1;
363   vtkIdType streamRead;
364   int idx0, idx1, idx2, pixelRead;
365   int inExtent[6];
366   int dataExtent[6];
367   int pixelSkip;
368   unsigned char* Colors;
369   unsigned long count = 0;
370   unsigned long target;
371   int Keep8bit = 0;
372 
373   // Get the requested extents.
374   data->GetExtent(inExtent);
375   // Convert them into to the extent needed from the file.
376   self->ComputeInverseTransformedExtent(inExtent, dataExtent);
377 
378   // get and transform the increments
379   data->GetIncrements(inIncr);
380   self->ComputeInverseTransformedIncrements(inIncr, outIncr);
381 
382   // get the color lut
383   Colors = self->GetColors();
384 
385   // are we converting to RGB or staying as 8bit
386   if ((self->GetDepth() == 8) && self->GetAllow8BitBMP())
387   {
388     Keep8bit = 1;
389   }
390 
391   // compute outPtr2
392   outPtr2 = outPtr;
393   if (outIncr[0] < 0)
394   {
395     outPtr2 = outPtr2 - outIncr[0] * (dataExtent[1] - dataExtent[0]);
396   }
397   if (outIncr[1] < 0)
398   {
399     outPtr2 = outPtr2 - outIncr[1] * (dataExtent[3] - dataExtent[2]);
400   }
401   if (outIncr[2] < 0)
402   {
403     outPtr2 = outPtr2 - outIncr[2] * (dataExtent[5] - dataExtent[4]);
404   }
405 
406   // length of a row, num pixels read at a time
407   pixelRead = dataExtent[1] - dataExtent[0] + 1;
408   streamRead = (vtkIdType)(pixelRead * self->GetDataIncrements()[0]);
409   streamSkip0 = (vtkIdType)(self->GetDataIncrements()[1] - streamRead);
410   streamSkip1 = (vtkIdType)(self->GetDataIncrements()[2] -
411     (dataExtent[3] - dataExtent[2] + 1) * self->GetDataIncrements()[1]);
412   pixelSkip = self->GetDepth() / 8;
413 
414   // read from the bottom up
415   if (!self->GetFileLowerLeft())
416   {
417     streamSkip0 = (vtkIdType)(-streamRead - self->GetDataIncrements()[1]);
418   }
419 
420   target = (unsigned long)((dataExtent[5] - dataExtent[4] + 1) *
421     (dataExtent[3] - dataExtent[2] + 1) / 50.0);
422   target++;
423 
424   if (self->GetFileDimensionality() == 3)
425   {
426     if (!self->OpenAndSeekFile(dataExtent, 0))
427     {
428       return;
429     }
430   }
431 
432   // create a buffer to hold a row of the data
433   std::vector<unsigned char> buf(streamRead);
434 
435   // read the data row by row
436   for (idx2 = dataExtent[4]; idx2 <= dataExtent[5]; ++idx2)
437   {
438     if (self->GetFileDimensionality() == 2)
439     {
440       if (!self->OpenAndSeekFile(dataExtent, idx2))
441       {
442         return;
443       }
444     }
445     outPtr1 = outPtr2;
446     for (idx1 = dataExtent[2]; !self->AbortExecute && idx1 <= dataExtent[3]; ++idx1)
447     {
448       if (!(count % target))
449       {
450         self->UpdateProgress(count / (50.0 * target));
451       }
452       count++;
453       outPtr0 = outPtr1;
454 
455       // read the row.
456       if (!self->GetFile()->read(reinterpret_cast<char*>(buf.data()), streamRead))
457       {
458         vtkErrorWithObjectMacro(self,
459           "File operation failed. row = "
460             << idx1 << ", Read = " << streamRead << ", Skip0 = " << streamSkip0 << ", Skip1 = "
461             << streamSkip1 << ", FilePos = " << static_cast<vtkIdType>(self->GetFile()->tellg())
462             << ", FileName = " << self->GetInternalFileName());
463         self->CloseFile();
464         return;
465       }
466 
467       // copy the bytes into the typed data
468       auto inPtr = buf.cbegin();
469       for (idx0 = dataExtent[0]; idx0 <= dataExtent[1]; ++idx0)
470       {
471         // Copy pixel into the output.
472         if (self->GetDepth() == 8 && !Keep8bit)
473         {
474           outPtr0[0] = (OT)(Colors[inPtr[0] * 3]);
475           outPtr0[1] = (OT)(Colors[inPtr[0] * 3 + 1]);
476           outPtr0[2] = (OT)(Colors[inPtr[0] * 3 + 2]);
477         }
478         else if (self->GetDepth() == 8 && Keep8bit)
479         {
480           outPtr0[0] = (OT)(inPtr[0]);
481         }
482         else
483         {
484           outPtr0[0] = (OT)(inPtr[2]);
485           outPtr0[1] = (OT)(inPtr[1]);
486           outPtr0[2] = (OT)(inPtr[0]);
487         }
488         // move to next pixel
489         inPtr += pixelSkip;
490         outPtr0 += outIncr[0];
491       }
492       // move to the next row in the file and data
493       self->GetFile()->seekg(static_cast<long>(self->GetFile()->tellg()) + streamSkip0, ios::beg);
494       outPtr1 += outIncr[1];
495     }
496     // move to the next image in the file and data
497     self->GetFile()->seekg(static_cast<long>(self->GetFile()->tellg()) + streamSkip1, ios::beg);
498     outPtr2 += outIncr[2];
499   }
500 
501   self->CloseFile();
502 
503   // delete the temporary buffer
504 }
505 
506 //------------------------------------------------------------------------------
507 // This function reads a data from a file.  The datas extent/axes
508 // are assumed to be the same as the file extent/order.
ExecuteDataWithInformation(vtkDataObject * output,vtkInformation * outInfo)509 void vtkBMPReader::ExecuteDataWithInformation(vtkDataObject* output, vtkInformation* outInfo)
510 {
511   vtkImageData* data = this->AllocateOutputData(output, outInfo);
512 
513   if (this->UpdateExtentIsEmpty(outInfo, output))
514   {
515     return;
516   }
517   if (this->InternalFileName == nullptr)
518   {
519     vtkErrorMacro(<< "Either a FileName or FilePrefix must be specified.");
520     return;
521   }
522 
523   data->GetPointData()->GetScalars()->SetName("BMPImage");
524 
525   this->ComputeDataIncrements();
526 
527   // Call the correct templated function for the output
528   void* outPtr;
529 
530   // Call the correct templated function for the input
531   outPtr = data->GetScalarPointer();
532   switch (data->GetScalarType())
533   {
534     vtkTemplateMacro(vtkBMPReaderUpdate2(this, data, static_cast<VTK_TT*>(outPtr)));
535     default:
536       vtkErrorMacro(<< "Execute: Unknown data type");
537   }
538 }
539 
540 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)541 void vtkBMPReader::PrintSelf(ostream& os, vtkIndent indent)
542 {
543   this->Superclass::PrintSelf(os, indent);
544 
545   // this->Colors is not printed
546   os << indent << "Depth: " << this->Depth << "\n";
547   os << indent << "Allow8BitBMP: " << this->Allow8BitBMP << "\n";
548   if (this->LookupTable)
549   {
550     os << indent << "LookupTable: " << this->LookupTable << "\n";
551   }
552   else
553   {
554     os << indent << "LookupTable: nullptr\n";
555   }
556 }
557 
558 //------------------------------------------------------------------------------
CanReadFile(const char * fname)559 int vtkBMPReader::CanReadFile(const char* fname)
560 {
561   // get the magic number by reading in a file
562   FILE* fp = vtksys::SystemTools::Fopen(fname, "rb");
563   if (!fp)
564   {
565     return 0;
566   }
567 
568   // compare magic number to determine file type
569   if ((fgetc(fp) != 'B') || (fgetc(fp) != 'M'))
570   {
571     fclose(fp);
572     return 0;
573   }
574 
575   vtkTypeInt32 tmp;
576   vtkTypeInt32 infoSize = 0;
577 
578   // error indicator
579   bool errorOccurred = false;
580 
581   // skip 4 bytes
582   if (fread(&tmp, 4, 1, fp) != 1)
583   {
584     errorOccurred = true;
585   }
586   // skip 4 more bytes
587   else if (fread(&tmp, 4, 1, fp) != 1)
588   {
589     errorOccurred = true;
590   }
591   // read the offset
592   else if (fread(&tmp, 4, 1, fp) != 1)
593   {
594     errorOccurred = true;
595   }
596   // get size of header
597   else if (fread(&infoSize, 4, 1, fp) != 1)
598   {
599     infoSize = 0;
600     errorOccurred = true;
601   }
602 
603   vtkByteSwap::Swap4LE(&infoSize);
604 
605   // error checking
606   if ((infoSize != 40) && (infoSize != 12))
607   {
608     errorOccurred = true;
609   }
610 
611   fclose(fp);
612   return !errorOccurred;
613 }
614