1 /*=========================================================================
2 *
3 * Copyright Insight Software Consortium
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0.txt
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *=========================================================================*/
18
19 #include "itkJPEGImageIO.h"
20 #include "itksys/SystemTools.hxx"
21
22 #include "itk_jpeg.h"
23 #include <csetjmp>
24
25 // create an error handler for jpeg that
26 // can longjmp out of the jpeg library
27 struct itk_jpeg_error_mgr {
28 struct jpeg_error_mgr pub; /* "public" fields */
29 jmp_buf setjmp_buffer; /* for return to caller */
30 };
31
32 extern "C" {
itk_jpeg_error_exit(j_common_ptr cinfo)33 METHODDEF(void) itk_jpeg_error_exit (j_common_ptr cinfo)
34 {
35 /* cinfo->err really points to a itk_jpeg_error_mgr struct, so coerce pointer
36 */
37 auto * myerr = reinterpret_cast<itk_jpeg_error_mgr *>(cinfo->err);
38
39 /* Always display the message. */
40 /* We could postpone this until after returning, if we chose. */
41 ( *cinfo->err->output_message )(cinfo);
42
43 jpeg_abort(cinfo); /* clean up libjpeg state */
44 /* Return control to the setjmp point */
45 longjmp(myerr->setjmp_buffer, 1);
46 }
47
itk_jpeg_output_message(j_common_ptr)48 METHODDEF(void) itk_jpeg_output_message (j_common_ptr)
49 {
50 }
51 }
52
53 namespace itk
54 {
55
56 namespace
57 {
58 // Wrap setjmp call to avoid warnings about variable clobbering.
wrapSetjmp(itk_jpeg_error_mgr & jerr)59 bool wrapSetjmp( itk_jpeg_error_mgr & jerr )
60 {
61 if( setjmp( jerr.setjmp_buffer ) )
62 {
63 return true;
64 }
65 return false;
66 }
67 }
68
69 // simple class to call fopen on construct and
70 // fclose on destruct
71 class JPEGFileWrapper
72 {
73 public:
JPEGFileWrapper(const char * const fname,const char * const openMode)74 JPEGFileWrapper(const char *const fname, const char *const openMode):m_FilePointer(nullptr)
75 {
76 m_FilePointer = fopen(fname, openMode);
77 }
78
~JPEGFileWrapper()79 virtual ~JPEGFileWrapper()
80 {
81 if ( m_FilePointer != nullptr )
82 {
83 fclose(m_FilePointer);
84 }
85 }
86
87 FILE *m_FilePointer;
88 };
89
CanReadFile(const char * file)90 bool JPEGImageIO::CanReadFile(const char *file)
91 {
92 // First check the extension
93 std::string filename = file;
94
95 if ( filename.empty() )
96 {
97 itkDebugMacro(<< "No filename specified.");
98 return false;
99 }
100
101 bool extensionFound = this->HasSupportedReadExtension(file, false);
102
103 if ( !extensionFound )
104 {
105 itkDebugMacro(<< "The filename extension is not recognized");
106 return false;
107 }
108
109 // Now check the file header
110 JPEGFileWrapper JPEGfp(file, "rb");
111 if ( JPEGfp.m_FilePointer == nullptr )
112 {
113 return false;
114 }
115
116 // read the first two bytes
117 unsigned char magic[2];
118 auto n = static_cast< int >( fread(magic, sizeof( magic ), 1, JPEGfp.m_FilePointer) );
119 if ( n != 1 )
120 {
121 return false;
122 }
123
124 // check for the magic stuff:
125 // 0xFF followed by 0xD8
126 if ( magic[0] != 0xFF
127 || magic[1] != 0xD8 )
128 {
129 return false;
130 }
131 // go back to the start of the file
132 fseek(JPEGfp.m_FilePointer, 0, SEEK_SET);
133 // magic number is ok, try and read the header
134 struct itk_jpeg_error_mgr jerr;
135 struct jpeg_decompress_struct cinfo;
136 cinfo.err = jpeg_std_error(&jerr.pub);
137 // for any jpeg error call itk_jpeg_error_exit
138 jerr.pub.error_exit = itk_jpeg_error_exit;
139 // for any output message call itk_jpeg_output_message
140 jerr.pub.output_message = itk_jpeg_output_message;
141 // set the jump point, if there is a jpeg error or warning
142 // this will evaluate to true
143 if( wrapSetjmp( jerr ) )
144 {
145 // clean up
146 jpeg_destroy_decompress(&cinfo);
147 // this is not a valid jpeg file
148 return false;
149 }
150 /* Now we can initialize the JPEG decompression object. */
151 jpeg_create_decompress(&cinfo);
152 /* Step 2: specify data source (eg, a file) */
153 jpeg_stdio_src(&cinfo, JPEGfp.m_FilePointer);
154 /* Step 3: read file parameters with jpeg_read_header() */
155 jpeg_read_header(&cinfo, TRUE);
156
157 // if no errors have occurred yet, then it must be jpeg
158 jpeg_destroy_decompress(&cinfo);
159
160 return true;
161 }
162
ReadVolume(void *)163 void JPEGImageIO::ReadVolume(void *)
164 {}
165
166 //-----------------------------------------------------------------------------
167
Read(void * buffer)168 void JPEGImageIO::Read(void *buffer)
169 {
170 unsigned int ui;
171
172 // use this class so return will call close
173 JPEGFileWrapper JPEGfp(this->GetFileName(), "rb");
174 FILE * fp = JPEGfp.m_FilePointer;
175
176 if ( !fp )
177 {
178 itkExceptionMacro( "Error JPEGImageIO could not open file: "
179 << this->GetFileName()
180 << std::endl
181 << "Reason: "
182 << itksys::SystemTools::GetLastSystemError() );
183 }
184
185 // create jpeg decompression object and error handler
186 struct jpeg_decompress_struct cinfo;
187 struct itk_jpeg_error_mgr jerr;
188
189 cinfo.err = jpeg_std_error(&jerr.pub);
190 // for any jpeg error call itk_jpeg_error_exit
191 jerr.pub.error_exit = itk_jpeg_error_exit;
192 // for any output message call itk_jpeg_output_message
193 jerr.pub.output_message = itk_jpeg_output_message;
194 if( wrapSetjmp( jerr ) )
195 {
196 // clean up
197 jpeg_destroy_decompress(&cinfo);
198 itkExceptionMacro( "libjpeg could not read file: "
199 << this->GetFileName() );
200 // this is not a valid jpeg file
201 }
202
203 jpeg_create_decompress(&cinfo);
204
205 // set the source file
206 jpeg_stdio_src(&cinfo, fp);
207
208 // read the header
209 jpeg_read_header(&cinfo, TRUE);
210
211 // prepare to read the bulk data
212 jpeg_start_decompress(&cinfo);
213
214 SizeValueType rowbytes = cinfo.output_components * cinfo.output_width;
215 auto * tempImage = static_cast< JSAMPLE * >( buffer );
216
217 auto * row_pointers = new JSAMPROW[cinfo.output_height];
218 for ( ui = 0; ui < cinfo.output_height; ++ui )
219 {
220 row_pointers[ui] = tempImage + rowbytes * ui;
221 }
222
223 // read the bulk data
224 unsigned int remainingRows;
225 while ( cinfo.output_scanline < cinfo.output_height )
226 {
227 remainingRows = cinfo.output_height - cinfo.output_scanline;
228 jpeg_read_scanlines(&cinfo, &row_pointers[cinfo.output_scanline],
229 remainingRows);
230 }
231
232 // finish the decompression step
233 jpeg_finish_decompress(&cinfo);
234
235 // destroy the decompression object
236 jpeg_destroy_decompress(&cinfo);
237
238 delete[] row_pointers;
239 }
240
JPEGImageIO()241 JPEGImageIO::JPEGImageIO()
242 {
243 this->SetNumberOfDimensions(2);
244 m_PixelType = SCALAR;
245 // 12bits is not working right now, but this should be doable
246 #if BITS_IN_JSAMPLE == 8
247 m_ComponentType = UCHAR;
248 #else
249 m_ComponentType = UINT;
250 #endif
251 m_UseCompression = false;
252 this->Self::SetQuality(95);
253 m_Progressive = true;
254 m_Spacing[0] = 1.0;
255 m_Spacing[1] = 1.0;
256
257 m_Origin[0] = 0.0;
258 m_Origin[1] = 0.0;
259
260 const char *extensions[] =
261 {
262 ".jpg",".JPG", ".jpeg", ".JPEG"
263 };
264
265 for(auto ext : extensions)
266 {
267 this->AddSupportedWriteExtension(ext);
268 this->AddSupportedReadExtension(ext);
269 }
270 }
271
272 JPEGImageIO::~JPEGImageIO() = default;
273
PrintSelf(std::ostream & os,Indent indent) const274 void JPEGImageIO::PrintSelf(std::ostream & os, Indent indent) const
275 {
276 Superclass::PrintSelf(os, indent);
277 os << indent << "Quality : " << this->GetQuality() << "\n";
278 os << indent << "Progressive : " << m_Progressive << "\n";
279 }
280
ReadImageInformation()281 void JPEGImageIO::ReadImageInformation()
282 {
283 m_Spacing[0] = 1.0; // We'll look for JPEG pixel size information later,
284 m_Spacing[1] = 1.0; // but set the defaults now
285
286 m_Origin[0] = 0.0;
287 m_Origin[1] = 0.0;
288
289 // use this class so return will call close
290 JPEGFileWrapper JPEGfp(m_FileName.c_str(), "rb");
291 FILE * fp = JPEGfp.m_FilePointer;
292 if ( !fp )
293 {
294 itkExceptionMacro( "Error JPEGImageIO could not open file: "
295 << this->GetFileName()
296 << std::endl
297 << "Reason: "
298 << itksys::SystemTools::GetLastSystemError() );
299 }
300
301 // create jpeg decompression object and error handler
302 struct jpeg_decompress_struct cinfo;
303 struct itk_jpeg_error_mgr jerr;
304
305 cinfo.err = jpeg_std_error(&jerr.pub);
306 jerr.pub.error_exit = itk_jpeg_error_exit;
307 if ( setjmp(jerr.setjmp_buffer) )
308 {
309 // clean up
310 jpeg_destroy_decompress(&cinfo);
311 // this is not a valid jpeg file
312 itkExceptionMacro( "Error JPEGImageIO could not open file: "
313 << this->GetFileName() );
314 }
315 jpeg_create_decompress(&cinfo);
316
317 // set the source file
318 jpeg_stdio_src(&cinfo, fp);
319
320 // read the header
321 jpeg_read_header(&cinfo, TRUE);
322
323 // force the output image size to be calculated (we could have used
324 // cinfo.image_height etc. but that would preclude using libjpeg's
325 // ability to scale an image on input).
326 jpeg_calc_output_dimensions(&cinfo);
327
328 // pull out the width/height
329 this->SetNumberOfDimensions(2);
330 m_Dimensions[0] = cinfo.output_width;
331 m_Dimensions[1] = cinfo.output_height;
332
333 this->SetNumberOfComponents(cinfo.output_components);
334
335 switch ( this->GetNumberOfComponents() )
336 {
337 case 1:
338 m_PixelType = SCALAR;
339 break;
340 case 2:
341 m_PixelType = VECTOR;
342 break;
343 case 3:
344 m_PixelType = RGB;
345 break;
346 case 4:
347 m_PixelType = RGBA;
348 break;
349 }
350
351 // If we have some spacing information we use it
352 if ( cinfo.density_unit > 0
353 && cinfo.X_density > 0
354 && cinfo.Y_density > 0
355 )
356 {
357 if ( cinfo.density_unit == 1 ) // inches
358 {
359 m_Spacing[0] = 25.4 / cinfo.X_density;
360 m_Spacing[1] = 25.4 / cinfo.Y_density;
361 }
362 else if ( cinfo.density_unit == 2 ) // cm
363 {
364 m_Spacing[0] = 10.0 / cinfo.X_density;
365 m_Spacing[1] = 10.0 / cinfo.Y_density;
366 }
367 }
368
369 // close the file
370 jpeg_destroy_decompress(&cinfo);
371 }
372
CanWriteFile(const char * name)373 bool JPEGImageIO::CanWriteFile(const char *name)
374 {
375 std::string filename = name;
376
377 if ( filename.empty() )
378 {
379 return false;
380 }
381
382 return this->HasSupportedWriteExtension(name, false);
383 }
384
WriteImageInformation()385 void JPEGImageIO::WriteImageInformation()
386 {}
387
Write(const void * buffer)388 void JPEGImageIO::Write(const void *buffer)
389 {
390 // the IORegion is not required to be set so we must use GetNumberOfDimensions
391 if ( this->GetNumberOfDimensions() != 2 )
392 {
393 itkExceptionMacro(<< "JPEG Writer can only write 2-dimensional images");
394 }
395
396 if ( this->GetComponentType() != UCHAR
397 && this->GetComponentType() != UINT )
398 {
399 itkExceptionMacro(<< "JPEG supports unsigned char/int only");
400 }
401
402 this->WriteSlice(m_FileName, buffer);
403 }
404
WriteSlice(std::string & fileName,const void * buffer)405 void JPEGImageIO::WriteSlice(std::string & fileName, const void *buffer)
406 {
407 // use this class so return will call close
408 JPEGFileWrapper JPEGfp(fileName.c_str(), "wb");
409 FILE * fp = JPEGfp.m_FilePointer;
410
411 if ( !fp )
412 {
413 itkExceptionMacro( "Unable to open file "
414 << fileName
415 << " for writing."
416 << std::endl
417 << "Reason: "
418 << itksys::SystemTools::GetLastSystemError() );
419 }
420
421 // Call the correct templated function for the output
422
423 // overriding jpeg_error_mgr so we don't exit when an error happens
424 // Create the jpeg compression object and error handler
425 //struct jpeg_compress_struct cinfo;
426 //struct itk_jpeg_error_mgr jerr;
427
428 struct itk_jpeg_error_mgr jerr;
429 struct jpeg_compress_struct cinfo;
430 cinfo.err = jpeg_std_error(&jerr.pub);
431 // set the jump point, if there is a jpeg error or warning
432 // this will evaluate to true
433 if ( wrapSetjmp( jerr ) )
434 {
435 jpeg_destroy_compress(&cinfo);
436 itkExceptionMacro(<< "JPEG : Out of disk space");
437 }
438
439 jpeg_create_compress(&cinfo);
440
441 // set the destination file
442 //struct jpeg_destination_mgr compressionDestination;
443 jpeg_stdio_dest(&cinfo, fp);
444
445 // set the information about image
446 const SizeValueType width = m_Dimensions[0];
447 const SizeValueType height = m_Dimensions[1];
448
449 // The JPEG standard only supports images up to 64K*64K due to 16-bit fields
450 // in SOF markers.
451 cinfo.image_width = width;
452 cinfo.image_height = height;
453 if( cinfo.image_width > 65536 || cinfo.image_height > 65536 )
454 {
455 itkExceptionMacro(<<"JPEG : Image is too large for JPEG");
456 }
457
458 cinfo.input_components = this->GetNumberOfComponents();
459 const unsigned int numComp = this->GetNumberOfComponents();
460
461 // Maximum number of components (color channels) allowed in JPEG image.
462 // JPEG spec set this to 255. However ijg default it to 10.
463 if( cinfo.input_components > 255)
464 {
465 itkExceptionMacro(<<"JPEG : Too many components for JPEG");
466 }
467 if( cinfo.input_components > MAX_COMPONENTS)
468 {
469 itkExceptionMacro(<<"JPEG : Too many components for IJG. Recompile IJG.");
470 }
471
472 switch ( cinfo.input_components )
473 {
474 case 1:
475 cinfo.in_color_space = JCS_GRAYSCALE;
476 break;
477 case 3:
478 cinfo.in_color_space = JCS_RGB;
479 break;
480 default:
481 cinfo.in_color_space = JCS_UNKNOWN;
482 break;
483 }
484
485 // set the compression parameters
486 jpeg_set_defaults(&cinfo); // start with reasonable defaults
487 jpeg_set_quality(&cinfo, this->GetQuality(), TRUE);
488 if ( m_Progressive )
489 {
490 jpeg_simple_progression(&cinfo);
491 }
492
493 if ( m_Spacing[0] > 0 && m_Spacing[1] > 0 )
494 {
495 // store the spacing information as pixels per inch or cm, depending on which option
496 // retains as much precision as possible
497 std::vector< UINT16 > densityPerInch( 2 );
498 densityPerInch[0] = static_cast<UINT16>(25.4/m_Spacing[0] + 0.5);
499 densityPerInch[1] = static_cast<UINT16>(25.4/m_Spacing[1] + 0.5);
500
501 std::vector< UINT16 > densityPerCm( 2 );
502 densityPerCm[0] = static_cast<UINT16>(10.0/m_Spacing[0] + 0.5);
503 densityPerCm[1] = static_cast<UINT16>(10.0/m_Spacing[1] + 0.5);
504
505 if (std::abs(25.4/m_Spacing[0] - densityPerInch[0]) + std::abs(25.4/m_Spacing[1] - densityPerInch[1])
506 <= std::abs(10.0/m_Spacing[0] - densityPerCm[0]) + std::abs(10.0/m_Spacing[1] - densityPerCm[1]))
507 {
508 cinfo.density_unit = 1;
509 cinfo.X_density = densityPerInch[0];
510 cinfo.Y_density = densityPerInch[1];
511 }
512 else
513 {
514 cinfo.density_unit = 0;
515 cinfo.X_density = densityPerCm[0];
516 cinfo.Y_density = densityPerCm[1];
517 }
518 }
519
520 // start compression
521 jpeg_start_compress(&cinfo, TRUE);
522
523 volatile const JSAMPLE *outPtr = ( (const JSAMPLE *)buffer );
524
525 // write the data. in jpeg, the first row is the top row of the image
526 auto * row_pointers = new JSAMPROW[height];
527 const int rowInc = numComp * width;
528 for ( unsigned int ui = 0; ui < height; ui++ )
529 {
530 row_pointers[ui] = const_cast< JSAMPROW >( outPtr );
531 outPtr = const_cast< JSAMPLE * >( outPtr ) + rowInc;
532 }
533 jpeg_write_scanlines(&cinfo, row_pointers, height);
534
535 if ( fflush(fp) == EOF )
536 {
537 itkExceptionMacro(<< "JPEG : Out of disk space");
538 }
539
540 // finish the compression
541 jpeg_finish_compress(&cinfo);
542
543 // clean up and close the file
544 delete[] row_pointers;
545 jpeg_destroy_compress(&cinfo);
546 }
547 } // end namespace itk
548