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