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