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