efros_freeman  0.1
io_png.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2011, Nicolas Limare <nicolas.limare@cmla.ens-cachan.fr>
3  * All rights reserved.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under, at your option, the terms of the GNU General Public
7  * License as published by the Free Software Foundation, either
8  * version 3 of the License, or (at your option) any later version, or
9  * the terms of the simplified BSD license.
10  *
11  * You should have received a copy of these licenses along this
12  * program. If not, see <http://www.gnu.org/licenses/> and
13  * <http://www.opensource.org/licenses/bsd-license.html>.
14  */
15 
16 /**
17  * @file io_png.c
18  * @brief PNG read/write simplified interface
19  *
20  * This is a front-end to libpng, with routines to:
21  * @li read a PNG file as a de-interlaced 8bit integer or float array
22  * @li write a 8bit integer or float array to a PNG file
23  *
24  * Multi-channel images are handled: gray, gray+alpha, rgb and
25  * rgb+alpha, as well as on-the-fly color model conversion.
26  *
27  * @todo handle 16bit data
28  * @todo replace rgb/gray with sRGB / Y references
29  * @todo implement sRGB gamma and better RGBY conversion
30  * @todo process the data as float before quantization
31  * @todo output float in [o..1]
32  *
33  * @author Nicolas Limare <nicolas.limare@cmla.ens-cachan.fr>
34  */
35 
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <math.h>
39 #include <limits.h>
40 #include <string.h>
41 
42 /* option to use a local version of the libpng */
43 #ifdef IO_PNG_LOCAL_LIBPNG
44 #include "png.h"
45 #else
46 #include <png.h>
47 #endif
48 
49 /* ensure consistency */
50 #include "io_png.h"
51 
52 #define PNG_SIG_LEN 4
53 
54 /* internal only data type identifiers */
55 #define IO_PNG_U8 0x0001 /* 8bit unsigned integer */
56 #define IO_PNG_F32 0x0002 /* 32bit float */
57 
58 /*
59  * INFO
60  */
61 
62 /* string tag inserted into the binary */
63 static char _io_png_tag[] = "using io_png " IO_PNG_VERSION;
64 /**
65  * @brief helps tracking versions, via the string tag inserted into
66  * the library
67  *
68  * This function is not expected to be used in real-world programs.
69  *
70  * @return a pointer to a version info string
71  */
72 char *io_png_info(void)
73 {
74  return _io_png_tag;
75 }
76 
77 /**
78  * local error structure
79  * see http://www.libpng.org/pub/png/book/chapter14.htmlpointer
80  */
81 typedef struct _io_png_err_s {
82  jmp_buf jmpbuf;
84 
85 /**
86  * local error handler
87  * see http://www.libpng.org/pub/png/book/chapter14.htmlpointer
88  */
89 static void _io_png_err_hdl(png_structp png_ptr, png_const_charp msg)
90 {
91  _io_png_err_t *err_ptr;
92 
93  fprintf(stderr, "libpng error: %s\n", msg);
94 
95  err_ptr = (_io_png_err_t *) png_get_error_ptr(png_ptr);
96  if (NULL == png_ptr) {
97  fprintf(stderr, "fatal unrecoverable error, terminating\n");
98  fflush(stderr);
99  abort();
100  }
101 
102  longjmp(err_ptr->jmpbuf, 1);
103 }
104 
105 /*
106  * READ
107  */
108 
109 /**
110  * @brief internal function used to cleanup the memory when
111  * png_read_raw() fails
112  *
113  * @param fp file pointer to close, ignored if NULL
114  * @param png_ptr_p, info_ptr_p, pointers to PNG structure pointers,
115  * ignored if NULL
116  * @return NULL
117  */
118 static void *_io_png_read_abort(FILE * fp,
119  png_structp * png_ptr_p,
120  png_infop * info_ptr_p)
121 {
122  png_destroy_read_struct(png_ptr_p, info_ptr_p, NULL);
123  if (NULL != fp && stdin != fp)
124  (void) fclose(fp);
125  return NULL;
126 }
127 
128 /**
129  * @brief internal function used to read a PNG file into an array
130  *
131  * @todo don't loose 16bit info
132  *
133  * @param fname PNG file name, "-" means stdin
134  * @param nxp, nyp, ncp pointers to variables to be filled
135  * with the number of columns, lines and channels of the image
136  * @param png_transform a PNG_TRANSFORM flag to be added to the
137  * default libpng read transforms
138  * @param dtype identifier for the data type to be used for output
139  * @return pointer to an allocated array of pixels,
140  * or NULL if an error happens
141  */
142 static void *io_png_read_raw(const char *fname,
143  size_t * nxp, size_t * nyp, size_t * ncp,
144  int png_transform, int dtype)
145 {
146  png_byte png_sig[PNG_SIG_LEN];
147  png_structp png_ptr;
148  png_infop info_ptr;
149  png_bytepp row_pointers;
150  png_bytep row_ptr;
151  /* volatile: because of setjmp/longjmp */
152  FILE *volatile fp = NULL;
153  void *data = NULL;
154  unsigned char *data_u8 = NULL;
155  unsigned char *data_u8_ptr = NULL;
156  float *data_f32 = NULL;
157  float *data_f32_ptr = NULL;
158  size_t size;
159  size_t i, j, k;
160  /* local error structure */
161  _io_png_err_t err;
162 
163  /* parameters check */
164  if (NULL == fname || NULL == nxp || NULL == nyp || NULL == ncp)
165  return NULL;
166  if (IO_PNG_U8 != dtype && IO_PNG_F32 != dtype)
167  return NULL;
168 
169  /* open the PNG input file */
170  if (0 == strcmp(fname, "-"))
171  fp = stdin;
172  else if (NULL == (fp = fopen(fname, "rb")))
173  return NULL;
174 
175  /* read in some of the signature bytes and check this signature */
176  if ((PNG_SIG_LEN != fread(png_sig, 1, PNG_SIG_LEN, fp))
177  || 0 != png_sig_cmp(png_sig, (png_size_t) 0, PNG_SIG_LEN))
178  return _io_png_read_abort(fp, NULL, NULL);
179 
180  /*
181  * create and initialize the png_struct
182  * with local error handling
183  */
184  if (NULL == (png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
185  &err, &_io_png_err_hdl,
186  NULL)))
187  return _io_png_read_abort(fp, NULL, NULL);
188 
189  /* allocate/initialize the memory for image information */
190  if (NULL == (info_ptr = png_create_info_struct(png_ptr)))
191  return _io_png_read_abort(fp, &png_ptr, NULL);
192 
193  /* handle read errors */
194  if (setjmp(err.jmpbuf))
195  /* if we get here, we had a problem reading from the file */
196  return _io_png_read_abort(fp, &png_ptr, NULL);
197 
198  /* set up the input control using standard C streams */
199  png_init_io(png_ptr, fp);
200 
201  /* let libpng know that some bytes have been read */
202  png_set_sig_bytes(png_ptr, PNG_SIG_LEN);
203 
204  /*
205  * set the read filter transforms, to get 8bit RGB whatever the
206  * original file may contain:
207  * PNG_TRANSFORM_STRIP_16 strip 16-bit samples to 8 bits
208  * PNG_TRANSFORM_PACKING expand 1, 2 and 4-bit
209  * samples to bytes
210  */
211  png_transform |= (PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING);
212 
213  /* read in the entire image at once */
214  png_read_png(png_ptr, info_ptr, png_transform, NULL);
215 
216  /* get image informations */
217  *nxp = (size_t) png_get_image_width(png_ptr, info_ptr);
218  *nyp = (size_t) png_get_image_height(png_ptr, info_ptr);
219  *ncp = (size_t) png_get_channels(png_ptr, info_ptr);
220  row_pointers = png_get_rows(png_ptr, info_ptr);
221 
222  /*
223  * allocate the output data RGB array
224  * de-interlace and convert png RGB RGB RGB 8bit to RRR GGG BBB
225  * the image is de-interlaced layer after layer
226  * this generic loop also works for one single channel
227  */
228  size = *nxp * *nyp * *ncp;
229  switch (dtype) {
230  case IO_PNG_U8:
231  if (NULL == (data_u8 =
232  (unsigned char *) malloc(size * sizeof(unsigned char))))
233  return _io_png_read_abort(fp, &png_ptr, &info_ptr);
234  data = (void *) data_u8;
235  for (k = 0; k < *ncp; k++) {
236  /* channel loop */
237  data_u8_ptr = data_u8 + (size_t) (*nxp * *nyp * k);
238  for (j = 0; j < *nyp; j++) {
239  /* row loop */
240  row_ptr = row_pointers[j] + k;
241  for (i = 0; i < *nxp; i++) {
242  /* pixel loop */
243  *data_u8_ptr++ = (unsigned char) *row_ptr;
244  row_ptr += *ncp;
245  }
246  }
247  }
248  break;
249  case IO_PNG_F32:
250  if (NULL == (data_f32 = (float *) malloc(size * sizeof(float))))
251  return _io_png_read_abort(fp, &png_ptr, &info_ptr);
252  data = (void *) data_f32;
253  for (k = 0; k < *ncp; k++) {
254  /* channel loop */
255  data_f32_ptr = data_f32 + (size_t) (*nxp * *nyp * k);
256  for (j = 0; j < *nyp; j++) {
257  /* row loop */
258  row_ptr = row_pointers[j] + k;
259  for (i = 0; i < *nxp; i++) {
260  /* pixel loop */
261  *data_f32_ptr++ = (float) *row_ptr;
262  row_ptr += *ncp;
263  }
264  }
265  }
266  break;
267  }
268 
269  /* clean up and free any memory allocated, close the file */
270  (void) _io_png_read_abort(fp, &png_ptr, &info_ptr);
271 
272  return data;
273 }
274 
275 /**
276  * @brief read a PNG file into a 8bit integer array
277  *
278  * The array contains the de-interlaced channels.
279  * 1, 2 and 4bit images are converted to 8bit.
280  * 16bit images are previously downscaled to 8bit.
281  *
282  * @todo don't downscale 16bit images.
283  *
284  * @param fname PNG file name
285  * @param nxp, nyp, ncp pointers to variables to be filled with the number of
286  * columns, lines and channels of the image
287  * @return pointer to an allocated unsigned char array of pixels,
288  * or NULL if an error happens
289  */
290 unsigned char *io_png_read_u8(const char *fname,
291  size_t * nxp, size_t * nyp, size_t * ncp)
292 {
293  /* read the image as unsigned char */
294  return (unsigned char *) io_png_read_raw(fname, nxp, nyp, ncp,
295  PNG_TRANSFORM_IDENTITY,
296  IO_PNG_U8);
297 }
298 
299 /**
300  * @brief read a PNG file into a 8bit integer array, converted to RGB
301  *
302  * See io_png_read_u8() for details.
303  */
304 unsigned char *io_png_read_u8_rgb(const char *fname, size_t * nxp,
305  size_t * nyp)
306 {
307  size_t nc;
308  unsigned char *img;
309 
310  /* read the image */
311  img = (unsigned char *) io_png_read_raw(fname, nxp, nyp, &nc,
312  PNG_TRANSFORM_STRIP_ALPHA,
313  IO_PNG_U8);
314  if (NULL == img)
315  /* error */
316  return NULL;
317  if (3 == nc)
318  /* already RGB */
319  return img;
320  else {
321  /* convert to RGB */
322  size_t i, size;
323  unsigned char *img_r, *img_g, *img_b;
324 
325  /* resize the image */
326  size = *nxp * *nyp;
327  img = (unsigned char *)
328  realloc(img, 3 * size * sizeof(unsigned char));
329  img_r = img;
330  img_g = img + size;
331  img_b = img + 2 * size;
332 
333  /* gray->RGB conversion */
334  for (i = 0; i < size; i++) {
335  img_g[i] = img_r[i];
336  img_b[i] = img_r[i];
337  }
338  return img;
339  }
340 }
341 
342 /**
343  * @brief read a PNG file into a 8bit integer array, converted to gray
344  *
345  * See io_png_read_u8() for details.
346  */
347 unsigned char *io_png_read_u8_gray(const char *fname,
348  size_t * nxp, size_t * nyp)
349 {
350  size_t nc;
351  unsigned char *img;
352 
353  /* read the image */
354  img = (unsigned char *) io_png_read_raw(fname, nxp, nyp, &nc,
355  PNG_TRANSFORM_STRIP_ALPHA,
356  IO_PNG_U8);
357  if (NULL == img)
358  /* error */
359  return NULL;
360  if (1 == nc)
361  /* already gray */
362  return img;
363  else {
364  /* convert to gray */
365  size_t i, size;
366  unsigned char *img_r, *img_g, *img_b;
367 
368  /*
369  * RGB->gray conversion
370  * Y = (6968 * R + 23434 * G + 2366 * B) / 32768
371  * integer approximation of
372  * Y = Cr* R + Cg * G + Cb * B
373  * with
374  * Cr = 0.212639005871510
375  * Cg = 0.715168678767756
376  * Cb = 0.072192315360734
377  * derived from ITU BT.709-5 (Rec 709) sRGB and D65 definitions
378  * http://www.itu.int/rec/R-REC-BT.709/en
379  */
380  size = *nxp * *nyp;
381  img_r = img;
382  img_g = img + size;
383  img_b = img + 2 * size;
384  for (i = 0; i < size; i++)
385  /*
386  * if int type is less than 24 bits, we use long ints,
387  * guaranteed to be >=32 bit
388  */
389 #if (UINT_MAX>>24 == 0)
390 #define CR 6968ul
391 #define CG 23434ul
392 #define CB 2366ul
393 #else
394 #define CR 6968u
395 #define CG 23434u
396 #define CB 2366u
397 #endif
398  /* (1 << 14) is added for rounding instead of truncation */
399  img[i] = (unsigned char) ((CR * img_r[i] + CG * img_g[i]
400  + CB * img_b[i] + (1 << 14)) >> 15);
401 #undef CR
402 #undef CG
403 #undef CB
404  /* resize and return the image */
405  img = (unsigned char *) realloc(img, size * sizeof(unsigned char));
406  return img;
407  }
408 }
409 
410 /**
411  * @brief read a PNG file into a 32bit float array
412  *
413  * The array contains the deinterlaced channels.
414  * 1, 2, 4 and 8bit images are converted to float values
415  * between 0. and 1., 3., 15. or 255.
416  * 16bit images are also downscaled to 8bit before conversion.
417  *
418  * @param fname PNG file name
419  * @param nxp, nyp, ncp pointers to variables to be filled with the number of
420  * columns, lines and channels of the image
421  * @return pointer to an allocated unsigned char array of pixels,
422  * or NULL if an error happens
423  */
424 float *io_png_read_f32(const char *fname,
425  size_t * nxp, size_t * nyp, size_t * ncp)
426 {
427  /* read the image as float */
428  return (float *) io_png_read_raw(fname, nxp, nyp, ncp,
429  PNG_TRANSFORM_IDENTITY, IO_PNG_F32);
430 }
431 
432 /**
433  * @brief read a PNG file into a 32bit float array, converted to RGB
434  *
435  * See io_png_read_f32() for details.
436  */
437 float *io_png_read_f32_rgb(const char *fname, size_t * nxp, size_t * nyp)
438 {
439  size_t nc;
440  float *img;
441 
442  /* read the image */
443  img = (float *) io_png_read_raw(fname, nxp, nyp, &nc,
444  PNG_TRANSFORM_STRIP_ALPHA, IO_PNG_F32);
445  if (NULL == img)
446  /* error */
447  return NULL;
448  if (3 == nc)
449  /* already RGB */
450  return img;
451  else {
452  /* convert to RGB */
453  size_t i, size;
454  float *img_r, *img_g, *img_b;
455 
456  /* resize the image */
457  size = *nxp * *nyp;
458  img = (float *) realloc(img, 3 * size * sizeof(float));
459  img_r = img;
460  img_g = img + size;
461  img_b = img + 2 * size;
462 
463  /* gray->RGB conversion */
464  for (i = 0; i < size; i++) {
465  img_g[i] = img_r[i];
466  img_b[i] = img_r[i];
467  }
468  return img;
469  }
470 }
471 
472 /**
473  * @brief read a PNG file into a 32bit float array, converted to gray
474  *
475  * See io_png_read_f32() for details.
476  */
477 float *io_png_read_f32_gray(const char *fname, size_t * nxp, size_t * nyp)
478 {
479  size_t nc;
480  float *img;
481 
482  /* read the image */
483  img = (float *) io_png_read_raw(fname, nxp, nyp, &nc,
484  PNG_TRANSFORM_STRIP_ALPHA, IO_PNG_F32);
485  if (NULL == img)
486  /* error */
487  return NULL;
488  if (1 == nc)
489  /* already gray */
490  return img;
491  else {
492  /* convert to gray */
493  size_t i, size;
494  float *img_r, *img_g, *img_b;
495 
496  /*
497  * RGB->gray conversion
498  * Y = Cr* R + Cg * G + Cb * B
499  * with
500  * Cr = 0.212639005871510
501  * Cg = 0.715168678767756
502  * Cb = 0.072192315360734
503  * derived from ITU BT.709-5 (Rec 709) sRGB and D65 definitions
504  * http://www.itu.int/rec/R-REC-BT.709/en
505  */
506  size = *nxp * *nyp;
507  img_r = img;
508  img_g = img + size;
509  img_b = img + 2 * size;
510  for (i = 0; i < size; i++)
511  img[i] = (float) (0.212639005871510 * img_r[i]
512  + 0.715168678767756 * img_g[i]
513  + 0.072192315360734 * img_b[i]);
514  /* resize and return the image */
515  img = (float *) realloc(img, size * sizeof(float));
516  return img;
517  }
518 }
519 
520 /*
521  * WRITE
522  */
523 
524 /**
525  * @brief internal function used to cleanup the memory when
526  * png_write_raw() fails
527  *
528  * @param fp file pointer to close, ignored if NULL
529  * @param idata, row_pointers arrays to free, ignored if NULL
530  * @param png_ptr_p, info_ptr_p, pointers to PNG structure pointers,
531  * ignored if NULL
532  * @return -1
533  */
534 static int _io_png_write_abort(FILE * fp,
535  png_byte * idata, png_bytep * row_pointers,
536  png_structp * png_ptr_p,
537  png_infop * info_ptr_p)
538 {
539  png_destroy_write_struct(png_ptr_p, info_ptr_p);
540  if (NULL != row_pointers)
541  free(row_pointers);
542  if (NULL != idata)
543  free(idata);
544  if (NULL != fp && stdout != fp)
545  (void) fclose(fp);
546  return -1;
547 }
548 
549 /**
550  * @brief internal function used to write a byte array as a PNG file
551  *
552  * The PNG file is written as a 8bit image file, interlaced,
553  * truecolor. Depending on the number of channels, the color model is
554  * gray, gray+alpha, rgb, rgb+alpha.
555  *
556  * @todo handle 16bit
557  *
558  * @param fname PNG file name, "-" means stdout
559  * @param data deinterlaced (RRR..GGG..BBB..AAA) image byte array
560  * @param nx, ny, nc number of columns, lines and channels
561  * @param dtype identifier for the data type to be used for output
562  * @return 0 if everything OK, -1 if an error occured
563  */
564 static int io_png_write_raw(const char *fname, const void *data,
565  size_t nx, size_t ny, size_t nc, int dtype)
566 {
567  png_structp png_ptr;
568  png_infop info_ptr;
569  png_byte *idata = NULL, *idata_ptr = NULL;
570  png_bytep *row_pointers = NULL;
571  png_byte bit_depth;
572  /* volatile: because of setjmp/longjmp */
573  FILE *volatile fp;
574  const unsigned char *data_u8 = NULL;
575  const unsigned char *data_u8_ptr = NULL;
576  const float *data_f32 = NULL;
577  const float *data_f32_ptr = NULL;
578  float tmp;
579  int color_type, interlace, compression, filter;
580  size_t size;
581  size_t i, j, k;
582  /* error structure */
583  _io_png_err_t err;
584 
585  /* parameters check */
586  if (0 >= nx || 0 >= ny || 0 >= nc)
587  return -1;
588  if (NULL == fname || NULL == data)
589  return -1;
590  if (IO_PNG_U8 != dtype && IO_PNG_F32 != dtype)
591  return -1;
592 
593  /* open the PNG output file */
594  if (0 == strcmp(fname, "-"))
595  fp = stdout;
596  else if (NULL == (fp = fopen(fname, "wb")))
597  return -1;
598 
599  /* allocate the interlaced array and row pointers */
600  size = nx * ny * nc;
601  if (NULL == (idata = (png_byte *) malloc(size * sizeof(png_byte))))
602  return _io_png_write_abort(fp, NULL, NULL, NULL, NULL);
603 
604  if (NULL == (row_pointers = (png_bytep *) malloc(ny * sizeof(png_bytep))))
605  return _io_png_write_abort(fp, idata, NULL, NULL, NULL);
606 
607  /*
608  * create and initialize the png_struct
609  * with local error handling
610  */
611  if (NULL == (png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
612  &err, &_io_png_err_hdl,
613  NULL)))
614  return _io_png_write_abort(fp, idata, row_pointers, NULL, NULL);
615 
616  /* allocate/initialize the memory for image information */
617  if (NULL == (info_ptr = png_create_info_struct(png_ptr)))
618  return _io_png_write_abort(fp, idata, row_pointers, &png_ptr, NULL);
619 
620  /* handle write errors */
621  if (0 != setjmp(err.jmpbuf))
622  /* if we get here, we had a problem writing to the file */
623  return _io_png_write_abort(fp, idata, row_pointers, &png_ptr,
624  &info_ptr);
625 
626  /* set up the input control using standard C streams */
627  png_init_io(png_ptr, fp);
628 
629  /* set image informations */
630  bit_depth = 8;
631  switch (nc) {
632  case 1:
633  color_type = PNG_COLOR_TYPE_GRAY;
634  break;
635  case 2:
636  color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
637  break;
638  case 3:
639  color_type = PNG_COLOR_TYPE_RGB;
640  break;
641  case 4:
642  color_type = PNG_COLOR_TYPE_RGB_ALPHA;
643  break;
644  default:
645  png_destroy_read_struct(&png_ptr, NULL, NULL);
646  free(row_pointers);
647  free(idata);
648  (void) fclose(fp);
649  return -1;
650  }
651  interlace = PNG_INTERLACE_ADAM7;
652  compression = PNG_COMPRESSION_TYPE_BASE;
653  filter = PNG_FILTER_TYPE_BASE;
654 
655  /* set image header */
656  png_set_IHDR(png_ptr, info_ptr, (png_uint_32) nx, (png_uint_32) ny,
657  bit_depth, color_type, interlace, compression, filter);
658  /* TODO : significant bit (sBIT), gamma (gAMA), comments (text) chunks */
659  png_write_info(png_ptr, info_ptr);
660 
661  /*
662  * interlace and convert RRR GGG BBB to RGB RGB RGB
663  * the image is interlaced layer after layer
664  * this involves more memory exchange, but allows a generic loop
665  */
666  switch (dtype) {
667  case IO_PNG_U8:
668  data_u8 = (unsigned char *) data;
669  for (k = 0; k < nc; k++) {
670  /* channel loop */
671  data_u8_ptr = data_u8 + (size_t) (nx * ny * k);
672  idata_ptr = idata + (size_t) k;
673  for (j = 0; j < ny; j++) {
674  /* row loop */
675  for (i = 0; i < nx; i++) {
676  /* pixel loop */
677  *idata_ptr = (png_byte) * data_u8_ptr++;
678  idata_ptr += nc;
679  }
680  }
681  }
682  break;
683  case IO_PNG_F32:
684  data_f32 = (float *) data;
685  for (k = 0; k < nc; k++) {
686  /* channel loop */
687  data_f32_ptr = data_f32 + (size_t) (nx * ny * k);
688  idata_ptr = idata + (size_t) k;
689  for (j = 0; j < ny; j++) {
690  /* row loop */
691  for (i = 0; i < nx; i++) {
692  /* pixel loop */
693  tmp = floor(*data_f32_ptr++ + .5);
694  *idata_ptr = (png_byte) (tmp < 0. ? 0. :
695  (tmp > 255. ? 255. : tmp));
696  idata_ptr += nc;
697  }
698  }
699  }
700  break;
701  }
702 
703  /* set row pointers */
704  for (j = 0; j < ny; j++)
705  row_pointers[j] = idata + (size_t) (nc * nx * j);
706 
707  /* write out the entire image and end it */
708  png_write_image(png_ptr, row_pointers);
709  png_write_end(png_ptr, info_ptr);
710 
711  /* clean up and free any memory allocated, close the file */
712  (void) _io_png_write_abort(fp, idata, row_pointers, &png_ptr, &info_ptr);
713 
714  return 0;
715 }
716 
717 /**
718  * @brief write a 8bit unsigned integer array into a PNG file
719  *
720  * @param fname PNG file name
721  * @param data array to write
722  * @param nx, ny, nc number of columns, lines and channels of the image
723  * @return 0 if everything OK, -1 if an error occured
724  */
725 int io_png_write_u8(const char *fname, const unsigned char *data,
726  size_t nx, size_t ny, size_t nc)
727 {
728  return io_png_write_raw(fname, (void *) data,
729  (png_uint_32) nx, (png_uint_32) ny, (png_byte) nc,
730  IO_PNG_U8);
731 }
732 
733 /**
734  * @brief write a float array into a PNG file
735  *
736  * The float values are rounded to 8bit integers, and bounded to [0, 255].
737  *
738  * @todo handle 16bit images and flexible min/max
739  *
740  * @param fname PNG file name
741  * @param data array to write
742  * @param nx, ny, nc number of columns, lines and channels of the image
743  * @return 0 if everything OK, -1 if an error occured
744  */
745 int io_png_write_f32(const char *fname, const float *data,
746  size_t nx, size_t ny, size_t nc)
747 {
748  return io_png_write_raw(fname, (void *) data,
749  (png_uint_32) nx, (png_uint_32) ny, (png_byte) nc,
750  IO_PNG_F32);
751 }