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