- Main Page
- Related Pages
- Files

- File List
- Globals

# io_png.c

Go to the documentation of this file.

    00001 /*
    00002  * Copyright (c) 2010-2011, Nicolas Limare <nicolas.limare@cmla.ens-cachan.fr>
    00003  * All rights reserved.
    00004  *
    00005  * This program is free software: you can redistribute it and/or modify
    00006  * it under, at your option, the terms of the GNU General Public
    00007  * License as published by the Free Software Foundation, either
    00008  * version 3 of the License, or (at your option) any later version, or
    00009  * the terms of the simplified BSD license.
    00010  *
    00011  * You should have received a copy of these licenses along this
    00012  * program. If not, see <http://www.gnu.org/licenses/> and
    00013  * <http://www.opensource.org/licenses/bsd-license.html>.
    00014  */
    00015
    00034 #include <stdlib.h>
    00035 #include <stdio.h>
    00036 #include <math.h>
    00037
    00038 /* option to use a local version of the libpng */
    00039 #ifdef IO_PNG_LOCAL_LIBPNG
    00040 #include "png.h"
    00041 #else
    00042 #include <png.h>
    00043 #endif
    00044
    00045 /* ensure consistency */
    00046 #include "io_png.h"
    00047
    00048 #define PNG_SIG_LEN 4
    00049
    00050 /* internal only datatype identifiers */
    00051 #define IO_PNG_U8  0x0001       /*  8bit unsigned integer */
    00052 #define IO_PNG_F32 0x0002       /* 32bit float */
    00053
    00054 /*
    00055  * INFO
    00056  */
    00057
    00058 /* string tag inserted into the binary */
    00059 static char io_png_tag[] = "using io_png " IO_PNG_VERSION;
    00068 char *io_png_info(void)
    00069 {
    00070     return io_png_tag;
    00071 }
    00072
    00073 /*
    00074  * READ
    00075  */
    00076
    00086 static void *io_png_read_abort(FILE * fp,
    00087                                png_structp * png_ptr_p,
    00088                                png_infop * info_ptr_p)
    00089 {
    00090     png_destroy_read_struct(png_ptr_p, info_ptr_p, NULL);
    00091     if (NULL != fp && stdin != fp)
    00092         (void) fclose(fp);
    00093     return NULL;
    00094 }
    00095
    00110 static void *io_png_read_raw(const char *fname,
    00111                              size_t * nxp, size_t * nyp, size_t * ncp,
    00112                              int png_transform, int dtype)
    00113 {
    00114     png_byte png_sig[PNG_SIG_LEN];
    00115     png_structp png_ptr;
    00116     png_infop info_ptr;
    00117     png_bytepp row_pointers;
    00118     png_bytep row_ptr;
    00119     /* volatile: because of setjmp/longjmp */
    00120     FILE *volatile fp = NULL;
    00121     void *data = NULL;
    00122     unsigned char *data_u8 = NULL;
    00123     unsigned char *data_u8_ptr = NULL;
    00124     float *data_f32 = NULL;
    00125     float *data_f32_ptr = NULL;
    00126     size_t size;
    00127     size_t i, j, k;
    00128
    00129     /* parameters check */
    00130     if (NULL == fname || NULL == nxp || NULL == nyp || NULL == ncp)
    00131         return NULL;
    00132     if (IO_PNG_U8 != dtype && IO_PNG_F32 != dtype)
    00133         return NULL;
    00134
    00135     /* open the PNG input file */
    00136     if (0 == strcmp(fname, "-"))
    00137         fp = stdin;
    00138     else if (NULL == (fp = fopen(fname, "rb")))
    00139         return NULL;
    00140
    00141     /* read in some of the signature bytes and check this signature */
    00142     if ((PNG_SIG_LEN != fread(png_sig, 1, PNG_SIG_LEN, fp))
    00143         || 0 != png_sig_cmp(png_sig, (png_size_t) 0, PNG_SIG_LEN))
    00144         return io_png_read_abort(fp, NULL, NULL);
    00145
    00146     /*
    00147      * create and initialize the png_struct
    00148      * with the default stderr and error handling
    00149      */
    00150     if (NULL == (png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
    00151                                                   NULL, NULL, NULL)))
    00152         return io_png_read_abort(fp, NULL, NULL);
    00153
    00154     /* allocate/initialize the memory for image information */
    00155     if (NULL == (info_ptr = png_create_info_struct(png_ptr)))
    00156         return io_png_read_abort(fp, &png_ptr, NULL);
    00157
    00158     /* set error handling */
    00159     if (0 != setjmp(png_jmpbuf(png_ptr)))
    00160         /* if we get here, we had a problem reading the file */
    00161         /* free all of the memory associated with the png_ptr and info_ptr */
    00162         return io_png_read_abort(fp, &png_ptr, &info_ptr);
    00163
    00164     /* set up the input control using standard C streams */
    00165     png_init_io(png_ptr, fp);
    00166
    00167     /* let libpng know that some bytes have been read */
    00168     png_set_sig_bytes(png_ptr, PNG_SIG_LEN);
    00169
    00170     /*
    00171      * set the read filter transforms, to get 8bit RGB whatever the
    00172      * original file may contain:
    00173      * PNG_TRANSFORM_STRIP_16      strip 16-bit samples to 8 bits
    00174      * PNG_TRANSFORM_PACKING       expand 1, 2 and 4-bit
    00175      *                             samples to bytes
    00176      */
    00177     png_transform |= (PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING);
    00178
    00179     /* read in the entire image at once */
    00180     png_read_png(png_ptr, info_ptr, png_transform, NULL);
    00181
    00182     /* get image informations */
    00183     *nxp = (size_t) png_get_image_width(png_ptr, info_ptr);
    00184     *nyp = (size_t) png_get_image_height(png_ptr, info_ptr);
    00185     *ncp = (size_t) png_get_channels(png_ptr, info_ptr);
    00186     row_pointers = png_get_rows(png_ptr, info_ptr);
    00187
    00188     /*
    00189      * allocate the output data RGB array
    00190      * deinterlace and convert png RGB RGB RGB 8bit to RRR GGG BBB
    00191      * the image is deinterlaced layer after layer
    00192      * this generic loop also works for one single channel
    00193      */
    00194     size = *nxp * *nyp * *ncp;
    00195     switch (dtype) {
    00196     case IO_PNG_U8:
    00197         if (NULL == (data_u8 =
    00198                      (unsigned char *) malloc(size * sizeof(unsigned char))))
    00199             return io_png_read_abort(fp, &png_ptr, &info_ptr);
    00200         data = (void *) data_u8;
    00201         for (k = 0; k < *ncp; k++) {
    00202             /* channel loop */
    00203             data_u8_ptr = data_u8 + (size_t) (*nxp * *nyp * k);
    00204             for (j = 0; j < *nyp; j++) {
    00205                 /* row loop */
    00206                 row_ptr = row_pointers[j] + k;
    00207                 for (i = 0; i < *nxp; i++) {
    00208                     /* pixel loop */
    00209                     *data_u8_ptr++ = (unsigned char) *row_ptr;
    00210                     row_ptr += *ncp;
    00211                 }
    00212             }
    00213         }
    00214         break;
    00215     case IO_PNG_F32:
    00216         if (NULL == (data_f32 = (float *) malloc(size * sizeof(float))))
    00217             return io_png_read_abort(fp, &png_ptr, &info_ptr);
    00218         data = (void *) data_f32;
    00219         for (k = 0; k < *ncp; k++) {
    00220             /* channel loop */
    00221             data_f32_ptr = data_f32 + (size_t) (*nxp * *nyp * k);
    00222             for (j = 0; j < *nyp; j++) {
    00223                 /* row loop */
    00224                 row_ptr = row_pointers[j] + k;
    00225                 for (i = 0; i < *nxp; i++) {
    00226                     /* pixel loop */
    00227                     *data_f32_ptr++ = (float) *row_ptr;
    00228                     row_ptr += *ncp;
    00229                 }
    00230             }
    00231         }
    00232         break;
    00233     }
    00234
    00235     /* clean up and free any memory allocated, close the file */
    00236     (void) io_png_read_abort(fp, &png_ptr, &info_ptr);
    00237
    00238     return data;
    00239 }
    00240
    00256 unsigned char *io_png_read_u8(const char *fname,
    00257                               size_t * nxp, size_t * nyp, size_t * ncp)
    00258 {
    00259     /* read the image as unsigned char */
    00260     return (unsigned char *) io_png_read_raw(fname, nxp, nyp, ncp,
    00261                                              PNG_TRANSFORM_IDENTITY,
    00262                                              IO_PNG_U8);
    00263 }
    00264
    00270 unsigned char *io_png_read_u8_rgb(const char *fname, size_t * nxp,
    00271                                   size_t * nyp)
    00272 {
    00273     size_t nc;
    00274     unsigned char *img;
    00275
    00276     /* read the image */
    00277     img = (unsigned char *) io_png_read_raw(fname, nxp, nyp, &nc,
    00278                                             PNG_TRANSFORM_STRIP_ALPHA,
    00279                                             IO_PNG_U8);
    00280     if (NULL == img)
    00281         /* error */
    00282         return NULL;
    00283     if (3 == nc)
    00284         /* already RGB */
    00285         return img;
    00286     else {
    00287         /* convert to RGB */
    00288         size_t i, size;
    00289         unsigned char *img_r, *img_g, *img_b;
    00290
    00291         /* resize the image */
    00292         size = *nxp * *nyp;
    00293         img = (unsigned char *)
    00294             realloc(img, 3 * size * sizeof(unsigned char));
    00295         img_r = img;
    00296         img_g = img + size;
    00297         img_b = img + 2 * size;
    00298
    00299         /* gray->RGB conversion */
    00300         for (i = 0; i < size; i++) {
    00301             img_g[i] = img_r[i];
    00302             img_b[i] = img_r[i];
    00303         }
    00304         return img;
    00305     }
    00306 }
    00307
    00313 unsigned char *io_png_read_u8_gray(const char *fname,
    00314                                    size_t * nxp, size_t * nyp)
    00315 {
    00316     size_t nc;
    00317     unsigned char *img;
    00318
    00319     /* read the image */
    00320     img = (unsigned char *) io_png_read_raw(fname, nxp, nyp, &nc,
    00321                                             PNG_TRANSFORM_STRIP_ALPHA,
    00322                                             IO_PNG_U8);
    00323     if (NULL == img)
    00324         /* error */
    00325         return NULL;
    00326     if (1 == nc)
    00327         /* already gray */
    00328         return img;
    00329     else {
    00330         /* convert to gray */
    00331         size_t i, size;
    00332         unsigned char *img_r, *img_g, *img_b;
    00333
    00334         /*
    00335          * RGB->gray conversion
    00336          * Y = (6969 * R + 23434 * G + 2365 * B)/32768
    00337          * integer approximation of
    00338          * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
    00339          */
    00340         size = *nxp * *nyp;
    00341         img_r = img;
    00342         img_g = img + size;
    00343         img_b = img + 2 * size;
    00344         for (i = 0; i < size; i++)
    00345             img[i] = (unsigned char) ((6969 * img_r[i]
    00346                                        + 23434 * img_g[i]
    00347                                        + 2365 * img_b[i]) / 32768);
    00348         /* resize and return the image */
    00349         img = (unsigned char *) realloc(img, size * sizeof(unsigned char));
    00350         return img;
    00351     }
    00352 }
    00353
    00368 float *io_png_read_f32(const char *fname,
    00369                        size_t * nxp, size_t * nyp, size_t * ncp)
    00370 {
    00371     /* read the image as float */
    00372     return (float *) io_png_read_raw(fname, nxp, nyp, ncp,
    00373                                      PNG_TRANSFORM_IDENTITY, IO_PNG_F32);
    00374 }
    00375
    00381 float *io_png_read_f32_rgb(const char *fname, size_t * nxp, size_t * nyp)
    00382 {
    00383     size_t nc;
    00384     float *img;
    00385
    00386     /* read the image */
    00387     img = (float *) io_png_read_raw(fname, nxp, nyp, &nc,
    00388                                     PNG_TRANSFORM_STRIP_ALPHA, IO_PNG_F32);
    00389     if (NULL == img)
    00390         /* error */
    00391         return NULL;
    00392     if (3 == nc)
    00393         /* already RGB */
    00394         return img;
    00395     else {
    00396         /* convert to RGB */
    00397         size_t i, size;
    00398         float *img_r, *img_g, *img_b;
    00399
    00400         /* resize the image */
    00401         size = *nxp * *nyp;
    00402         img = (float *) realloc(img, 3 * size * sizeof(float));
    00403         img_r = img;
    00404         img_g = img + size;
    00405         img_b = img + 2 * size;
    00406
    00407         /* gray->RGB conversion */
    00408         for (i = 0; i < size; i++) {
    00409             img_g[i] = img_r[i];
    00410             img_b[i] = img_r[i];
    00411         }
    00412         return img;
    00413     }
    00414 }
    00415
    00421 float *io_png_read_f32_gray(const char *fname, size_t * nxp, size_t * nyp)
    00422 {
    00423     size_t nc;
    00424     float *img;
    00425
    00426     /* read the image */
    00427     img = (float *) io_png_read_raw(fname, nxp, nyp, &nc,
    00428                                     PNG_TRANSFORM_STRIP_ALPHA, IO_PNG_F32);
    00429     if (NULL == img)
    00430         /* error */
    00431         return NULL;
    00432     if (1 == nc)
    00433         /* already gray */
    00434         return img;
    00435     else {
    00436         /* convert to gray */
    00437         size_t i, size;
    00438         float *img_r, *img_g, *img_b;
    00439
    00440         /*
    00441          * RGB->gray conversion
    00442          * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
    00443          */
    00444         size = *nxp * *nyp;
    00445         img_r = img;
    00446         img_g = img + size;
    00447         img_b = img + 2 * size;
    00448         for (i = 0; i < size; i++)
    00449             img[i] = (float) (0.212671 * img_r[i]
    00450                               + 0.715160 * img_g[i]
    00451                               + 0.072169 * img_b[i]);
    00452         /* resize and return the image */
    00453         img = (float *) realloc(img, size * sizeof(float));
    00454         return img;
    00455     }
    00456 }
    00457
    00458 /*
    00459  * WRITE
    00460  */
    00461
    00472 static int io_png_write_abort(FILE * fp,
    00473                               png_byte * idata, png_bytep * row_pointers,
    00474                               png_structp * png_ptr_p, png_infop * info_ptr_p)
    00475 {
    00476     png_destroy_write_struct(png_ptr_p, info_ptr_p);
    00477     if (NULL != row_pointers)
    00478         free(row_pointers);
    00479     if (NULL != idata)
    00480         free(idata);
    00481     if (NULL != fp && stdout != fp)
    00482         (void) fclose(fp);
    00483     return -1;
    00484 }
    00485
    00501 static int io_png_write_raw(const char *fname, const void *data,
    00502                             size_t nx, size_t ny, size_t nc, int dtype)
    00503 {
    00504     png_structp png_ptr;
    00505     png_infop info_ptr;
    00506     png_byte *idata = NULL, *idata_ptr = NULL;
    00507     png_bytep *row_pointers = NULL;
    00508     png_byte bit_depth;
    00509     /* volatile: because of setjmp/longjmp */
    00510     FILE *volatile fp;
    00511     const unsigned char *data_u8 = NULL;
    00512     const unsigned char *data_u8_ptr = NULL;
    00513     const float *data_f32 = NULL;
    00514     const float *data_f32_ptr = NULL;
    00515     float tmp;
    00516     int color_type, interlace, compression, filter;
    00517     size_t size;
    00518     size_t i, j, k;
    00519
    00520     /* parameters check */
    00521     if (0 >= nx || 0 >= ny || 0 >= nc)
    00522         return -1;
    00523     if (NULL == fname || NULL == data)
    00524         return -1;
    00525     if (IO_PNG_U8 != dtype && IO_PNG_F32 != dtype)
    00526         return -1;
    00527
    00528     /* open the PNG output file */
    00529     if (0 == strcmp(fname, "-"))
    00530         fp = stdout;
    00531     else if (NULL == (fp = fopen(fname, "wb")))
    00532         return -1;
    00533
    00534     /* allocate the interlaced array and row pointers */
    00535     size = nx * ny * nc;
    00536     if (NULL == (idata = (png_byte *) malloc(size * sizeof(png_byte))))
    00537         return io_png_write_abort(fp, NULL, NULL, NULL, NULL);
    00538
    00539     if (NULL == (row_pointers = (png_bytep *) malloc(ny * sizeof(png_bytep))))
    00540         return io_png_write_abort(fp, idata, NULL, NULL, NULL);
    00541
    00542     /*
    00543      * create and initialize the png_struct
    00544      * with the default stderr and error handling
    00545      */
    00546     if (NULL == (png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
    00547                                                    NULL, NULL, NULL)))
    00548         return io_png_write_abort(fp, idata, row_pointers, NULL, NULL);
    00549
    00550     /* allocate/initialize the memory for image information */
    00551     if (NULL == (info_ptr = png_create_info_struct(png_ptr)))
    00552         return io_png_write_abort(fp, idata, row_pointers, &png_ptr, NULL);
    00553
    00554     /* set error handling */
    00555     if (0 != setjmp(png_jmpbuf(png_ptr)))
    00556         /* if we get here, we had a problem reading the file */
    00557         return io_png_write_abort(fp, idata, row_pointers, &png_ptr,
    00558                                   &info_ptr);
    00559
    00560     /* set up the input control using standard C streams */
    00561     png_init_io(png_ptr, fp);
    00562
    00563     /* set image informations */
    00564     bit_depth = 8;
    00565     switch (nc) {
    00566     case 1:
    00567         color_type = PNG_COLOR_TYPE_GRAY;
    00568         break;
    00569     case 2:
    00570         color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
    00571         break;
    00572     case 3:
    00573         color_type = PNG_COLOR_TYPE_RGB;
    00574         break;
    00575     case 4:
    00576         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
    00577         break;
    00578     default:
    00579         png_destroy_read_struct(&png_ptr, NULL, NULL);
    00580         free(row_pointers);
    00581         free(idata);
    00582         (void) fclose(fp);
    00583         return -1;
    00584     }
    00585     interlace = PNG_INTERLACE_ADAM7;
    00586     compression = PNG_COMPRESSION_TYPE_BASE;
    00587     filter = PNG_FILTER_TYPE_BASE;
    00588
    00589     /* set image header */
    00590     png_set_IHDR(png_ptr, info_ptr, (png_uint_32) nx, (png_uint_32) ny,
    00591                  bit_depth, color_type, interlace, compression, filter);
    00592     /* TODO : significant bit (sBIT), gamma (gAMA), comments (text) chunks */
    00593     png_write_info(png_ptr, info_ptr);
    00594
    00595     /*
    00596      * interlace and convert RRR GGG BBB to RGB RGB RGB
    00597      * the image is interlaced layer after layer
    00598      * this involves more memory exchange, but allows a generic loop
    00599      */
    00600     switch (dtype) {
    00601     case IO_PNG_U8:
    00602         data_u8 = (unsigned char *) data;
    00603         for (k = 0; k < nc; k++) {
    00604             /* channel loop */
    00605             data_u8_ptr = data_u8 + (size_t) (nx * ny * k);
    00606             idata_ptr = idata + (size_t) k;
    00607             for (j = 0; j < ny; j++) {
    00608                 /* row loop */
    00609                 for (i = 0; i < nx; i++) {
    00610                     /* pixel loop */
    00611                     *idata_ptr = (png_byte) * data_u8_ptr++;
    00612                     idata_ptr += nc;
    00613                 }
    00614             }
    00615         }
    00616         break;
    00617     case IO_PNG_F32:
    00618         data_f32 = (float *) data;
    00619         for (k = 0; k < nc; k++) {
    00620             /* channel loop */
    00621             data_f32_ptr = data_f32 + (size_t) (nx * ny * k);
    00622             idata_ptr = idata + (size_t) k;
    00623             for (j = 0; j < ny; j++) {
    00624                 /* row loop */
    00625                 for (i = 0; i < nx; i++) {
    00626                     /* pixel loop */
    00627                     tmp = floor(*data_f32_ptr++ + .5);
    00628                     *idata_ptr = (png_byte) (tmp < 0. ? 0. :
    00629                                              (tmp > 255. ? 255. : tmp));
    00630                     idata_ptr += nc;
    00631                 }
    00632             }
    00633         }
    00634         break;
    00635     }
    00636
    00637     /* set row pointers */
    00638     for (j = 0; j < ny; j++)
    00639         row_pointers[j] = idata + (size_t) (nc * nx * j);
    00640
    00641     /* write out the entire image and end it */
    00642     png_write_image(png_ptr, row_pointers);
    00643     png_write_end(png_ptr, info_ptr);
    00644
    00645     /* clean up and free any memory allocated, close the file */
    00646     (void) io_png_write_abort(fp, idata, row_pointers, &png_ptr, &info_ptr);
    00647
    00648     return 0;
    00649 }
    00650
    00659 int io_png_write_u8(const char *fname, const unsigned char *data,
    00660                     size_t nx, size_t ny, size_t nc)
    00661 {
    00662     return io_png_write_raw(fname, (void *) data,
    00663                             (png_uint_32) nx, (png_uint_32) ny, (png_byte) nc,
    00664                             IO_PNG_U8);
    00665 }
    00666
    00679 int io_png_write_f32(const char *fname, const float *data,
    00680                      size_t nx, size_t ny, size_t nc)
    00681 {
    00682     return io_png_write_raw(fname, (void *) data,
    00683                             (png_uint_32) nx, (png_uint_32) ny, (png_byte) nc,
    00684                             IO_PNG_F32);
    00685 }