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