Visualization Library 2.1.0

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
ioPNG.cpp
Go to the documentation of this file.
1 /**************************************************************************************/
2 /* */
3 /* Visualization Library */
4 /* http://visualizationlibrary.org */
5 /* */
6 /* Copyright (c) 2005-2020, Michele Bosi */
7 /* All rights reserved. */
8 /* */
9 /* Redistribution and use in source and binary forms, with or without modification, */
10 /* are permitted provided that the following conditions are met: */
11 /* */
12 /* - Redistributions of source code must retain the above copyright notice, this */
13 /* list of conditions and the following disclaimer. */
14 /* */
15 /* - Redistributions in binary form must reproduce the above copyright notice, this */
16 /* list of conditions and the following disclaimer in the documentation and/or */
17 /* other materials provided with the distribution. */
18 /* */
19 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
20 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
21 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
22 /* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
23 /* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
24 /* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
25 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
26 /* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
27 /* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
28 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* */
30 /**************************************************************************************/
31 
32 #include "ioPNG.hpp"
35 #include <vlCore/FileSystem.hpp>
36 #include <vlCore/VirtualFile.hpp>
37 #include <vlCore/Image.hpp>
38 #include "png.h"
39 
40 using namespace vl;
41 
42 namespace
43 {
44  void png_read_vfile(png_structp png_ptr, png_bytep data, png_size_t byte_count)
45  {
46  VirtualFile* vfile = (VirtualFile*)png_get_io_ptr(png_ptr);
47  vfile->read(data, byte_count);
48  }
49  void png_write_vfile(png_structp png_ptr, png_bytep data, png_size_t byte_count)
50  {
51  VirtualFile* vfile = (VirtualFile*)png_get_io_ptr(png_ptr);
52  vfile->write(data,byte_count);
53  }
54  void png_flush_vfile(png_structp /*png_ptr*/)
55  {
56  // do nothing
57  }
58  void vl_error_fn(png_structp /*png_ptr*/, png_const_charp error_msg)
59  {
60  Log::error( Say("libPNG: %s\n") << error_msg);
61  }
62  void vl_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg)
63  {
64  Log::warning( Say("libPNG: %s\n") << warning_msg);
65  }
66 }
67 
68 //-----------------------------------------------------------------------------
70 {
72  if ( !file )
73  {
74  Log::error( Say("File '%s' not found.\n") << path );
75  return NULL;
76  }
77  else
78  return loadPNG(file.get());
79 }
80 //-----------------------------------------------------------------------------
82 {
83  if ( !file->open(OM_ReadOnly) )
84  {
85  Log::error( Say("loadPNG: cannot load PNG file '%s'\n") << file->path() );
86  return NULL;
87  }
88 
89  png_structp png_ptr;
90  png_infop info_ptr;
91  png_infop endinfo;
92  // unsigned int sig_read = 0;
93  png_uint_32 width, height;
94  int bit_depth, color_type;
95 
96  /* Create and initialize the png_struct with the desired error handler
97  * functions. If you want to use the default stderr and longjump method,
98  * you can supply NULL for the last three parameters. We also supply the
99  * the compiler header file version, so that we know if the application
100  * was compiled with a compatible version of the library. REQUIRED
101  */
102  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
103  png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), vl_error_fn, vl_warning_fn);
104 
105  if (png_ptr == NULL)
106  {
107  file->close();
108  return NULL;
109  }
110 
111  /* Allocate/initialize the memory for image information. REQUIRED. */
112  info_ptr = png_create_info_struct(png_ptr);
113  endinfo = png_create_info_struct(png_ptr);
114  VL_CHECK(info_ptr)
115  VL_CHECK(endinfo)
116 
117  // fixme? "warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable"
118 
119 #if 0
120  if (setjmp(png_jmpbuf(png_ptr)))
121  {
122  /* Free all of the memory associated with the png_ptr and info_ptr */
123  png_destroy_read_struct(&png_ptr, &info_ptr, &endinfo);
124  file->close();
125  /* If we get here, we had a problem reading the file */
126  return NULL;
127  }
128 #endif
129 
130  unsigned char header[8];
131  int count = (int)file->read(header,8);
132  if (count == 8 && png_check_sig(header, 8))
133  {
134  png_set_read_fn(png_ptr,file,png_read_vfile);
135  png_set_sig_bytes(png_ptr, 8);
136  }
137  else
138  {
139  png_destroy_read_struct(&png_ptr, &info_ptr, &endinfo);
140  file->close();
141  return NULL;
142  }
143 
144  /* The call to png_read_info() gives us all of the information from the
145  * PNG file before the first IDAT (image data chunk). REQUIRED
146  */
147  png_read_info(png_ptr, info_ptr);
148 
149  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL/*&interlace_type*/, NULL/*int_p_NULL*/, NULL/*int_p_NULL*/);
150 
151  ref<Image> img = new Image;
152  img->setObjectName(file->path().toStdString().c_str());
153 
154  if (bit_depth == 16)
155  {
156  switch(color_type)
157  {
158  case PNG_COLOR_TYPE_GRAY: img->allocate2D(width, height, 1, IF_LUMINANCE, IT_UNSIGNED_SHORT); break;
159  case PNG_COLOR_TYPE_GRAY_ALPHA: img->allocate2D(width, height, 1, IF_LUMINANCE_ALPHA, IT_UNSIGNED_SHORT); break;
160  case PNG_COLOR_TYPE_PALETTE: img->allocate2D(width, height, 1, IF_RGB, IT_UNSIGNED_SHORT); break;
161  case PNG_COLOR_TYPE_RGB: img->allocate2D(width, height, 1, IF_RGB, IT_UNSIGNED_SHORT); break;
162  case PNG_COLOR_TYPE_RGB_ALPHA: img->allocate2D(width, height, 1, IF_RGBA, IT_UNSIGNED_SHORT); break;
163  default:
164  VL_TRAP()
165  break;
166  }
167  }
168  else
169  {
170  switch(color_type)
171  {
172  case PNG_COLOR_TYPE_GRAY: img->allocate2D(width, height, 1, IF_LUMINANCE, IT_UNSIGNED_BYTE); break;
173  case PNG_COLOR_TYPE_GRAY_ALPHA: img->allocate2D(width, height, 1, IF_LUMINANCE_ALPHA, IT_UNSIGNED_BYTE); break;
174  case PNG_COLOR_TYPE_PALETTE: img->allocate2D(width, height, 1, IF_RGBA, IT_UNSIGNED_BYTE); break;
175  case PNG_COLOR_TYPE_RGB: img->allocate2D(width, height, 1, IF_RGB, IT_UNSIGNED_BYTE); break;
176  case PNG_COLOR_TYPE_RGB_ALPHA: img->allocate2D(width, height, 1, IF_RGBA, IT_UNSIGNED_BYTE); break;
177  default:
178  VL_TRAP()
179  break;
180  }
181  }
182 
183  /* Set up the data transformations you want. Note that these are all
184  * optional. Only call them if you want/need them. Many of the
185  * transformations only work on specific types of images, and many
186  * are mutually exclusive.
187  */
188 
189  /* tell libpng to strip 16 bit/color files down to 8 bits/color */
190  // png_set_strip_16(png_ptr);
191 
192  /* Strip alpha bytes from the input data without combining with the
193  * background (not recommended).
194  */
196 
197  /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
198  * byte into separate bytes (useful for paletted and grayscale images).
199  */
200  png_set_packing(png_ptr);
201 
202  /* Change the order of packed pixels to least significant bit first
203  * (not useful if you are using png_set_packing). */
204  // png_set_packswap(png_ptr);
205 
206  /* Expand paletted colors into true RGB triplets */
207  if (color_type == PNG_COLOR_TYPE_PALETTE)
208  png_set_palette_to_rgb(png_ptr);
209 
210  /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
211  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
212  png_set_expand_gray_1_2_4_to_8(png_ptr);
213 
214  #if 1
215  /* Expand paletted or RGB images with transparency to full alpha channels
216  * so the data will be available as RGBA quartets.
217  */
218  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
219  png_set_tRNS_to_alpha(png_ptr);
220  #endif
221 
222  #if 0
223  /* Set the background color to draw transparent and alpha images over.
224  * It is possible to set the red, green, and blue components directly
225  * for paletted images instead of supplying a palette index. Note that
226  * even if the PNG file supplies a background, you are not required to
227  * use it - you should use the (solid) application background if it has one.
228  */
229  png_color_16 my_background, *image_background;
230  if (png_get_bKGD(png_ptr, info_ptr, &image_background))
231  png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
232  else
233  png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
234  #endif
235 
236  // char* gamma_str = NULL;
237  double screen_gamma = 2.2; /* A good guess for a PC monitors in a dimly lit room */
238  // double screen_gamma = 1.7 or 1.0; /* A good guess for Mac systems */
239 
240  /*if ((gamma_str = getenv("SCREEN_GAMMA")) != NULL)
241  screen_gamma = atof(gamma_str);*/
242 
243  /* Tell libpng to handle the gamma conversion for you. The final call
244  * is a good guess for PC generated images, but it should be configurable
245  * by the user at run time by the user. It is strongly suggested that
246  * your application support gamma correction.
247  */
248 
249  double image_gamma;
250  if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
251  png_set_gamma(png_ptr, screen_gamma, image_gamma);
252  else
253  png_set_gamma(png_ptr, screen_gamma, 1.0/screen_gamma/*0.45455*/);
254 
255 #if 0
256  /* Dither RGB files down to 8 bit palette or reduce palettes
257  * to the number of colors available on your screen.
258  */
259  if (color_type & PNG_COLOR_MASK_COLOR)
260  {
261  int num_palette;
262  png_colorp palette;
263 
264  * This reduces the image to the application supplied palette */
265  if (/* we have our own palette */)
266  {
267  /* An array of colors to which the image should be dithered */
268  png_color std_color_cube[MAX_SCREEN_COLORS];
269 
270  png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
271  MAX_SCREEN_COLORS, png_uint_16p_NULL, 0);
272  }
273  * This reduces the image to the palette supplied in the file */
274  else
275  if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette))
276  {
277  png_uint_16p histogram = NULL;
278 
279  png_get_hIST(png_ptr, info_ptr, &histogram);
280 
281  png_set_dither(png_ptr, palette, num_palette,
282  max_screen_colors, histogram, 0);
283  }
284  }
285 #endif
286 
287 #if 0
288  /* invert monochrome files to have 0 as white and 1 as black */
289  png_set_invert_mono(png_ptr);
290 #endif
291 
292 #if 0
293  /* If you want to shift the pixel values from the range [0,255] or
294  * [0,65535] to the original [0,7] or [0,31], or whatever range the
295  * colors were originally in:
296  */
297  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
298  {
299  png_color_8p sig_bit;
300 
301  png_get_sBIT(png_ptr, info_ptr, &sig_bit);
302  png_set_shift(png_ptr, sig_bit);
303  }
304 #endif
305 
306 #if 0
307  /* flip the RGB pixels to BGR (or RGBA to BGRA) */
308  if (color_type & PNG_COLOR_MASK_COLOR)
309  png_set_bgr(png_ptr);
310 #endif
311 
312 #if 0
313  /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
314  png_set_swap_alpha(png_ptr);
315 #endif
316 
317 #if 1
318  /* swap bytes of 16 bit files to least significant byte first */
319  unsigned short bet = 0x00FF;
320  bool little_endian_cpu = ((unsigned char*)&bet)[0] == 0xFF;
321  if (little_endian_cpu && bit_depth > 8)
322  png_set_swap(png_ptr);
323 #endif
324 
325 #if 0
326  /* Add filler (or alpha) byte (before/after each RGB triplet) */
327  png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
328 #endif
329 
331  // * png_read_image(). To see how to handle interlacing passes,
332  // * see the png_read_row() method below:
333  // */
334  //int number_passes = png_set_interlace_handling(png_ptr);
335 
337  // * and update info structure. REQUIRED if you are expecting libpng to
338  // * update the palette for you (ie you selected such a transform above).
339  // */
340  //png_read_update_info(png_ptr, info_ptr);
341 
343 
346  //std::vector<png_bytep> row_pointers;
347  //row_pointers.resize(height);
348 
349  //for (unsigned row = 0; row < height; row++)
350  //{
351  // row_pointers[row] = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
352  //}
353 
354  //for (int pass = 0; pass < number_passes; pass++)
355  //{
356  // for (unsigned y = 0; y < height; y++)
357  // {
358  // png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL, 1);
359  // }
360  //}
361  /* At this point you have read the entire image */
362 
363  // initialize row pointers
364  std::vector<png_bytep> row_p;
365  row_p.resize(height);
366  for(unsigned i=0; i<height; ++i)
367  row_p[height - 1 - i] = (png_bytep)img->pixels()+img->pitch()*i;
368 
369  png_read_image(png_ptr, &row_p[0]);
370  png_read_end(png_ptr, endinfo);
371 
372  /* clean up after the read, and free any memory allocated - REQUIRED */
373  png_destroy_read_struct(&png_ptr, &info_ptr, &endinfo);
374 
375  file->close();
376  return img;
377 }
378 //-----------------------------------------------------------------------------
380 {
381  if ( !file->open(OM_ReadOnly) )
382  {
383  // Log::error( Say("loadPNG: cannot load PNG file '%s'\n") << file->path() );
384  return false;
385  }
386 
387  unsigned char header[8];
388  int count = (int)file->read(header,8);
389  file->close();
390  if (count == 8 && png_check_sig(header, 8))
391  return true;
392  else
393  return false;
394 }
395 //-----------------------------------------------------------------------------
396 bool vl::savePNG(const Image* src, const String& path, int compression)
397 {
398  ref<DiskFile> file = new DiskFile(path);
399  return savePNG(src, file.get(), compression);
400 }
401 //-----------------------------------------------------------------------------
402 bool vl::savePNG(const Image* src, VirtualFile* fout, int compression)
403 {
404  /*if ( src->dimension() != ID_2D && src->dimension() != ID_1D && src->dimension() != ID_3D )
405  {
406  Log::error( Say("savePNG('%s'): can save only 1D, 2D and 3D images.\n") << fout->path() );
407  return false;
408  }*/
409 
410  int w = src->width();
411  int h = src->height();
412  int d = src->depth();
413  if (h == 0) h=1;
414  if (d == 0) d=1;
415  if (src->isCubemap()) d=6;
416  h = h*d;
417 
418  ref<Image> cimg;
419  // convert to IT_UNSIGNED_SHORT if necessary
420  if (src->type() == IT_FLOAT || src->type() == IT_SHORT)
421  {
422  cimg = src->convertType(IT_UNSIGNED_SHORT);
423  src = cimg.get();
424  if (!cimg)
425  {
426  Log::error( Say("savePNG('%s'): could not convert image to IT_UNSIGNED_SHORT.\n") << fout->path() );
427  return false;
428  }
429  }
430 
431  bool format_ok = src->format() == IF_RGB || src->format() == IF_RGBA || src->format() == IF_LUMINANCE || src->format() == IF_ALPHA || src->format() == IF_LUMINANCE_ALPHA;
432  if (!format_ok)
433  {
434  cimg = src->convertFormat(IF_RGBA);
435  src = cimg.get();
436  if (!cimg)
437  {
438  Log::error( Say("savePNG('%s'): could not convert image to IF_RGBA.\n") << fout->path() );
439  return false;
440  }
441  }
442 
443  VL_CHECK(src->type() == IT_UNSIGNED_BYTE || src->type() == IT_UNSIGNED_SHORT)
444  VL_CHECK(src->format() == IF_RGB || src->format() == IF_RGBA || src->format() == IF_LUMINANCE || src->format() == IF_ALPHA || src->format() == IF_LUMINANCE_ALPHA)
445 
446  if(!fout->open(OM_WriteOnly))
447  {
448  Log::error( Say("PNG: could not write to '%s'.\n") << fout->path() );
449  return false;
450  }
451 
452  png_structp png = NULL;
453  png_infop info = NULL;
454 
455  png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
456  if(!png)
457  return false;
458 
459  info = png_create_info_struct(png);
460  if(!info)
461  return false;
462 
463  png_set_write_fn(png,fout,png_write_vfile,png_flush_vfile);
464 
465  png_set_compression_level(png, compression);
466 
467  std::vector< png_bytep > rows;
468  rows.resize(h);
469  for(int i=0, y = h-1; i<h; ++i,--y)
470  rows[i] = (png_bytep)(src->pixels()+src->pitch()*y);
471 
472  int color;
473  switch(src->format())
474  {
475  case IF_RGB: color = PNG_COLOR_TYPE_RGB; break;
476  case IF_RGBA: color = PNG_COLOR_TYPE_RGB_ALPHA; break;
477  case IF_ALPHA:
478  case IF_LUMINANCE: color = PNG_COLOR_TYPE_GRAY; break;
479  case IF_LUMINANCE_ALPHA: color = PNG_COLOR_TYPE_GRAY_ALPHA; break;
480  default:
481  return false;
482  }
483 
484  int bit_depth = src->type() == IT_UNSIGNED_SHORT ? 16 : 8;
485 
486  png_set_IHDR( png, info, w, h, bit_depth, color,
487  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
488 
489  unsigned short bet = 0x00FF;
490  bool little_endian_cpu = ((unsigned char*)&bet)[0] == 0xFF;
491  if (little_endian_cpu && bit_depth == 16)
492  {
493  png_set_rows(png, info, &rows[0]);
494  png_write_png(png, info, PNG_TRANSFORM_SWAP_ENDIAN, NULL);
495  }
496  else
497  {
498  png_write_info(png, info);
499  png_write_image(png, &rows[0]);
500  png_write_end(png, NULL);
501  }
502 
503  png_destroy_write_struct(&png,&info);
504 
505  fout->close();
506  return true;
507 }
508 //-----------------------------------------------------------------------------
long long read(void *buffer, long long byte_count)
Reads byte_count bytes from a file. Returns the number of bytes actually read.
Definition: VirtualFile.cpp:82
VLCORE_EXPORT FileSystem * defFileSystem()
Returns the default FileSystem used by VisualizationLibrary.
Definition: pimpl.cpp:97
int depth() const
Definition: Image.hpp:211
const T * get() const
Definition: Object.hpp:128
An abstract class representing a file.
Definition: VirtualFile.hpp:60
A simple String formatting class.
Definition: Say.hpp:124
const unsigned char * pixels() const
Raw pointer to pixels.
Definition: Image.hpp:170
void setObjectName(const char *name)
The name of the object, by default set to the object&#39;s class name in debug builds.
Definition: Object.hpp:220
static void warning(const String &message)
Use this function to provide information about situations that might lead to errors or loss of data...
Definition: Log.cpp:155
The String class implements an advanced UTF16 (Unicode BMP) string manipulation engine.
Definition: String.hpp:62
ref< Image > convertType(EImageType new_type) const
Converts the type() of an image.
Definition: Image.cpp:1303
static void error(const String &message)
Use this function to provide information about run-time errors: file not found, out of memory...
Definition: Log.cpp:165
virtual ref< VirtualFile > locateFile(const String &full_path, const String &alternate_path=String()) const
Looks for a VirtualFile on the disk and in the currently active FileSystem.
Definition: FileSystem.cpp:61
virtual void close()=0
Closes the file.
const String & path() const
Returns the path of the file.
Definition: VirtualFile.hpp:98
Visualization Library main namespace.
int height() const
Definition: Image.hpp:209
#define VL_TRAP()
Definition: checks.hpp:70
VLCORE_EXPORT bool savePNG(const Image *src, const String &path, int compression=6)
Definition: ioPNG.cpp:396
int width() const
Definition: Image.hpp:207
long long write(const void *buffer, long long byte_count)
Writes byte_count bytes to a file. Returns the number of bytes actually written.
Definition: VirtualFile.cpp:90
virtual bool open(EOpenMode mode)=0
Opens the file in the specified mode.
VLCORE_EXPORT bool isPNG(VirtualFile *file)
Definition: ioPNG.cpp:379
int pitch() const
Definition: Image.hpp:213
#define NULL
Definition: OpenGLDefs.hpp:81
ref< Image > convertFormat(EImageFormat new_format) const
Converts the format() of an image.
Definition: Image.cpp:1719
A VirtualFile that operates on regular disk files.
Definition: DiskFile.hpp:64
EImageType type() const
Definition: Image.hpp:217
Implements a generic 1d, 2d, 3d and cubemap image that can have mipmaps.
Definition: Image.hpp:54
The ref<> class is used to reference-count an Object.
Definition: Object.hpp:55
void allocate2D(int x, int y, int bytealign, EImageFormat format, EImageType type)
Definition: Image.cpp:608
std::string toStdString() const
Returns a UTF8 encoded std::string.
Definition: String.cpp:1156
VLCORE_EXPORT ref< Image > loadPNG(VirtualFile *file)
Definition: ioPNG.cpp:81
#define VL_CHECK(expr)
Definition: checks.hpp:73
EImageFormat format() const
Definition: Image.hpp:215
bool isCubemap() const
Definition: Image.hpp:78