Coin Logo Coin3D is Free Software,
published under the BSD 3-clause license.
https://coin3d.github.io
https://www.kongsberg.com/en/kogt/
simage_png.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) Kongsberg Oil & Gas Technologies
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /*
18  * Based heavily on example code in libpng. Some bugs fixed though.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif /* HAVE_CONFIG_H */
24 
25 #ifdef HAVE_PNGLIB
26 
27 #include <simage_png.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 
32 #include <png.h>
33 
34 #define ERR_NO_ERROR 0
35 #define ERR_OPEN 1
36 #define ERR_MEM 2
37 #define ERR_PNGLIB 3
38 #define ERR_OPEN_WRITE 4
39 #define ERR_PNGLIB_WRITE 5
40 #define ERR_MEM_WRITE 6
41 
42 static int pngerror = ERR_NO_ERROR;
43 
44 /* my setjmp buffer */
45 static jmp_buf setjmp_buffer;
46 
47 /* called my libpng */
48 static void
49 warn_callback(png_structp ps, png_const_charp pc)
50 {
51 /* fprintf(stderr,"PNG warn: %s\n", pc); */
52  /*FIXME: notify? */
53 }
54 
55 static void
56 err_callback(png_structp ps, png_const_charp pc)
57 {
58 /* fprintf(stderr,"PNG error: %s\n", pc); */
59 
60  /* FIXME: store error message? */
61  longjmp(setjmp_buffer, 1);
62 }
63 
64 int
65 simage_png_error(char * buffer, int buflen)
66 {
67  switch (pngerror) {
68  case ERR_OPEN:
69  strncpy(buffer, "PNG loader: Error opening file", buflen);
70  break;
71  case ERR_MEM:
72  strncpy(buffer, "PNG loader: Out of memory error", buflen);
73  break;
74  case ERR_PNGLIB:
75  strncpy(buffer, "PNG loader: Illegal png file", buflen);
76  break;
77  case ERR_OPEN_WRITE:
78  strncpy(buffer, "PNG saver: Error opening file", buflen);
79  break;
80  case ERR_PNGLIB_WRITE:
81  strncpy(buffer, "PNG saver: Internal libpng error", buflen);
82  break;
83  case ERR_MEM_WRITE:
84  strncpy(buffer, "PNG saver: Out of memory error", buflen);
85  break;
86  }
87  return pngerror;
88 
89 }
90 
91 int
92 simage_png_identify(const char * ptr,
93  const unsigned char *header,
94  int headerlen)
95 {
96  static unsigned char pngcmp[] = {0x89, 'P', 'N', 'G', 0xd, 0xa, 0x1a, 0xa};
97  if (headerlen < 8) return 0;
98  if (memcmp((const void*)header,
99  (const void*)pngcmp, 8) == 0) return 1;
100  return 0;
101 }
102 
103 /* our method that reads from a FILE* and fills up the buffer that
104  libpng wants when parsing a PNG file */
105 static void
106 user_read_cb(png_structp png_ptr, png_bytep data, png_uint_32 length)
107 {
108  int readlen = (int) fread(data, 1, length, (FILE *)png_get_io_ptr(png_ptr));
109  if (readlen != length) {
110  /* FIXME: then what? png_error()? 20020821 mortene */
111  }
112 }
113 
114 /* our method that write compressed png image data to a FILE* */
115 static void
116 user_write_cb(png_structp png_ptr, png_bytep data, png_uint_32 length)
117 {
118  int writelen = (int) fwrite(data, 1, length, (FILE *)png_get_io_ptr(png_ptr));
119  if (writelen != length) {
120  /* FIXME: then what? png_error()? 20020821 mortene */
121  }
122 }
123 
124 /* our method that flushes written compressed png image data */
125 static void
126 user_flush_cb(png_structp png_ptr)
127 {
128  int err = fflush((FILE *)png_get_io_ptr(png_ptr));
129  if (err != 0) {
130  /* FIXME: then what? png_error()? 20020821 mortene */
131  }
132 }
133 
134 unsigned char *
135 simage_png_load(const char *filename,
136  int *width_ret,
137  int *height_ret,
138  int *numComponents_ret)
139 {
140  png_structp png_ptr;
141  png_infop info_ptr;
142  png_uint_32 width, height;
143 
144  int bit_depth, color_type, interlace_type;
145  FILE *fp;
146  unsigned char *buffer;
147  int y, bytes_per_row;
148  int channels;
149  int format;
150  png_bytepp row_pointers;
151 
152  if ((fp = fopen(filename, "rb")) == NULL) {
153  pngerror = ERR_OPEN;
154  return NULL;
155  }
156 
157  /* Create and initialize the png_struct with the desired error handler
158  * functions. If you want to use the default stderr and longjump method,
159  * you can supply NULL for the last three parameters. We also supply the
160  * the compiler header file version, so that we know if the application
161  * was compiled with a compatible version of the library. REQUIRED
162  */
163  /*png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
164  (void *)user_error_ptr, user_error_fn, user_warning_fn);*/
165 
166  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
167  NULL, err_callback, warn_callback);
168 
169  if (png_ptr == NULL) {
170  pngerror = ERR_MEM;
171  fclose(fp);
172  return 0;
173  }
174 
175  /* Allocate/initialize the memory for image information. REQUIRED. */
176  info_ptr = png_create_info_struct(png_ptr);
177  if (info_ptr == NULL) {
178  pngerror = ERR_MEM;
179  fclose(fp);
180  png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
181  return 0;
182  }
183 
184  /* Set error handling if you are using the setjmp/longjmp method (this is
185  * the normal method of doing things with libpng). REQUIRED unless you
186  * set up your own error handlers in the png_create_read_struct() earlier.
187  */
188 
189  buffer = NULL;
190 
191  if (setjmp(setjmp_buffer)) {
192  pngerror = ERR_PNGLIB;
193  /* Free all of the memory associated with the png_ptr and info_ptr */
194  png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
195  fclose(fp);
196  /* If we get here, we had a problem reading the file */
197 
198  if (buffer) free(buffer);
199  return NULL;
200  }
201 
202  /* we're not using png_init_io(), as we don't want to pass a FILE*
203  into libpng, in case it's an MSWindows DLL with a different CRT
204  (C run-time library) */
205  png_set_read_fn(png_ptr, (void *)fp, (png_rw_ptr)user_read_cb);
206 
207  /* The call to png_read_info() gives us all of the information from the
208  * PNG file before the first IDAT (image data chunk). REQUIRED
209  */
210  png_read_info(png_ptr, info_ptr);
211 
212  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
213  &interlace_type, NULL, NULL);
214 
215  /**** Set up the data transformations you want. Note that these are all
216  **** optional. Only call them if you want/need them. Many of the
217  **** transformations only work on specific types of images, and many
218  **** are mutually exclusive.
219  ****/
220 
221  /* tell libpng to strip 16 bit/color files down to 8 bits/color */
222  png_set_strip_16(png_ptr);
223 
224  /* strip alpha bytes from the input data without combining with th
225  * background (not recommended) */
226  /* png_set_strip_alpha(png_ptr); */
227 
228  /* extract multiple pixels with bit depths of 1, 2, and 4 from a single
229  * byte into separate bytes (useful for paletted and grayscale images).
230  */
231  /* png_set_packing(png_ptr); */
232 
233  /* change the order of packed pixels to least significant bit first
234  * (not useful if you are using png_set_packing). */
235  /* png_set_packswap(png_ptr); */
236 
237  /* expand paletted colors into true RGB triplets */
238  if (color_type == PNG_COLOR_TYPE_PALETTE)
239  png_set_expand(png_ptr);
240 
241  /* expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
242  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
243  png_set_expand(png_ptr);
244 
245  /* expand paletted or RGB images with transparency to full alpha channels
246  * so the data will be available as RGBA quartets */
247  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
248  png_set_expand(png_ptr);
249 
250  /* Add filler (or alpha) byte (before/after each RGB triplet) */
251  /* png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); */
252 
253  png_read_update_info(png_ptr, info_ptr);
254 
255  channels = png_get_channels(png_ptr, info_ptr);
256 
257  /* allocate the memory to hold the image using the fields of info_ptr. */
258 
259  bytes_per_row = png_get_rowbytes(png_ptr, info_ptr);
260 
261 
262  buffer = (unsigned char*) malloc((size_t)bytes_per_row*height);
263 
264  format = channels;
265 
266  row_pointers = (png_bytepp) malloc(height*sizeof(png_bytep));
267  for (y = 0; y < height; y++) {
268  row_pointers[height-y-1] = buffer + y*bytes_per_row;
269  }
270 
271  png_read_image(png_ptr, row_pointers);
272  png_read_end(png_ptr, info_ptr);
273 
274  free(row_pointers);
275 
276  /* clean up after the read, and free any memory allocated - REQUIRED */
277  png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
278 
279  /* close the file */
280  fclose(fp);
281 
282  /* that's it */
283  if (buffer) {
284  *width_ret = width;
285  *height_ret = height;
286  *numComponents_ret = format;
287  pngerror = ERR_NO_ERROR;
288  }
289  else {
290  pngerror = ERR_MEM;
291  }
292  return buffer;
293 }
294 
295 int
296 simage_png_save(const char *filename,
297  const unsigned char * bytes,
298  int width,
299  int height,
300  int numcomponents)
301 {
302  FILE * fp;
303  png_structp png_ptr;
304  png_infop info_ptr;
305  int colortype;
306  int y, bytesperrow;
307 #ifdef PNG_TEXT_SUPPORTED
308  png_text text_ptr[3];
309 #endif
310 
311  /* open the file */
312  fp = fopen(filename, "wb");
313  if (fp == NULL) {
314  pngerror = ERR_OPEN_WRITE;
315  return 0;
316  }
317 
318  /* Create and initialize the png_struct with the desired error handler
319  * functions. If you want to use the default stderr and longjump method,
320  * you can supply NULL for the last three parameters. We also check that
321  * the library version is compatible with the one used at compile time,
322  * in case we are using dynamically linked libraries. REQUIRED.
323  */
324  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
325  NULL, NULL, NULL);
326 
327  if (png_ptr == NULL) {
328  pngerror = ERR_OPEN_WRITE;
329  fclose(fp);
330  return 0;
331  }
332 
333  /* Allocate/initialize the image information data. REQUIRED */
334  info_ptr = png_create_info_struct(png_ptr);
335  if (info_ptr == NULL) {
336  fclose(fp);
337  png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
338  pngerror = ERR_MEM_WRITE;
339  return 0;
340  }
341 
342  /* Set error handling. REQUIRED if you aren't supplying your own
343  * error hadnling functions in the png_create_write_struct() call.
344  */
345 #if PNG_LIBPNG_VER < 10400
346  if (setjmp(png_ptr->jmpbuf)) {
347 #else
348  if (setjmp(png_jmpbuf(png_ptr))) {
349 #endif /* PNG_LIBPNG_VER < 10400 */
350  /* If we get here, we had a problem reading the file */
351  fclose(fp);
352  png_destroy_write_struct(&png_ptr, (png_infopp)info_ptr);
353  pngerror = ERR_PNGLIB_WRITE;
354  return 0;
355  }
356 
357  /* we're not using png_init_io(), as we don't want to pass a FILE*
358  into libpng, in case it's an MSWindows DLL with a different CRT
359  (C run-time library) */
360  png_set_write_fn(png_ptr, (void *)fp, (png_rw_ptr)user_write_cb,
361  (png_flush_ptr)user_flush_cb);
362 
363  /* Set the image information here. Width and height are up to 2^31,
364  * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
365  * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
366  * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
367  * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
368  * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
369  * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
370  */
371 
372  switch (numcomponents) {
373  case 1:
374  colortype = PNG_COLOR_TYPE_GRAY;
375  break;
376  case 2:
377  colortype = PNG_COLOR_TYPE_GRAY_ALPHA;
378  break;
379  case 3:
380  colortype = PNG_COLOR_TYPE_RGB;
381  break;
382  default:
383  case 4:
384  colortype = PNG_COLOR_TYPE_RGB_ALPHA;
385  break;
386  }
387 
388  png_set_IHDR(png_ptr, info_ptr, width, height, 8, colortype,
389  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
390 
391  /* Optional gamma chunk is strongly suggested if you have any guess
392  * as to the correct gamma of the image. */
393  /* png_set_gAMA(png_ptr, info_ptr, gamma); */
394 
395 #if defined(PNG_TEXT_SUPPORTED)
396  /* Optionally write comments into the image */
397  text_ptr[0].key = "Title";
398  text_ptr[0].text = (char*)filename;
399  text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
400  text_ptr[1].key = "Author";
401  text_ptr[1].text = "simage (https://coin3d.github.io)";
402  text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
403  text_ptr[2].key = "Description";
404  text_ptr[2].text = "Image saved using simage.";
405  text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt;
406  png_set_text(png_ptr, info_ptr, text_ptr, 3);
407 #endif /* PNG_TEXT_SUPPORTED */
408 
409  /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */
410 
411  /* Write the file header information. REQUIRED */
412  png_write_info(png_ptr, info_ptr);
413 
414  /* Once we write out the header, the compression type on the text
415  * chunks gets changed to PNG_TEXT_COMPRESSION_NONE_WR or
416  * PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again
417  * at the end.
418  */
419 
420  /* set up the transformations you want. Note that these are
421  * all optional. Only call them if you want them. */
422 
423  /* invert monocrome pixels */
424  /* png_set_invert(png_ptr); */
425 
426  /* Shift the pixels up to a legal bit depth and fill in
427  * as appropriate to correctly scale the image */
428  /* png_set_shift(png_ptr, &sig_bit);*/
429 
430  /* pack pixels into bytes */
431  /* png_set_packing(png_ptr); */
432 
433  /* swap location of alpha bytes from ARGB to RGBA */
434  /* png_set_swap_alpha(png_ptr); */
435 
436  /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into
437  * RGB (4 channels -> 3 channels). The second parameter is not used. */
438  /* png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); */
439 
440  /* flip BGR pixels to RGB */
441  /* png_set_bgr(png_ptr); */
442 
443  /* swap bytes of 16-bit files to most significant byte first */
444  /* png_set_swap(png_ptr); */
445 
446  /* swap bits of 1, 2, 4 bit packed pixel formats */
447  /* png_set_packswap(png_ptr); */
448 
449 
450  /* The easiest way to write the image (you may have a different memory
451  * layout, however, so choose what fits your needs best). You need to
452  * use the first method if you aren't handling interlacing yourself.
453  */
454 
455  /* If you are only writing one row at a time, this works */
456 
457  bytesperrow = width * numcomponents;
458 
459  for (y = 0; y < height; y++) {
460  png_write_row(png_ptr, (png_bytep) bytes + bytesperrow * (height-y-1));
461  }
462 
463  /* You can write optional chunks like tEXt, zTXt, and tIME at the end
464  * as well.
465  */
466 
467  /* It is REQUIRED to call this to finish writing the rest of the file */
468  png_write_end(png_ptr, info_ptr);
469 
470  /* if you allocated any text comments, free them here */
471 
472  /* clean up after the write, and free any memory allocated */
473  png_destroy_write_struct(&png_ptr, &info_ptr);
474 
475  /* close the file */
476  fclose(fp);
477 
478  /* that's it */
479  return 1;
480 }
481 
482 #endif /* HAVE_PNGLIB */
int simage_png_identify(const char *filename, const unsigned char *header, int headerlen)
int simage_png_error(char *buffer, int bufferlen)
#define ERR_OPEN
#define ERR_MEM
unsigned char * simage_png_load(const char *filename, int *width, int *height, int *numComponents)
#define ERR_NO_ERROR
int simage_png_save(const char *filename, const unsigned char *bytes, int width, int height, int numcomponents)