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