]> Creatis software - gdcm.git/blob - src/gdcmFile.cxx
* CLEANUP_ROUND (7) for gdcmPixelConvert (lost at sea)
[gdcm.git] / src / gdcmFile.cxx
1   /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFile.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/10/01 12:40:57 $
7   Version:   $Revision: 1.134 $
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 "jpeg/ljpg/jpegless.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    ConvertReorderEndianity( (uint8_t*) destination,
523                             ImageDataSize,
524                             numberBitsStored,
525                             numberBitsAllocated,
526                             signedPixel );
527
528    ConvertReArrangeBits( (uint8_t*) destination,
529                          ImageDataSize,
530                          numberBitsStored,
531                          numberBitsAllocated,
532                          highBitPosition );
533
534 #ifdef GDCM_DEBUG
535    FILE*  DebugFile;
536    DebugFile = fopen( "SpuriousFile.RAW", "wb" );
537    fwrite( PixelConvertor.GetUncompressed(),
538            PixelConvertor.GetUncompressedsSize(),
539            1, DebugFile );
540    fclose( DebugFile );
541 #endif //GDCM_DEBUG
542
543 // SPLIT ME
544 //////////////////////////////////
545 // Deal with the color
546    
547    // Monochrome pictures don't require color intervention
548    if ( Header->IsMonochrome() )
549    {
550       return ImageDataSize; 
551    }
552       
553    // Planar configuration = 0 : Pixels are already RGB
554    // Planar configuration = 1 : 3 planes : R, G, B
555    // Planar configuration = 2 : 1 gray Plane + 3 LUT
556
557    // Well ... supposed to be !
558    // See US-PAL-8-10x-echo.dcm: PlanarConfiguration=0,
559    //                            PhotometricInterpretation=PALETTE COLOR
560    // and heuristic has to be found :-( 
561
562    int planConf = Header->GetPlanarConfiguration();
563
564    // Planar configuration = 2 ==> 1 gray Plane + 3 LUT
565    //   ...and...
566    // whatever the Planar Configuration might be, "PALETTE COLOR "
567    // implies that we deal with the palette. 
568    if ( ( planConf == 2 ) || Header->IsPaletteColor() )
569    {
570       return ImageDataSize;
571    }
572
573    // When planConf is 0, pixels are allready in RGB
574
575    if ( planConf == 1 )
576    {
577       uint8_t* newDest = new uint8_t[ImageDataSize];
578       // Warning : YBR_FULL_422 acts as RGB
579       if ( Header->IsYBRFull() )
580       {
581          ConvertYcBcRPlanesToRGBPixels((uint8_t*)destination, newDest);
582       }
583       else
584       {
585          ConvertRGBPlanesToRGBPixels((uint8_t*)destination, newDest);
586       }
587       memmove(destination, newDest, ImageDataSize);
588       delete[] newDest;
589    }
590
591 ///////////////////////////////////////////////////
592    // now, it's an RGB image
593    // Lets's write it in the Header
594  
595    // Droping Palette Color out of the Header
596    // has been moved to the Write process.
597
598    // TODO : move 'values' modification to the write process
599    //      : save also (in order to be able to restore)
600    //      : 'high bit' -when not equal to 'bits stored' + 1
601    //      : 'bits allocated', when it's equal to 12 ?!
602
603    std::string spp = "3";            // Samples Per Pixel
604    std::string photInt = "RGB ";     // Photometric Interpretation
605    std::string planConfig = "0";     // Planar Configuration
606      
607    Header->SetEntryByNumber(spp,0x0028,0x0002);
608    Header->SetEntryByNumber(photInt,0x0028,0x0004);
609    Header->SetEntryByNumber(planConfig,0x0028,0x0006);
610  
611    return ImageDataSize; 
612 }
613
614 /**
615  * \brief   Re-arrange the bits within the bytes.
616  */
617 void gdcmFile::ConvertReArrangeBits( uint8_t* pixelZone,
618                                      size_t imageDataSize,
619                                      int numberBitsStored,
620                                      int numberBitsAllocated,
621                                      int highBitPosition)
622      throw ( gdcmFormatError )
623 {
624    if ( numberBitsStored != numberBitsAllocated )
625    {
626       int l = (int)(imageDataSize / (numberBitsAllocated/8));
627       if ( numberBitsAllocated == 16 )
628       {
629          uint16_t mask = 0xffff;
630          mask = mask >> ( numberBitsAllocated - numberBitsStored );
631          uint16_t* deb = (uint16_t*)pixelZone;
632          for(int i = 0; i<l; i++)
633          {
634             *deb = (*deb >> (numberBitsStored - highBitPosition - 1)) & mask;
635             deb++;
636          }
637       }
638       else if ( numberBitsAllocated == 32 )
639       {
640          uint32_t mask = 0xffffffff;
641          mask = mask >> ( numberBitsAllocated - numberBitsStored );
642          uint32_t* deb = (uint32_t*)pixelZone;
643          for(int i = 0; i<l; i++)
644          {
645             *deb = (*deb >> (numberBitsStored - highBitPosition - 1)) & mask;
646             deb++;
647          }
648       }
649       else
650       {
651          dbg.Verbose(0, "gdcmFile::ConvertReArrangeBits: weird image");
652          throw gdcmFormatError( "gdcmFile::ConvertReArrangeBits()",
653                                 "weird image !?" );
654       }
655    }
656 }
657
658 /**
659  * \brief Deal with endianity i.e. re-arange bytes inside the integer
660  */
661 void gdcmFile::ConvertReorderEndianity( uint8_t* pixelZone,
662                                         size_t imageDataSize,
663                                         int numberBitsStored,
664                                         int numberBitsAllocated,
665                                         bool signedPixel)
666 {
667    if ( numberBitsAllocated != 8 )
668    {
669       SwapZone( pixelZone, Header->GetSwapCode(), ImageDataSize,
670                 numberBitsAllocated );
671    }
672      
673    // Special kludge in order to deal with xmedcon broken images:
674    if (  ( numberBitsAllocated == 16 )
675       && ( numberBitsStored < numberBitsAllocated )
676       && ( ! signedPixel ) )
677    {
678       int l = (int)(ImageDataSize / (numberBitsAllocated/8));
679       uint16_t *deb = (uint16_t *)pixelZone;
680       for(int i = 0; i<l; i++)
681       {
682          if( *deb == 0xffff )
683          {
684            *deb = 0;
685          }
686          deb++;   
687       }
688    }
689 }
690
691 /**
692  * \brief   Convert (Y plane, cB plane, cR plane) to RGB pixels
693  * \warning Works on all the frames at a time
694  */
695 void gdcmFile::ConvertYcBcRPlanesToRGBPixels(uint8_t* source,
696                                              uint8_t* destination)
697 {
698    // to see the tricks about YBR_FULL, YBR_FULL_422, 
699    // YBR_PARTIAL_422, YBR_ICT, YBR_RCT have a look at :
700    // ftp://medical.nema.org/medical/dicom/final/sup61_ft.pdf
701    // and be *very* affraid
702    //
703    int l        = Header->GetXSize() * Header->GetYSize();
704    int nbFrames = Header->GetZSize();
705
706    uint8_t* a = source;
707    uint8_t* b = source + l;
708    uint8_t* c = source + l + l;
709    double R, G, B;
710
711    /// \todo : Replace by the 'well known' integer computation
712    ///         counterpart. Refer to
713    ///            http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
714    ///         for code optimisation.
715  
716    for (int i = 0; i < nbFrames; i++)
717    {
718       for (int j = 0; j < l; j++)
719       {
720          R = 1.164 *(*a-16) + 1.596 *(*c -128) + 0.5;
721          G = 1.164 *(*a-16) - 0.813 *(*c -128) - 0.392 *(*b -128) + 0.5;
722          B = 1.164 *(*a-16) + 2.017 *(*b -128) + 0.5;
723
724          if (R < 0.0)   R = 0.0;
725          if (G < 0.0)   G = 0.0;
726          if (B < 0.0)   B = 0.0;
727          if (R > 255.0) R = 255.0;
728          if (G > 255.0) G = 255.0;
729          if (B > 255.0) B = 255.0;
730
731          *(destination++) = (uint8_t)R;
732          *(destination++) = (uint8_t)G;
733          *(destination++) = (uint8_t)B;
734          a++;
735          b++;
736          c++;  
737       }
738    }
739 }
740
741 /**
742  * \brief   Convert (Red plane, Green plane, Blue plane) to RGB pixels
743  * \warning Works on all the frames at a time
744  */
745 void gdcmFile::ConvertRGBPlanesToRGBPixels(uint8_t* source,
746                                            uint8_t* destination)
747 {
748    int l = Header->GetXSize() * Header->GetYSize() * Header->GetZSize();
749
750    uint8_t* a = source;
751    uint8_t* b = source + l;
752    uint8_t* c = source + l + l;
753
754    for (int j = 0; j < l; j++)
755    {
756       *(destination++) = *(a++);
757       *(destination++) = *(b++);
758       *(destination++) = *(c++);
759    }
760 }
761
762 /**
763  * \brief   Points the internal Pixel_Data pointer to the callers inData
764  *          image representation, BUT WITHOUT COPYING THE DATA.
765  *          'image' Pixels are presented as C-like 2D arrays : line per line.
766  *          'volume'Pixels are presented as C-like 3D arrays : plane per plane 
767  * \warning Since the pixels are not copied, it is the caller's responsability
768  *          not to deallocate it's data before gdcm uses them (e.g. with
769  *          the Write() method.
770  * @param inData user supplied pixel area
771  * @param expectedSize total image size, in Bytes
772  *
773  * @return boolean
774  */
775 bool gdcmFile::SetImageData(uint8_t* inData, size_t expectedSize)
776 {
777    Header->SetImageDataSize( expectedSize );
778 // FIXME : if already allocated, memory leak !
779    Pixel_Data     = inData;
780    ImageDataSize = ImageDataSizeRaw = expectedSize;
781    PixelRead     = 1;
782 // FIXME : 7fe0, 0010 IS NOT set ...
783    return true;
784 }
785
786 /**
787  * \brief Writes on disk A SINGLE Dicom file
788  *        NO test is performed on  processor "Endiannity".
789  *        It's up to the user to call his Reader properly
790  * @param fileName name of the file to be created
791  *                 (any already existing file is over written)
792  * @return false if write fails
793  */
794
795 bool gdcmFile::WriteRawData(std::string const & fileName)
796 {
797    FILE* fp1 = fopen(fileName.c_str(), "wb");
798    if (fp1 == NULL)
799    {
800       printf("Fail to open (write) file [%s] \n", fileName.c_str());
801       return false;
802    }
803    fwrite (Pixel_Data, ImageDataSize, 1, fp1);
804    fclose (fp1);
805
806    return true;
807 }
808
809 /**
810  * \brief Writes on disk A SINGLE Dicom file, 
811  *        using the Implicit Value Representation convention
812  *        NO test is performed on  processor "Endiannity".
813  * @param fileName name of the file to be created
814  *                 (any already existing file is overwritten)
815  * @return false if write fails
816  */
817
818 bool gdcmFile::WriteDcmImplVR (std::string const & fileName)
819 {
820    return WriteBase(fileName, gdcmImplicitVR);
821 }
822
823 /**
824 * \brief Writes on disk A SINGLE Dicom file, 
825  *        using the Explicit Value Representation convention
826  *        NO test is performed on  processor "Endiannity". * @param fileName name of the file to be created
827  *                 (any already existing file is overwritten)
828  * @return false if write fails
829  */
830
831 bool gdcmFile::WriteDcmExplVR (std::string const & fileName)
832 {
833    return WriteBase(fileName, gdcmExplicitVR);
834 }
835
836 /**
837  * \brief Writes on disk A SINGLE Dicom file, 
838  *        using the ACR-NEMA convention
839  *        NO test is performed on  processor "Endiannity".
840  *        (a l'attention des logiciels cliniques 
841  *        qui ne prennent en entrée QUE des images ACR ...
842  * \warning if a DICOM_V3 header is supplied,
843  *         groups < 0x0008 and shadow groups are ignored
844  * \warning NO TEST is performed on processor "Endiannity".
845  * @param fileName name of the file to be created
846  *                 (any already existing file is overwritten)
847  * @return false if write fails
848  */
849
850 bool gdcmFile::WriteAcr (std::string const & fileName)
851 {
852    return WriteBase(fileName, gdcmACR);
853 }
854
855 //-----------------------------------------------------------------------------
856 // Protected
857 /**
858  * \brief NOT a end user inteded function
859  *        (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
860  * @param fileName name of the file to be created
861  *                 (any already existing file is overwritten)
862  * @param  type file type (ExplicitVR, ImplicitVR, ...)
863  * @return false if write fails
864  */
865 bool gdcmFile::WriteBase (std::string const & fileName, FileType type)
866 {
867    if ( PixelRead == -1 && type != gdcmExplicitVR)
868    {
869       return false;
870    }
871
872    FILE* fp1 = fopen(fileName.c_str(), "wb");
873    if (fp1 == NULL)
874    {
875       printf("Failed to open (write) File [%s] \n", fileName.c_str());
876       return false;
877    }
878
879    if ( type == gdcmImplicitVR || type == gdcmExplicitVR )
880    {
881       // writing Dicom File Preamble
882       uint8_t* filePreamble = new uint8_t[128];
883       memset(filePreamble, 0, 128);
884       fwrite(filePreamble, 128, 1, fp1);
885       fwrite("DICM", 4, 1, fp1);
886
887       delete[] filePreamble;
888    }
889
890    // --------------------------------------------------------------
891    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
892    //
893    // if recognition code tells us we dealt with a LibIDO image
894    // we reproduce on disk the switch between lineNumber and columnNumber
895    // just before writting ...
896    
897    /// \todo the best trick would be *change* the recognition code
898    ///       but pb expected if user deals with, e.g. COMPLEX images
899
900    std::string rows, columns; 
901    if ( Header->GetFileType() == gdcmACR_LIBIDO)
902    {
903       rows    = Header->GetEntryByNumber(0x0028, 0x0010);
904       columns = Header->GetEntryByNumber(0x0028, 0x0011);
905
906       Header->SetEntryByNumber(columns,  0x0028, 0x0010);
907       Header->SetEntryByNumber(rows   ,  0x0028, 0x0011);
908    }
909    // ----------------- End of Special Patch ----------------
910       
911    uint16_t grPixel  = Header->GetGrPixel();
912    uint16_t numPixel = Header->GetNumPixel();;
913           
914    gdcmDocEntry* PixelElement = 
915       GetHeader()->GetDocEntryByNumber(grPixel, numPixel);  
916  
917    if ( PixelRead == 1 )
918    {
919       // we read pixel 'as is' (no tranformation LUT -> RGB)
920       PixelElement->SetLength( ImageDataSizeRaw );
921    }
922    else if ( PixelRead == 0 )
923    {
924       // we tranformed GrayLevel pixels + LUT into RGB Pixel
925       PixelElement->SetLength( ImageDataSize );
926    }
927  
928    Header->Write(fp1, type);
929
930    // --------------------------------------------------------------
931    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
932    // 
933    // ...and we restore the Header to be Dicom Compliant again 
934    // just after writting
935
936    if ( Header->GetFileType() == gdcmACR_LIBIDO )
937    {
938       Header->SetEntryByNumber(rows   , 0x0028, 0x0010);
939       Header->SetEntryByNumber(columns, 0x0028, 0x0011);
940    }
941    // ----------------- End of Special Patch ----------------
942    
943    // fwrite(Pixel_Data, ImageDataSize, 1, fp1);  // should be useless, now
944    fclose (fp1);
945
946    return true;
947 }
948
949 //-----------------------------------------------------------------------------
950 // Private
951 /**
952  * \brief   Swap the bytes, according to swap code.
953  * \warning not end user intended
954  * @param   im area to deal with
955  * @param   swap swap code
956  * @param   lgr Area Length
957  * @param   nb Pixels Bit number 
958  */
959 void gdcmFile::SwapZone(void* im, int swap, int lgr, int nb)
960 {
961    int i;
962
963    if( nb == 16 )
964    {
965       uint16_t* im16 = (uint16_t*)im;
966       switch( swap )
967       {
968          case 0:
969          case 12:
970          case 1234:
971             break;
972          case 21:
973          case 3412:
974          case 2143:
975          case 4321:
976             for(i=0; i < lgr/2; i++)
977             {
978                im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
979             }
980             break;
981          default:
982             std::cout << "SWAP value (16 bits) not allowed :i" << swap << 
983             std::endl;
984       }
985    }
986    else if( nb == 32 )
987    {
988       uint32_t s32;
989       uint16_t fort, faible;
990       uint32_t* im32 = (uint32_t*)im;
991       switch ( swap )
992       {
993          case 0:
994          case 1234:
995             break;
996          case 4321:
997             for(i = 0; i < lgr/4; i++)
998             {
999                faible  = im32[i] & 0x0000ffff;  // 4321
1000                fort    = im32[i] >> 16;
1001                fort    = ( fort >> 8   ) | ( fort << 8 );
1002                faible  = ( faible >> 8 ) | ( faible << 8);
1003                s32     = faible;
1004                im32[i] = ( s32 << 16 ) | fort;
1005             }
1006             break;
1007          case 2143:
1008             for(i = 0; i < lgr/4; i++)
1009             {
1010                faible  = im32[i] & 0x0000ffff;   // 2143
1011                fort    = im32[i] >> 16;
1012                fort    = ( fort >> 8 ) | ( fort << 8 );
1013                faible  = ( faible >> 8) | ( faible << 8);
1014                s32     = fort; 
1015                im32[i] = ( s32 << 16 ) | faible;
1016             }
1017             break;
1018          case 3412:
1019             for(i = 0; i < lgr/4; i++)
1020             {
1021                faible  = im32[i] & 0x0000ffff; // 3412
1022                fort    = im32[i] >> 16;
1023                s32     = faible;
1024                im32[i] = ( s32 << 16 ) | fort;
1025             }
1026             break;
1027          default:
1028             std::cout << "SWAP value (32 bits) not allowed : " << swap << 
1029             std::endl;
1030       }
1031    }
1032 }
1033
1034 /**
1035  * \brief   Read pixel data from disk (optionaly decompressing) into the
1036  *          caller specified memory location.
1037  * @param   destination where the pixel data should be stored.
1038  *
1039  */
1040 bool gdcmFile::ReadPixelData(void* destination) 
1041 {
1042    FILE* fp = Header->OpenFile();
1043
1044    if ( !fp )
1045    {
1046       return false;
1047    }
1048    if ( fseek(fp, Header->GetPixelOffset(), SEEK_SET) == -1 )
1049    {
1050       Header->CloseFile();
1051       return false;
1052    }
1053
1054    if ( Header->GetBitsAllocated() == 12 )
1055    {
1056       ConvertDecompress12BitsTo16Bits( (uint8_t*)destination, 
1057                                        Header->GetXSize(),
1058                                        Header->GetYSize(),
1059                                        fp);
1060       Header->CloseFile();
1061       return true;
1062    }
1063
1064    // ----------------------  Uncompressed File
1065    if ( !Header->IsDicomV3()                             ||
1066         Header->IsImplicitVRLittleEndianTransferSyntax() ||
1067         Header->IsExplicitVRLittleEndianTransferSyntax() ||
1068         Header->IsExplicitVRBigEndianTransferSyntax()    ||
1069         Header->IsDeflatedExplicitVRLittleEndianTransferSyntax() )
1070    {
1071       size_t ItemRead = fread(destination, Header->GetPixelAreaLength(), 1, fp);
1072       Header->CloseFile();
1073       if ( ItemRead != 1 )
1074       {
1075          return false;
1076       }
1077       else
1078       {
1079          return true;
1080       }
1081    }
1082
1083    // ---------------------- Run Length Encoding
1084    if ( Header->IsRLELossLessTransferSyntax() )
1085    {
1086       bool res = gdcm_read_RLE_file (fp,destination);
1087       Header->CloseFile();
1088       return res; 
1089    }  
1090     
1091    // --------------- SingleFrame/Multiframe JPEG Lossless/Lossy/2000 
1092    int numberBitsAllocated = Header->GetBitsAllocated();
1093    if ( ( numberBitsAllocated == 0 ) || ( numberBitsAllocated == 12 ) )
1094    {
1095       numberBitsAllocated = 16;
1096    }
1097
1098    int nBytes= numberBitsAllocated/8;
1099    int taille = Header->GetXSize() * Header->GetYSize()  
1100                 * Header->GetSamplesPerPixel();
1101    long fragmentBegining; // for ftell, fseek
1102
1103    bool jpg2000     = Header->IsJPEG2000();
1104    bool jpgLossless = Header->IsJPEGLossless();
1105
1106    bool res = true;
1107    uint16_t ItemTagGr, ItemTagEl;
1108    int ln;  
1109    
1110    //  Position on begining of Jpeg Pixels
1111    
1112    fread(&ItemTagGr,2,1,fp);  // Reading (fffe) : Item Tag Gr
1113    fread(&ItemTagEl,2,1,fp);  // Reading (e000) : Item Tag El
1114    if(Header->GetSwapCode())
1115    {
1116       ItemTagGr = Header->SwapShort(ItemTagGr);
1117       ItemTagEl = Header->SwapShort(ItemTagEl);
1118    }
1119
1120    fread(&ln,4,1,fp);
1121    if( Header->GetSwapCode() )
1122    {
1123       ln = Header->SwapLong( ln );    // Basic Offset Table Item length
1124    }
1125
1126    if ( ln != 0 )
1127    {
1128       // What is it used for ?!?
1129       uint8_t* BasicOffsetTableItemValue = new uint8_t[ln+1];
1130       fread(BasicOffsetTableItemValue,ln,1,fp);
1131       //delete[] BasicOffsetTableItemValue;
1132    }
1133
1134    // first Fragment initialisation
1135    fread(&ItemTagGr,2,1,fp);  // Reading (fffe) : Item Tag Gr
1136    fread(&ItemTagEl,2,1,fp);  // Reading (e000) : Item Tag El
1137    if( Header->GetSwapCode() )
1138    {
1139       ItemTagGr = Header->SwapShort( ItemTagGr );
1140       ItemTagEl = Header->SwapShort( ItemTagEl );
1141    }
1142
1143    // parsing fragments until Sequence Delim. Tag found
1144    while ( ItemTagGr == 0xfffe && ItemTagEl != 0xe0dd )
1145    {
1146       // --- for each Fragment
1147       fread(&ln,4,1,fp);
1148       if( Header->GetSwapCode() )
1149       {
1150          ln = Header->SwapLong(ln);    // Fragment Item length
1151       }
1152       fragmentBegining = ftell( fp );
1153
1154       if ( jpg2000 )
1155       {
1156       // JPEG 2000 :    call to ???
1157       res = gdcm_read_JPEG2000_file (fp,destination);  // Not Yet written 
1158       // ------------------------------------- endif (JPEG2000)
1159       }
1160       else if (jpgLossless)
1161       {
1162          // JPEG LossLess : call to xmedcom Lossless JPEG
1163          // Reading Fragment pixels
1164          JPEGLosslessDecodeImage (fp, (uint16_t*)destination,
1165                Header->GetPixelSize() * 8 * Header->GetSamplesPerPixel(), ln);
1166          res = 1; // in order not to break the loop
1167   
1168       } // ------------------------------------- endif (JPEGLossless)
1169       else
1170       {
1171          // JPEG Lossy : call to IJG 6b
1172          if ( Header->GetBitsStored() == 8)
1173          {
1174             // Reading Fragment pixels
1175             res = gdcm_read_JPEG_file (fp,destination);
1176          }
1177          else
1178          {
1179             // Reading Fragment pixels
1180             res = gdcm_read_JPEG_file12 (fp,destination);
1181          }
1182          // ------------------------------------- endif (JPEGLossy)
1183       }
1184
1185       if ( !res )
1186       {
1187          break;
1188       }
1189                
1190       // location in user's memory 
1191       // for next fragment (if any) 
1192       destination = (uint8_t*)destination + taille * nBytes;
1193
1194       fseek(fp,fragmentBegining, SEEK_SET); // To be sure we start 
1195       fseek(fp,ln,SEEK_CUR);                // at the begining of next fragment
1196       
1197       ItemTagGr = ItemTagEl = 0;
1198       fread(&ItemTagGr,2,1,fp);  // Reading (fffe) : Item Tag Gr
1199       fread(&ItemTagEl,2,1,fp);  // Reading (e000) : Item Tag El
1200       if( Header->GetSwapCode() )
1201       {
1202          ItemTagGr = Header->SwapShort( ItemTagGr );
1203          ItemTagEl = Header->SwapShort( ItemTagEl );
1204       }
1205    }
1206    // endWhile parsing fragments until Sequence Delim. Tag found    
1207
1208    Header->CloseFile();
1209    return res;
1210 }
1211
1212 /**
1213  * \brief Read from file a 12 bits per pixel image and uncompress it
1214  *        into a 16 bits per pixel image.
1215  */
1216 void gdcmFile::ConvertDecompress12BitsTo16Bits(
1217                   uint8_t* pixelZone,
1218                   int sizeX,
1219                   int sizeY,
1220                   FILE* filePtr)
1221                throw ( gdcmFormatError )
1222 {
1223    int nbPixels = sizeX * sizeY;
1224    uint16_t* destination = (uint16_t*)pixelZone;    
1225
1226    for( int p = 0; p < nbPixels; p += 2 )
1227    {
1228       uint8_t b0, b1, b2;
1229       size_t ItemRead;
1230
1231       ItemRead = fread( &b0, 1, 1, filePtr);
1232       if ( ItemRead != 1 )
1233       {
1234          throw gdcmFormatError( "gdcmFile::ConvertDecompress12BitsTo16Bits()",
1235                                 "Unfound first block" );
1236       }
1237
1238       ItemRead = fread( &b1, 1, 1, filePtr);
1239       if ( ItemRead != 1 )
1240       {
1241          throw gdcmFormatError( "gdcmFile::ConvertDecompress12BitsTo16Bits()",
1242                                 "Unfound second block" );
1243       }
1244
1245       ItemRead = fread( &b2, 1, 1, filePtr);      
1246       if ( ItemRead != 1 )
1247       {
1248          throw gdcmFormatError( "gdcmFile::ConvertDecompress12BitsTo16Bits()",
1249                                 "Unfound second block" );
1250       }
1251
1252       // Two steps are necessary to please VC++
1253       //
1254       // 2 pixels 12bit =     [0xABCDEF]
1255       // 2 pixels 16bit = [0x0ABD] + [0x0FCE]
1256       //                     A                     B                 D
1257       *destination++ =  ((b0 >> 4) << 8) + ((b0 & 0x0f) << 4) + (b1 & 0x0f);
1258       //                     F                     C                 E
1259       *destination++ =  ((b2 & 0x0f) << 8) + ((b1 >> 4) << 4) + (b2 >> 4);
1260   
1261       /// \todo JPR Troubles expected on Big-Endian processors ?
1262    }
1263 }
1264 //-----------------------------------------------------------------------------