1 /*
2  *
3  *  Copyright (C) 2001-2014, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module:  dcmjpeg
15  *
16  *  Author:  Norbert Olges, Marco Eichelberg
17  *
18  *  Purpose: decompression routines of the IJG JPEG library configured for 8 bits/sample.
19  *
20  */
21 
22 #include "dcmtk/config/osconfig.h"
23 #include "dcmtk/dcmjpeg/djdijg8.h"
24 #include "dcmtk/dcmjpeg/djcparam.h"
25 #include "dcmtk/dcmdata/dcerror.h"
26 
27 #define INCLUDE_CSTDIO
28 #define INCLUDE_CSETJMP
29 #define INCLUDE_CSTRING
30 #include "dcmtk/ofstd/ofstdinc.h"
31 
32 // These two macros are re-defined in the IJG header files.
33 // We undefine them here and hope that IJG's configure has
34 // come to the same conclusion that we have...
35 #ifdef HAVE_STDLIB_H
36 #undef HAVE_STDLIB_H
37 #endif
38 #ifdef HAVE_STDDEF_H
39 #undef HAVE_STDDEF_H
40 #endif
41 
42 BEGIN_EXTERN_C
43 #define boolean ijg_boolean
44 #include "jpeglib8.h"
45 #include "jerror8.h"
46 #undef boolean
47 
48 // disable any preprocessor magic the IJG library might be doing with the "const" keyword
49 #ifdef const
50 #undef const
51 #endif
52 
53 #ifdef USE_STD_CXX_INCLUDES
54 // Solaris defines longjmp() in namespace std, other compilers don't...
55 namespace std { }
56 using namespace std;
57 #endif
58 
59 // private error handler struct
60 struct DJDIJG8ErrorStruct
61 {
62   // the standard IJG error handler object
63   struct jpeg_error_mgr pub;
64 
65   // our jump buffer
66   jmp_buf setjmp_buffer;
67 
68   // pointer to this
69   DJDecompressIJG8Bit *instance;
70 };
71 
72 // private source manager struct
73 struct DJDIJG8SourceManagerStruct
74 {
75   // the standard IJG source manager object
76   struct jpeg_source_mgr pub;
77 
78   // number of bytes to skip at start of buffer
79   long skip_bytes;
80 
81   // buffer from which reading will continue as soon as the current buffer is empty
82   Uint8 *next_buffer;
83 
84   // buffer size
85   Uint32 next_buffer_size;
86 };
87 
88 // callback forward declarations
89 void DJDIJG8ErrorExit(j_common_ptr);
90 void DJDIJG8EmitMessage(j_common_ptr cinfo, int msg_level);
91 void DJDIJG8initSource(j_decompress_ptr);
92 ijg_boolean DJDIJG8fillInputBuffer(j_decompress_ptr);
93 void DJDIJG8skipInputData(j_decompress_ptr, long);
94 void DJDIJG8termSource(j_decompress_ptr);
95 
96 // helper method to fix old-style casts warnings
OFjpeg_create_decompress(j_decompress_ptr cinfo)97 static void OFjpeg_create_decompress(j_decompress_ptr cinfo)
98 {
99   jpeg_create_decompress(cinfo);
100 }
101 
102 END_EXTERN_C
103 
104 
105 // error handler, executes longjmp
DJDIJG8ErrorExit(j_common_ptr cinfo)106 void DJDIJG8ErrorExit(j_common_ptr cinfo)
107 {
108   DJDIJG8ErrorStruct *myerr = OFreinterpret_cast(DJDIJG8ErrorStruct*, cinfo->err);
109   longjmp(myerr->setjmp_buffer, 1);
110 }
111 
112 // message handler for warning messages and the like
DJDIJG8EmitMessage(j_common_ptr cinfo,int msg_level)113 void DJDIJG8EmitMessage(j_common_ptr cinfo, int msg_level)
114 {
115   DJDIJG8ErrorStruct *myerr = OFreinterpret_cast(DJDIJG8ErrorStruct*, cinfo->err);
116   myerr->instance->emitMessage(msg_level);
117 }
118 
119 
120 // methods for decompress-source-manager
121 
DJDIJG8initSource(j_decompress_ptr)122 void DJDIJG8initSource(j_decompress_ptr /* cinfo */)
123 {
124 }
125 
DJDIJG8fillInputBuffer(j_decompress_ptr cinfo)126 ijg_boolean DJDIJG8fillInputBuffer(j_decompress_ptr cinfo)
127 {
128   DJDIJG8SourceManagerStruct *src = OFreinterpret_cast(DJDIJG8SourceManagerStruct*, cinfo->src);
129 
130   // if we already have the next buffer, switch buffers
131   if (src->next_buffer)
132   {
133     src->pub.next_input_byte    = src->next_buffer;
134     src->pub.bytes_in_buffer    = OFstatic_cast(unsigned int, src->next_buffer_size);
135     src->next_buffer            = NULL;
136     src->next_buffer_size       = 0;
137 
138     // The suspension was caused by DJDIJG8skipInputData iff src->skip_bytes > 0.
139     // In this case we must skip the remaining number of bytes here.
140     if (src->skip_bytes > 0)
141     {
142       if (src->pub.bytes_in_buffer < OFstatic_cast(unsigned long, src->skip_bytes))
143       {
144         src->skip_bytes            -= OFstatic_cast(Uint32, src->pub.bytes_in_buffer);
145         src->pub.next_input_byte   += src->pub.bytes_in_buffer;
146         src->pub.bytes_in_buffer    = 0;
147         // cause a suspension return
148         return FALSE;
149       }
150       else
151       {
152         src->pub.bytes_in_buffer   -= OFstatic_cast(unsigned int, src->skip_bytes);
153         src->pub.next_input_byte   += src->skip_bytes;
154         src->skip_bytes             = 0;
155       }
156     }
157     return TRUE;
158   }
159 
160   // otherwise cause a suspension return
161   return FALSE;
162 }
163 
DJDIJG8skipInputData(j_decompress_ptr cinfo,long num_bytes)164 void DJDIJG8skipInputData(
165   j_decompress_ptr cinfo,
166   long num_bytes)
167 {
168   DJDIJG8SourceManagerStruct *src = OFreinterpret_cast(DJDIJG8SourceManagerStruct*, cinfo->src);
169 
170   if (src->pub.bytes_in_buffer < OFstatic_cast(size_t, num_bytes))
171   {
172     src->skip_bytes             = num_bytes - OFstatic_cast(Uint32, src->pub.bytes_in_buffer);
173     src->pub.next_input_byte   += src->pub.bytes_in_buffer;
174     src->pub.bytes_in_buffer    = 0; // causes a suspension return
175   }
176   else
177   {
178     src->pub.bytes_in_buffer   -= OFstatic_cast(unsigned int, num_bytes);
179     src->pub.next_input_byte   += num_bytes;
180     src->skip_bytes             = 0;
181   }
182 }
183 
DJDIJG8termSource(j_decompress_ptr)184 void DJDIJG8termSource(j_decompress_ptr /* cinfo */)
185 {
186 }
187 
188 
DJDecompressIJG8Bit(const DJCodecParameter & cp,OFBool isYBR)189 DJDecompressIJG8Bit::DJDecompressIJG8Bit(const DJCodecParameter& cp, OFBool isYBR)
190 : DJDecoder()
191 , cparam(&cp)
192 , cinfo(NULL)
193 , suspension(0)
194 , jsampBuffer(NULL)
195 , dicomPhotometricInterpretationIsYCbCr(isYBR)
196 , decompressedColorModel(EPI_Unknown)
197 {
198 }
199 
~DJDecompressIJG8Bit()200 DJDecompressIJG8Bit::~DJDecompressIJG8Bit()
201 {
202   cleanup();
203 }
204 
205 
init()206 OFCondition DJDecompressIJG8Bit::init()
207 {
208   suspension = 0;
209   decompressedColorModel = EPI_Unknown;
210   cleanup(); // prevent double initialization
211 
212   cinfo = new jpeg_decompress_struct();
213   if (cinfo)
214   {
215     volatile DJDIJG8SourceManagerStruct *src = NULL;
216     volatile DJDIJG8ErrorStruct *jerr = new DJDIJG8ErrorStruct();
217     if (jerr)
218     {
219       src = new DJDIJG8SourceManagerStruct();
220       if (src)
221       {
222         // Specify the source of the compressed data
223         src->pub.init_source       = DJDIJG8initSource;
224         src->pub.fill_input_buffer = DJDIJG8fillInputBuffer;
225         src->pub.skip_input_data   = DJDIJG8skipInputData;
226         src->pub.resync_to_restart = jpeg_resync_to_restart;
227         src->pub.term_source       = DJDIJG8termSource;
228         src->pub.bytes_in_buffer   = 0;
229         src->pub.next_input_byte   = NULL;
230         src->skip_bytes             = 0;
231         src->next_buffer            = NULL;
232         src->next_buffer_size       = 0;
233       }
234       else
235       {
236         delete OFconst_cast(DJDIJG8ErrorStruct *, jerr);
237         delete cinfo;
238         cinfo = NULL;
239         return EC_MemoryExhausted;
240       }
241       cinfo->err = jpeg_std_error(& OFconst_cast(DJDIJG8ErrorStruct *, jerr)->pub);
242       jerr->instance = this;
243       jerr->pub.error_exit = DJDIJG8ErrorExit;
244       jerr->pub.emit_message = DJDIJG8EmitMessage;
245       if (setjmp(OFconst_cast(DJDIJG8ErrorStruct *, jerr)->setjmp_buffer))
246       {
247         // the IJG error handler will cause the following code to be executed
248         char buffer[JMSG_LENGTH_MAX];
249         (*cinfo->err->format_message)(OFreinterpret_cast(jpeg_common_struct*, cinfo), buffer); /* Create the message */
250         cleanup();
251         delete OFconst_cast(DJDIJG8SourceManagerStruct *, src);
252         return makeOFCondition(OFM_dcmjpeg, EJCode_IJG8_Decompression, OF_error, buffer);
253       }
254     }
255     else
256     {
257       delete cinfo;
258       cinfo = NULL;
259       return EC_MemoryExhausted;
260     }
261     OFjpeg_create_decompress(cinfo);
262     cinfo->src = &OFconst_cast(DJDIJG8SourceManagerStruct *, src)->pub;
263   } else return EC_MemoryExhausted;
264 
265   // everything OK
266   return EC_Normal;
267 }
268 
269 
cleanup()270 void DJDecompressIJG8Bit::cleanup()
271 {
272   if (cinfo)
273   {
274     jpeg_destroy_decompress(cinfo);
275     delete OFreinterpret_cast(DJDIJG8ErrorStruct*, cinfo->err);
276     delete OFreinterpret_cast(DJDIJG8SourceManagerStruct*, cinfo->src);
277     delete cinfo;
278     cinfo = NULL;
279   }
280 }
281 
282 
decode(Uint8 * compressedFrameBuffer,Uint32 compressedFrameBufferSize,Uint8 * uncompressedFrameBuffer,Uint32 uncompressedFrameBufferSize,OFBool isSigned)283 OFCondition DJDecompressIJG8Bit::decode(
284   Uint8 *compressedFrameBuffer,
285   Uint32 compressedFrameBufferSize,
286   Uint8 *uncompressedFrameBuffer,
287   Uint32 uncompressedFrameBufferSize,
288   OFBool isSigned)
289 {
290 
291   if (cinfo==NULL || compressedFrameBuffer==NULL || uncompressedFrameBuffer==NULL) return EC_IllegalCall;
292 
293   if (setjmp(OFreinterpret_cast(DJDIJG8ErrorStruct*, cinfo->err)->setjmp_buffer))
294   {
295     // the IJG error handler will cause the following code to be executed
296     char buffer[JMSG_LENGTH_MAX];
297     (*cinfo->err->format_message)(OFreinterpret_cast(jpeg_common_struct*, cinfo), buffer); /* Create the message */
298     cleanup();
299     return makeOFCondition(OFM_dcmjpeg, EJCode_IJG8_Decompression, OF_error, buffer);
300   }
301 
302   // feed compressed buffer into cinfo structure.
303   // The buffer will be activated by the next call to DJDIJG8fillInputBuffer.
304   DJDIJG8SourceManagerStruct *src = OFreinterpret_cast(DJDIJG8SourceManagerStruct*, cinfo->src);
305   src->next_buffer = compressedFrameBuffer;
306   src->next_buffer_size = compressedFrameBufferSize;
307 
308   // Obtain image info
309   if (suspension < 2)
310   {
311     if (JPEG_SUSPENDED == jpeg_read_header(cinfo, TRUE))
312     {
313       suspension = 1;
314       return EJ_Suspension;
315     }
316 
317     // check if color space conversion is enabled
318     OFBool colorSpaceConversion = OFFalse;
319     // check whether to use the IJG library guess for the JPEG color space
320     OFBool colorSpaceGuess = OFFalse;
321     switch (cparam->getDecompressionColorSpaceConversion())
322     {
323       case EDC_photometricInterpretation: // color space conversion if DICOM photometric interpretation is YCbCr
324         colorSpaceConversion = dicomPhotometricInterpretationIsYCbCr;
325         break;
326       case EDC_lossyOnly: // color space conversion if lossy JPEG
327         if (cinfo->process != JPROC_LOSSLESS)
328           colorSpaceConversion = OFTrue;
329         break;
330       case EDC_always: // always do color space conversion
331         colorSpaceConversion = OFTrue;
332         break;
333       case EDC_never: // never do color space conversion
334         break;
335       case EDC_guessLossyOnly: // use color space guess by IJG library if lossy JPEG
336         if (cinfo->process != JPROC_LOSSLESS)
337         {
338           colorSpaceGuess = OFTrue;
339           if (cinfo->jpeg_color_space == JCS_YCbCr)
340             colorSpaceConversion = OFTrue;
341         }
342         break;
343       case EDC_guess: // always use color space guess by IJG library
344         colorSpaceGuess = OFTrue;
345         if (cinfo->jpeg_color_space == JCS_YCbCr)
346           colorSpaceConversion = OFTrue;
347         break;
348     }
349     // decline color space conversion to RGB for signed pixel data,
350     // because IJG can handle only unsigned
351     if (colorSpaceConversion && isSigned)
352       return EJ_UnsupportedColorConversion;
353 
354     // let the IJG library guess the JPEG color space
355     if (colorSpaceGuess)
356     {
357       switch (cinfo->jpeg_color_space)
358       {
359         case JCS_GRAYSCALE:
360           // cinfo->out_color_space = JCS_GRAYSCALE;
361           decompressedColorModel = EPI_Monochrome2;
362           break;
363         case JCS_YCbCr: // enforce conversion YCbCr to RGB
364           cinfo->out_color_space = JCS_RGB;
365           decompressedColorModel = EPI_RGB;
366           break;
367         case JCS_RGB:
368           // cinfo->out_color_space = JCS_RGB;
369           decompressedColorModel = EPI_RGB;
370           break;
371         default:
372           decompressedColorModel = EPI_Unknown;
373           break;
374       }
375     }
376     // set color space for decompression
377     else if (colorSpaceConversion)
378     {
379       switch (cinfo->out_color_space)
380       {
381         case JCS_GRAYSCALE:
382           decompressedColorModel = EPI_Monochrome2;
383           break;
384         case JCS_YCbCr: // enforce conversion YCbCr to RGB
385           cinfo->jpeg_color_space = JCS_YCbCr;
386           cinfo->out_color_space = JCS_RGB;
387           decompressedColorModel = EPI_RGB;
388           break;
389         case JCS_RGB: // enforce conversion YCbCr to RGB
390           cinfo->jpeg_color_space = JCS_YCbCr;
391           decompressedColorModel = EPI_RGB;
392           break;
393         default:
394           decompressedColorModel = EPI_Unknown;
395           break;
396       }
397     }
398     else
399     {
400       decompressedColorModel = EPI_Unknown;
401       // prevent the library from performing any color space conversion
402       cinfo->jpeg_color_space = JCS_UNKNOWN;
403       cinfo->out_color_space = JCS_UNKNOWN;
404     }
405   }
406 
407   JSAMPARRAY buffer = NULL;
408   int bufsize = 0;
409   size_t rowsize = 0;
410 
411   if (suspension < 3)
412   {
413     if (FALSE == jpeg_start_decompress(cinfo))
414     {
415       suspension = 2;
416       return EJ_Suspension;
417     }
418     bufsize = cinfo->output_width * cinfo->output_components; // number of JSAMPLEs per row
419     rowsize = bufsize * sizeof(JSAMPLE); // number of bytes per row
420     buffer = (*cinfo->mem->alloc_sarray)(OFreinterpret_cast(j_common_ptr, cinfo), JPOOL_IMAGE, bufsize, 1);
421     if (buffer == NULL) return EC_MemoryExhausted;
422     jsampBuffer = buffer;
423   }
424   else
425   {
426     bufsize = cinfo->output_width * cinfo->output_components;
427     rowsize = bufsize * sizeof(JSAMPLE);
428     buffer = OFreinterpret_cast(JSAMPARRAY, jsampBuffer);
429   }
430 
431   if (uncompressedFrameBufferSize < rowsize * cinfo->output_height) return EJ_IJG8_FrameBufferTooSmall;
432 
433   while (cinfo->output_scanline < cinfo->output_height)
434   {
435     if (0 == jpeg_read_scanlines(cinfo, buffer, 1))
436     {
437       suspension = 3;
438       return EJ_Suspension;
439     }
440     memcpy(uncompressedFrameBuffer + (cinfo->output_scanline-1) * rowsize, *buffer, rowsize);
441   }
442 
443   if (FALSE == jpeg_finish_decompress(cinfo))
444   {
445     suspension = 4;
446     return EJ_Suspension;
447   }
448 
449   return EC_Normal;
450 }
451 
emitMessage(int msg_level) const452 void DJDecompressIJG8Bit::emitMessage(int msg_level) const
453 {
454   // This is how we map the message levels:
455   // -1 - 0: Warning (could also be errors, but no way to find out)
456   //  1    : Debug
457   // Everything else: Trace (No point in splitting this further up)
458   OFLogger::LogLevel level;
459 
460   switch (msg_level)
461   {
462   case -1:
463   case 0:
464     level = OFLogger::WARN_LOG_LEVEL;
465     break;
466   case 1:
467     level = OFLogger::DEBUG_LOG_LEVEL;
468     break;
469   default:
470     level = OFLogger::TRACE_LOG_LEVEL;
471     break;
472   }
473 
474   if (cinfo && DCM_dcmjpegLogger.isEnabledFor(level))
475   {
476     char buffer[JMSG_LENGTH_MAX];
477     (*cinfo->err->format_message)(OFreinterpret_cast(jpeg_common_struct*, cinfo), buffer); /* Create the message */
478     DCM_dcmjpegLogger.forcedLog(level, buffer, __FILE__, __LINE__);
479   }
480 }
481