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