]> Creatis software - gdcm.git/blob - src/gdcmFile.cxx
ENH: Some cosmetic cleanup of gdcmFile, main changes are: lgrTotal -> ImageDataSize...
[gdcm.git] / src / gdcmFile.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFile.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/06/26 04:09:33 $
7   Version:   $Revision: 1.111 $
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.htm 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  * \ingroup   gdcmFile
29  * \brief Constructor dedicated to writing a new DICOMV3 part10 compliant
30  *        file (see SetFileName, SetDcmTag and Write)
31  *        Opens (in read only and when possible) an existing file and checks
32  *        for DICOM compliance. Returns NULL on failure.
33  * \note  the in-memory representation of all available tags found in
34  *        the DICOM header is post-poned to first header information access.
35  *        This avoid a double parsing of public part of the header when
36  *        one sets an a posteriori shadow dictionary (efficiency can be
37  *        seen as a side effect).   
38  * @param header file to be opened for reading datas
39  * @return
40  */
41 gdcmFile::gdcmFile(gdcmHeader *header)
42 {
43    Header     = header;
44    SelfHeader = false;
45    PixelRead  = -1; // no ImageData read yet.
46
47    if (Header->IsReadable())
48    {
49       SetPixelDataSizeFromHeader();
50    }
51 }
52
53 /**
54  * \ingroup   gdcmFile
55  * \brief Constructor dedicated to writing a new DICOMV3 part10 compliant
56  *        file (see SetFileName, SetDcmTag and Write)
57  *        Opens (in read only and when possible) an existing file and checks
58  *        for DICOM compliance. Returns NULL on failure.
59  * \note  the in-memory representation of all available tags found in
60  *        the DICOM header is post-poned to first header information access.
61  *        This avoid a double parsing of public part of the header when
62  *        one sets an a posteriori shadow dictionary (efficiency can be
63  *        seen as a side effect).   
64  * @param filename file to be opened for parsing
65  * @param   exception_on_error whether we throw an exception or not
66  * @param   enable_sequences = true to allow the header 
67  *          to be parsed *inside* the SeQuences, 
68  *          when they have an actual length 
69  * \warning enable_sequences *has to be* true for reading PAPYRUS 3.0 files
70  * @param   ignore_shadow to allow skipping the shadow elements, 
71  *          to save memory space.
72  * \warning The TRUE value for this param has to be used 
73  *          with a FALSE value for the 'enable_sequence' param.
74  *          ('public elements' may be embedded in 'shadow Sequences')
75  */
76 gdcmFile::gdcmFile(std::string const & filename, 
77                    bool exception_on_error,
78                    bool enable_sequences, 
79                    bool ignore_shadow)
80 {
81    Header = new gdcmHeader( filename,
82                             exception_on_error,
83                             enable_sequences,
84                             ignore_shadow );
85    SelfHeader = true;
86    PixelRead  = -1; // no ImageData read yet.
87
88    if ( Header->IsReadable() )
89    {
90       SetPixelDataSizeFromHeader();
91    }
92 }
93
94 /**
95  * \ingroup   gdcmFile
96  * \brief canonical destructor
97  * \note  If the gdcmHeader is created by the gdcmFile, it is destroyed
98  *        by the gdcmFile
99  */
100 gdcmFile::~gdcmFile()
101 {
102    if( SelfHeader )
103    {
104       delete Header;
105    }
106    Header = 0;
107 }
108
109 //-----------------------------------------------------------------------------
110 // Print
111
112 //-----------------------------------------------------------------------------
113 // Public
114
115 /**
116  * \ingroup   gdcmFile
117  * \brief     computes the length (in bytes) to ALLOCATE to receive the
118  *            image(s) pixels (multiframes taken into account) 
119  * \warning : it is NOT the group 7FE0 length
120  *          (no interest for compressed images).
121  * @return length to allocate
122  */
123 void gdcmFile::SetPixelDataSizeFromHeader()
124 {
125    // see PS 3.3-2003 : C.7.6.3.2.1  
126    // 
127    //   MONOCHROME1
128    //   MONOCHROME2
129    //   PALETTE COLOR
130    //   RGB
131    //   HSV  (Retired)
132    //   ARGB (Retired)
133    //   CMYK (Retired)
134    //   YBR_FULL
135    //   YBR_FULL_422 (no LUT, no Palette)
136    //   YBR_PARTIAL_422
137    //   YBR_ICT
138    //   YBR_RCT
139
140    // LUT's
141    // ex : gdcm-US-ALOKA-16.dcm
142    // 0028|1221 [OW]   [Segmented Red Palette Color Lookup Table Data]
143    // 0028|1222 [OW]   [Segmented Green Palette Color Lookup Table Data]  
144    // 0028|1223 [OW]   [Segmented Blue Palette Color Lookup Table Data]
145
146    // ex  : OT-PAL-8-face.dcm
147    // 0028|1201 [US]   [Red Palette Color Lookup Table Data]
148    // 0028|1202 [US]   [Green Palette Color Lookup Table Data]
149    // 0028|1203 [US]   [Blue Palette Color Lookup Table Data]
150
151    // Number of "Bits Allocated"
152    int nb;
153    std::string str_nb = Header->GetEntryByNumber(0x0028,0x0100);
154
155    if ( str_nb == GDCM_UNFOUND )
156    {
157       nb = 16;
158    } 
159    else
160    {
161       nb = atoi( str_nb.c_str() );
162       if (nb == 12) 
163       {
164          nb =16;
165       }
166    }
167    ImageDataSize =  ImageDataSizeRaw = Header->GetXSize() * Header->GetYSize() 
168                 * Header->GetZSize() * (nb/8) * Header->GetSamplesPerPixel();
169    std::string str_PhotometricInterpretation = 
170                              Header->GetEntryByNumber(0x0028,0x0004);
171     
172    /*if ( str_PhotometricInterpretation == "PALETTE COLOR " )*/
173    // pb when undealt Segmented Palette Color
174    
175    if ( Header->HasLUT() )
176    {
177       ImageDataSize *= 3;
178    }
179 }
180
181 /**
182  * \ingroup   gdcmFile
183  * \brief     Returns the size (in bytes) of required memory to hold
184  *            the pixel data represented in this file.
185  * @return    The size of pixel data in bytes.
186  */
187 size_t gdcmFile::GetImageDataSize()
188 {
189    return ImageDataSize;
190 }
191
192 /**
193  * \ingroup   gdcmFile
194  * \brief     Returns the size (in bytes) of required memory to hold
195  *            the pixel data represented in this file, when user DOESN'T want 
196  *            to get RGB pixels image when it's stored as a PALETTE COLOR image
197  *            -the (vtk) user is supposed to know how deal with LUTs- 
198  * \warning   to be used with GetImagePixelsRaw()
199  * @return    The size of pixel data in bytes.
200  */
201 size_t gdcmFile::GetImageDataSizeRaw()
202 {
203    return ImageDataSizeRaw;
204 }
205
206 /**
207  * \ingroup gdcmFile
208  * \brief   Allocates necessary memory, copies the pixel data
209  *          (image[s]/volume[s]) to newly allocated zone.
210  *          Transforms YBR pixels into RGB pixels if any
211  *          Transforms 3 planes R, G, B into a single RGB Plane
212  *          Transforms single Grey plane + 3 Palettes into a RGB Plane
213  * @return  Pointer to newly allocated pixel data.
214  *          NULL if alloc fails 
215  */
216 void *gdcmFile::GetImageData()
217 {
218    // FIXME
219    // I need to deallocate PixelData before doing any allocation:
220    PixelData = new uint8_t[ImageDataSize];
221    if ( PixelData )
222    {
223       GetImageDataIntoVector(PixelData, ImageDataSize);
224       GetHeader()->SetEntryVoidAreaByNumber( PixelData, 
225          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); 
226    }      
227    PixelRead = 0; // no PixelRaw
228
229    return PixelData;
230 }
231
232 /**
233  * \ingroup gdcmFile
234  * \brief   Copies at most MaxSize bytes of pixel data to caller's
235  *          memory space.
236  * \warning This function was designed to avoid people that want to build
237  *          a volume from an image stack to need first to get the image pixels 
238  *          and then move them to the volume area.
239  *          It's absolutely useless for any VTK user since vtk chooses 
240  *          to invert the lines of an image, that is the last line comes first
241  *          (for some axis related reasons?). Hence he will have 
242  *          to load the image line by line, starting from the end.
243  *          VTK users have to call GetImageData
244  *     
245  * @param   destination Address (in caller's memory space) at which the
246  *          pixel data should be copied
247  * @param   MaxSize Maximum number of bytes to be copied. When MaxSize
248  *          is not sufficient to hold the pixel data the copy is not
249  *          executed (i.e. no partial copy).
250  * @return  On success, the number of bytes actually copied. Zero on
251  *          failure e.g. MaxSize is lower than necessary.
252  */
253 size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t maxSize)
254 {
255    //size_t l = GetImageDataIntoVectorRaw (destination, maxSize);
256    GetImageDataIntoVectorRaw (destination, maxSize);
257    PixelRead = 0 ; // no PixelRaw
258    if ( !Header->HasLUT() )
259    {
260       return ImageDataSize;
261    }
262                             
263    // from Lut R + Lut G + Lut B
264    uint8_t *newDest = new uint8_t[ImageDataSize];
265    uint8_t *a       = (uint8_t *)destination;
266    uint8_t *lutRGBA =  Header->GetLUTRGBA();
267
268    if ( lutRGBA )
269    {
270       int j;
271       //int l = ImageDataSizeRaw;  //loss of precision
272       // move Gray pixels to temp area  
273       memmove(newDest, destination, ImageDataSizeRaw);
274       for (size_t i=0; i<ImageDataSizeRaw; ++i) 
275       {
276          // Build RGB Pixels
277          j    = newDest[i]*4;
278          *a++ = lutRGBA[j];
279          *a++ = lutRGBA[j+1];
280          *a++ = lutRGBA[j+2];
281       }
282       delete[] newDest;
283     
284    // now, it's an RGB image
285    // Lets's write it in the Header
286
287          // CreateOrReplaceIfExist ?
288
289    std::string spp = "3";        // Samples Per Pixel
290    Header->SetEntryByNumber(spp,0x0028,0x0002);
291    std::string rgb= "RGB ";      // Photometric Interpretation
292    Header->SetEntryByNumber(rgb,0x0028,0x0004);
293    std::string planConfig = "0"; // Planar Configuration
294    Header->SetEntryByNumber(planConfig,0x0028,0x0006);
295
296    }
297    else  //why is there a 'else' when an allocation failed ?
298    {
299       // need to make RGB Pixels (?)
300       //    from grey Pixels (?!)
301       //     and Gray Lut  (!?!) 
302       //    or Segmented xxx Palette Color Lookup Table Data and so on
303  
304       // Oops! I get one (gdcm-US-ALOKA-16.dcm)
305       // No idea how to manage such an image 
306       // It seems that *no Dicom Viewer* has any idea :-(
307       // Segmented xxx Palette Color are *more* than 65535 long ?!?
308   
309       std::string rgb = "MONOCHROME1 ";      // Photometric Interpretation
310       Header->SetEntryByNumber(rgb,0x0028,0x0004);
311    } 
312
313    /// \todo Drop Palette Color out of the Header?
314    return ImageDataSize; 
315 }
316
317 /**
318  * \ingroup gdcmFile
319  * \brief   Allocates necessary memory, copies the pixel data
320  *          (image[s]/volume[s]) to newly allocated zone.
321  *          Transforms YBR pixels into RGB pixels if any
322  *          Transforms 3 planes R, G, B into a single RGB Plane
323  *          DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
324  * @return  Pointer to newly allocated pixel data.
325  * \        NULL if alloc fails 
326  */
327 void * gdcmFile::GetImageDataRaw ()
328 {
329    size_t imgDataSize;
330    if ( Header->HasLUT() )
331    {
332       /// \todo Let gdcmHeader user a chance to get the right value
333       // ImageDataSize /= 3;  //dangerous
334       imgDataSize = ImageDataSizeRaw;
335    }
336
337    // FIXME
338    // I need to deallocate PixelData before doing any allocation:
339    PixelData = new uint8_t[imgDataSize];
340    if ( PixelData )
341    {
342       GetImageDataIntoVectorRaw(PixelData, ImageDataSize);
343       GetHeader()->SetEntryVoidAreaByNumber(PixelData, 
344          GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); 
345    } 
346    PixelRead = 1; // PixelRaw
347
348    return PixelData;
349 }
350
351 /**
352  * \ingroup gdcmFile
353  * \brief   Copies at most MaxSize bytes of pixel data to caller's
354  *          memory space.
355  * \warning This function was designed to avoid people that want to build
356  *          a volume from an image stack to need first to get the image pixels 
357  *          and then move them to the volume area.
358  *          It's absolutely useless for any VTK user since vtk chooses 
359  *          to invert the lines of an image, that is the last line comes first
360  *          (for some axis related reasons?). Hence he will have 
361  *          to load the image line by line, starting from the end.
362  *          VTK users hace to call GetImageData
363  * \warning DOES NOT transform the Grey Plane + Palette Color (if any) 
364  *                   into a single RGB Pixels Plane
365  *          the (VTK) user will manage the palettes
366  *     
367  * @param   destination Address (in caller's memory space) at which the
368  *          pixel data should be copied
369  * @param   MaxSize Maximum number of bytes to be copied. When MaxSize
370  *          is not sufficient to hold the pixel data the copy is not
371  *          executed (i.e. no partial copy).
372  * @return  On success, the number of bytes actually copied. Zero on
373  *          failure e.g. MaxSize is lower than necessary.
374  */
375 size_t gdcmFile::GetImageDataIntoVectorRaw (void *destination, size_t maxSize)
376 {
377    int nb, nbu, highBit, sign;
378    PixelRead = 1 ; // PixelRaw
379  
380    if ( ImageDataSize > maxSize )
381    {
382       dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: pixel data bigger"
383                      "than caller's expected MaxSize");
384       return (size_t)0;
385    }
386
387    ReadPixelData( destination );
388
389    // Number of Bits Allocated for storing a Pixel
390    std::string str_nb = Header->GetEntryByNumber(0x0028,0x0100);
391    if ( str_nb == GDCM_UNFOUND )
392    {
393       nb = 16;
394    }
395    else
396    {
397       nb = atoi( str_nb.c_str() );
398       // FIXME
399       // From reading SetPixelDataSizeFromHeader, it seems 12 should be treated
400       // separately, correct ?
401    }
402
403    // Number of Bits actually used
404    std::string str_nbu = Header->GetEntryByNumber(0x0028,0x0101);
405    if ( str_nbu == GDCM_UNFOUND )
406    {
407       nbu = nb;
408    } 
409    else 
410    {
411       nbu = atoi( str_nbu.c_str() );
412    }
413
414    // High Bit Position
415    std::string str_highBit = Header->GetEntryByNumber(0x0028,0x0102);
416    if ( str_highBit == GDCM_UNFOUND )
417    {
418       highBit = nb - 1;
419    }
420    else
421    {
422       highBit = atoi( str_highBit.c_str() );
423    } 
424
425    // Pixel sign
426    // 0 = Unsigned
427    // 1 = Signed
428    std::string str_sign = Header->GetEntryByNumber(0x0028,0x0103);
429    if ( str_sign == GDCM_UNFOUND )
430    {
431       sign = 0;  // default is unsigned
432    }
433    else
434    {
435       sign = atoi( str_sign.c_str() );
436    }
437
438    // re arange bytes inside the integer (processor endianity)
439    if ( nb != 8 )
440    {
441       SwapZone(destination, Header->GetSwapCode(), ImageDataSize, nb);
442    }
443      
444    // to avoid pb with some xmedcon breakers images 
445    if ( nb == 16 && nbu < nb && sign == 0)
446    {
447       int l = (int)(ImageDataSize / (nb/8));
448       uint16_t *deb = (uint16_t *)destination;
449       for(int i = 0; i<l; i++)
450       {
451          if( *deb == 0xffff )
452          {
453            *deb = 0;
454          }
455          deb++;   
456       }
457    }
458
459    // re arange bits inside the bytes
460    if ( nbu != nb )
461    {
462       int l = (int)(ImageDataSize / (nb/8));
463       if ( nb == 16 )
464       {
465          uint16_t mask = 0xffff;
466          mask = mask >> (nb-nbu);
467          uint16_t *deb = (uint16_t *)destination;
468          for(int i = 0; i<l; i++)
469          {
470             *deb = (*deb >> (nbu - highBit - 1)) & mask;
471             deb++;
472          }
473       }
474       else if ( nb == 32 )
475       {
476          guint32 mask = 0xffffffff;
477          mask         = mask >> (nb - nbu);
478          guint32 *deb = (guint32 *)destination;
479          for(int i = 0; i<l; i++)
480          {
481             *deb = (*deb >> (nbu - highBit - 1)) & mask;
482             deb++;
483          }
484       }
485       else
486       {
487          dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: weird image");
488          return 0;
489       }
490    }
491 // DO NOT remove this code commented out.
492 // Nobody knows what's expecting you ...
493 // Just to 'see' what was actually read on disk :-(
494
495 //   FILE * f2;
496 //   f2 = fopen("SpuriousFile.RAW","wb");
497 //   fwrite(destination,ImageDataSize,1,f2);
498 //   fclose(f2);
499
500    // Deal with the color
501    // -------------------
502    
503    std::string str_PhotometricInterpretation = 
504       Header->GetEntryByNumber(0x0028,0x0004);
505
506    if ( str_PhotometricInterpretation == "MONOCHROME1 " 
507      || str_PhotometricInterpretation == "MONOCHROME2 " )
508    {
509       return ImageDataSize; 
510    }
511       
512    // Planar configuration = 0 : Pixels are already RGB
513    // Planar configuration = 1 : 3 planes : R, G, B
514    // Planar configuration = 2 : 1 gray Plane + 3 LUT
515
516    // Well ... supposed to be !
517    // See US-PAL-8-10x-echo.dcm: PlanarConfiguration=0,
518    //                            PhotometricInterpretation=PALETTE COLOR
519    // and heuristic has to be found :-( 
520
521    int planConf = Header->GetPlanarConfiguration();  // 0028,0006
522
523    // Whatever Planar Configuration is, 
524    // "PALETTE COLOR " implies that we deal with the palette. 
525    if ( str_PhotometricInterpretation == "PALETTE COLOR ")
526    {
527       planConf = 2;
528    }
529
530    switch ( planConf )
531    {
532       case 0:
533          // Pixels are already RGB
534          break;
535       case 1:
536          if (str_PhotometricInterpretation == "YBR_FULL")
537          {
538             // Warning : YBR_FULL_422 acts as RGB
539             //         : we need to make RGB Pixels from Planes Y,cB,cR
540
541             // to see the tricks about YBR_FULL, YBR_FULL_422, 
542             // YBR_PARTIAL_422, YBR_ICT, YBR_RCT have a look at :
543             // ftp://medical.nema.org/medical/dicom/final/sup61_ft.pdf
544             // and be *very* affraid
545             //
546             int l        = Header->GetXSize() * Header->GetYSize();
547             int nbFrames = Header->GetZSize();
548
549             uint8_t* newDest = new uint8_t[ImageDataSize];
550             uint8_t* x       = newDest;
551             uint8_t* a       = (uint8_t*)destination;
552             uint8_t* b       = a + l;
553             uint8_t* c       = b + l;
554             double R,G,B;
555
556             /// \todo : Replace by the 'well known' integer computation
557             /// counterpart
558             /// see http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
559             /// for code optimisation
560     
561             for (int i = 0; i < nbFrames; i++)
562             {
563                for (int j = 0; j < l; j++)
564                {
565                   R = 1.164 *(*a-16) + 1.596 *(*c -128) + 0.5;
566                   G = 1.164 *(*a-16) - 0.813 *(*c -128) - 0.392 *(*b -128) + 0.5;
567                   B = 1.164 *(*a-16) + 2.017 *(*b -128) + 0.5;
568
569                   if (R < 0.0)   R = 0.0;
570                   if (G < 0.0)   G = 0.0;
571                   if (B < 0.0)   B = 0.0;
572                   if (R > 255.0) R = 255.0;
573                   if (G > 255.0) G = 255.0;
574                   if (B > 255.0) B = 255.0;
575
576                   *(x++) = (uint8_t)R;
577                   *(x++) = (uint8_t)G;
578                   *(x++) = (uint8_t)B;
579                   a++; b++; c++;  
580                }
581             }
582             memmove(destination, newDest, ImageDataSize);
583             delete[] newDest;
584          }
585          else
586          {
587             // need to make RGB Pixels from R,G,B Planes
588             // (all the Frames at a time)
589
590             int l = Header->GetXSize() * Header->GetYSize() * Header->GetZSize();
591
592             uint8_t *newDest = new uint8_t[ImageDataSize];
593             uint8_t *x       = newDest;
594             uint8_t *a       = (uint8_t *)destination;
595             uint8_t *b       = a + l;
596             uint8_t *c       = b + l;
597
598             for (int j = 0; j < l; j++)
599             {
600                *(x++) = *(a++);
601                *(x++) = *(b++);
602                *(x++) = *(c++);
603             }
604             memmove(destination, newDest, ImageDataSize);
605             delete[] newDest;
606          }
607          break;
608       case 2:                      
609          // Palettes were found
610          // Let the user deal with them !
611          return ImageDataSize;
612    }
613    // now, it's an RGB image
614    // Lets's write it in the Header
615
616    // CreateOrReplaceIfExist ?
617
618    std::string spp = "3";        // Samples Per Pixel
619    std::string rgb = "RGB ";     // Photometric Interpretation
620    std::string planConfig = "0"; // Planar Configuration
621
622    Header->SetEntryByNumber(spp,0x0028,0x0002);
623    Header->SetEntryByNumber(rgb,0x0028,0x0004);
624    Header->SetEntryByNumber(planConfig,0x0028,0x0006);
625  
626    /// \todo Drop Palette Color out of the Header? 
627    return ImageDataSize; 
628 }
629
630 /**
631  * \ingroup   gdcmFile
632  * \brief performs a shalow copy (not a deep copy) of the user given
633  *        pixel area.
634  *        'image' Pixels are presented as C-like 2D arrays : line per line.
635  *        'volume'Pixels are presented as C-like 3D arrays : lane per plane 
636  * \warning user is kindly requested NOT TO 'free' the Pixel area
637  * @param inData user supplied pixel area
638  * @param ExpectedSize total image size, in Bytes
639  *
640  * @return boolean
641  */
642 bool gdcmFile::SetImageData(void *inData, size_t expectedSize)
643 {
644    Header->SetImageDataSize( expectedSize );
645    PixelData     = inData;
646    ImageDataSize = expectedSize;
647    PixelRead     = 1;
648
649    return true;
650 }
651
652 /**
653  * \ingroup   gdcmFile
654  * \brief Writes on disk A SINGLE Dicom file
655  *        NO test is performed on  processor "Endiannity".
656  *        It's up to the user to call his Reader properly
657  * @param fileName name of the file to be created
658  *                 (any already existing file is over written)
659  * @return false if write fails
660  */
661
662 bool gdcmFile::WriteRawData(std::string const & fileName)
663 {
664    FILE *fp1;
665    fp1 = fopen(fileName.c_str(), "wb");
666    if (fp1 == NULL)
667    {
668       printf("Fail to open (write) file [%s] \n", fileName.c_str());
669       return false;
670    }
671    fwrite (PixelData,ImageDataSize, 1, fp1);
672    fclose (fp1);
673
674    return true;
675 }
676
677 /**
678  * \ingroup   gdcmFile
679  * \brief Writes on disk A SINGLE Dicom file, 
680  *        using the Implicit Value Representation convention
681  *        NO test is performed on  processor "Endiannity".
682  * @param fileName name of the file to be created
683  *                 (any already existing file is overwritten)
684  * @return false if write fails
685  */
686
687 bool gdcmFile::WriteDcmImplVR (std::string const & fileName)
688 {
689    return WriteBase(fileName, gdcmImplicitVR);
690 }
691
692 /**
693  * \ingroup   gdcmFile
694 * \brief Writes on disk A SINGLE Dicom file, 
695  *        using the Explicit Value Representation convention
696  *        NO test is performed on  processor "Endiannity". * @param fileName name of the file to be created
697  *                 (any already existing file is overwritten)
698  * @return false if write fails
699  */
700
701 bool gdcmFile::WriteDcmExplVR (std::string const & fileName)
702 {
703    return WriteBase(fileName, gdcmExplicitVR);
704 }
705
706 /**
707  * \ingroup   gdcmFile
708  * \brief Writes on disk A SINGLE Dicom file, 
709  *        using the ACR-NEMA convention
710  *        NO test is performed on  processor "Endiannity".
711  *        (a l'attention des logiciels cliniques 
712  *        qui ne prennent en entrée QUE des images ACR ...
713  * \warning if a DICOM_V3 header is supplied,
714  *         groups < 0x0008 and shadow groups are ignored
715  * \warning NO TEST is performed on processor "Endiannity".
716  * @param fileName name of the file to be created
717  *                 (any already existing file is overwritten)
718  * @return false if write fails
719  */
720
721 bool gdcmFile::WriteAcr (std::string const & fileName)
722 {
723    return WriteBase(fileName, gdcmACR);
724 }
725
726 //-----------------------------------------------------------------------------
727 // Protected
728 /**
729  * \ingroup   gdcmFile
730  * \brief NOT a end user inteded function
731  *        (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
732  * @param fileName name of the file to be created
733  *                 (any already existing file is overwritten)
734  * @param  type file type (ExplicitVR, ImplicitVR, ...)
735  * @return false if write fails
736  */
737 bool gdcmFile::WriteBase (std::string const & fileName, FileType type)
738 {
739    FILE *fp1;
740
741    if ( PixelRead == -1 && type != gdcmExplicitVR)
742    {
743       return false;
744    }
745
746    fp1 = fopen(fileName.c_str(), "wb");
747    if (fp1 == NULL)
748    {
749       printf("Failed to open (write) File [%s] \n", fileName.c_str());
750       return false;
751    }
752
753    if ( type == gdcmImplicitVR || type == gdcmExplicitVR )
754    {
755       // writing Dicom File Preamble
756       uint8_t* filePreamble = new uint8_t[128];
757       memset(filePreamble, 0, 128);
758       fwrite(filePreamble, 128, 1, fp1);
759       fwrite("DICM", 4, 1, fp1);
760
761       delete[] filePreamble;
762    }
763
764    // --------------------------------------------------------------
765    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
766    //
767    // if recognition code tells us we dealt with a LibIDO image
768    // we reproduce on disk the switch between lineNumber and columnNumber
769    // just before writting ...
770    
771    /// \todo the best trick would be *change* the recognition code
772    ///       but pb expected if user deals with, e.g. COMPLEX images
773
774    std::string rows, columns; 
775    if ( Header->GetFileType() == gdcmACR_LIBIDO)
776    {
777       rows    = Header->GetEntryByNumber(0x0028, 0x0010);
778       columns = Header->GetEntryByNumber(0x0028, 0x0011);
779
780       Header->SetEntryByNumber(columns,  0x0028, 0x0010);
781       Header->SetEntryByNumber(rows   ,  0x0028, 0x0011);
782    }
783    // ----------------- End of Special Patch ----------------
784    
785    /// \todo get the grPixel, numPixel values (for some ACR-NEMA images only)
786    
787    uint16_t grPixel  = Header->GetGrPixel();
788    uint16_t numPixel = Header->GetNumPixel();;
789     
790    // Update Pixel Data Length
791    // the *last* of the (GrPixel, NumPixel), if many.
792           
793    TagKey key = gdcmDictEntry::TranslateToKey(grPixel, numPixel); 
794    TagDocEntryHT::iterator p2;
795    gdcmDocEntry* PixelElement;
796    
797    IterHT it = Header->GetEntry().equal_range(key); // get a pair of iterators first-last synonym   
798
799    if ( Header->GetEntry().count(key) == 1 ) // only the first is significant
800    {
801       p2 = it.first; // iterator on the first (unique) synonym
802    }
803    else
804    {
805       p2 = it.second;// iterator on the last synonym
806    }
807    
808    PixelElement = p2->second;        // H Table target column (2-nd col)
809   // PixelElement->SetPrintLevel(2);
810   // PixelElement->Print();      
811  
812    if ( PixelRead == 1 )
813    {
814       PixelElement->SetLength( ImageDataSizeRaw );
815    }
816    else if ( PixelRead == 0 )
817    {
818       PixelElement->SetLength( ImageDataSize );
819    }
820    
821    //PixelElement->SetPrintLevel(2);
822    //PixelElement->Print();    
823    Header->Write(fp1, type);
824
825    // --------------------------------------------------------------
826    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
827    // 
828    // ...and we restore the Header to be Dicom Compliant again 
829    // just after writting
830
831    if ( Header->GetFileType() == gdcmACR_LIBIDO )
832    {
833       Header->SetEntryByNumber(rows   , 0x0028, 0x0010);
834       Header->SetEntryByNumber(columns, 0x0028, 0x0011);
835    }
836    // ----------------- End of Special Patch ----------------
837    
838    // fwrite(PixelData, ImageDataSize, 1, fp1);  // should be useless, now
839    fclose (fp1);
840
841    return true;
842 }
843
844 //-----------------------------------------------------------------------------
845 // Private
846 /**
847  * \ingroup gdcmFile
848  * \brief   Swap the bytes, according to swap code.
849  * \warning not end user intended
850  * @param   im area to deal with
851  * @param   swap swap code
852  * @param   lgr Area Length
853  * @param   nb Pixels Bit number 
854  */
855 void gdcmFile::SwapZone(void *im, int swap, int lgr, int nb)
856 {
857    int i;
858
859    if( nb == 16 )
860    {
861       uint16_t* im16 = (uint16_t*)im;
862       switch( swap )
863       {
864          case 0:
865          case 12:
866          case 1234:
867             break;
868          case 21:
869          case 3412:
870          case 2143:
871          case 4321:
872             for(i=0; i < lgr/2; i++)
873             {
874             im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
875             }
876             break;
877          default:
878             std::cout << "SWAP value (16 bits) not allowed :i" << swap << 
879             std::endl;
880       }
881    }
882    else if( nb == 32 )
883    {
884       uint32_t s32;
885       uint16_t fort, faible;
886       uint32_t* im32 = (uint32_t*)im;
887       switch ( swap )
888       {
889          case 0:
890          case 1234:
891             break;
892          case 4321:
893             for(i = 0; i < lgr/4; i++)
894             {
895                faible  = im32[i] & 0x0000ffff;  // 4321
896                fort    = im32[i] >> 16;
897                fort    = ( fort >> 8   ) | ( fort << 8 );
898                faible  = ( faible >> 8 ) | ( faible << 8);
899                s32     = faible;
900                im32[i] = ( s32 << 16 ) | fort;
901             }
902             break;
903          case 2143:
904             for(i = 0; i < lgr/4; i++)
905             {
906                faible  = im32[i] & 0x0000ffff;   // 2143
907                fort    = im32[i] >> 16;
908                fort    = ( fort >> 8 ) | ( fort << 8 );
909                faible  = ( faible >> 8) | ( faible << 8);
910                s32     = fort; 
911                im32[i] = ( s32 << 16 ) | faible;
912             }
913             break;
914          case 3412:
915             for(i = 0; i < lgr/4; i++)
916             {
917                faible  = im32[i] & 0x0000ffff; // 3412
918                fort    = im32[i] >> 16;
919                s32     = faible;
920                im32[i] = ( s32 << 16 ) | fort;
921             }
922             break;
923          default:
924             std::cout << "SWAP value (32 bits) not allowed : " << swap << 
925             std::endl;
926       }
927    }
928 }
929
930 /**
931  * \ingroup gdcmFile
932  * \brief   Read pixel data from disk (optionaly decompressing) into the
933  *          caller specified memory location.
934  * @param   destination where the pixel data should be stored.
935  *
936  */
937 bool gdcmFile::ReadPixelData(void *destination) 
938 {
939    FILE *fp = Header->OpenFile();
940
941    if ( !fp )
942    {
943       return false;
944    }
945    if ( fseek(fp, Header->GetPixelOffset(), SEEK_SET) == -1 )
946    {
947       Header->CloseFile();
948       return false;
949    }
950
951    // ----------------------  Compacted File (12 Bits Per Pixel)
952    // unpack 12 Bits pixels into 16 Bits pixels
953    // 2 pixels 12bit =     [0xABCDEF]
954    // 2 pixels 16bit = [0x0ABD] + [0x0FCE]
955    
956    if ( Header->GetBitsAllocated() == 12 )
957    {
958       int nbPixels = Header->GetXSize() * Header->GetYSize();
959       uint8_t b0, b1, b2;
960       
961       uint16_t* pdestination = (uint16_t*)destination;    
962       for(int p = 0; p < nbPixels; p += 2 )
963       {
964          fread(&b0,1,1,fp);
965          fread(&b1,1,1,fp);
966          fread(&b2,1,1,fp);      
967
968          //Two steps is necessary to please VC++
969          *pdestination++ =  ((b0 >> 4) << 8) + ((b0 & 0x0f) << 4) + (b1 & 0x0f);
970          //                     A                     B                 D
971          *pdestination++ =  ((b2 & 0x0f) << 8) + ((b1 >> 4) << 4) + (b2 >> 4);
972          //                     F                     C                 E
973   
974          // Troubles expected on Big-Endian processors ?
975       }
976
977       Header->CloseFile();
978       return true;
979    }
980
981    // ----------------------  Uncompressed File
982    if ( !Header->IsDicomV3()                             ||
983         Header->IsImplicitVRLittleEndianTransferSyntax() ||
984         Header->IsExplicitVRLittleEndianTransferSyntax() ||
985         Header->IsExplicitVRBigEndianTransferSyntax()    ||
986         Header->IsDeflatedExplicitVRLittleEndianTransferSyntax() )
987    {
988       size_t ItemRead = fread(destination, Header->GetPixelAreaLength(), 1, fp);
989       Header->CloseFile();
990       if ( ItemRead != 1 )
991       {
992          return false;
993       }
994       else
995       {
996          return true;
997       }
998    }
999
1000    // ---------------------- Run Length Encoding
1001    if ( Header->IsRLELossLessTransferSyntax() )
1002    {
1003       bool res = (bool)gdcm_read_RLE_file (fp,destination);
1004       Header->CloseFile();
1005       return res; 
1006    }  
1007     
1008    // --------------- SingleFrame/Multiframe JPEG Lossless/Lossy/2000 
1009    int nb;
1010    std::string str_nb = Header->GetEntryByNumber(0x0028,0x0100);
1011    if ( str_nb == GDCM_UNFOUND )
1012    {
1013       nb = 16;
1014    }
1015    else
1016    {
1017       nb = atoi( str_nb.c_str() );
1018       if ( nb == 12 )
1019       {
1020          nb = 16;  // ?? 12 should be ACR-NEMA only
1021       }
1022    }
1023
1024    int nBytes= nb/8;
1025    int taille = Header->GetXSize() * Header->GetYSize()  
1026                 * Header->GetSamplesPerPixel();
1027    long fragmentBegining; // for ftell, fseek
1028
1029    bool jpg2000     = Header->IsJPEG2000();
1030    bool jpgLossless = Header->IsJPEGLossless();
1031
1032    bool res = true;
1033    uint16_t ItemTagGr, ItemTagEl;
1034    int ln;  
1035    
1036    //  Position on begining of Jpeg Pixels
1037    
1038    fread(&ItemTagGr,2,1,fp);  // Reading (fffe) : Item Tag Gr
1039    fread(&ItemTagEl,2,1,fp);  // Reading (e000) : Item Tag El
1040    if(Header->GetSwapCode())
1041    {
1042       ItemTagGr = Header->SwapShort(ItemTagGr);
1043       ItemTagEl = Header->SwapShort(ItemTagEl);
1044    }
1045
1046    fread(&ln,4,1,fp);
1047    if( Header->GetSwapCode() )
1048    {
1049       ln = Header->SwapLong( ln );    // Basic Offset Table Item length
1050    }
1051
1052    if ( ln != 0 )
1053    {
1054       // What is it used for ?!?
1055       uint8_t* BasicOffsetTableItemValue = new uint8_t[ln+1];
1056       fread(BasicOffsetTableItemValue,ln,1,fp);
1057       //delete[] BasicOffsetTableItemValue;
1058    }
1059
1060    // first Fragment initialisation
1061    fread(&ItemTagGr,2,1,fp);  // Reading (fffe) : Item Tag Gr
1062    fread(&ItemTagEl,2,1,fp);  // Reading (e000) : Item Tag El
1063    if( Header->GetSwapCode() )
1064    {
1065       ItemTagGr = Header->SwapShort( ItemTagGr );
1066       ItemTagEl = Header->SwapShort( ItemTagEl );
1067    }
1068
1069    // parsing fragments until Sequence Delim. Tag found
1070    while ( ItemTagGr == 0xfffe && ItemTagEl != 0xe0dd )
1071    {
1072       // --- for each Fragment
1073       fread(&ln,4,1,fp);
1074       if( Header->GetSwapCode() )
1075       {
1076          ln = Header->SwapLong(ln);    // Fragment Item length
1077       }
1078       fragmentBegining = ftell( fp );
1079
1080       if ( jpg2000 )
1081       {
1082       // JPEG 2000 :    call to ???
1083       res = (bool)gdcm_read_JPEG2000_file (fp,destination);  // Not Yet written 
1084       // ------------------------------------- endif (JPEG2000)
1085       }
1086       else if (jpgLossless)
1087       {
1088          // JPEG LossLess : call to xmedcom Lossless JPEG
1089          // Reading Fragment pixels
1090          JPEGLosslessDecodeImage (fp, (uint16_t*)destination,
1091                Header->GetPixelSize() * 8 * Header->GetSamplesPerPixel(), ln);
1092          res = 1; // in order not to break the loop
1093   
1094       } // ------------------------------------- endif (JPEGLossless)
1095       else
1096       {
1097          // JPEG Lossy : call to IJG 6b
1098          if ( Header->GetBitsStored() == 8)
1099          {
1100             // Reading Fragment pixels
1101             res = (bool)gdcm_read_JPEG_file (fp,destination);
1102          }
1103          else
1104          {
1105             // Reading Fragment pixels
1106             res = (bool)gdcm_read_JPEG_file12 (fp,destination);
1107          }
1108          // ------------------------------------- endif (JPEGLossy)
1109       }
1110
1111       if ( !res )
1112       {
1113          break;
1114       }
1115                
1116       // location in user's memory 
1117       // for next fragment (if any) 
1118       destination = (uint8_t*)destination + taille * nBytes;
1119
1120       fseek(fp,fragmentBegining, SEEK_SET); // To be sure we start 
1121       fseek(fp,ln,SEEK_CUR);                // at the begining of next fragment
1122       
1123       ItemTagGr = ItemTagEl = 0;
1124       fread(&ItemTagGr,2,1,fp);  // Reading (fffe) : Item Tag Gr
1125       fread(&ItemTagEl,2,1,fp);  // Reading (e000) : Item Tag El
1126       if( Header->GetSwapCode() )
1127       {
1128          ItemTagGr = Header->SwapShort( ItemTagGr );
1129          ItemTagEl = Header->SwapShort( ItemTagEl );
1130       }
1131    }
1132    // endWhile parsing fragments until Sequence Delim. Tag found    
1133
1134    Header->CloseFile();
1135    return res;
1136 }
1137 //-----------------------------------------------------------------------------