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