]> Creatis software - gdcm.git/blob - vtk/vtkGdcmReader.cxx
91556c82e9803f3999f1f3decf2ee05f470c3d8e
[gdcm.git] / vtk / vtkGdcmReader.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: vtkGdcmReader.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/12/09 10:59:59 $
7   Version:   $Revision: 1.64 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18                                                                                 
19 //-----------------------------------------------------------------------------
20 // //////////////////////////////////////////////////////////////
21 // WARNING TODO CLEANME 
22 // Actual limitations of this code:
23 //
24 // /////// Redundant and unnecessary header parsing
25 // In it's current state this code actually parses three times the Dicom
26 // header of a file before the corresponding image gets loaded in the
27 // ad-hoc vtkData !
28 // Here is the process:
29 //  1/ First loading happens in ExecuteInformation which in order to
30 //     positionate the vtk extents calls CheckFileCoherence. The purpose
31 //     of CheckFileCoherence is to make sure all the images in the future
32 //     stack are "homogenous" (same size, same representation...). This
33 //     can only be achieved by parsing all the Dicom headers...
34 //  2/ ExecuteData is then responsible for the next two loadings:
35 //  2a/ ExecuteData calls AllocateOutputData that in turn seems to 
36 //      (indirectely call) ExecuteInformation which ends up in a second
37 //      header parsing
38 //      This is fixed by adding a test at the beginning of ExecuteInformation
39 //      on the modification of the object instance. If a modification have been
40 //      made (method Modified() ), the MTime value is increased. The fileTime
41 //      is compared to this new value to find a modification in the class
42 //      parameters
43 //  2b/ the core of ExecuteData then needs gdcmFile (which in turns
44 //      initialises gdcmHeader in the constructor) in order to access
45 //      the data-image.
46 //
47 // Possible solution:
48 // maintain a list of gdcmFiles (created by say ExecuteInformation) created
49 // once and for all accross the life of vtkGdcmHeader (it would only load
50 // new gdcmFile if the user changes the list). ExecuteData would then use 
51 // those gdcmFile and hence avoid calling the construtor:
52 //  - advantage: the header of the files would only be parser once.
53 //  - drawback: once execute information is called (i.e. on creation of
54 //              a vtkGdcmHeader) the gdcmFile structure is loaded in memory.
55 //              The average size of a gdcmHeader being of 100Ko, is one
56 //              loads 10 stacks of images with say 200 images each, you
57 //              end-up with a loss of 200Mo...
58 //
59 // /////// Never unallocated memory:
60 // ExecuteData allocates space for the pixel data [which will get pointed
61 // by the vtkPointData() through the call
62 // data->GetPointData()->GetScalars()->SetVoidArray(mem, StackNumPixels, 0);]
63 // This data is never "freed" neither in the destructor nor when the
64 // filename list is extended, ExecuteData is called a second (or third)
65 // time...
66 // //////////////////////////////////////////////////////////////
67
68 #include "gdcmFile.h"
69 #include "gdcmHeader.h"
70 #include "vtkGdcmReader.h"
71
72 //#include <stdio.h>
73 #include <vtkObjectFactory.h>
74 #include <vtkImageData.h>
75 #include <vtkPointData.h>
76 #include <vtkLookupTable.h>
77
78 vtkCxxRevisionMacro(vtkGdcmReader, "$Revision: 1.64 $");
79 vtkStandardNewMacro(vtkGdcmReader);
80
81 //-----------------------------------------------------------------------------
82 // Constructor / Destructor
83 vtkGdcmReader::vtkGdcmReader()
84 {
85    this->LookupTable = NULL;
86    this->AllowLookupTable = 0;
87 }
88
89 vtkGdcmReader::~vtkGdcmReader()
90 {
91    this->RemoveAllFileName();
92    this->InternalFileNameList.clear();
93    if(this->LookupTable) 
94       this->LookupTable->Delete();
95 }
96
97 //-----------------------------------------------------------------------------
98 // Print
99 void vtkGdcmReader::PrintSelf(ostream& os, vtkIndent indent)
100 {
101    this->Superclass::PrintSelf(os,indent);
102    os << indent << "Filenames  : " << endl;
103    vtkIndent nextIndent = indent.GetNextIndent();
104    for (std::list<std::string>::iterator it = FileNameList.begin();
105         it != FileNameList.end();
106         ++it)
107    {
108       os << nextIndent << it->c_str() << endl ;
109    }
110 }
111
112 //-----------------------------------------------------------------------------
113 // Public
114 /*
115  * Remove all files from the list of images to read.
116  */
117 void vtkGdcmReader::RemoveAllFileName(void)
118 {
119    this->FileNameList.clear();
120    this->Modified();
121 }
122
123 /*
124  * Adds a file name to the list of images to read.
125  */
126 void vtkGdcmReader::AddFileName(const char* name)
127 {
128    // We need to bypass the const pointer [since list<>.push_bash() only
129    // takes a char* (but not a const char*)] by making a local copy:
130    char * LocalName = new char[strlen(name) + 1];
131    strcpy(LocalName, name);
132    this->FileNameList.push_back(LocalName);
133    delete[] LocalName;
134    this->Modified();
135 }
136
137 /*
138  * Sets up a filename to be read.
139  */
140 void vtkGdcmReader::SetFileName(const char *name) 
141 {
142    vtkImageReader2::SetFileName(name);
143    // Since we maintain a list of filenames, when building a volume,
144    // (see vtkGdcmReader::AddFileName), we additionaly need to purge
145    // this list when we manually positionate the filename.
146    vtkDebugMacro(<< "Clearing all files given with AddFileName");
147    this->FileNameList.clear();
148    this->Modified();
149 }
150
151 //-----------------------------------------------------------------------------
152 // Protected
153 /*
154  * Configure the output e.g. WholeExtent, spacing, origin, scalar type...
155  */
156 void vtkGdcmReader::ExecuteInformation()
157 {
158    if(this->MTime>this->fileTime)
159    {
160       this->TotalNumberOfPlanes = this->CheckFileCoherence();
161       if ( this->TotalNumberOfPlanes == 0)
162       {
163          vtkErrorMacro(<< "File set is not coherent. Exiting...");
164          return;
165       }
166       
167       // if the user has not set the extent, but has set the VOI
168       // set the z axis extent to the VOI z axis
169       if (this->DataExtent[4]==0 && this->DataExtent[5] == 0 &&
170       (this->DataVOI[4] || this->DataVOI[5]))
171       {
172          this->DataExtent[4] = this->DataVOI[4];
173          this->DataExtent[5] = this->DataVOI[5];
174       }
175
176       // When the user has set the VOI, check it's coherence with the file content.
177       if (this->DataVOI[0] || this->DataVOI[1] || 
178       this->DataVOI[2] || this->DataVOI[3] ||
179       this->DataVOI[4] || this->DataVOI[5])
180       { 
181          if ((this->DataVOI[0] < 0) ||
182              (this->DataVOI[1] >= this->NumColumns) ||
183              (this->DataVOI[2] < 0) ||
184              (this->DataVOI[3] >= this->NumLines) ||
185              (this->DataVOI[4] < 0) ||
186              (this->DataVOI[5] >= this->TotalNumberOfPlanes ))
187          {
188             vtkWarningMacro(<< "The requested VOI is larger than expected extent.");
189             this->DataVOI[0] = 0;
190             this->DataVOI[1] = this->NumColumns - 1;
191             this->DataVOI[2] = 0;
192             this->DataVOI[3] = this->NumLines - 1;
193             this->DataVOI[4] = 0;
194             this->DataVOI[5] = this->TotalNumberOfPlanes - 1;
195          }
196       }
197
198       // Positionate the Extent.
199       this->DataExtent[0] = 0;
200       this->DataExtent[1] = this->NumColumns - 1;
201       this->DataExtent[2] = 0;
202       this->DataExtent[3] = this->NumLines - 1;
203       this->DataExtent[4] = 0;
204       this->DataExtent[5] = this->TotalNumberOfPlanes - 1;
205   
206       // We don't need to positionate the Endian related stuff (by using
207       // this->SetDataByteOrderToBigEndian() or SetDataByteOrderToLittleEndian()
208       // since the reading of the file is done by gdcm.
209       // But we do need to set up the data type for downstream filters:
210       if      ( ImageType == "8U" )
211       {
212          vtkDebugMacro(<< "8 bits unsigned image");
213          this->SetDataScalarTypeToUnsignedChar(); 
214       }
215       else if ( ImageType == "8S" )
216       {
217          vtkErrorMacro(<< "Cannot handle 8 bit signed files");
218          return;
219       }
220       else if ( ImageType == "16U" )
221       {
222          vtkDebugMacro(<< "16 bits unsigned image");
223          this->SetDataScalarTypeToUnsignedShort();
224       }
225       else if ( ImageType == "16S" )
226       {
227          vtkDebugMacro(<< "16 bits signed image");
228          this->SetDataScalarTypeToShort();
229          //vtkErrorMacro(<< "Cannot handle 16 bit signed files");
230       }
231       else if ( ImageType == "32U" )
232       {
233          vtkDebugMacro(<< "32 bits unsigned image");
234          vtkDebugMacro(<< "WARNING: forced to signed int !");
235          this->SetDataScalarTypeToInt();
236       }
237       else if ( ImageType == "32S" )
238       {
239          vtkDebugMacro(<< "32 bits signed image");
240          this->SetDataScalarTypeToInt();
241       }
242       else if ( ImageType == "FD" )
243       {
244          vtkDebugMacro(<< "64 bits Double image");
245          this->SetDataScalarTypeToDouble();
246       }
247       //Set number of scalar components:
248       this->SetNumberOfScalarComponents(this->NumComponents);
249
250       this->fileTime=this->MTime;
251    }
252
253    this->Superclass::ExecuteInformation();
254 }
255
256 /*
257  * Update => ouput->Update => UpdateData => Execute => ExecuteData 
258  * (see vtkSource.cxx for last step).
259  * This function (redefinition of vtkImageReader::ExecuteData, see 
260  * VTK/IO/vtkImageReader.cxx) reads a data from a file. The datas
261  * extent/axes are assumed to be the same as the file extent/order.
262  */
263 void vtkGdcmReader::ExecuteData(vtkDataObject *output)
264 {
265    if (this->InternalFileNameList.empty())
266    {
267       vtkErrorMacro(<< "A least a valid FileName must be specified.");
268       return;
269    }
270
271    // FIXME : extraneous parsing of header is made when allocating OuputData
272    vtkImageData *data = this->AllocateOutputData(output);
273    data->SetExtent(this->DataExtent);
274    data->GetPointData()->GetScalars()->SetName("DicomImage-Volume");
275
276    // Test if output has valid extent
277    // Prevent memory errors
278    if((this->DataExtent[1]-this->DataExtent[0]>=0) &&
279       (this->DataExtent[3]-this->DataExtent[2]>=0) &&
280       (this->DataExtent[5]-this->DataExtent[4]>=0))
281    {
282       // The memory size for a full stack of images of course depends
283       // on the number of planes and the size of each image:
284       //size_t StackNumPixels = this->NumColumns * this->NumLines
285       //                      * this->TotalNumberOfPlanes * this->NumComponents;
286       //size_t stack_size = StackNumPixels * this->PixelSize; //not used
287       // Allocate pixel data space itself.
288
289       // Variables for the UpdateProgress. We shall use 50 steps to signify
290       // the advance of the process:
291       unsigned long UpdateProgressTarget = (unsigned long) ceil (this->NumLines
292                                          * this->TotalNumberOfPlanes
293                                          / 50.0);
294       // The actual advance measure:
295       unsigned long UpdateProgressCount = 0;
296
297       // Feeling the allocated memory space with each image/volume:
298       unsigned char *Dest = (unsigned char *)data->GetScalarPointer();
299       for (std::list<std::string>::iterator filename  = InternalFileNameList.begin();
300            filename != InternalFileNameList.end();
301            ++filename)
302       { 
303          // Images that were tagged as unreadable in CheckFileCoherence()
304          // are substituted with a black image to let the caller visually
305          // notice something wrong is going on:
306          if (*filename != "GDCM_UNREADABLE")
307          {
308             // Update progress related for good files is made in LoadImageInMemory
309             Dest += this->LoadImageInMemory(*filename, Dest,
310                                             UpdateProgressTarget,
311                                             UpdateProgressCount);
312          } 
313          else 
314          {
315             // We insert a black image in the stack for the user to be aware that
316             // this image/volume couldn't be loaded. We simply skip one image
317             // size:
318             Dest += this->NumColumns * this->NumLines * this->PixelSize;
319
320             // Update progress related for bad files:
321             UpdateProgressCount += this->NumLines;
322             if (UpdateProgressTarget > 0)
323             {
324                if (!(UpdateProgressCount%UpdateProgressTarget))
325                {
326                   this->UpdateProgress(UpdateProgressCount/(50.0*UpdateProgressTarget));
327                }
328             }
329          } // Else, file not loadable
330       } // Loop on files
331    }
332 }
333
334 /*
335  * vtkGdcmReader can have the file names specified through two ways:
336  * (1) by calling the vtkImageReader2::SetFileName(), SetFilePrefix() and
337  *     SetFilePattern()
338  * (2) By successive calls to vtkGdcmReader::AddFileName()
339  * When the first method was used by caller we need to update the local
340  * filename list
341  */
342 void vtkGdcmReader::BuildFileListFromPattern()
343 {
344    this->RemoveAllInternalFileName();
345
346    if ((! this->FileNameList.empty()) && this->FileName )
347    {
348       vtkErrorMacro(<< "Both AddFileName and SetFileName schemes were used");
349       vtkErrorMacro(<< "No images loaded ! ");
350       return;
351    }
352
353    if ((! this->FileNameList.empty()) && this->FilePrefix )
354    {
355       vtkErrorMacro(<< "Both AddFileName and SetFilePrefix schemes were used");
356       vtkErrorMacro(<< "No images loaded ! ");
357       return;
358    }
359
360    if (this->FileName && this->FilePrefix)
361    {
362       vtkErrorMacro(<< "Both SetFileName and SetFilePrefix schemes were used");
363       vtkErrorMacro(<< "No images loaded ! ");
364       return;
365    }
366
367    if (! this->FileNameList.empty()  )
368    {
369       vtkDebugMacro(<< "Using the AddFileName specified files");
370       this->InternalFileNameList=this->FileNameList;
371       return;
372    }
373
374    if (!this->FileName && !this->FilePrefix)
375    {
376       vtkErrorMacro(<< "FileNames are not set. Either use AddFileName() or");
377       vtkErrorMacro(<< "specify a FileName or FilePrefix.");
378       return;
379    }
380
381    if( this->FileName )
382    {
383       // Single file loading (as given with ::SetFileName()):
384       // Case of multi-frame file considered here
385       this->ComputeInternalFileName(this->DataExtent[4]);
386       vtkDebugMacro(<< "Adding file " << this->InternalFileName);
387       this->AddInternalFileName(this->InternalFileName);
388    }
389    else
390    {
391       // Multi file loading (as given with ::SetFilePattern()):
392       for (int idx = this->DataExtent[4]; idx <= this->DataExtent[5]; ++idx)
393       {
394          this->ComputeInternalFileName(idx);
395          vtkDebugMacro(<< "Adding file " << this->InternalFileName);
396          this->AddInternalFileName(this->InternalFileName);
397       }
398    }
399 }
400
401 /*
402  * When more than one filename is specified (i.e. we expect loading
403  * a stack or volume) we need to check that the corresponding images/volumes
404  * to be loaded are coherent i.e. to make sure:
405  *     - they all share the same X dimensions
406  *     - they all share the same Y dimensions
407  *     - they all share the same ImageType ( 8 bit signed, or unsigned...)
408  *
409  * Eventually, we emit a warning when all the files do NOT share the
410  * Z dimension, since we can still build a stack but the
411  * files are not coherent in Z, which is probably a source a trouble...
412  *   When files are not readable (either the file cannot be opened or
413  * because gdcm cannot parse it), they are flagged as "GDCM_UNREADABLE".  
414  *   This method returns the total number of planar images to be loaded
415  * (i.e. an image represents one plane, but a volume represents many planes)
416  */
417 int vtkGdcmReader::CheckFileCoherence()
418 {
419    int ReturnedTotalNumberOfPlanes = 0;   // The returned value.
420
421    this->BuildFileListFromPattern();
422    if (this->InternalFileNameList.empty())
423    {
424       vtkErrorMacro(<< "FileNames are not set.");
425       return 0;
426    }
427
428    bool FoundReferenceFile = false;
429    int  ReferenceNZ = 0;
430
431    // Loop on the filenames:
432    // - check for their existence and gdcm "parsability"
433    // - get the coherence check done:
434    for (std::list<std::string>::iterator filename = InternalFileNameList.begin();
435         filename != InternalFileNameList.end();
436         ++filename)
437    {
438       // The file is always added in the number of planes
439       //  - If file doesn't exist, it will be replaced by a black plane in the 
440       //    ExecuteData method
441       //  - If file has more than 1 plane, other planes will be added later to
442       //    to the ReturnedTotalNumberOfPlanes variable counter
443       ReturnedTotalNumberOfPlanes += 1;
444
445       /////// Stage 0: check for file name:
446       if(*filename == std::string("GDCM_UNREADABLE"))
447          continue;
448
449       /////// Stage 1: check for file readability:
450       // Stage 1.1: check for file existence.
451       FILE *fp;
452       fp = fopen(filename->c_str(),"rb");
453       if (!fp)
454       {
455          vtkErrorMacro(<< "Unable to open file " << filename->c_str());
456          vtkErrorMacro(<< "Removing this file from readed files "
457                      << filename->c_str());
458          *filename = "GDCM_UNREADABLE";
459          continue;
460       }
461       fclose(fp);
462
463       // Stage 1.2: check for Gdcm parsability
464       gdcm::Header GdcmHeader(filename->c_str() );
465       if (!GdcmHeader.IsReadable())
466       {
467          vtkErrorMacro(<< "Gdcm cannot parse file " << filename->c_str());
468          vtkErrorMacro(<< "Removing this file from readed files "
469                         << filename->c_str());
470          *filename = "GDCM_UNREADABLE";
471          continue;
472       }
473
474       // Stage 1.3: further gdcm compatibility on PixelType
475       std::string type = GdcmHeader.GetPixelType();
476       if (   (type !=  "8U") && (type !=  "8S")
477           && (type != "16U") && (type != "16S")
478           && (type != "32U") && (type != "32S") )
479       {
480          vtkErrorMacro(<< "Bad File Type for file " << filename->c_str() << "\n"
481                        << "   File type found : " << type.c_str() 
482                        << " (might be 8U, 8S, 16U, 16S, 32U, 32S) \n"
483                        << "   Removing this file from readed files");
484          *filename = "GDCM_UNREADABLE";
485          continue;
486       }
487
488       // Stage 2: check coherence of the set of files
489       int NX = GdcmHeader.GetXSize();
490       int NY = GdcmHeader.GetYSize();
491       int NZ = GdcmHeader.GetZSize();
492       if (FoundReferenceFile) 
493       {
494          // Stage 2.1: mandatory coherence stage:
495          if (   ( NX   != this->NumColumns )
496              || ( NY   != this->NumLines )
497              || ( type != this->ImageType ) ) 
498          {
499             vtkErrorMacro(<< "This file is not coherent with previous ones"
500                            << filename->c_str());
501             vtkErrorMacro(<< "Removing this file from readed files "
502                            << filename->c_str());
503             *filename = "GDCM_UNREADABLE";
504             continue;
505          }
506
507          // Stage 2.2: optional coherence stage
508          if ( NZ != ReferenceNZ )
509          {
510             vtkErrorMacro(<< "File is not coherent in Z with previous ones"
511                            << filename->c_str());
512          }
513          else
514          {
515             vtkDebugMacro(<< "File is coherent with previous ones"
516                            << filename->c_str());
517          }
518
519          // Stage 2.3: when the file contains a volume (as opposed to an image),
520          // notify the caller.
521          if (NZ > 1)
522          {
523             vtkErrorMacro(<< "This file contains multiple planes (images)"
524                            << filename->c_str());
525          }
526
527          // Eventually, this file can be added on the stack. Update the
528          // full size of the stack
529          vtkDebugMacro("Number of planes added to the stack: " << NZ);
530          ReturnedTotalNumberOfPlanes += NZ - 1; // First plane already added
531          continue;
532
533       } 
534       else 
535       {
536          // We didn't have a workable reference file yet. Set this one
537          // as the reference.
538          FoundReferenceFile = true;
539          vtkDebugMacro(<< "This file taken as coherence reference:"
540                         << filename->c_str());
541          vtkDebugMacro(<< "Image dimension of reference file as read from Gdcm:" 
542                         << NX << " " << NY << " " << NZ);
543          vtkDebugMacro(<< "Number of planes added to the stack: " << NZ);
544          // Set aside the size of the image
545          this->NumColumns = NX;
546          this->NumLines   = NY;
547          ReferenceNZ      = NZ;
548          ReturnedTotalNumberOfPlanes += NZ - 1; // First plane already added
549          this->ImageType = type;
550          this->PixelSize = GdcmHeader.GetPixelSize();
551
552          if( GdcmHeader.HasLUT() && this->AllowLookupTable )
553          {
554             // I could raise an error is AllowLookupTable is on and HasLUT() off
555             this->NumComponents = GdcmHeader.GetNumberOfScalarComponentsRaw();
556          }
557          else
558          {
559             this->NumComponents = GdcmHeader.GetNumberOfScalarComponents(); //rgb or mono
560          }
561        
562          //Set image spacing
563          this->DataSpacing[0] = GdcmHeader.GetXSpacing();
564          this->DataSpacing[1] = GdcmHeader.GetYSpacing();
565          this->DataSpacing[2] = GdcmHeader.GetZSpacing();
566
567          //Set image origin
568          //this->DataOrigin[0] = GdcmHeader.GetXOrigin();
569          //this->DataOrigin[1] = GdcmHeader.GetYOrigin();
570          //this->DataOrigin[2] = GdcmHeader.GetZOrigin();
571
572       }
573    } // End of loop on filename
574
575    ///////// The files we CANNOT load are flaged. On debugging purposes
576    // count the loadable number of files and display their number:
577    int NumberCoherentFiles = 0;
578    for (std::list<std::string>::iterator it = InternalFileNameList.begin();
579         it != InternalFileNameList.end();
580         ++it)
581    {
582       if (*it != "GDCM_UNREADABLE")
583       {
584          NumberCoherentFiles++;
585       }
586    }
587    vtkDebugMacro(<< "Number of coherent files: " << NumberCoherentFiles);
588
589    if (ReturnedTotalNumberOfPlanes == 0)
590    {
591       vtkErrorMacro(<< "No loadable file.");
592    }
593
594    vtkDebugMacro(<< "Total number of planes on the stack: "
595                   << ReturnedTotalNumberOfPlanes);
596    
597    return ReturnedTotalNumberOfPlanes;
598 }
599
600 //-----------------------------------------------------------------------------
601 // Private
602 /*
603  * Remove all file names to the internal list of images to read.
604  */
605 void vtkGdcmReader::RemoveAllInternalFileName(void)
606 {
607    this->InternalFileNameList.clear();
608 }
609
610 /*
611  * Adds a file name to the internal list of images to read.
612  */
613 void vtkGdcmReader::AddInternalFileName(const char* name)
614 {
615    char * LocalName = new char[strlen(name) + 1];
616    strcpy(LocalName, name);
617    this->InternalFileNameList.push_back(LocalName);
618    delete[] LocalName;
619 }
620
621 /*
622  * Loads the contents of the image/volume contained by Filename at
623  * the Dest memory address. Returns the size of the data loaded.
624  */
625 size_t vtkGdcmReader::LoadImageInMemory(
626              std::string fileName, 
627              unsigned char *dest,
628              const unsigned long updateProgressTarget,
629              unsigned long & updateProgressCount)
630 {
631    vtkDebugMacro(<< "Copying to memory image [" << fileName.c_str() << "]");
632    gdcm::File file( fileName.c_str() );
633    size_t size;
634
635    // If the data structure of vtk for image/volume representation
636    // were straigthforwards the following would be enough:
637    //    GdcmFile.GetImageDataIntoVector((void*)Dest, size);
638    // But vtk chooses to invert the lines of an image, that is the last
639    // line comes first (for some axis related reasons?). Hence we need
640    // to load the image line by line, starting from the end.
641
642    int numColumns = file.GetHeader()->GetXSize();
643    int numLines   = file.GetHeader()->GetYSize();
644    int numPlanes  = file.GetHeader()->GetZSize();
645    int lineSize   = NumComponents * numColumns * file.GetHeader()->GetPixelSize();
646    int planeSize  = lineSize * numLines;
647
648    unsigned char *src;
649    
650    if( file.GetHeader()->HasLUT() && AllowLookupTable )
651    {
652       size               = file.GetImageDataSize();
653       src                = (unsigned char*) file.GetImageDataRaw();
654       unsigned char *lut = (unsigned char*) file.GetLutRGBA();
655
656       if(!this->LookupTable)
657       {
658          this->LookupTable = vtkLookupTable::New();
659       }
660
661       this->LookupTable->SetNumberOfTableValues(256);
662       for (int tmp=0; tmp<256; tmp++)
663       {
664          this->LookupTable->SetTableValue(tmp,
665          (float)lut[4*tmp+0]/255.0,
666          (float)lut[4*tmp+1]/255.0,
667          (float)lut[4*tmp+2]/255.0,
668          1);
669       }
670       this->LookupTable->SetRange(0,255);
671       vtkDataSetAttributes *a = this->GetOutput()->GetPointData();
672       a->GetScalars()->SetLookupTable(this->LookupTable);
673       free(lut);
674    }
675    else
676    {
677       size = file.GetImageDataSize();
678       src  = (unsigned char*)file.GetImageData();
679    } 
680
681    unsigned char *dst = dest + planeSize - lineSize;
682    for (int plane = 0; plane < numPlanes; plane++)
683    {
684       for (int line = 0; line < numLines; line++)
685       {
686          // Copy one line at proper destination:
687          memcpy((void*)dst, (void*)src, lineSize);
688          src += lineSize;
689          dst -= lineSize;
690          // Update progress related:
691          if (!(updateProgressCount%updateProgressTarget))
692          {
693             this->UpdateProgress(updateProgressCount/(50.0*updateProgressTarget));
694          }
695          updateProgressCount++;
696       }
697       dst += 2 * planeSize;
698    }
699    
700    return size;
701 }
702
703 //-----------------------------------------------------------------------------