]> Creatis software - gdcm.git/blob - src/gdcmFile.cxx
2cd116775855d125c2ab91b0342d6617c8ab7b91
[gdcm.git] / src / gdcmFile.cxx
1   /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFile.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/10/10 16:44:00 $
7   Version:   $Revision: 1.138 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18
19 #include "gdcmFile.h"
20 #include "gdcmDebug.h"
21 #include "gdcmPixelConvert.h"
22
23 typedef std::pair<TagDocEntryHT::iterator,TagDocEntryHT::iterator> IterHT;
24
25 //-------------------------------------------------------------------------
26 // Constructor / Destructor
27 /**
28  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
29  *        file (gdcmHeader only deals with the ... header)
30  *        Opens (in read only and when possible) an existing file and checks
31  *        for DICOM compliance. Returns NULL on failure.
32  *        It will be up to the user to load the pixels into memory
33  *        (see GetImageData, GetImageDataRaw)
34  * \note  the in-memory representation of all available tags found in
35  *        the DICOM header is post-poned to first header information access.
36  *        This avoid a double parsing of public part of the header when
37  *        user sets an a posteriori shadow dictionary (efficiency can be
38  *        seen as a side effect).   
39  * @param header already built gdcmHeader
40  */
41 gdcmFile::gdcmFile(gdcmHeader *header)
42 {
43    Header     = header;
44    SelfHeader = false;
45    Initialise();
46 }
47
48 /**
49  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
50  *        file (gdcmHeader only deals with the ... header)
51  *        Opens (in read only and when possible) an existing file and checks
52  *        for DICOM compliance. Returns NULL on failure.
53  *        It will be up to the user to load the pixels into memory
54  *        (see GetImageData, GetImageDataRaw)
55  * \note  the in-memory representation of all available tags found in
56  *        the DICOM header is post-poned to first header information access.
57  *        This avoid a double parsing of public part of the header when
58  *        one sets an a posteriori shadow dictionary (efficiency can be
59  *        seen as a side effect).   
60  * @param filename file to be opened for parsing
61  */
62 gdcmFile::gdcmFile(std::string const & filename )
63 {
64    Header = new gdcmHeader( filename );
65    SelfHeader = true;
66    Initialise();
67 }
68
69 /**
70  * \brief Factorization for various forms of constructors.
71  */
72 void gdcmFile::Initialise()
73 {
74    if ( Header->IsReadable() )
75    {
76       ImageDataSizeRaw = ComputeDecompressedPixelDataSizeFromHeader();
77       if ( Header->HasLUT() )
78       {
79          ImageDataSize = 3 * ImageDataSizeRaw;
80       }
81       else
82       {
83          ImageDataSize = ImageDataSizeRaw;
84       }
85    }
86    SaveInitialValues();
87 }
88
89 /**
90  * \brief canonical destructor
91  * \note  If the gdcmHeader was created by the gdcmFile constructor,
92  *        it is destroyed by the gdcmFile
93  */
94 gdcmFile::~gdcmFile()
95
96    if( SelfHeader )
97    {
98       delete Header;
99    }
100    Header = 0;
101
102    DeleteInitialValues();
103 }
104
105 /**
106  * \brief Sets some initial values for the Constructor
107  * \warning not end user intended
108  */
109 void gdcmFile::SaveInitialValues()
110
111
112    PixelRead  = -1; // no ImageData read yet.
113    LastAllocatedPixelDataLength = 0;
114    Pixel_Data = 0;
115
116    InitialSpp = "";     
117    InitialPhotInt = "";
118    InitialPlanConfig = "";
119    InitialBitsAllocated = "";
120    InitialHighBit = "";
121   
122    InitialRedLUTDescr   = 0;
123    InitialGreenLUTDescr = 0;
124    InitialBlueLUTDescr  = 0;
125    InitialRedLUTData    = 0;
126    InitialGreenLUTData  = 0;
127    InitialBlueLUTData   = 0; 
128                 
129    if ( Header->IsReadable() )
130    {
131       // the following values *may* be modified 
132       // by gdcmFile::GetImageDataIntoVectorRaw
133       // we save their initial value.
134       InitialSpp           = Header->GetEntryByNumber(0x0028,0x0002);
135       InitialPhotInt       = Header->GetEntryByNumber(0x0028,0x0004);
136       InitialPlanConfig    = Header->GetEntryByNumber(0x0028,0x0006);
137       
138       InitialBitsAllocated = Header->GetEntryByNumber(0x0028,0x0100);
139       InitialHighBit       = Header->GetEntryByNumber(0x0028,0x0102);
140
141       // the following entries *may* be removed from the H table
142       // (NOT deleted ...) by gdcmFile::GetImageDataIntoVectorRaw  
143       // we keep a pointer on them.
144       InitialRedLUTDescr   = Header->GetDocEntryByNumber(0x0028,0x1101);
145       InitialGreenLUTDescr = Header->GetDocEntryByNumber(0x0028,0x1102);
146       InitialBlueLUTDescr  = Header->GetDocEntryByNumber(0x0028,0x1103);
147
148       InitialRedLUTData    = Header->GetDocEntryByNumber(0x0028,0x1201);
149       InitialGreenLUTData  = Header->GetDocEntryByNumber(0x0028,0x1202);
150       InitialBlueLUTData   = Header->GetDocEntryByNumber(0x0028,0x1203); 
151    }
152 }
153
154 /**
155  * \brief restores some initial values
156  * \warning not end user intended
157  */
158 void gdcmFile::RestoreInitialValues()
159 {   
160    if ( Header->IsReadable() )
161    {      
162       // the following values *may* have been modified 
163       // by gdcmFile::GetImageDataIntoVectorRaw
164       // we restore their initial value.
165       if ( InitialSpp != "")
166          Header->SetEntryByNumber(InitialSpp,0x0028,0x0002);
167       if ( InitialPhotInt != "")
168          Header->SetEntryByNumber(InitialPhotInt,0x0028,0x0004);
169       if ( InitialPlanConfig != "")
170
171          Header->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006);
172       if ( InitialBitsAllocated != "")
173           Header->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100);
174       if ( InitialHighBit != "")
175           Header->SetEntryByNumber(InitialHighBit,0x0028,0x0102);
176                
177       // the following entries *may* be have been removed from the H table
178       // (NOT deleted ...) by gdcmFile::GetImageDataIntoVectorRaw  
179       // we restore them.
180
181       if (InitialRedLUTDescr)
182          Header->AddEntry(InitialRedLUTDescr);
183       if (InitialGreenLUTDescr)
184          Header->AddEntry(InitialGreenLUTDescr);
185       if (InitialBlueLUTDescr)
186          Header->AddEntry(InitialBlueLUTDescr);
187
188       if (InitialRedLUTData)
189          Header->AddEntry(InitialBlueLUTDescr);
190       if (InitialGreenLUTData)
191          Header->AddEntry(InitialGreenLUTData);
192       if (InitialBlueLUTData)
193          Header->AddEntry(InitialBlueLUTData);
194    }
195 }
196
197 /**
198  * \brief delete initial values (il they were saved)
199  *        of InitialLutDescriptors and InitialLutData
200  */
201 void gdcmFile::DeleteInitialValues()
202
203
204 // InitialLutDescriptors and InitialLutData
205 // will have to be deleted if the don't belong any longer
206 // to the Header H table when the header is deleted...
207
208    if ( InitialRedLUTDescr )           
209       delete InitialRedLUTDescr;
210   
211    if ( InitialGreenLUTDescr )
212       delete InitialGreenLUTDescr;
213       
214    if ( InitialBlueLUTDescr )      
215       delete InitialBlueLUTDescr; 
216        
217    if ( InitialRedLUTData )      
218       delete InitialRedLUTData;
219    
220    if ( InitialGreenLUTData != NULL)
221       delete InitialGreenLUTData;
222       
223    if ( InitialBlueLUTData != NULL)      
224       delete InitialBlueLUTData;      
225 }
226
227 //-----------------------------------------------------------------------------
228 // Print
229
230 //-----------------------------------------------------------------------------
231 // Public
232
233 /**
234  * \brief     computes the length (in bytes) we must ALLOCATE to receive the
235  *            image(s) pixels (multiframes taken into account) 
236  * \warning : it is NOT the group 7FE0 length
237  *          (no interest for compressed images).
238  */
239 int gdcmFile::ComputeDecompressedPixelDataSizeFromHeader()
240 {
241    // see PS 3.3-2003 : C.7.6.3.2.1  
242    // 
243    //   MONOCHROME1
244    //   MONOCHROME2
245    //   PALETTE COLOR
246    //   RGB
247    //   HSV  (Retired)
248    //   ARGB (Retired)
249    //   CMYK (Retired)
250    //   YBR_FULL
251    //   YBR_FULL_422 (no LUT, no Palette)
252    //   YBR_PARTIAL_422
253    //   YBR_ICT
254    //   YBR_RCT
255
256    // LUT's
257    // ex : gdcm-US-ALOKA-16.dcm
258    // 0028|1221 [OW]   [Segmented Red Palette Color Lookup Table Data]
259    // 0028|1222 [OW]   [Segmented Green Palette Color Lookup Table Data]  
260    // 0028|1223 [OW]   [Segmented Blue Palette Color Lookup Table Data]
261
262    // ex  : OT-PAL-8-face.dcm
263    // 0028|1201 [US]   [Red Palette Color Lookup Table Data]
264    // 0028|1202 [US]   [Green Palette Color Lookup Table Data]
265    // 0028|1203 [US]   [Blue Palette Color Lookup Table Data]
266
267    // Number of "Bits Allocated"
268    int numberBitsAllocated = Header->GetBitsAllocated();
269    if ( ( numberBitsAllocated == 0 ) || ( numberBitsAllocated == 12 ) )
270    {
271       numberBitsAllocated = 16;
272    } 
273
274    int DecompressedSize = Header->GetXSize()
275                         * Header->GetYSize() 
276                         * Header->GetZSize()
277                         * ( numberBitsAllocated / 8 )
278                         * Header->GetSamplesPerPixel();
279    
280    return DecompressedSize;
281 }
282
283 /**
284  * \brief   - Allocates necessary memory, 
285  *          - Reads the pixels from disk (uncompress if necessary),
286  *          - Transforms YBR pixels, if any, into RGB pixels
287  *          - Transforms 3 planes R, G, B, if any, into a single RGB Plane
288  *          - Transforms single Grey plane + 3 Palettes into a RGB Plane
289  *          - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
290  * @return  Pointer to newly allocated pixel data.
291  *          NULL if alloc fails 
292  */
293 uint8_t* gdcmFile::GetImageData()
294 {
295    // FIXME (Mathieu)
296    // I need to deallocate Pixel_Data before doing any allocation:
297    
298    if ( Pixel_Data )
299      if ( LastAllocatedPixelDataLength != ImageDataSize ) 
300         free(Pixel_Data);
301    if ( !Pixel_Data )
302       Pixel_Data = new uint8_t[ImageDataSize];
303     
304    if ( Pixel_Data )
305    {
306       LastAllocatedPixelDataLength = ImageDataSize;
307
308       // we load the pixels (and transform grey level + LUT into RGB)
309       GetImageDataIntoVector(Pixel_Data, ImageDataSize);
310
311       // We say the value *is* loaded.
312       GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
313          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
314
315       // Will be 7fe0, 0010 in standard case
316       GetHeader()->SetEntryBinAreaByNumber( Pixel_Data, 
317          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); 
318    }      
319    PixelRead = 0; // no PixelRaw
320
321    return Pixel_Data;
322 }
323
324 /**
325  * \brief
326  *          Read the pixels from disk (uncompress if necessary),
327  *          Transforms YBR pixels, if any, into RGB pixels
328  *          Transforms 3 planes R, G, B, if any, into a single RGB Plane
329  *          Transforms single Grey plane + 3 Palettes into a RGB Plane   
330  *          Copies at most MaxSize bytes of pixel data to caller allocated
331  *          memory space.
332  * \warning This function allows people that want to build a volume
333  *          from an image stack *not to* have, first to get the image pixels, 
334  *          and then move them to the volume area.
335  *          It's absolutely useless for any VTK user since vtk chooses 
336  *          to invert the lines of an image, that is the last line comes first
337  *          (for some axis related reasons?). Hence he will have 
338  *          to load the image line by line, starting from the end.
339  *          VTK users have to call GetImageData
340  *     
341  * @param   destination Address (in caller's memory space) at which the
342  *          pixel data should be copied
343  * @param   maxSize Maximum number of bytes to be copied. When MaxSize
344  *          is not sufficient to hold the pixel data the copy is not
345  *          executed (i.e. no partial copy).
346  * @return  On success, the number of bytes actually copied. Zero on
347  *          failure e.g. MaxSize is lower than necessary.
348  */
349 size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t maxSize)
350 {
351    GetImageDataIntoVectorRaw (destination, maxSize);
352    PixelRead = 0 ; // =0 : no ImageDataRaw 
353    if ( !Header->HasLUT() )
354    {
355       return ImageDataSize;
356    }
357                             
358    // from Lut R + Lut G + Lut B
359    uint8_t *newDest = new uint8_t[ImageDataSize];
360    uint8_t *a       = (uint8_t *)destination;
361    uint8_t *lutRGBA = Header->GetLUTRGBA();
362
363    if ( lutRGBA )
364    {
365       int j;
366       // move Gray pixels to temp area  
367       memmove(newDest, destination, ImageDataSizeRaw);
368       for (size_t i=0; i<ImageDataSizeRaw; ++i) 
369       {
370          // Build RGB Pixels
371          j    = newDest[i]*4;
372          *a++ = lutRGBA[j];
373          *a++ = lutRGBA[j+1];
374          *a++ = lutRGBA[j+2];
375       }
376       delete[] newDest;
377     
378    // now, it's an RGB image
379    // Lets's write it in the Header
380
381    // FIXME : Better use CreateOrReplaceIfExist ?
382
383    std::string spp = "3";        // Samples Per Pixel
384    Header->SetEntryByNumber(spp,0x0028,0x0002);
385    std::string rgb = "RGB ";     // Photometric Interpretation
386    Header->SetEntryByNumber(rgb,0x0028,0x0004);
387    std::string planConfig = "0"; // Planar Configuration
388    Header->SetEntryByNumber(planConfig,0x0028,0x0006);
389
390    }
391    else  // GetLUTRGBA() failed
392    { 
393       // (gdcm-US-ALOKA-16.dcm), contains Segmented xxx Palette Color 
394       // that are *more* than 65535 long ?!? 
395       // No idea how to manage such an image !
396       // Need to make RGB Pixels (?) from grey Pixels (?!) and Gray Lut  (!?!)
397       // It seems that *no Dicom Viewer* has any idea :-(
398         
399       std::string photomInterp = "MONOCHROME1 ";  // Photometric Interpretation
400       Header->SetEntryByNumber(photomInterp,0x0028,0x0004);
401    } 
402
403    /// \todo Drop Palette Color out of the Header?
404    return ImageDataSize; 
405 }
406
407 /**
408  * \brief   Allocates necessary memory, 
409  *          Transforms YBR pixels (if any) into RGB pixels
410  *          Transforms 3 planes R, G, B  (if any) into a single RGB Plane
411  *          Copies the pixel data (image[s]/volume[s]) to newly allocated zone. 
412  *          DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
413  * @return  Pointer to newly allocated pixel data.
414  * \        NULL if alloc fails 
415  */
416 uint8_t* gdcmFile::GetImageDataRaw ()
417 {
418    size_t imgDataSize;
419    if ( Header->HasLUT() )
420       /// \todo Let gdcmHeader user a chance to get the right value
421       imgDataSize = ImageDataSizeRaw;
422    else 
423       imgDataSize = ImageDataSize;
424     
425    // FIXME (Mathieu)
426    // I need to deallocate Pixel_Data before doing any allocation:
427    
428    if ( Pixel_Data )
429       if ( LastAllocatedPixelDataLength != imgDataSize )
430          free(Pixel_Data);
431    if ( !Pixel_Data ) 
432       Pixel_Data = new uint8_t[imgDataSize];
433
434    if ( Pixel_Data )
435    {
436       LastAllocatedPixelDataLength = imgDataSize;
437       
438       // we load the pixels ( grey level or RGB, but NO transformation)
439        GetImageDataIntoVectorRaw(Pixel_Data, imgDataSize);
440
441       // We say the value *is* loaded.
442       GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
443          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
444  
445       // will be 7fe0, 0010 in standard cases
446       GetHeader()->SetEntryBinAreaByNumber(Pixel_Data, 
447          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
448    } 
449    PixelRead = 1; // PixelRaw
450
451    return Pixel_Data;
452 }
453
454 /**
455  * \brief   Copies at most MaxSize bytes of pixel data to caller's
456  *          memory space.
457  * \warning This function was designed to avoid people that want to build
458  *          a volume from an image stack to need first to get the image pixels 
459  *          and then move them to the volume area.
460  *          It's absolutely useless for any VTK user since vtk chooses 
461  *          to invert the lines of an image, that is the last line comes first
462  *          (for some axis related reasons?). Hence he will have 
463  *          to load the image line by line, starting from the end.
464  *          VTK users hace to call GetImageData
465  * \warning DOES NOT transform the Grey Plane + Palette Color (if any) 
466  *                   into a single RGB Pixels Plane
467  *          the (VTK) user will manage the palettes
468  *     
469  * @param   destination Address (in caller's memory space) at which the
470  *          pixel data should be copied
471  * @param   maxSize Maximum number of bytes to be copied. When MaxSize
472  *          is not sufficient to hold the pixel data the copy is not
473  *          executed (i.e. no partial copy).
474  * @return  On success, the number of bytes actually copied. Zero on
475  *          failure e.g. MaxSize is lower than necessary.
476  */
477 size_t gdcmFile::GetImageDataIntoVectorRaw (void* destination, size_t maxSize)
478 {
479   // we save the initial values of the following
480   // in order to be able to restore the header in a disk-consistent state
481   // (if user asks twice to get the pixels from disk)
482
483    if ( PixelRead != -1 ) // File was "read" before
484    {  
485       RestoreInitialValues(); 
486    }
487    
488    PixelRead = 1 ; // PixelRaw
489     
490    if ( ImageDataSize > maxSize )
491    {
492       dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: pixel data bigger"
493                      "than caller's expected MaxSize");
494       return (size_t)0;
495    }
496
497    ReadPixelData( destination );
498
499    // Number of Bits Allocated for storing a Pixel
500    int numberBitsAllocated = Header->GetBitsAllocated();
501    if ( numberBitsAllocated == 0 )
502    {
503       numberBitsAllocated = 16;
504    }
505
506    // Number of Bits actually used
507    int numberBitsStored = Header->GetBitsStored();
508    if ( numberBitsStored == 0 )
509    {
510       numberBitsStored = numberBitsAllocated;
511    }
512
513    // High Bit Position
514    int highBitPosition = Header->GetHighBitPosition();
515    if ( highBitPosition == 0 )
516    {
517       highBitPosition = numberBitsAllocated - 1;
518    }
519
520    bool signedPixel = Header->IsSignedPixelData();
521
522    gdcmPixelConvert::ConvertReorderEndianity(
523                          (uint8_t*) destination,
524                          ImageDataSize,
525                          numberBitsStored,
526                          numberBitsAllocated,
527                          Header->GetSwapCode(),
528                          signedPixel );
529
530    gdcmPixelConvert::ConvertReArrangeBits(
531                          (uint8_t*) destination,
532                          ImageDataSize,
533                          numberBitsStored,
534                          numberBitsAllocated,
535                          highBitPosition );
536
537 #ifdef GDCM_DEBUG
538    FILE*  DebugFile;
539    DebugFile = fopen( "SpuriousFile.RAW", "wb" );
540    fwrite( PixelConvertor.GetUncompressed(),
541            PixelConvertor.GetUncompressedsSize(),
542            1, DebugFile );
543    fclose( DebugFile );
544 #endif //GDCM_DEBUG
545
546 // SPLIT ME
547 //////////////////////////////////
548 // Deal with the color
549    
550    // Monochrome pictures don't require color intervention
551    if ( Header->IsMonochrome() )
552    {
553       return ImageDataSize; 
554    }
555       
556    // Planar configuration = 0 : Pixels are already RGB
557    // Planar configuration = 1 : 3 planes : R, G, B
558    // Planar configuration = 2 : 1 gray Plane + 3 LUT
559
560    // Well ... supposed to be !
561    // See US-PAL-8-10x-echo.dcm: PlanarConfiguration=0,
562    //                            PhotometricInterpretation=PALETTE COLOR
563    // and heuristic has to be found :-( 
564
565    int planConf = Header->GetPlanarConfiguration();
566
567    // Planar configuration = 2 ==> 1 gray Plane + 3 LUT
568    //   ...and...
569    // whatever the Planar Configuration might be, "PALETTE COLOR "
570    // implies that we deal with the palette. 
571    if ( ( planConf == 2 ) || Header->IsPaletteColor() )
572    {
573       return ImageDataSize;
574    }
575
576    // When planConf is 0, pixels are allready in RGB
577
578    if ( planConf == 1 )
579    {
580       uint8_t* newDest = new uint8_t[ImageDataSize];
581       // Warning : YBR_FULL_422 acts as RGB
582       if ( Header->IsYBRFull() )
583       {
584          ConvertYcBcRPlanesToRGBPixels((uint8_t*)destination, newDest);
585       }
586       else
587       {
588          ConvertRGBPlanesToRGBPixels((uint8_t*)destination, newDest);
589       }
590       memmove(destination, newDest, ImageDataSize);
591       delete[] newDest;
592    }
593
594 ///////////////////////////////////////////////////
595    // now, it's an RGB image
596    // Lets's write it in the Header
597  
598    // Droping Palette Color out of the Header
599    // has been moved to the Write process.
600
601    // TODO : move 'values' modification to the write process
602    //      : save also (in order to be able to restore)
603    //      : 'high bit' -when not equal to 'bits stored' + 1
604    //      : 'bits allocated', when it's equal to 12 ?!
605
606    std::string spp = "3";            // Samples Per Pixel
607    std::string photInt = "RGB ";     // Photometric Interpretation
608    std::string planConfig = "0";     // Planar Configuration
609      
610    Header->SetEntryByNumber(spp,0x0028,0x0002);
611    Header->SetEntryByNumber(photInt,0x0028,0x0004);
612    Header->SetEntryByNumber(planConfig,0x0028,0x0006);
613  
614    return ImageDataSize; 
615 }
616
617 /**
618  * \brief   Convert (Y plane, cB plane, cR plane) to RGB pixels
619  * \warning Works on all the frames at a time
620  */
621 void gdcmFile::ConvertYcBcRPlanesToRGBPixels(uint8_t* source,
622                                              uint8_t* destination)
623 {
624    // to see the tricks about YBR_FULL, YBR_FULL_422, 
625    // YBR_PARTIAL_422, YBR_ICT, YBR_RCT have a look at :
626    // ftp://medical.nema.org/medical/dicom/final/sup61_ft.pdf
627    // and be *very* affraid
628    //
629    int l        = Header->GetXSize() * Header->GetYSize();
630    int nbFrames = Header->GetZSize();
631
632    uint8_t* a = source;
633    uint8_t* b = source + l;
634    uint8_t* c = source + l + l;
635    double R, G, B;
636
637    /// \todo : Replace by the 'well known' integer computation
638    ///         counterpart. Refer to
639    ///            http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
640    ///         for code optimisation.
641  
642    for (int i = 0; i < nbFrames; i++)
643    {
644       for (int j = 0; j < l; j++)
645       {
646          R = 1.164 *(*a-16) + 1.596 *(*c -128) + 0.5;
647          G = 1.164 *(*a-16) - 0.813 *(*c -128) - 0.392 *(*b -128) + 0.5;
648          B = 1.164 *(*a-16) + 2.017 *(*b -128) + 0.5;
649
650          if (R < 0.0)   R = 0.0;
651          if (G < 0.0)   G = 0.0;
652          if (B < 0.0)   B = 0.0;
653          if (R > 255.0) R = 255.0;
654          if (G > 255.0) G = 255.0;
655          if (B > 255.0) B = 255.0;
656
657          *(destination++) = (uint8_t)R;
658          *(destination++) = (uint8_t)G;
659          *(destination++) = (uint8_t)B;
660          a++;
661          b++;
662          c++;  
663       }
664    }
665 }
666
667 /**
668  * \brief   Convert (Red plane, Green plane, Blue plane) to RGB pixels
669  * \warning Works on all the frames at a time
670  */
671 void gdcmFile::ConvertRGBPlanesToRGBPixels(uint8_t* source,
672                                            uint8_t* destination)
673 {
674    int l = Header->GetXSize() * Header->GetYSize() * Header->GetZSize();
675
676    uint8_t* a = source;
677    uint8_t* b = source + l;
678    uint8_t* c = source + l + l;
679
680    for (int j = 0; j < l; j++)
681    {
682       *(destination++) = *(a++);
683       *(destination++) = *(b++);
684       *(destination++) = *(c++);
685    }
686 }
687
688 /**
689  * \brief   Points the internal Pixel_Data pointer to the callers inData
690  *          image representation, BUT WITHOUT COPYING THE DATA.
691  *          'image' Pixels are presented as C-like 2D arrays : line per line.
692  *          'volume'Pixels are presented as C-like 3D arrays : plane per plane 
693  * \warning Since the pixels are not copied, it is the caller's responsability
694  *          not to deallocate it's data before gdcm uses them (e.g. with
695  *          the Write() method.
696  * @param inData user supplied pixel area
697  * @param expectedSize total image size, in Bytes
698  *
699  * @return boolean
700  */
701 bool gdcmFile::SetImageData(uint8_t* inData, size_t expectedSize)
702 {
703    Header->SetImageDataSize( expectedSize );
704 // FIXME : if already allocated, memory leak !
705    Pixel_Data     = inData;
706    ImageDataSize = ImageDataSizeRaw = expectedSize;
707    PixelRead     = 1;
708 // FIXME : 7fe0, 0010 IS NOT set ...
709    return true;
710 }
711
712 /**
713  * \brief Writes on disk A SINGLE Dicom file
714  *        NO test is performed on  processor "Endiannity".
715  *        It's up to the user to call his Reader properly
716  * @param fileName name of the file to be created
717  *                 (any already existing file is over written)
718  * @return false if write fails
719  */
720
721 bool gdcmFile::WriteRawData(std::string const & fileName)
722 {
723    FILE* fp1 = fopen(fileName.c_str(), "wb");
724    if (fp1 == NULL)
725    {
726       printf("Fail to open (write) file [%s] \n", fileName.c_str());
727       return false;
728    }
729    fwrite (Pixel_Data, ImageDataSize, 1, fp1);
730    fclose (fp1);
731
732    return true;
733 }
734
735 /**
736  * \brief Writes on disk A SINGLE Dicom file, 
737  *        using the Implicit Value Representation convention
738  *        NO test is performed on  processor "Endiannity".
739  * @param fileName name of the file to be created
740  *                 (any already existing file is overwritten)
741  * @return false if write fails
742  */
743
744 bool gdcmFile::WriteDcmImplVR (std::string const & fileName)
745 {
746    return WriteBase(fileName, gdcmImplicitVR);
747 }
748
749 /**
750 * \brief Writes on disk A SINGLE Dicom file, 
751  *        using the Explicit Value Representation convention
752  *        NO test is performed on  processor "Endiannity". * @param fileName name of the file to be created
753  *                 (any already existing file is overwritten)
754  * @return false if write fails
755  */
756
757 bool gdcmFile::WriteDcmExplVR (std::string const & fileName)
758 {
759    return WriteBase(fileName, gdcmExplicitVR);
760 }
761
762 /**
763  * \brief Writes on disk A SINGLE Dicom file, 
764  *        using the ACR-NEMA convention
765  *        NO test is performed on  processor "Endiannity".
766  *        (a l'attention des logiciels cliniques 
767  *        qui ne prennent en entrĂ©e QUE des images ACR ...
768  * \warning if a DICOM_V3 header is supplied,
769  *         groups < 0x0008 and shadow groups are ignored
770  * \warning NO TEST is performed on processor "Endiannity".
771  * @param fileName name of the file to be created
772  *                 (any already existing file is overwritten)
773  * @return false if write fails
774  */
775
776 bool gdcmFile::WriteAcr (std::string const & fileName)
777 {
778    return WriteBase(fileName, gdcmACR);
779 }
780
781 //-----------------------------------------------------------------------------
782 // Protected
783 /**
784  * \brief NOT a end user inteded function
785  *        (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
786  * @param fileName name of the file to be created
787  *                 (any already existing file is overwritten)
788  * @param  type file type (ExplicitVR, ImplicitVR, ...)
789  * @return false if write fails
790  */
791 bool gdcmFile::WriteBase (std::string const & fileName, FileType type)
792 {
793    if ( PixelRead == -1 && type != gdcmExplicitVR)
794    {
795       return false;
796    }
797
798    FILE* fp1 = fopen(fileName.c_str(), "wb");
799    if (fp1 == NULL)
800    {
801       printf("Failed to open (write) File [%s] \n", fileName.c_str());
802       return false;
803    }
804
805    if ( type == gdcmImplicitVR || type == gdcmExplicitVR )
806    {
807       // writing Dicom File Preamble
808       uint8_t* filePreamble = new uint8_t[128];
809       memset(filePreamble, 0, 128);
810       fwrite(filePreamble, 128, 1, fp1);
811       fwrite("DICM", 4, 1, fp1);
812
813       delete[] filePreamble;
814    }
815
816    // --------------------------------------------------------------
817    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
818    //
819    // if recognition code tells us we dealt with a LibIDO image
820    // we reproduce on disk the switch between lineNumber and columnNumber
821    // just before writting ...
822    
823    /// \todo the best trick would be *change* the recognition code
824    ///       but pb expected if user deals with, e.g. COMPLEX images
825
826    std::string rows, columns; 
827    if ( Header->GetFileType() == gdcmACR_LIBIDO)
828    {
829       rows    = Header->GetEntryByNumber(0x0028, 0x0010);
830       columns = Header->GetEntryByNumber(0x0028, 0x0011);
831
832       Header->SetEntryByNumber(columns,  0x0028, 0x0010);
833       Header->SetEntryByNumber(rows   ,  0x0028, 0x0011);
834    }
835    // ----------------- End of Special Patch ----------------
836       
837    uint16_t grPixel  = Header->GetGrPixel();
838    uint16_t numPixel = Header->GetNumPixel();;
839           
840    gdcmDocEntry* PixelElement = 
841       GetHeader()->GetDocEntryByNumber(grPixel, numPixel);  
842  
843    if ( PixelRead == 1 )
844    {
845       // we read pixel 'as is' (no tranformation LUT -> RGB)
846       PixelElement->SetLength( ImageDataSizeRaw );
847    }
848    else if ( PixelRead == 0 )
849    {
850       // we tranformed GrayLevel pixels + LUT into RGB Pixel
851       PixelElement->SetLength( ImageDataSize );
852    }
853  
854    Header->Write(fp1, type);
855
856    // --------------------------------------------------------------
857    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
858    // 
859    // ...and we restore the Header to be Dicom Compliant again 
860    // just after writting
861
862    if ( Header->GetFileType() == gdcmACR_LIBIDO )
863    {
864       Header->SetEntryByNumber(rows   , 0x0028, 0x0010);
865       Header->SetEntryByNumber(columns, 0x0028, 0x0011);
866    }
867    // ----------------- End of Special Patch ----------------
868    
869    // fwrite(Pixel_Data, ImageDataSize, 1, fp1);  // should be useless, now
870    fclose (fp1);
871
872    return true;
873 }
874
875 //-----------------------------------------------------------------------------
876 // Private
877 /**
878  * \brief   Read pixel data from disk (optionaly decompressing) into the
879  *          caller specified memory location.
880  * @param   destination where the pixel data should be stored.
881  *
882  */
883 bool gdcmFile::ReadPixelData(void* destination) 
884 {
885    FILE* fp = Header->OpenFile();
886
887    if ( !fp )
888    {
889       return false;
890    }
891    if ( fseek(fp, Header->GetPixelOffset(), SEEK_SET) == -1 )
892    {
893       Header->CloseFile();
894       return false;
895    }
896
897    if ( Header->GetBitsAllocated() == 12 )
898    {
899       gdcmPixelConvert::ConvertDecompress12BitsTo16Bits(
900                                        (uint8_t*)destination, 
901                                        Header->GetXSize(),
902                                        Header->GetYSize(),
903                                        fp);
904       Header->CloseFile();
905       return true;
906    }
907
908    // ----------------------  Uncompressed File
909    if ( !Header->IsDicomV3()                             ||
910         Header->IsImplicitVRLittleEndianTransferSyntax() ||
911         Header->IsExplicitVRLittleEndianTransferSyntax() ||
912         Header->IsExplicitVRBigEndianTransferSyntax()    ||
913         Header->IsDeflatedExplicitVRLittleEndianTransferSyntax() )
914    {
915       size_t ItemRead = fread(destination, Header->GetPixelAreaLength(), 1, fp);
916       Header->CloseFile();
917       if ( ItemRead != 1 )
918       {
919          return false;
920       }
921       else
922       {
923          return true;
924       }
925    }
926
927    // ---------------------- Run Length Encoding
928    if ( Header->IsRLELossLessTransferSyntax() )
929    {
930       bool res = gdcmPixelConvert::ReadAndDecompressRLEFile(
931                                       destination,
932                                       Header->GetXSize(),
933                                       Header->GetYSize(),
934                                       Header->GetZSize(),
935                                       Header->GetBitsAllocated(),
936                                       &(Header->RLEInfo),
937                                       fp );
938       Header->CloseFile();
939       return res; 
940    }  
941     
942    // --------------- SingleFrame/Multiframe JPEG Lossless/Lossy/2000 
943    int numberBitsAllocated = Header->GetBitsAllocated();
944    if ( ( numberBitsAllocated == 0 ) || ( numberBitsAllocated == 12 ) )
945    {
946       numberBitsAllocated = 16;
947    }
948
949    bool res = gdcmPixelConvert::ReadAndDecompressJPEGFile(
950                                    (uint8_t*)destination,
951                                    Header->GetXSize(),
952                                    Header->GetYSize(),
953                                    Header->GetBitsAllocated(),
954                                    Header->GetBitsStored(),
955                                    Header->GetSamplesPerPixel(),
956                                    Header->GetPixelSize(),
957                                    Header->IsJPEG2000(),
958                                    Header->IsJPEGLossless(),
959                                    &(Header->JPEGInfo),
960                                    fp );
961    Header->CloseFile();
962    return res;
963 }
964