Orfeo Toolbox  3.16
itkPNGImageIO.cxx
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: Insight Segmentation & Registration Toolkit
4  Module: $RCSfile: itkPNGImageIO.cxx,v $
5  Language: C++
6  Date: $Date: 2008-09-30 22:01:48 $
7  Version: $Revision: 1.70 $
8 
9  Copyright (c) Insight Software Consortium. All rights reserved.
10  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
11 
12  This software is distributed WITHOUT ANY WARRANTY; without even
13  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14  PURPOSE. See the above copyright notices for more information.
15 
16 =========================================================================*/
17 #include "itkPNGImageIO.h"
18 #include "itkRGBPixel.h"
19 #include "itkRGBAPixel.h"
20 #include "itk_png.h"
21 #include <itksys/SystemTools.hxx>
22 
23 namespace itk
24 {
25 
26 extern "C"
27 {
28  #include <setjmp.h>
29  /* The PNG library does not expect the error function to return.
30  Therefore we must use this ugly longjmp call. */
31  void itkPNGWriteErrorFunction(png_structp png_ptr,
32  png_const_charp itkNotUsed(error_msg))
33  {
34  longjmp(png_ptr->jmpbuf, 1);
35  }
36 }
37 
38 
39 extern "C"
40 {
41  void itkPNGWriteWarningFunction(png_structp itkNotUsed(png_ptr),
42  png_const_charp itkNotUsed(warning_msg))
43  {
44  }
45 }
46 
47 
48 // simple class to call fopen on construct and
49 // fclose on destruct
51 {
52 public:
53  PNGFileWrapper(const char * const fname, const char * const openMode):m_FilePointer(NULL)
54  {
55  m_FilePointer = fopen(fname, openMode);
56  }
57  virtual ~PNGFileWrapper()
58  {
59  if(m_FilePointer)
60  {
61  fclose(m_FilePointer);
62  }
63  }
65 };
66 
67 bool PNGImageIO::CanReadFile(const char* file)
68 {
69  // First check the extension
70  std::string filename = file;
71  if( filename == "" )
72  {
73  itkDebugMacro(<<"No filename specified.");
74  return false;
75  }
76 
77  this->AddSupportedWriteExtension(".png");
78  this->AddSupportedWriteExtension(".PNG");
79 
80  this->AddSupportedReadExtension(".png");
81  this->AddSupportedReadExtension(".PNG");
82 
83  // Now check the file header
84  PNGFileWrapper pngfp(file,"rb");
85  if(pngfp.m_FilePointer==NULL)
86  {
87  return false;
88  }
89  unsigned char header[8];
90  fread(header, 1, 8, pngfp.m_FilePointer);
91  bool is_png = !png_sig_cmp(header, 0, 8);
92  if(!is_png)
93  {
94  return false;
95  }
96  png_structp png_ptr = png_create_read_struct
97  (PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
98  NULL, NULL);
99  if (!png_ptr)
100  {
101  return false;
102  }
103 
104  png_infop info_ptr = png_create_info_struct(png_ptr);
105  if (!info_ptr)
106  {
107  png_destroy_read_struct(&png_ptr,
108  (png_infopp)NULL, (png_infopp)NULL);
109  return false;
110  }
111 
112  png_infop end_info = png_create_info_struct(png_ptr);
113  if (!end_info)
114  {
115  png_destroy_read_struct(&png_ptr, &info_ptr,
116  (png_infopp)NULL);
117  return false;
118  }
119  png_destroy_read_struct(&png_ptr, &info_ptr,
120  &end_info);
121 
122  return true;
123 }
124 
125 
127 {
128 
129 }
130 
131 
132 void PNGImageIO::Read(void* buffer)
133 {
134  itkDebugMacro("Read: file dimensions = " << this->GetNumberOfDimensions() );
135  // use this class so return will call close
136  PNGFileWrapper pngfp(this->GetFileName(),"rb");
137  FILE* fp = pngfp.m_FilePointer;
138  if(!fp)
139  {
140  itkExceptionMacro("PNGImageIO could not open file: "
141  << this->GetFileName() << " for reading."
142  << std::endl
143  << "Reason: "
144  << itksys::SystemTools::GetLastSystemError());
145  return;
146  }
147  unsigned char header[8];
148  fread(header, 1, 8, fp);
149  bool is_png = !png_sig_cmp(header, 0, 8);
150  if(!is_png)
151  {
152  itkExceptionMacro("File is not png type: " << this->GetFileName());
153  return;
154  }
155  png_structp png_ptr = png_create_read_struct
156  (PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
157  NULL, NULL);
158  if (!png_ptr)
159  {
160  itkExceptionMacro("File is not png type" << this->GetFileName());
161  return;
162  }
163 
164  png_infop info_ptr = png_create_info_struct(png_ptr);
165  if (!info_ptr)
166  {
167  png_destroy_read_struct(&png_ptr,
168  (png_infopp)NULL, (png_infopp)NULL);
169  itkExceptionMacro("File is not png type " << this->GetFileName());
170  return;
171  }
172 
173  png_infop end_info = png_create_info_struct(png_ptr);
174  if (!end_info)
175  {
176  png_destroy_read_struct(&png_ptr, &info_ptr,
177  (png_infopp)NULL);
178  itkExceptionMacro("File is not png type " << this->GetFileName());
179  return;
180  }
181 
182  // VS 7.1 has problems with setjmp/longjmp in C++ code
183 #if !defined(MSC_VER) || _MSC_VER != 1310
184  if( setjmp( png_jmpbuf( png_ptr ) ) )
185  {
186  png_destroy_read_struct( &png_ptr, &info_ptr, &end_info );
187  itkExceptionMacro("File is not png type " << this->GetFileName());
188  return;
189  }
190 #endif
191 
192  png_init_io(png_ptr, fp);
193  png_set_sig_bytes(png_ptr, 8);
194 
195  png_read_info(png_ptr, info_ptr);
196 
197  png_uint_32 width, height;
198  int bitDepth, colorType, interlaceType;
199  int compression_type, filter_method;
200  png_get_IHDR(png_ptr, info_ptr,
201  &width, &height,
202  &bitDepth, &colorType, &interlaceType,
203  &compression_type, &filter_method);
204 
205  // convert palettes to RGB
206  if (colorType == PNG_COLOR_TYPE_PALETTE)
207  {
208  png_set_palette_to_rgb(png_ptr);
209  }
210 
211  // minimum of a byte per pixel
212  if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
213  {
214  png_set_gray_1_2_4_to_8(png_ptr);
215  }
216 
217  // add alpha if any alpha found
218  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
219  {
220  png_set_tRNS_to_alpha(png_ptr);
221  }
222 
223  if (bitDepth > 8)
224  {
225 #ifndef ITK_WORDS_BIGENDIAN
226  png_set_swap(png_ptr);
227 #endif
228  }
229 
230  if (info_ptr->valid & PNG_INFO_sBIT)
231  {
232  png_set_shift(png_ptr, &(info_ptr->sig_bit));
233  }
234  // have libpng handle interlacing
235  //int number_of_passes = png_set_interlace_handling(png_ptr);
236  // update the info now that we have defined the filters
237  png_read_update_info(png_ptr, info_ptr);
238 
239  unsigned long rowbytes = png_get_rowbytes(png_ptr, info_ptr);
240  unsigned char *tempImage = static_cast<unsigned char*>(buffer);
241  png_bytep *row_pointers = new png_bytep [height];
242  for (unsigned int ui = 0; ui < height; ++ui)
243  {
244  row_pointers[ui] = tempImage + rowbytes*ui;
245  }
246  png_read_image(png_ptr, row_pointers);
247  delete [] row_pointers;
248  // close the file
249  png_read_end(png_ptr, NULL);
250  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
251 
252 }
253 
254 
256 {
257  this->SetNumberOfDimensions(2);
260  m_UseCompression = false;
261  m_CompressionLevel = 4; // Range 0-9; 0 = no file compression, 9 = maximum file compression
262  m_Spacing[0] = 1.0;
263  m_Spacing[1] = 1.0;
264 
265  m_Origin[0] = 0.0;
266  m_Origin[1] = 0.0;
267 }
268 
270 {
271 }
272 
273 void PNGImageIO::PrintSelf(std::ostream& os, Indent indent) const
274 {
275  Superclass::PrintSelf(os, indent);
276  os << indent << "Compression Level : " << m_CompressionLevel << "\n";
277 }
278 
279 
280 
282 {
283  m_Spacing[0] = 1.0; // We'll look for PNG 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  PNGFileWrapper pngfp(m_FileName.c_str(),"rb");
291  FILE* fp = pngfp.m_FilePointer;
292  if(!fp)
293  {
294  return;
295  }
296  unsigned char header[8];
297  fread(header, 1, 8, fp);
298  bool is_png = !png_sig_cmp(header, 0, 8);
299  if(!is_png)
300  {
301  return;
302  }
303  png_structp png_ptr = png_create_read_struct
304  (PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
305  NULL, NULL);
306  if (!png_ptr)
307  {
308  return;
309  }
310 
311  png_infop info_ptr = png_create_info_struct(png_ptr);
312  if (!info_ptr)
313  {
314  png_destroy_read_struct(&png_ptr,
315  (png_infopp)NULL, (png_infopp)NULL);
316  return;
317  }
318 
319  png_infop end_info = png_create_info_struct(png_ptr);
320  if (!end_info)
321  {
322  png_destroy_read_struct(&png_ptr, &info_ptr,
323  (png_infopp)NULL);
324  return;
325  }
326 
327  png_init_io(png_ptr, fp);
328  png_set_sig_bytes(png_ptr, 8);
329 
330  png_read_info(png_ptr, info_ptr);
331 
332  png_uint_32 width, height;
333  int bitDepth, colorType, interlaceType;
334  int compression_type, filter_method;
335  png_get_IHDR(png_ptr, info_ptr,
336  &width, &height,
337  &bitDepth, &colorType, &interlaceType,
338  &compression_type, &filter_method);
339 
340  // convert palettes to RGB
341  if (colorType == PNG_COLOR_TYPE_PALETTE)
342  {
343  png_set_palette_to_rgb(png_ptr);
344  }
345 
346  // minimum of a byte per pixel
347  if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
348  {
349  png_set_gray_1_2_4_to_8(png_ptr);
350  }
351 
352  // add alpha if any alpha found
353  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
354  {
355  png_set_tRNS_to_alpha(png_ptr);
356  }
357 
358  // update the info now that we have defined the filters
359  png_read_update_info(png_ptr, info_ptr);
360  this->SetNumberOfDimensions(2);
361  m_Dimensions[0] = width;
362  m_Dimensions[1] = height;
363  if (bitDepth <= 8)
364  {
367  }
368  else
369  {
372  }
373  this->SetNumberOfComponents(png_get_channels(png_ptr, info_ptr));
374 
375  if (this->GetNumberOfComponents() == 3)
376  {
377  m_PixelType = RGB;
378  }
379  else if (this->GetNumberOfComponents() == 4)
380  {
381  m_PixelType = RGBA;
382  }
383 
384  // see if the PNG file stored spacing information,
385  // ignore the units (for now).
386  double px_width = 1.0, px_height = 1.0; // use default values if not in file
387  int units = PNG_SCALE_UNKNOWN;
388  png_get_sCAL(png_ptr, info_ptr, &units, &px_width, &px_height);
389 
390  m_Spacing[0] = px_width;
391  m_Spacing[1] = px_height;
392 
393 
394  // clean up
395  png_destroy_read_struct(&png_ptr, &info_ptr,
396  &end_info);
397 
398  return;
399 }
400 
401 bool PNGImageIO::CanWriteFile( const char * name )
402 {
403  std::string filename = name;
404 
405  if (filename == "")
406  {
407  return false;
408  }
409 
410  std::string::size_type pngPos = filename.rfind(".png");
411  if ( (pngPos != std::string::npos)
412  && (pngPos == filename.length() - 4) )
413  {
414  return true;
415  }
416 
417  pngPos = filename.rfind(".PNG");
418  if ( (pngPos != std::string::npos)
419  && (pngPos == filename.length() - 4) )
420  {
421  return true;
422  }
423 
424 
425  return false;
426 }
427 
428 
430 {
431 }
432 
433 void PNGImageIO::Write(const void* buffer)
434 {
435  this->WriteSlice(m_FileName, buffer);
436 }
437 
438 void PNGImageIO::WriteSlice(const std::string& fileName, const void* buffer)
439 {
440  volatile const unsigned char *outPtr = ( (const unsigned char *) buffer);
441 
442  // use this class so return will call close
443  PNGFileWrapper pngfp(fileName.c_str(),"wb");
444  FILE* fp = pngfp.m_FilePointer;
445  if(!fp)
446  {
447  // IMPORTANT: The itkExceptionMacro() cannot be used here due to a bug in Visual
448  // Studio 7.1 in release mode. That compiler will corrupt the RTTI type
449  // of the Exception and prevent the catch() from recognizing it.
450  // For details, see Bug # 1872 in the bugtracker.
451 
452  ::itk::ExceptionObject excp(__FILE__, __LINE__, "Problem while opening the file.", ITK_LOCATION);
453  throw excp;
454  }
455 
456  volatile int bitDepth;
457  switch (this->GetComponentType())
458  {
459  case UCHAR:
460  bitDepth = 8;
461  break;
462 
463  case USHORT:
464  bitDepth = 16;
465  break;
466 
467  default:
468  {
469  // IMPORTANT: The itkExceptionMacro() cannot be used here due to a bug in Visual
470  // Studio 7.1 in release mode. That compiler will corrupt the RTTI type
471  // of the Exception and prevent the catch() from recognizing it.
472  // For details, see Bug # 1872 in the bugtracker.
473  ::itk::ExceptionObject excp(__FILE__, __LINE__, "PNG supports unsigned char and unsigned short", ITK_LOCATION);
474  throw excp;
475  }
476  }
477 
478  png_structp png_ptr = png_create_write_struct
479  (PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
480  if (!png_ptr)
481  {
482  itkExceptionMacro(<<"Unable to write PNG file! png_create_write_struct failed.");
483  }
484 
485  png_infop info_ptr = png_create_info_struct(png_ptr);
486  if (!info_ptr)
487  {
488  png_destroy_write_struct(&png_ptr,
489  (png_infopp)NULL);
490  itkExceptionMacro(<<"Unable to write PNG file!. png_create_info_struct failed.");
491  }
492 
493  png_init_io(png_ptr, fp);
494 
495 // VS 7.1 has problems with setjmp/longjmp in C++ code
496 #if !defined(_MSC_VER) || _MSC_VER != 1310
497  png_set_error_fn(png_ptr, png_ptr,
499  if (setjmp(png_ptr->jmpbuf))
500  {
501  fclose(fp);
502  itkExceptionMacro("Error while writing Slice to file: "
503  <<this->GetFileName()
504  << std::endl
505  << "Reason: "
506  << itksys::SystemTools::GetLastSystemError());
507  return;
508  }
509 #endif
510 
511  int colorType;
512  unsigned int numComp = this->GetNumberOfComponents();
513  switch ( numComp )
514  {
515  case 1: colorType = PNG_COLOR_TYPE_GRAY;
516  break;
517  case 2: colorType = PNG_COLOR_TYPE_GRAY_ALPHA;
518  break;
519  case 3: colorType = PNG_COLOR_TYPE_RGB;
520  break;
521  default: colorType = PNG_COLOR_TYPE_RGB_ALPHA;
522  break;
523  }
524 
525  png_uint_32 width, height;
526  double rowSpacing, colSpacing;
527  width = this->GetDimensions(0);
528  colSpacing = m_Spacing[0];
529 
530  if( m_NumberOfDimensions > 1 )
531  {
532  height = this->GetDimensions(1);
533  rowSpacing = m_Spacing[1];
534  }
535  else
536  {
537  height = 1;
538  rowSpacing = 1;
539  }
540 
541  png_set_IHDR(png_ptr, info_ptr, width, height,
542  bitDepth, colorType, PNG_INTERLACE_NONE,
543  PNG_COMPRESSION_TYPE_DEFAULT,
544  PNG_FILTER_TYPE_DEFAULT);
545  // interlaceType - PNG_INTERLACE_NONE or
546  // PNG_INTERLACE_ADAM7
547 
548  if(m_UseCompression)
549  {
550  // Set the image compression level.
551  png_set_compression_level(png_ptr, m_CompressionLevel);
552  }
553 
554  // write out the spacing information:
555  // set the unit_type to unknown. if we add units to ITK, we should
556  // convert pixel size to meters and store units as meters (png
557  // has three set of units: meters, radians, and unknown).
558  png_set_sCAL(png_ptr, info_ptr, PNG_SCALE_UNKNOWN, colSpacing,
559  rowSpacing);
560 
561  //std::cout << "PNG_INFO_sBIT: " << PNG_INFO_sBIT << std::endl;
562 
563  png_write_info(png_ptr, info_ptr);
564  // default is big endian
565  if (bitDepth > 8)
566  {
567 #ifndef ITK_WORDS_BIGENDIAN
568  png_set_swap(png_ptr);
569 #endif
570  }
571  png_byte **row_pointers = new png_byte *[height];
572  int rowInc = width*numComp*bitDepth/8;
573  for (unsigned int ui = 0; ui < height; ui++)
574  {
575  row_pointers[ui] = const_cast<png_byte *>(outPtr);
576  outPtr = const_cast<unsigned char *>(outPtr) + rowInc;
577  }
578  png_write_image(png_ptr, row_pointers);
579  png_write_end(png_ptr, info_ptr);
580 
581  delete [] row_pointers;
582  png_destroy_write_struct(&png_ptr, &info_ptr);
583 }
584 
585 
586 } // end namespace itk

Generated at Sat Feb 2 2013 23:59:38 for Orfeo Toolbox with doxygen 1.8.1.1