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