]> Creatis software - gdcm.git/blob - src/gdcmFile.cxx
f044feb11eb06bf4ffbd20d45c59ca749f2cb6af
[gdcm.git] / src / gdcmFile.cxx
1   /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFile.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/10/28 03:10:58 $
7   Version:   $Revision: 1.150 $
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    if ( ! GetDecompressed() )
304    {
305       // If the decompression failed nothing can be done.
306       return 0;
307    }
308                                                                                 
309    uint8_t* pixelData;
310    if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
311    {
312       pixelData = PixelConverter->GetRGB();
313    }
314    else
315    {
316       // When no LUT or LUT conversion fails, return the decompressed
317       pixelData = PixelConverter->GetDecompressed();
318    }
319
320 // PIXELCONVERT CLEANME
321    // Restore the header in a disk-consistent state
322    // (if user asks twice to get the pixels from disk)
323    if ( PixelRead != -1 ) // File was "read" before
324    {
325       RestoreInitialValues();
326    }
327    if ( PixelConverter->GetRGB() )
328    {
329       // now, it's an RGB image
330       // Lets's write it in the Header
331       std::string spp = "3";        // Samples Per Pixel
332       HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
333       std::string rgb = "RGB ";     // Photometric Interpretation
334       HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
335       std::string planConfig = "0"; // Planar Configuration
336       HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
337       PixelRead = 0; // no PixelRaw
338    }
339    else
340    {
341       if ( HeaderInternal->HasLUT() )
342       {
343          // The LUT interpretation failed
344          std::string photometricInterpretation = "MONOCHROME1 ";
345          HeaderInternal->SetEntryByNumber( photometricInterpretation,
346                                            0x0028, 0x0004 );
347          PixelRead = 0; // no PixelRaw
348       }
349       else
350       {
351          if ( PixelConverter->IsDecompressedRGB() )
352          {
353             ///////////////////////////////////////////////////
354             // now, it's an RGB image
355             // Lets's write it in the Header
356             // Droping Palette Color out of the Header
357             // has been moved to the Write process.
358             // TODO : move 'values' modification to the write process
359             //      : save also (in order to be able to restore)
360             //      : 'high bit' -when not equal to 'bits stored' + 1
361             //      : 'bits allocated', when it's equal to 12 ?!
362             std::string spp = "3";            // Samples Per Pixel
363             std::string photInt = "RGB ";     // Photometric Interpretation
364             std::string planConfig = "0";     // Planar Configuration
365             HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
366             HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
367             HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
368          }
369          PixelRead = 1; // PixelRaw
370       } 
371    }
372
373    // We say the value *is* loaded.
374    GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
375       GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
376
377    // Will be 7fe0, 0010 in standard case
378    GetHeader()->SetEntryBinAreaByNumber( pixelData, 
379       GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); 
380 // END PIXELCONVERT CLEANME
381
382    return pixelData;
383 }
384
385 /**
386  * \brief
387  *          Read the pixels from disk (uncompress if necessary),
388  *          Transforms YBR pixels, if any, into RGB pixels
389  *          Transforms 3 planes R, G, B, if any, into a single RGB Plane
390  *          Transforms single Grey plane + 3 Palettes into a RGB Plane   
391  *          Copies at most MaxSize bytes of pixel data to caller allocated
392  *          memory space.
393  * \warning This function allows people that want to build a volume
394  *          from an image stack *not to* have, first to get the image pixels, 
395  *          and then move them to the volume area.
396  *          It's absolutely useless for any VTK user since vtk chooses 
397  *          to invert the lines of an image, that is the last line comes first
398  *          (for some axis related reasons?). Hence he will have 
399  *          to load the image line by line, starting from the end.
400  *          VTK users have to call GetImageData
401  *     
402  * @param   destination Address (in caller's memory space) at which the
403  *          pixel data should be copied
404  * @param   maxSize Maximum number of bytes to be copied. When MaxSize
405  *          is not sufficient to hold the pixel data the copy is not
406  *          executed (i.e. no partial copy).
407  * @return  On success, the number of bytes actually copied. Zero on
408  *          failure e.g. MaxSize is lower than necessary.
409  */
410 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
411 {
412    if ( ! GetDecompressed() )
413    {
414       // If the decompression failed nothing can be done.
415       return 0;
416    }
417
418    if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
419    {
420       if ( PixelConverter->GetRGBSize() > maxSize )
421       {
422          dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
423                         "than caller's expected MaxSize");
424          return 0;
425       }
426       memmove( destination,
427                (void*)PixelConverter->GetRGB(),
428                PixelConverter->GetRGBSize() );
429       return PixelConverter->GetRGBSize();
430    }
431
432    // Either no LUT conversion necessary or LUT conversion failed
433    if ( PixelConverter->GetDecompressedSize() > maxSize )
434    {
435       dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
436                      "than caller's expected MaxSize");
437       return 0;
438    }
439    memmove( destination,
440             (void*)PixelConverter->GetDecompressed(),
441             PixelConverter->GetDecompressedSize() );
442    return PixelConverter->GetDecompressedSize();
443 }
444
445 /**
446  * \brief   Allocates necessary memory, 
447  *          Transforms YBR pixels (if any) into RGB pixels
448  *          Transforms 3 planes R, G, B  (if any) into a single RGB Plane
449  *          Copies the pixel data (image[s]/volume[s]) to newly allocated zone. 
450  *          DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
451  * @return  Pointer to newly allocated pixel data.
452  * \        NULL if alloc fails 
453  */
454 uint8_t* File::GetImageDataRaw ()
455 {
456    uint8_t* decompressed = GetDecompressed();
457    if ( ! decompressed )
458    {
459       return 0;
460    }
461
462 // PIXELCONVERT CLEANME
463    // Restore the header in a disk-consistent state
464    // (if user asks twice to get the pixels from disk)
465    if ( PixelRead != -1 ) // File was "read" before
466    {
467       RestoreInitialValues();
468    }
469    if ( PixelConverter->IsDecompressedRGB() )
470    {
471       ///////////////////////////////////////////////////
472       // now, it's an RGB image
473       // Lets's write it in the Header
474       // Droping Palette Color out of the Header
475       // has been moved to the Write process.
476       // TODO : move 'values' modification to the write process
477       //      : save also (in order to be able to restore)
478       //      : 'high bit' -when not equal to 'bits stored' + 1
479       //      : 'bits allocated', when it's equal to 12 ?!
480       std::string spp = "3";            // Samples Per Pixel
481       std::string photInt = "RGB ";     // Photometric Interpretation
482       std::string planConfig = "0";     // Planar Configuration
483       HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
484       HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
485       HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
486    }
487
488    // We say the value *is* loaded.
489    GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
490    GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
491  
492    // will be 7fe0, 0010 in standard cases
493    GetHeader()->SetEntryBinAreaByNumber( decompressed,
494    GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
495  
496    PixelRead = 1; // PixelRaw
497 // END PIXELCONVERT CLEANME
498
499    return decompressed;
500 }
501
502 uint8_t* File::GetDecompressed()
503 {
504    uint8_t* decompressed = PixelConverter->GetDecompressed();
505    if ( ! decompressed )
506    {
507       // The decompressed image migth not be loaded yet:
508       std::ifstream* fp = HeaderInternal->OpenFile();
509       PixelConverter->ReadAndDecompressPixelData( fp );
510       HeaderInternal->CloseFile();
511       decompressed = PixelConverter->GetDecompressed();
512       if ( ! decompressed )
513       {
514         dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
515                        "pixel data apparently went wrong.");
516          return 0;
517       }
518    }
519
520    return decompressed;
521 }
522
523 /**
524  * \brief   Points the internal Pixel_Data pointer to the callers inData
525  *          image representation, BUT WITHOUT COPYING THE DATA.
526  *          'image' Pixels are presented as C-like 2D arrays : line per line.
527  *          'volume'Pixels are presented as C-like 3D arrays : plane per plane 
528  * \warning Since the pixels are not copied, it is the caller's responsability
529  *          not to deallocate it's data before gdcm uses them (e.g. with
530  *          the Write() method.
531  * @param inData user supplied pixel area
532  * @param expectedSize total image size, in Bytes
533  *
534  * @return boolean
535  */
536 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
537 {
538    HeaderInternal->SetImageDataSize( expectedSize );
539 // FIXME : if already allocated, memory leak !
540    Pixel_Data     = inData;
541    ImageDataSize = ImageDataSizeRaw = expectedSize;
542    PixelRead     = 1;
543 // FIXME : 7fe0, 0010 IS NOT set ...
544    return true;
545 }
546
547 /**
548  * \brief Writes on disk A SINGLE Dicom file
549  *        NO test is performed on  processor "Endiannity".
550  *        It's up to the user to call his Reader properly
551  * @param fileName name of the file to be created
552  *                 (any already existing file is over written)
553  * @return false if write fails
554  */
555
556 bool File::WriteRawData(std::string const & fileName)
557 {
558   std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
559    if (!fp1)
560    {
561       printf("Fail to open (write) file [%s] \n", fileName.c_str());
562       return false;
563    }
564    fp1.write((char*)Pixel_Data, ImageDataSize);
565    fp1.close();
566
567    return true;
568 }
569
570 /**
571  * \brief Writes on disk A SINGLE Dicom file, 
572  *        using the Implicit Value Representation convention
573  *        NO test is performed on  processor "Endiannity".
574  * @param fileName name of the file to be created
575  *                 (any already existing file is overwritten)
576  * @return false if write fails
577  */
578
579 bool File::WriteDcmImplVR (std::string const & fileName)
580 {
581    return WriteBase(fileName, ImplicitVR);
582 }
583
584 /**
585 * \brief Writes on disk A SINGLE Dicom file, 
586  *        using the Explicit Value Representation convention
587  *        NO test is performed on  processor "Endiannity". * @param fileName name of the file to be created
588  *                 (any already existing file is overwritten)
589  * @return false if write fails
590  */
591
592 bool File::WriteDcmExplVR (std::string const & fileName)
593 {
594    return WriteBase(fileName, ExplicitVR);
595 }
596
597 /**
598  * \brief Writes on disk A SINGLE Dicom file, 
599  *        using the ACR-NEMA convention
600  *        NO test is performed on  processor "Endiannity".
601  *        (a l'attention des logiciels cliniques 
602  *        qui ne prennent en entrĂ©e QUE des images ACR ...
603  * \warning if a DICOM_V3 header is supplied,
604  *         groups < 0x0008 and shadow groups are ignored
605  * \warning NO TEST is performed on processor "Endiannity".
606  * @param fileName name of the file to be created
607  *                 (any already existing file is overwritten)
608  * @return false if write fails
609  */
610
611 bool File::WriteAcr (std::string const & fileName)
612 {
613    return WriteBase(fileName, ACR);
614 }
615
616 //-----------------------------------------------------------------------------
617 // Protected
618 /**
619  * \brief NOT a end user inteded function
620  *        (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
621  * @param fileName name of the file to be created
622  *                 (any already existing file is overwritten)
623  * @param  type file type (ExplicitVR, ImplicitVR, ...)
624  * @return false if write fails
625  */
626 bool File::WriteBase (std::string const & fileName, FileType type)
627 {
628    if ( PixelRead == -1 && type != ExplicitVR)
629    {
630       return false;
631    }
632
633    std::ofstream* fp1 = new std::ofstream(fileName.c_str(), 
634                               std::ios::out | std::ios::binary);
635    if (fp1 == NULL)
636    {
637       printf("Failed to open (write) File [%s] \n", fileName.c_str());
638       return false;
639    }
640
641    if ( type == ImplicitVR || type == ExplicitVR )
642    {
643       // writing Dicom File Preamble
644       uint8_t filePreamble[128];
645       memset(filePreamble, 0, 128);
646       fp1->write((char*)filePreamble, 128);
647       fp1->write("DICM", 4);
648    }
649
650    // --------------------------------------------------------------
651    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
652    //
653    // if recognition code tells us we dealt with a LibIDO image
654    // we reproduce on disk the switch between lineNumber and columnNumber
655    // just before writting ...
656    
657    /// \todo the best trick would be *change* the recognition code
658    ///       but pb expected if user deals with, e.g. COMPLEX images
659
660    std::string rows, columns; 
661    if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
662    {
663       rows    = HeaderInternal->GetEntryByNumber(0x0028, 0x0010);
664       columns = HeaderInternal->GetEntryByNumber(0x0028, 0x0011);
665
666       HeaderInternal->SetEntryByNumber(columns,  0x0028, 0x0010);
667       HeaderInternal->SetEntryByNumber(rows   ,  0x0028, 0x0011);
668    }
669    // ----------------- End of Special Patch ----------------
670       
671    uint16_t grPixel  = HeaderInternal->GetGrPixel();
672    uint16_t numPixel = HeaderInternal->GetNumPixel();;
673           
674    DocEntry* PixelElement = 
675       GetHeader()->GetDocEntryByNumber(grPixel, numPixel);  
676  
677    if ( PixelRead == 1 )
678    {
679       // we read pixel 'as is' (no tranformation LUT -> RGB)
680       PixelElement->SetLength( ImageDataSizeRaw );
681    }
682    else if ( PixelRead == 0 )
683    {
684       // we tranformed GrayLevel pixels + LUT into RGB Pixel
685       PixelElement->SetLength( ImageDataSize );
686    }
687  
688    HeaderInternal->Write(fp1, type);
689
690    // --------------------------------------------------------------
691    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
692    // 
693    // ...and we restore the Header to be Dicom Compliant again 
694    // just after writting
695
696    if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
697    {
698       HeaderInternal->SetEntryByNumber(rows   , 0x0028, 0x0010);
699       HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0011);
700    }
701    // ----------------- End of Special Patch ----------------
702    fp1->close ();
703
704    return true;
705 }
706
707 /**
708  * \brief Access to the underlying \ref PixelConverter RGBA LUT
709  */
710 uint8_t* File::GetLutRGBA()
711 {
712    return PixelConverter->GetLutRGBA();
713 }
714
715 } // end namespace gdcm
716