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