]> Creatis software - gdcm.git/blob - src/gdcmFile.cxx
da8105e3b8eaca4f2bb10fb1851c8397f510ae9e
[gdcm.git] / src / gdcmFile.cxx
1   /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFile.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/10/18 12:49:22 $
7   Version:   $Revision: 1.145 $
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 #include "gdcmFile.h"
20 #include "gdcmDebug.h"
21
22 namespace gdcm 
23 {
24 typedef std::pair<TagDocEntryHT::iterator,TagDocEntryHT::iterator> IterHT;
25
26 //-------------------------------------------------------------------------
27 // Constructor / Destructor
28 /**
29  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
30  *        file (Header only deals with the ... header)
31  *        Opens (in read only and when possible) an existing file and checks
32  *        for DICOM compliance. Returns NULL on failure.
33  *        It will be up to the user to load the pixels into memory
34  *        (see GetImageData, GetImageDataRaw)
35  * \note  the in-memory representation of all available tags found in
36  *        the DICOM header is post-poned to first header information access.
37  *        This avoid a double parsing of public part of the header when
38  *        user sets an a posteriori shadow dictionary (efficiency can be
39  *        seen as a side effect).   
40  * @param header already built Header
41  */
42 File::File(Header *header)
43 {
44    HeaderInternal = header;
45    SelfHeader = false;
46    Initialise();
47 }
48
49 /**
50  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
51  *        file (Header only deals with the ... header)
52  *        Opens (in read only and when possible) an existing file and checks
53  *        for DICOM compliance. Returns NULL on failure.
54  *        It will be up to the user to load the pixels into memory
55  *        (see GetImageData, GetImageDataRaw)
56  * \note  the in-memory representation of all available tags found in
57  *        the DICOM header is post-poned to first header information access.
58  *        This avoid a double parsing of public part of the header when
59  *        one sets an a posteriori shadow dictionary (efficiency can be
60  *        seen as a side effect).   
61  * @param filename file to be opened for parsing
62  */
63 File::File(std::string const & filename )
64 {
65    HeaderInternal = new Header( filename );
66    SelfHeader = true;
67    Initialise();
68 }
69
70 /**
71  * \brief Factorization for various forms of constructors.
72  */
73 void File::Initialise()
74 {
75    if ( HeaderInternal->IsReadable() )
76    {
77       ImageDataSizeRaw = ComputeDecompressedPixelDataSizeFromHeader();
78       if ( HeaderInternal->HasLUT() )
79       {
80          ImageDataSize = 3 * ImageDataSizeRaw;
81       }
82       else
83       {
84          ImageDataSize = ImageDataSizeRaw;
85       }
86
87       PixelConverter.GrabInformationsFromHeader( HeaderInternal );
88    }
89    SaveInitialValues();
90 }
91
92 /**
93  * \brief canonical destructor
94  * \note  If the Header was created by the File constructor,
95  *        it is destroyed by the File
96  */
97 File::~File()
98
99    if( SelfHeader )
100    {
101       delete HeaderInternal;
102    }
103    HeaderInternal = 0;
104
105    DeleteInitialValues();
106 }
107
108 /**
109  * \brief Sets some initial values for the Constructor
110  * \warning not end user intended
111  */
112 void File::SaveInitialValues()
113
114
115    PixelRead  = -1; // no ImageData read yet.
116    LastAllocatedPixelDataLength = 0;
117    Pixel_Data = 0;
118
119    InitialSpp = "";     
120    InitialPhotInt = "";
121    InitialPlanConfig = "";
122    InitialBitsAllocated = "";
123    InitialHighBit = "";
124   
125    InitialRedLUTDescr   = 0;
126    InitialGreenLUTDescr = 0;
127    InitialBlueLUTDescr  = 0;
128    InitialRedLUTData    = 0;
129    InitialGreenLUTData  = 0;
130    InitialBlueLUTData   = 0; 
131                 
132    if ( HeaderInternal->IsReadable() )
133    {
134       // the following values *may* be modified 
135       // by File::GetImageDataIntoVectorRaw
136       // we save their initial value.
137       InitialSpp           = HeaderInternal->GetEntryByNumber(0x0028,0x0002);
138       InitialPhotInt       = HeaderInternal->GetEntryByNumber(0x0028,0x0004);
139       InitialPlanConfig    = HeaderInternal->GetEntryByNumber(0x0028,0x0006);
140       
141       InitialBitsAllocated = HeaderInternal->GetEntryByNumber(0x0028,0x0100);
142       InitialHighBit       = HeaderInternal->GetEntryByNumber(0x0028,0x0102);
143
144       // the following entries *may* be removed from the H table
145       // (NOT deleted ...) by File::GetImageDataIntoVectorRaw  
146       // we keep a pointer on them.
147       InitialRedLUTDescr   = HeaderInternal->GetDocEntryByNumber(0x0028,0x1101);
148       InitialGreenLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1102);
149       InitialBlueLUTDescr  = HeaderInternal->GetDocEntryByNumber(0x0028,0x1103);
150
151       InitialRedLUTData    = HeaderInternal->GetDocEntryByNumber(0x0028,0x1201);
152       InitialGreenLUTData  = HeaderInternal->GetDocEntryByNumber(0x0028,0x1202);
153       InitialBlueLUTData   = HeaderInternal->GetDocEntryByNumber(0x0028,0x1203); 
154    }
155 }
156
157 /**
158  * \brief restores some initial values
159  * \warning not end user intended
160  */
161 void File::RestoreInitialValues()
162 {   
163    if ( HeaderInternal->IsReadable() )
164    {      
165       // the following values *may* have been modified 
166       // by File::GetImageDataIntoVectorRaw
167       // we restore their initial value.
168       if ( InitialSpp != "")
169          HeaderInternal->SetEntryByNumber(InitialSpp,0x0028,0x0002);
170       if ( InitialPhotInt != "")
171          HeaderInternal->SetEntryByNumber(InitialPhotInt,0x0028,0x0004);
172       if ( InitialPlanConfig != "")
173
174          HeaderInternal->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006);
175       if ( InitialBitsAllocated != "")
176           HeaderInternal->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100);
177       if ( InitialHighBit != "")
178           HeaderInternal->SetEntryByNumber(InitialHighBit,0x0028,0x0102);
179                
180       // the following entries *may* be have been removed from the H table
181       // (NOT deleted ...) by File::GetImageDataIntoVectorRaw  
182       // we restore them.
183
184       if (InitialRedLUTDescr)
185          HeaderInternal->AddEntry(InitialRedLUTDescr);
186       if (InitialGreenLUTDescr)
187          HeaderInternal->AddEntry(InitialGreenLUTDescr);
188       if (InitialBlueLUTDescr)
189          HeaderInternal->AddEntry(InitialBlueLUTDescr);
190
191       if (InitialRedLUTData)
192          HeaderInternal->AddEntry(InitialBlueLUTDescr);
193       if (InitialGreenLUTData)
194          HeaderInternal->AddEntry(InitialGreenLUTData);
195       if (InitialBlueLUTData)
196          HeaderInternal->AddEntry(InitialBlueLUTData);
197    }
198 }
199
200 /**
201  * \brief delete initial values (il they were saved)
202  *        of InitialLutDescriptors and InitialLutData
203  */
204 void File::DeleteInitialValues()
205
206
207 // InitialLutDescriptors and InitialLutData
208 // will have to be deleted if the don't belong any longer
209 // to the Header H table when the header is deleted...
210
211    if ( InitialRedLUTDescr )           
212       delete InitialRedLUTDescr;
213   
214    if ( InitialGreenLUTDescr )
215       delete InitialGreenLUTDescr;
216       
217    if ( InitialBlueLUTDescr )      
218       delete InitialBlueLUTDescr; 
219        
220    if ( InitialRedLUTData )      
221       delete InitialRedLUTData;
222    
223    if ( InitialGreenLUTData != NULL)
224       delete InitialGreenLUTData;
225       
226    if ( InitialBlueLUTData != NULL)      
227       delete InitialBlueLUTData;      
228 }
229
230 //-----------------------------------------------------------------------------
231 // Print
232
233 //-----------------------------------------------------------------------------
234 // Public
235
236 /**
237  * \brief     computes the length (in bytes) we must ALLOCATE to receive the
238  *            image(s) pixels (multiframes taken into account) 
239  * \warning : it is NOT the group 7FE0 length
240  *          (no interest for compressed images).
241  */
242 int File::ComputeDecompressedPixelDataSizeFromHeader()
243 {
244    // see PS 3.3-2003 : C.7.6.3.2.1  
245    // 
246    //   MONOCHROME1
247    //   MONOCHROME2
248    //   PALETTE COLOR
249    //   RGB
250    //   HSV  (Retired)
251    //   ARGB (Retired)
252    //   CMYK (Retired)
253    //   YBR_FULL
254    //   YBR_FULL_422 (no LUT, no Palette)
255    //   YBR_PARTIAL_422
256    //   YBR_ICT
257    //   YBR_RCT
258
259    // LUT's
260    // ex : gdcm-US-ALOKA-16.dcm
261    // 0028|1221 [OW]   [Segmented Red Palette Color Lookup Table Data]
262    // 0028|1222 [OW]   [Segmented Green Palette Color Lookup Table Data]  
263    // 0028|1223 [OW]   [Segmented Blue Palette Color Lookup Table Data]
264
265    // ex  : OT-PAL-8-face.dcm
266    // 0028|1201 [US]   [Red Palette Color Lookup Table Data]
267    // 0028|1202 [US]   [Green Palette Color Lookup Table Data]
268    // 0028|1203 [US]   [Blue Palette Color Lookup Table Data]
269
270    int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
271    // Number of "Bits Allocated" is fixed to 16 when:
272    //  - it is not defined (i.e. it's value is 0)
273    //  - it's 12, since we will expand the image to 16 bits (see
274    //    PixelConvert::ConvertDecompress12BitsTo16Bits() )
275    if ( ( numberBitsAllocated == 0 ) || ( numberBitsAllocated == 12 ) )
276    {
277       numberBitsAllocated = 16;
278    } 
279
280    int DecompressedSize = HeaderInternal->GetXSize()
281                         * HeaderInternal->GetYSize() 
282                         * HeaderInternal->GetZSize()
283                         * ( numberBitsAllocated / 8 )
284                         * HeaderInternal->GetSamplesPerPixel();
285    
286    return DecompressedSize;
287 }
288
289 /**
290  * \brief   - Allocates necessary memory, 
291  *          - Reads the pixels from disk (uncompress if necessary),
292  *          - Transforms YBR pixels, if any, into RGB pixels
293  *          - Transforms 3 planes R, G, B, if any, into a single RGB Plane
294  *          - Transforms single Grey plane + 3 Palettes into a RGB Plane
295  *          - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
296  * @return  Pointer to newly allocated pixel data.
297  *          NULL if alloc fails 
298  */
299 uint8_t* File::GetImageData()
300 {
301    // FIXME (Mathieu)
302    // I need to deallocate Pixel_Data before doing any allocation:
303    
304    if ( Pixel_Data )
305      if ( LastAllocatedPixelDataLength != ImageDataSize ) 
306         free(Pixel_Data);
307    if ( !Pixel_Data )
308       Pixel_Data = new uint8_t[ImageDataSize];
309     
310    if ( Pixel_Data )
311    {
312       LastAllocatedPixelDataLength = ImageDataSize;
313
314       // we load the pixels (and transform grey level + LUT into RGB)
315       GetImageDataIntoVector(Pixel_Data, ImageDataSize);
316
317       // We say the value *is* loaded.
318       GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
319          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
320
321       // Will be 7fe0, 0010 in standard case
322       GetHeader()->SetEntryBinAreaByNumber( Pixel_Data, 
323          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); 
324    }      
325    PixelRead = 0; // no PixelRaw
326
327    return Pixel_Data;
328 }
329
330 /**
331  * \brief
332  *          Read the pixels from disk (uncompress if necessary),
333  *          Transforms YBR pixels, if any, into RGB pixels
334  *          Transforms 3 planes R, G, B, if any, into a single RGB Plane
335  *          Transforms single Grey plane + 3 Palettes into a RGB Plane   
336  *          Copies at most MaxSize bytes of pixel data to caller allocated
337  *          memory space.
338  * \warning This function allows people that want to build a volume
339  *          from an image stack *not to* have, first to get the image pixels, 
340  *          and then move them to the volume area.
341  *          It's absolutely useless for any VTK user since vtk chooses 
342  *          to invert the lines of an image, that is the last line comes first
343  *          (for some axis related reasons?). Hence he will have 
344  *          to load the image line by line, starting from the end.
345  *          VTK users have to call GetImageData
346  *     
347  * @param   destination Address (in caller's memory space) at which the
348  *          pixel data should be copied
349  * @param   maxSize Maximum number of bytes to be copied. When MaxSize
350  *          is not sufficient to hold the pixel data the copy is not
351  *          executed (i.e. no partial copy).
352  * @return  On success, the number of bytes actually copied. Zero on
353  *          failure e.g. MaxSize is lower than necessary.
354  */
355 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
356 {
357    GetImageDataIntoVectorRaw (destination, maxSize);
358    PixelRead = 0 ; // =0 : no ImageDataRaw 
359    if ( !HeaderInternal->HasLUT() )
360    {
361       return ImageDataSize;
362    }
363                             
364    FILE* fp = HeaderInternal->OpenFile();
365    if ( PixelConverter.BuildRGBImage( fp ) )
366    {
367       memmove( destination,
368                (void*)PixelConverter.GetRGB(),
369                PixelConverter.GetRGBSize() );
370     
371       // now, it's an RGB image
372       // Lets's write it in the Header
373
374       // FIXME : Better use CreateOrReplaceIfExist ?
375
376       std::string spp = "3";        // Samples Per Pixel
377       HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
378       std::string rgb = "RGB ";     // Photometric Interpretation
379       HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
380       std::string planConfig = "0"; // Planar Configuration
381       HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
382
383    }
384    else
385    { 
386       // PixelConverter.BuildRGBImage( fp ) failed probably because
387       // PixelConverter.GetLUTRGBA() failed:
388       // (gdcm-US-ALOKA-16.dcm), contains Segmented xxx Palette Color 
389       // that are *more* than 65535 long ?!? 
390       // No idea how to manage such an image !
391       // Need to make RGB Pixels (?) from grey Pixels (?!) and Gray Lut  (!?!)
392       // It seems that *no Dicom Viewer* has any idea :-(
393         
394       std::string photomInterp = "MONOCHROME1 ";  // Photometric Interpretation
395       HeaderInternal->SetEntryByNumber(photomInterp,0x0028,0x0004);
396    } 
397    HeaderInternal->CloseFile();
398
399    /// \todo Drop Palette Color out of the Header?
400    return ImageDataSize; 
401 }
402
403 /**
404  * \brief   Allocates necessary memory, 
405  *          Transforms YBR pixels (if any) into RGB pixels
406  *          Transforms 3 planes R, G, B  (if any) into a single RGB Plane
407  *          Copies the pixel data (image[s]/volume[s]) to newly allocated zone. 
408  *          DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
409  * @return  Pointer to newly allocated pixel data.
410  * \        NULL if alloc fails 
411  */
412 uint8_t* File::GetImageDataRaw ()
413 {
414    size_t imgDataSize;
415    if ( HeaderInternal->HasLUT() )
416       /// \todo Let Header user a chance to get the right value
417       imgDataSize = ImageDataSizeRaw;
418    else 
419       imgDataSize = ImageDataSize;
420     
421    // FIXME (Mathieu)
422    // I need to deallocate Pixel_Data before doing any allocation:
423    
424    if ( Pixel_Data )
425       if ( LastAllocatedPixelDataLength != imgDataSize )
426          free(Pixel_Data);
427    if ( !Pixel_Data ) 
428       Pixel_Data = new uint8_t[imgDataSize];
429
430    if ( Pixel_Data )
431    {
432       LastAllocatedPixelDataLength = imgDataSize;
433       
434       // we load the pixels ( grey level or RGB, but NO transformation)
435        GetImageDataIntoVectorRaw(Pixel_Data, imgDataSize);
436
437       // We say the value *is* loaded.
438       GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
439          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
440  
441       // will be 7fe0, 0010 in standard cases
442       GetHeader()->SetEntryBinAreaByNumber(Pixel_Data, 
443          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
444    } 
445    PixelRead = 1; // PixelRaw
446
447    return Pixel_Data;
448 }
449
450 /**
451  * \brief   Copies at most MaxSize bytes of pixel data to caller's
452  *          memory space.
453  * \warning This function was designed to avoid people that want to build
454  *          a volume from an image stack to need first to get the image pixels 
455  *          and then move them to the volume area.
456  *          It's absolutely useless for any VTK user since vtk chooses 
457  *          to invert the lines of an image, that is the last line comes first
458  *          (for some axis related reasons?). Hence he will have 
459  *          to load the image line by line, starting from the end.
460  *          VTK users hace to call GetImageData
461  * \warning DOES NOT transform the Grey Plane + Palette Color (if any) 
462  *                   into a single RGB Pixels Plane
463  *          the (VTK) user will manage the palettes
464  *     
465  * @param   destination Address (in caller's memory space) at which the
466  *          pixel data should be copied
467  * @param   maxSize Maximum number of bytes to be copied. When MaxSize
468  *          is not sufficient to hold the pixel data the copy is not
469  *          executed (i.e. no partial copy).
470  * @return  On success, the number of bytes actually copied. Zero on
471  *          failure e.g. MaxSize is lower than necessary.
472  */
473 void File::GetImageDataIntoVectorRaw (void* destination, size_t maxSize)
474 {
475   // we save the initial values of the following
476   // in order to be able to restore the header in a disk-consistent state
477   // (if user asks twice to get the pixels from disk)
478
479    if ( PixelRead != -1 ) // File was "read" before
480    {  
481       RestoreInitialValues(); 
482    }
483    
484    PixelRead = 1 ; // PixelRaw
485     
486    if ( ImageDataSize > maxSize )
487    {
488       dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
489                      "than caller's expected MaxSize");
490       return;
491    }
492
493    FILE* fp = HeaderInternal->OpenFile();
494    PixelConverter.ReadAndDecompressPixelData( fp );
495    HeaderInternal->CloseFile();
496    memmove( destination,
497             (void*)PixelConverter.GetDecompressed(),
498             PixelConverter.GetDecompressedSize() );
499
500    if ( ! PixelConverter.IsDecompressedRGB() )
501    {
502       return;
503    }
504
505 ///////////////////////////////////////////////////
506    // now, it's an RGB image
507    // Lets's write it in the Header
508  
509    // Droping Palette Color out of the Header
510    // has been moved to the Write process.
511
512    // TODO : move 'values' modification to the write process
513    //      : save also (in order to be able to restore)
514    //      : 'high bit' -when not equal to 'bits stored' + 1
515    //      : 'bits allocated', when it's equal to 12 ?!
516
517    std::string spp = "3";            // Samples Per Pixel
518    std::string photInt = "RGB ";     // Photometric Interpretation
519    std::string planConfig = "0";     // Planar Configuration
520      
521    HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
522    HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
523    HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
524  
525    return; 
526 }
527
528 /**
529  * \brief   Points the internal Pixel_Data pointer to the callers inData
530  *          image representation, BUT WITHOUT COPYING THE DATA.
531  *          'image' Pixels are presented as C-like 2D arrays : line per line.
532  *          'volume'Pixels are presented as C-like 3D arrays : plane per plane 
533  * \warning Since the pixels are not copied, it is the caller's responsability
534  *          not to deallocate it's data before gdcm uses them (e.g. with
535  *          the Write() method.
536  * @param inData user supplied pixel area
537  * @param expectedSize total image size, in Bytes
538  *
539  * @return boolean
540  */
541 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
542 {
543    HeaderInternal->SetImageDataSize( expectedSize );
544 // FIXME : if already allocated, memory leak !
545    Pixel_Data     = inData;
546    ImageDataSize = ImageDataSizeRaw = expectedSize;
547    PixelRead     = 1;
548 // FIXME : 7fe0, 0010 IS NOT set ...
549    return true;
550 }
551
552 /**
553  * \brief Writes on disk A SINGLE Dicom file
554  *        NO test is performed on  processor "Endiannity".
555  *        It's up to the user to call his Reader properly
556  * @param fileName name of the file to be created
557  *                 (any already existing file is over written)
558  * @return false if write fails
559  */
560
561 bool File::WriteRawData(std::string const & fileName)
562 {
563    FILE* fp1 = fopen(fileName.c_str(), "wb");
564    if (fp1 == NULL)
565    {
566       printf("Fail to open (write) file [%s] \n", fileName.c_str());
567       return false;
568    }
569    fwrite (Pixel_Data, ImageDataSize, 1, fp1);
570    fclose (fp1);
571
572    return true;
573 }
574
575 /**
576  * \brief Writes on disk A SINGLE Dicom file, 
577  *        using the Implicit Value Representation convention
578  *        NO test is performed on  processor "Endiannity".
579  * @param fileName name of the file to be created
580  *                 (any already existing file is overwritten)
581  * @return false if write fails
582  */
583
584 bool File::WriteDcmImplVR (std::string const & fileName)
585 {
586    return WriteBase(fileName, ImplicitVR);
587 }
588
589 /**
590 * \brief Writes on disk A SINGLE Dicom file, 
591  *        using the Explicit Value Representation convention
592  *        NO test is performed on  processor "Endiannity". * @param fileName name of the file to be created
593  *                 (any already existing file is overwritten)
594  * @return false if write fails
595  */
596
597 bool File::WriteDcmExplVR (std::string const & fileName)
598 {
599    return WriteBase(fileName, ExplicitVR);
600 }
601
602 /**
603  * \brief Writes on disk A SINGLE Dicom file, 
604  *        using the ACR-NEMA convention
605  *        NO test is performed on  processor "Endiannity".
606  *        (a l'attention des logiciels cliniques 
607  *        qui ne prennent en entrĂ©e QUE des images ACR ...
608  * \warning if a DICOM_V3 header is supplied,
609  *         groups < 0x0008 and shadow groups are ignored
610  * \warning NO TEST is performed on processor "Endiannity".
611  * @param fileName name of the file to be created
612  *                 (any already existing file is overwritten)
613  * @return false if write fails
614  */
615
616 bool File::WriteAcr (std::string const & fileName)
617 {
618    return WriteBase(fileName, ACR);
619 }
620
621 //-----------------------------------------------------------------------------
622 // Protected
623 /**
624  * \brief NOT a end user inteded function
625  *        (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
626  * @param fileName name of the file to be created
627  *                 (any already existing file is overwritten)
628  * @param  type file type (ExplicitVR, ImplicitVR, ...)
629  * @return false if write fails
630  */
631 bool File::WriteBase (std::string const & fileName, FileType type)
632 {
633    if ( PixelRead == -1 && type != ExplicitVR)
634    {
635       return false;
636    }
637
638    FILE* fp1 = fopen(fileName.c_str(), "wb");
639    if (fp1 == NULL)
640    {
641       printf("Failed to open (write) File [%s] \n", fileName.c_str());
642       return false;
643    }
644
645    if ( type == ImplicitVR || type == ExplicitVR )
646    {
647       // writing Dicom File Preamble
648       uint8_t* filePreamble = new uint8_t[128];
649       memset(filePreamble, 0, 128);
650       fwrite(filePreamble, 128, 1, fp1);
651       fwrite("DICM", 4, 1, fp1);
652
653       delete[] filePreamble;
654    }
655
656    // --------------------------------------------------------------
657    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
658    //
659    // if recognition code tells us we dealt with a LibIDO image
660    // we reproduce on disk the switch between lineNumber and columnNumber
661    // just before writting ...
662    
663    /// \todo the best trick would be *change* the recognition code
664    ///       but pb expected if user deals with, e.g. COMPLEX images
665
666    std::string rows, columns; 
667    if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
668    {
669       rows    = HeaderInternal->GetEntryByNumber(0x0028, 0x0010);
670       columns = HeaderInternal->GetEntryByNumber(0x0028, 0x0011);
671
672       HeaderInternal->SetEntryByNumber(columns,  0x0028, 0x0010);
673       HeaderInternal->SetEntryByNumber(rows   ,  0x0028, 0x0011);
674    }
675    // ----------------- End of Special Patch ----------------
676       
677    uint16_t grPixel  = HeaderInternal->GetGrPixel();
678    uint16_t numPixel = HeaderInternal->GetNumPixel();;
679           
680    DocEntry* PixelElement = 
681       GetHeader()->GetDocEntryByNumber(grPixel, numPixel);  
682  
683    if ( PixelRead == 1 )
684    {
685       // we read pixel 'as is' (no tranformation LUT -> RGB)
686       PixelElement->SetLength( ImageDataSizeRaw );
687    }
688    else if ( PixelRead == 0 )
689    {
690       // we tranformed GrayLevel pixels + LUT into RGB Pixel
691       PixelElement->SetLength( ImageDataSize );
692    }
693  
694    HeaderInternal->Write(fp1, type);
695
696    // --------------------------------------------------------------
697    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
698    // 
699    // ...and we restore the Header to be Dicom Compliant again 
700    // just after writting
701
702    if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
703    {
704       HeaderInternal->SetEntryByNumber(rows   , 0x0028, 0x0010);
705       HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0011);
706    }
707    // ----------------- End of Special Patch ----------------
708    
709    // fwrite(Pixel_Data, ImageDataSize, 1, fp1);  // should be useless, now
710    fclose (fp1);
711
712    return true;
713 }
714
715 /**
716  * \brief Access to the underlying \ref PixelConvertver RGBA LUT
717  */
718 uint8_t* File::GetLutRGBA()
719 {
720    return PixelConverter.GetLutRGBA();
721 }
722
723 } // end namespace gdcm
724