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