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