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