]> Creatis software - gdcm.git/blob - src/gdcmPixelConvert.cxx
ENH: Adding 'gdcm' namespace. Be nice with me this was a ~13000 lines patch. Also...
[gdcm.git] / src / gdcmPixelConvert.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmPixelConvert.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/10/12 04:35:47 $
7   Version:   $Revision: 1.8 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18
19 //////////////////   TEMPORARY NOT
20 // look for "fixMem" and convert that to a member of this class
21 // Removing the prefix fixMem and dealing with allocations should do the trick
22 //
23 // grep PIXELCONVERT everywhere and clean up !
24 #include "gdcmDebug.h"
25 #include "gdcmPixelConvert.h"
26
27 // External JPEG decompression
28
29 // for JPEGLosslessDecodeImage
30 #include "jpeg/ljpg/jpegless.h"
31
32
33 namespace gdcm 
34 {
35
36 #define str2num(str, typeNum) *((typeNum *)(str))
37
38 // For JPEG 2000, body in file gdcmJpeg2000.cxx
39 bool gdcm_read_JPEG2000_file (FILE* fp, void* image_buffer);
40
41 // For JPEG 8 Bits, body in file gdcmJpeg8.cxx
42 bool gdcm_read_JPEG_file     (FILE* fp, void* image_buffer);
43
44 // For JPEG 12 Bits, body in file gdcmJpeg12.cxx
45 bool gdcm_read_JPEG_file12   (FILE* fp, void* image_buffer);
46
47
48
49 //-----------------------------------------------------------------------------
50 // Constructor / Destructor
51 PixelConvert::PixelConvert() 
52 {
53    RGB = 0;
54    RGBSize = 0;
55    Uncompressed = 0;
56    UncompressedSize = 0;
57 }
58
59 void PixelConvert::Squeeze() 
60 {
61    if ( RGB ) {
62       delete [] RGB;
63    } 
64    if ( Uncompressed ) {
65       delete [] Uncompressed;
66    }
67 }
68
69 PixelConvert::~PixelConvert() 
70 {
71    Squeeze();
72 }
73
74 void PixelConvert::AllocateRGB()
75 {
76   if ( RGB ) {
77      delete [] RGB;
78   }
79   RGB = new uint8_t[RGBSize];
80 }
81
82 void PixelConvert::AllocateUncompressed()
83 {
84   if ( Uncompressed ) {
85      delete [] Uncompressed;
86   }
87   Uncompressed = new uint8_t[ UncompressedSize ];
88 }
89
90 /**
91  * \brief Read from file a 12 bits per pixel image and uncompress it
92  *        into a 16 bits per pixel image.
93  */
94 void PixelConvert::ConvertDecompress12BitsTo16Bits(
95                   uint8_t* pixelZone,
96                   int sizeX,
97                   int sizeY,
98                   FILE* filePtr)
99                throw ( FormatError )
100 {
101    int nbPixels = sizeX * sizeY;
102    uint16_t* destination = (uint16_t*)pixelZone;
103                                                                                 
104    for( int p = 0; p < nbPixels; p += 2 )
105    {
106       uint8_t b0, b1, b2;
107       size_t ItemRead;
108                                                                                 
109       ItemRead = fread( &b0, 1, 1, filePtr);
110       if ( ItemRead != 1 )
111       {
112          throw FormatError( "File::ConvertDecompress12BitsTo16Bits()",
113                             "Unfound first block" );
114       }
115                                                                                 
116       ItemRead = fread( &b1, 1, 1, filePtr);
117       if ( ItemRead != 1 )
118       {
119          throw FormatError( "File::ConvertDecompress12BitsTo16Bits()",
120                             "Unfound second block" );
121       }
122                                                                                 
123       ItemRead = fread( &b2, 1, 1, filePtr);
124       if ( ItemRead != 1 )
125       {
126          throw FormatError( "File::ConvertDecompress12BitsTo16Bits()",
127                             "Unfound second block" );
128       }
129                                                                                 
130       // Two steps are necessary to please VC++
131       //
132       // 2 pixels 12bit =     [0xABCDEF]
133       // 2 pixels 16bit = [0x0ABD] + [0x0FCE]
134       //                     A                     B                 D
135       *destination++ =  ((b0 >> 4) << 8) + ((b0 & 0x0f) << 4) + (b1 & 0x0f);
136       //                     F                     C                 E
137       *destination++ =  ((b2 & 0x0f) << 8) + ((b1 >> 4) << 4) + (b2 >> 4);
138                                                                                 
139       /// \todo JPR Troubles expected on Big-Endian processors ?
140    }
141 }
142
143 /**
144  * \brief     Try to deal with RLE 16 Bits. 
145  *            We assume the RLE has allready been parsed and loaded in
146  *            Uncompressed (through \ref ReadAndDecompressJPEGFile ).
147  *            We here need to make 16 Bits Pixels from Low Byte and
148  *            High Byte 'Planes'...(for what it may mean)
149  * @return    Boolean
150  */
151 bool PixelConvert::UncompressRLE16BitsFromRLE8Bits(
152                        int XSize,
153                        int YSize,
154                        int NumberOfFrames,
155                        uint8_t* fixMemUncompressed )
156 {
157    size_t PixelNumber = XSize * YSize;
158    size_t fixMemUncompressedSize = XSize * YSize * NumberOfFrames;
159
160    // We assumed Uncompressed contains the decoded RLE pixels but as
161    // 8 bits per pixel. In order to convert those pixels to 16 bits
162    // per pixel we cannot work in place within Uncompressed and hence
163    // we copy Uncompressed in a safe place, say OldUncompressed.
164
165    uint8_t* OldUncompressed = new uint8_t[ fixMemUncompressedSize * 2 ];
166    memmove( OldUncompressed, fixMemUncompressed, fixMemUncompressedSize * 2);
167
168    uint8_t* x = fixMemUncompressed;
169    uint8_t* a = OldUncompressed;
170    uint8_t* b = a + PixelNumber;
171
172    for ( int i = 0; i < NumberOfFrames; i++ )
173    {
174       for ( unsigned int j = 0; j < PixelNumber; j++ )
175       {
176          *(x++) = *(a++);
177          *(x++) = *(b++);
178       }
179    }
180
181    delete[] OldUncompressed;
182       
183    /// \todo check that operator new []didn't fail, and sometimes return false
184    return true;
185 }
186
187 /**
188  * \brief Implementation of the RLE decoding algorithm for uncompressing
189  *        a RLE fragment. [refer to PS 3.5-2003, section G.3.2 p 86]
190  */
191 bool PixelConvert::ReadAndUncompressRLEFragment( uint8_t* decodedZone,
192                                                long fragmentSize,
193                                                long uncompressedSegmentSize,
194                                                FILE* fp )
195 {
196    int8_t count;
197    long numberOfOutputBytes = 0;
198    long numberOfReadBytes = 0;
199                                                                                 
200    while( numberOfOutputBytes < uncompressedSegmentSize )
201    {
202       fread( &count, 1, 1, fp );
203       numberOfReadBytes += 1;
204       if ( count >= 0 )
205       // Note: count <= 127 comparison is always true due to limited range
206       //       of data type int8_t [since the maximum of an exact width
207       //       signed integer of width N is 2^(N-1) - 1, which for int8_t
208       //       is 127].
209       {
210          fread( decodedZone, count + 1, 1, fp);
211          numberOfReadBytes += count + 1;
212          decodedZone         += count + 1;
213          numberOfOutputBytes += count + 1;
214       }
215       else
216       {
217          if ( ( count <= -1 ) && ( count >= -127 ) )
218          {
219             int8_t newByte;
220             fread( &newByte, 1, 1, fp);
221             numberOfReadBytes += 1;
222             for( int i = 0; i < -count + 1; i++ )
223             {
224                decodedZone[i] = newByte;
225             }
226             decodedZone         += -count + 1;
227             numberOfOutputBytes += -count + 1;
228          }
229       }
230       // if count = 128 output nothing
231                                                                                 
232       if ( numberOfReadBytes > fragmentSize )
233       {
234          dbg.Verbose(0, "File::gdcm_read_RLE_fragment: we read more "
235                         "bytes than the segment size.");
236          return false;
237       }
238    }
239    return true;
240 }
241
242 /**
243  * \brief     Reads from disk the Pixel Data of 'Run Length Encoded'
244  *            Dicom encapsulated file and uncompress it.
245  * @param     fp already open File Pointer
246  * @param     image_buffer destination Address (in caller's memory space)
247  *            at which the pixel data should be copied
248  * @return    Boolean
249  */
250 bool PixelConvert::ReadAndDecompressRLEFile( void* image_buffer,
251                                    int XSize,
252                                    int YSize,
253                                    int ZSize,
254                                    int BitsAllocated,
255                                    RLEFramesInfo* RLEInfo,
256                                    FILE* fp )
257 {
258    uint8_t* im = (uint8_t*)image_buffer;
259    long uncompressedSegmentSize = XSize * YSize;
260                                                                                 
261                                                                                 
262    // Loop on the frame[s]
263    for( RLEFramesInfo::RLEFrameList::iterator
264         it  = RLEInfo->Frames.begin();
265         it != RLEInfo->Frames.end();
266       ++it )
267    {
268       // Loop on the fragments
269       for( unsigned int k = 1; k <= (*it)->NumberFragments; k++ )
270       {
271          fseek( fp, (*it)->Offset[k] ,SEEK_SET );
272          (void)PixelConvert::ReadAndUncompressRLEFragment(
273                                  (uint8_t*) im, (*it)->Length[k],
274                                  uncompressedSegmentSize, fp );
275          im += uncompressedSegmentSize;
276       }
277    }
278                                                                                 
279    if ( BitsAllocated == 16 )
280    {
281       // Try to deal with RLE 16 Bits
282       (void)PixelConvert::UncompressRLE16BitsFromRLE8Bits(
283                                              XSize,
284                                              YSize,
285                                              ZSize,
286                                              (uint8_t*) image_buffer);
287    }
288                                                                                 
289    return true;
290 }
291
292 /**
293  * \brief   Swap the bytes, according to swap code.
294  * \warning not end user intended
295  * @param   im area to deal with
296  * @param   swap swap code
297  * @param   lgr Area Length
298  * @param   nb Pixels Bit number
299  */
300 void PixelConvert::SwapZone(void* im, int swap, int lgr, int nb)
301 {
302    int i;
303                                                                                 
304    if( nb == 16 )
305    {
306       uint16_t* im16 = (uint16_t*)im;
307       switch( swap )
308       {
309          case 0:
310          case 12:
311          case 1234:
312             break;
313          case 21:
314          case 3412:
315          case 2143:
316          case 4321:
317             for(i=0; i < lgr/2; i++)
318             {
319                im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
320             }
321             break;
322          default:
323             std::cout << "SWAP value (16 bits) not allowed :i" << swap <<
324             std::endl;
325       }
326    }
327    else if( nb == 32 )
328    {
329       uint32_t s32;
330       uint16_t fort, faible;
331       uint32_t* im32 = (uint32_t*)im;
332       switch ( swap )
333       {
334          case 0:
335          case 1234:
336             break;
337          case 4321:
338             for(i = 0; i < lgr/4; i++)
339             {
340                faible  = im32[i] & 0x0000ffff;  // 4321
341                fort    = im32[i] >> 16;
342                fort    = ( fort >> 8   ) | ( fort << 8 );
343                faible  = ( faible >> 8 ) | ( faible << 8);
344                s32     = faible;
345                im32[i] = ( s32 << 16 ) | fort;
346             }
347             break;
348          case 2143:
349             for(i = 0; i < lgr/4; i++)
350             {
351                faible  = im32[i] & 0x0000ffff;   // 2143
352                fort    = im32[i] >> 16;
353                fort    = ( fort >> 8 ) | ( fort << 8 );
354                faible  = ( faible >> 8) | ( faible << 8);
355                s32     = fort;
356                im32[i] = ( s32 << 16 ) | faible;
357             }
358             break;
359          case 3412:
360             for(i = 0; i < lgr/4; i++)
361             {
362                faible  = im32[i] & 0x0000ffff; // 3412
363                fort    = im32[i] >> 16;
364                s32     = faible;
365                im32[i] = ( s32 << 16 ) | fort;
366             }
367             break;
368          default:
369             std::cout << "SWAP value (32 bits) not allowed : " << swap <<
370             std::endl;
371       }
372    }
373 }
374
375
376
377 /**
378  * \brief Deal with endianity i.e. re-arange bytes inside the integer
379  */
380 void PixelConvert::ConvertReorderEndianity( uint8_t* pixelZone,
381                                         size_t imageDataSize,
382                                         int numberBitsStored,
383                                         int numberBitsAllocated,
384                                         int swapCode,
385                                         bool signedPixel)
386 {
387    if ( numberBitsAllocated != 8 )
388    {
389       SwapZone( pixelZone, swapCode, imageDataSize, numberBitsAllocated );
390    }
391                                                                                 
392    // Special kludge in order to deal with xmedcon broken images:
393    if (  ( numberBitsAllocated == 16 )
394       && ( numberBitsStored < numberBitsAllocated )
395       && ( ! signedPixel ) )
396    {
397       int l = (int)(imageDataSize / (numberBitsAllocated/8));
398       uint16_t *deb = (uint16_t *)pixelZone;
399       for(int i = 0; i<l; i++)
400       {
401          if( *deb == 0xffff )
402          {
403            *deb = 0;
404          }
405          deb++;
406       }
407    }
408 }
409
410 /**
411  * \brief     Reads from disk the Pixel Data of JPEG Dicom encapsulated
412  &            file and uncompress it.
413  * @param     fp already open File Pointer
414  * @param     destination Where decompressed fragments should end up
415  * @return    Boolean
416  */
417 bool PixelConvert::ReadAndDecompressJPEGFile( uint8_t* destination,
418                                    int XSize,
419                                    int YSize,
420                                    int BitsAllocated,
421                                    int BitsStored,
422                                    int SamplesPerPixel,
423                                    int PixelSize,
424                                    bool isJPEG2000,
425                                    bool isJPEGLossless,
426                                    JPEGFragmentsInfo* JPEGInfo,
427                                    FILE* fp )
428 {
429    // Loop on the fragment[s]
430    for( JPEGFragmentsInfo::JPEGFragmentsList::iterator
431         it  = JPEGInfo->Fragments.begin();
432         it != JPEGInfo->Fragments.end();
433       ++it )
434    {
435       fseek( fp, (*it)->Offset, SEEK_SET );
436                                                                                 
437       if ( isJPEG2000 )
438       {
439          if ( ! gdcm_read_JPEG2000_file( fp, destination ) )
440          {
441             return false;
442          }
443       }
444       else if ( isJPEGLossless )
445       {
446          // JPEG LossLess : call to xmedcom Lossless JPEG
447          JPEGLosslessDecodeImage( fp,
448                                   (uint16_t*)destination,
449                                   PixelSize * 8 * SamplesPerPixel,
450                                   (*it)->Length );
451       }
452       else if ( BitsStored == 8)
453       {
454          // JPEG Lossy : call to IJG 6b
455          if ( ! gdcm_read_JPEG_file ( fp, destination ) )
456          {
457             return false;
458          }
459       }
460       else if ( BitsStored == 12)
461       {
462          // Reading Fragment pixels
463          if ( ! gdcm_read_JPEG_file12 ( fp, destination ) )
464          {
465             return false;
466          }
467       }
468       else
469       {
470          // other JPEG lossy not supported
471          dbg.Error(" File::ReadPixelData : unknown jpeg lossy "
472                    " compression ");
473          return false;
474       }
475                                                                                 
476       // Advance to next free location in destination 
477       // for next fragment decompression (if any)
478       int length = XSize * YSize * SamplesPerPixel;
479       int numberBytes = BitsAllocated / 8;
480                                                                                 
481       destination = (uint8_t*)destination + length * numberBytes;
482                                                                                 
483    }
484    return true;
485 }
486
487 /**
488  * \brief  Re-arrange the bits within the bytes.
489  * @param  fp already open File Pointer
490  * @param  destination Where decompressed fragments should end up
491  * @return Boolean
492  */
493 bool PixelConvert::ConvertReArrangeBits(
494                           uint8_t* pixelZone,
495                           size_t imageDataSize,
496                           int numberBitsStored,
497                           int numberBitsAllocated,
498                           int highBitPosition )
499      throw ( FormatError )
500 {
501    if ( numberBitsStored != numberBitsAllocated )
502    {
503       int l = (int)(imageDataSize / (numberBitsAllocated/8));
504       if ( numberBitsAllocated == 16 )
505       {
506          uint16_t mask = 0xffff;
507          mask = mask >> ( numberBitsAllocated - numberBitsStored );
508          uint16_t* deb = (uint16_t*)pixelZone;
509          for(int i = 0; i<l; i++)
510          {
511             *deb = (*deb >> (numberBitsStored - highBitPosition - 1)) & mask;
512             deb++;
513          }
514       }
515       else if ( numberBitsAllocated == 32 )
516       {
517          uint32_t mask = 0xffffffff;
518          mask = mask >> ( numberBitsAllocated - numberBitsStored );
519          uint32_t* deb = (uint32_t*)pixelZone;
520          for(int i = 0; i<l; i++)
521          {
522             *deb = (*deb >> (numberBitsStored - highBitPosition - 1)) & mask;
523             deb++;
524          }
525       }
526       else
527       {
528          dbg.Verbose(0, "PixelConvert::ConvertReArrangeBits: weird image");
529          throw FormatError( "File::ConvertReArrangeBits()",
530                                 "weird image !?" );
531       }
532    }
533    return true; //???
534 }
535 } // end namespace gdcm