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