Leptonica 1.68
C Image Processing Library

pngio.c

Go to the documentation of this file.
00001 /*====================================================================*
00002  -  Copyright (C) 2001 Leptonica.  All rights reserved.
00003  -  This software is distributed in the hope that it will be
00004  -  useful, but with NO WARRANTY OF ANY KIND.
00005  -  No author or distributor accepts responsibility to anyone for the
00006  -  consequences of using this software, or for whether it serves any
00007  -  particular purpose or works at all, unless he or she says so in
00008  -  writing.  Everyone is granted permission to copy, modify and
00009  -  redistribute this source code, for commercial or non-commercial
00010  -  purposes, with the following restrictions: (1) the origin of this
00011  -  source code must not be misrepresented; (2) modified versions must
00012  -  be plainly marked as such; and (3) this notice may not be removed
00013  -  or altered from any source or modified source distribution.
00014  *====================================================================*/
00015 
00016 /*
00017  *  pngio.c
00018  *                     
00019  *    Read png from file
00020  *          PIX        *pixReadStreamPng()
00021  *          l_int32     readHeaderPng()
00022  *          l_int32     freadHeaderPng()
00023  *          l_int32     sreadHeaderPng()
00024  *          l_int32     fgetPngResolution()
00025  *
00026  *    Write png to file
00027  *          l_int32     pixWritePng()  [ special top level ]
00028  *          l_int32     pixWriteStreamPng()
00029  *          
00030  *    Read and write of png to/from RGBA pix
00031  *          PIX        *pixReadRGBAPng();
00032  *          l_int32     pixWriteRGBAPng();
00033  *
00034  *    Setting flags for special modes
00035  *          void        l_pngSetStrip16To8()
00036  *          void        l_pngSetStripAlpha()
00037  *          void        l_pngSetWriteAlpha()
00038  *          void        l_pngSetZlibCompression()
00039  *
00040  *    Read/write to memory   [not on windows]
00041  *          PIX        *pixReadMemPng()
00042  *          l_int32     pixWriteMemPng()
00043  *
00044  *    Documentation: libpng.txt and example.c
00045  *
00046  *    On input (decompression from file), palette color images
00047  *    are read into an 8 bpp Pix with a colormap, and 24 bpp
00048  *    3 component color images are read into a 32 bpp Pix with
00049  *    rgb samples.  On output (compression to file), palette color
00050  *    images are written as 8 bpp with the colormap, and 32 bpp
00051  *    full color images are written compressed as a 24 bpp,
00052  *    3 component color image.
00053  *
00054  *    In the following, we use these abbreviations:
00055  *       bps == bit/sample
00056  *       spp == samples/pixel
00057  *       bpp == bits/pixel of image in Pix (memory)
00058  *    where each component is referred to as a "sample".
00059  *
00060  *    There are three special flags for determining the number or
00061  *    size of samples retained or written:
00062  *    (1) var_PNG_STRIP_16_to_8: default is TRUE.  This strips each
00063  *        16 bit sample down to 8 bps:
00064  *         - For 16 bps rgb (16 bps, 3 spp) --> 32 bpp rgb Pix
00065  *         - For 16 bps gray (16 bps, 1 spp) --> 8 bpp grayscale Pix
00066  *    (2) var_PNG_STRIP_ALPHA: default is TRUE.  This does not copy
00067  *        the alpha channel to the pix:
00068  *         - For 8 bps rgba (8 bps, 4 spp) --> 32 bpp rgb Pix
00069  *    (3) var_PNG_WRITE_ALPHA: default is FALSE.  The default generates
00070  *        an RGB png file with 3 spp.  If set to TRUE, this generates
00071  *        an RGBA png file with 4 spp, and writes the alpha channel.
00072  *    These are set with accessors.
00073  *
00074  *    Two convenience functions are included for reading the alpha
00075  *    channel (if it exists) into the pix, and for writing out the
00076  *    alpha sample of a pix to a png file:
00077  *        pixReadRGBAPng()
00078  *        pixWriteRGBAPng()
00079  *    These use two of the special flags, setting to the non-default
00080  *    value before use and resetting to default afterwards.
00081  *    In leptonica, we make almost no explicit use of the alpha channel.
00082  *        
00083  *    Another special flag, var_ZLIB_COMPRESSION, is used to determine
00084  *    the compression level.  Default is for standard png compression.
00085  *    The zlib compression value can be set [0 ... 9], with
00086  *         0     no compression (huge files)
00087  *         1     fastest compression
00088  *         -1    default compression  (equivalent to 6 in latest version)
00089  *         9     best compression
00090  *    If not set, we use the default compression in zlib.
00091  *    Note that if you are using the defined constants in zlib instead
00092  *    of the compression integers given above, you must include zlib.h.
00093  *
00094  *    Note: All special flags use global constants, so if used with
00095  *          multi-threaded applications, results can be non-deterministic.
00096  */
00097 
00098 #include <string.h>
00099 #include "allheaders.h"
00100 
00101 #ifdef HAVE_CONFIG_H
00102 #include "config_auto.h"
00103 #endif  /* HAVE_CONFIG_H */
00104 
00105 /* --------------------------------------------*/
00106 #if  HAVE_LIBPNG   /* defined in environ.h */
00107 /* --------------------------------------------*/
00108 
00109 #include "png.h"
00110 
00111 /* ----------------Set defaults for read/write options ----------------- */
00112     /* strip 16 bpp --> 8 bpp on reading png; default is for stripping */
00113 static l_int32   var_PNG_STRIP_16_TO_8 = 1;
00114     /* strip alpha on reading png; default is for stripping */
00115 static l_int32   var_PNG_STRIP_ALPHA = 1;
00116     /* write alpha for 32 bpp images; default is to write only RGB */
00117 static l_int32   var_PNG_WRITE_ALPHA = 0;
00118     /* zlib compression in png; default is for standard compression */
00119 static l_int32   var_ZLIB_COMPRESSION = Z_DEFAULT_COMPRESSION;
00120 
00121 
00122 #ifndef  NO_CONSOLE_IO
00123 #define  DEBUG     0
00124 #endif  /* ~NO_CONSOLE_IO */
00125 
00126 
00127 /*---------------------------------------------------------------------*
00128  *                              Reading png                            *
00129  *---------------------------------------------------------------------*/
00130 /*!
00131  *  pixReadStreamPng()
00132  *
00133  *      Input:  stream
00134  *      Return: pix, or null on error
00135  *
00136  *  Notes:
00137  *      (1) If called from pixReadStream(), the stream is positioned
00138  *          at the beginning of the file.
00139  *      (2) To do sequential reads of png format images from a stream,
00140  *          use pixReadStreamPng()
00141  */
00142 PIX *
00143 pixReadStreamPng(FILE  *fp)
00144 {
00145 l_uint8      rval, gval, bval;
00146 l_int32      i, j, k;
00147 l_int32      wpl, d, spp, cindex;
00148 l_uint32     png_transforms;
00149 l_uint32    *data, *line, *ppixel;
00150 int          num_palette, num_text;
00151 png_byte     bit_depth, color_type, channels;
00152 png_uint_32  w, h, rowbytes;
00153 png_uint_32  xres, yres;
00154 png_bytep    rowptr;
00155 png_bytep   *row_pointers;
00156 png_structp  png_ptr;
00157 png_infop    info_ptr, end_info;
00158 png_colorp   palette;
00159 png_textp    text_ptr;  /* ptr to text_chunk */
00160 PIX         *pix;
00161 PIXCMAP     *cmap;
00162 
00163     PROCNAME("pixReadStreamPng");
00164 
00165     if (!fp)
00166         return (PIX *)ERROR_PTR("fp not defined", procName, NULL);
00167     pix = NULL;
00168 
00169         /* Allocate the 3 data structures */
00170     if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
00171                    (png_voidp)NULL, NULL, NULL)) == NULL)
00172         return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL);
00173 
00174     if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
00175         png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00176         return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL);
00177     }
00178 
00179     if ((end_info = png_create_info_struct(png_ptr)) == NULL) {
00180         png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00181         return (PIX *)ERROR_PTR("end_info not made", procName, NULL);
00182     }
00183 
00184         /* Set up png setjmp error handling */
00185     if (setjmp(png_jmpbuf(png_ptr))) {
00186         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00187         return (PIX *)ERROR_PTR("internal png error", procName, NULL);
00188     }
00189 
00190     png_init_io(png_ptr, fp);
00191 
00192         /* ---------------------------------------------------------- *
00193          *  Set the transforms flags.  Whatever happens here,
00194          *  NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO.
00195          * ---------------------------------------------------------- */
00196         /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */
00197     if (var_PNG_STRIP_16_TO_8 == 1)   /* our default */
00198         png_transforms = PNG_TRANSFORM_STRIP_16;
00199     else
00200         png_transforms = PNG_TRANSFORM_IDENTITY;
00201         /* To remove alpha channel, use PNG_TRANSFORM_STRIP_ALPHA */
00202     if (var_PNG_STRIP_ALPHA == 1)   /* our default */
00203         png_transforms |= PNG_TRANSFORM_STRIP_ALPHA;
00204 
00205         /* Read it */
00206     png_read_png(png_ptr, info_ptr, png_transforms, NULL);
00207 
00208     row_pointers = png_get_rows(png_ptr, info_ptr);
00209     w = png_get_image_width(png_ptr, info_ptr);
00210     h = png_get_image_height(png_ptr, info_ptr);
00211     bit_depth = png_get_bit_depth(png_ptr, info_ptr);
00212     rowbytes = png_get_rowbytes(png_ptr, info_ptr);
00213     color_type = png_get_color_type(png_ptr, info_ptr);
00214     channels = png_get_channels(png_ptr, info_ptr);
00215     spp = channels;
00216 
00217     if (spp == 1)
00218         d = bit_depth;
00219     else if (spp == 2) {
00220         d = 2 * bit_depth;
00221         L_WARNING("there shouldn't be 2 spp!", procName);
00222     }
00223     else  /* spp == 3 (rgb), spp == 4 (rgba) */
00224         d = 4 * bit_depth;
00225 
00226         /* Remove if/when this is implemented for all bit_depths */
00227     if (spp == 3 && bit_depth != 8) {
00228         fprintf(stderr, "Help: spp = 3 and depth = %d != 8\n!!", bit_depth);
00229         return (PIX *)ERROR_PTR("not implemented for this depth",
00230             procName, NULL);
00231     }
00232 
00233     if (color_type == PNG_COLOR_TYPE_PALETTE ||
00234         color_type == PNG_COLOR_MASK_PALETTE) {   /* generate a colormap */
00235         png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
00236         cmap = pixcmapCreate(d);  /* spp == 1 */
00237         for (cindex = 0; cindex < num_palette; cindex++) {
00238             rval = palette[cindex].red;
00239             gval = palette[cindex].green;
00240             bval = palette[cindex].blue;
00241             pixcmapAddColor(cmap, rval, gval, bval);
00242         }
00243     }
00244     else
00245         cmap = NULL;
00246 
00247     if ((pix = pixCreate(w, h, d)) == NULL) {
00248         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00249         return (PIX *)ERROR_PTR("pix not made", procName, NULL);
00250     }
00251     wpl = pixGetWpl(pix);
00252     data = pixGetData(pix);
00253     pixSetColormap(pix, cmap);
00254 
00255     if (spp == 1) {   /* copy straight from buffer to pix */
00256         for (i = 0; i < h; i++) {
00257             line = data + i * wpl;
00258             rowptr = row_pointers[i];
00259             for (j = 0; j < rowbytes; j++) {
00260                 SET_DATA_BYTE(line, j, rowptr[j]);
00261             }
00262         }
00263     }
00264     else  {   /* spp == 3 or spp == 4 */
00265         for (i = 0; i < h; i++) {
00266             ppixel = data + i * wpl;
00267             rowptr = row_pointers[i];
00268             for (j = k = 0; j < w; j++) {
00269                 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]);
00270                 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]);
00271                 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
00272                 if (spp == 4)
00273                     SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
00274                 ppixel++;
00275             }
00276         }
00277     }
00278 
00279 #if  DEBUG
00280     if (cmap) {
00281         for (i = 0; i < 16; i++) {
00282             fprintf(stderr, "[%d] = %d\n", i,
00283                    ((l_uint8 *)(cmap->array))[i]);
00284         }
00285     }
00286 #endif  /* DEBUG */
00287 
00288         /* If there is no colormap, PNG defines black = 0 and
00289          * white = 1 by default for binary monochrome.  Therefore,
00290          * since we use the opposite definition, we must invert
00291          * the image in either of these cases:
00292          *    (i) there is no colormap (default)
00293          *    (ii) there is a colormap which defines black to
00294          *         be 0 and white to be 1.
00295          * We cannot use the PNG_TRANSFORM_INVERT_MONO flag
00296          * because that flag (since version 1.0.9) inverts 8 bpp
00297          * grayscale as well, which we don't want to do.
00298          * (It also doesn't work if there is a colormap.)
00299          * If there is a colormap that defines black = 1 and
00300          * white = 0, we don't need to do anything.
00301          * 
00302          * How do we check the polarity of the colormap?
00303          * The colormap determines the values of black and
00304          * white pixels in the following way:
00305          *     if black = 1 (255), white = 0
00306          *          255, 255, 255, 0, 0, 0, 0, 0, 0
00307          *     if black = 0, white = 1 (255)
00308          *          0, 0, 0, 0, 255, 255, 255, 0
00309          * So we test the first byte to see if it is 0;
00310          * if so, invert the data.  */
00311     if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) {
00312 /*        fprintf(stderr, "Inverting binary data on png read\n"); */
00313         pixInvert(pix, pix);
00314     }
00315 
00316     xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
00317     yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
00318     pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5));  /* to ppi */
00319     pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5));  /* to ppi */
00320 
00321         /* Get the text if there is any */
00322     png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
00323     if (num_text && text_ptr)
00324         pixSetText(pix, text_ptr->text);
00325 
00326     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00327     return pix;
00328 }
00329 
00330 
00331 /*!
00332  *  readHeaderPng()
00333  *
00334  *      Input:  filename
00335  *              &width (<return>)
00336  *              &height (<return>)
00337  *              &bps (<return>, bits/sample)
00338  *              &spp (<return>, samples/pixel)
00339  *              &iscmap (<optional return>; input NULL to ignore)
00340  *      Return: 0 if OK, 1 on error
00341  *
00342  *  Notes:
00343  *      (1) If there is a colormap, iscmap is returned as 1; else 0.
00344  */
00345 l_int32
00346 readHeaderPng(const char *filename,
00347               l_int32    *pwidth,
00348               l_int32    *pheight,
00349               l_int32    *pbps,
00350               l_int32    *pspp,
00351               l_int32    *piscmap)
00352 {
00353 l_int32  ret;
00354 FILE    *fp;
00355 
00356     PROCNAME("readHeaderPng");
00357 
00358     if (!filename)
00359         return ERROR_INT("filename not defined", procName, 1);
00360     if (!pwidth || !pheight || !pbps || !pspp)
00361         return ERROR_INT("input ptr(s) not defined", procName, 1);
00362     if ((fp = fopenReadStream(filename)) == NULL)
00363         return ERROR_INT("image file not found", procName, 1);
00364     ret = freadHeaderPng(fp, pwidth, pheight, pbps, pspp, piscmap);
00365     fclose(fp);
00366     return ret;
00367 }
00368 
00369 
00370 /*!
00371  *  freadHeaderPng()
00372  *
00373  *      Input:  stream
00374  *              &width (<return>)
00375  *              &height (<return>)
00376  *              &bps (<return>, bits/sample)
00377  *              &spp (<return>, samples/pixel)
00378  *              &iscmap (<optional return>; input NULL to ignore)
00379  *      Return: 0 if OK, 1 on error
00380  *
00381  *  Notes:
00382  *      (1) If there is a colormap, iscmap is returned as 1; else 0.
00383  */
00384 l_int32
00385 freadHeaderPng(FILE     *fp,
00386                l_int32  *pwidth,
00387                l_int32  *pheight,
00388                l_int32  *pbps,
00389                l_int32  *pspp,
00390                l_int32  *piscmap)
00391 {
00392 l_int32   nbytes, ret;
00393 l_uint8  *data;
00394 
00395     PROCNAME("freadHeaderPng");
00396 
00397     if (!fp)
00398         return ERROR_INT("stream not defined", procName, 1);
00399     if (!pwidth || !pheight || !pbps || !pspp)
00400         return ERROR_INT("input ptr(s) not defined", procName, 1);
00401     
00402     nbytes = fnbytesInFile(fp);
00403     if (nbytes < 40)
00404         return ERROR_INT("file too small to be png", procName, 1);
00405     if ((data = (l_uint8 *)CALLOC(40, sizeof(l_uint8))) == NULL)
00406         return ERROR_INT("CALLOC fail for data", procName, 1);
00407     if (fread(data, 1, 40, fp) != 40)
00408         return ERROR_INT("error reading data", procName, 1);
00409     ret = sreadHeaderPng(data, pwidth, pheight, pbps, pspp, piscmap);
00410     FREE(data);
00411     return ret;
00412 }
00413 
00414 
00415 /*!
00416  *  sreadHeaderPng()
00417  *
00418  *      Input:  data
00419  *              &width (<return>)
00420  *              &height (<return>)
00421  *              &bps (<return>, bits/sample)
00422  *              &spp (<return>, samples/pixel)
00423  *              &iscmap (<optional return>; input NULL to ignore)
00424  *      Return: 0 if OK, 1 on error
00425  *
00426  *  Notes:
00427  *      (1) If there is a colormap, iscmap is returned as 1; else 0.
00428  */
00429 l_int32
00430 sreadHeaderPng(const l_uint8  *data,
00431                l_int32        *pwidth,
00432                l_int32        *pheight,
00433                l_int32        *pbps,
00434                l_int32        *pspp,
00435                l_int32        *piscmap)
00436 {
00437 l_uint8    colortype, bps;
00438 l_uint16   twobytes;
00439 l_uint16  *pshort;
00440 l_uint32  *pword;
00441 
00442     PROCNAME("sreadHeaderPng");
00443 
00444     if (!data)
00445         return ERROR_INT("data not defined", procName, 1);
00446     if (!pwidth || !pheight || !pbps || !pspp)
00447         return ERROR_INT("input ptr(s) not defined", procName, 1);
00448     *pwidth = *pheight = *pbps = *pspp = 0;
00449     if (piscmap)
00450       *piscmap = 0;
00451     
00452         /* Check password */
00453     if (data[0] != 137 || data[1] != 80 || data[2] != 78 ||
00454         data[3] != 71 || data[4] != 13 || data[5] != 10 ||
00455         data[6] != 26 || data[7] != 10)
00456         return ERROR_INT("not a valid png file", procName, 1);
00457 
00458     pword = (l_uint32 *)data;
00459     pshort = (l_uint16 *)data;
00460     *pwidth = convertOnLittleEnd32(pword[4]);
00461     *pheight = convertOnLittleEnd32(pword[5]);
00462     twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/sample  */
00463                                                  /* and the color type     */
00464     colortype = twobytes & 0xff;  /* color type */
00465     bps = twobytes >> 8;   /* bits/sample */
00466     *pbps = bps;
00467     if (colortype == 2)  /* RGB */
00468         *pspp = 3;
00469     else if (colortype == 6)  /* RGBA */
00470         *pspp = 4;
00471     else   /* palette or gray */
00472         *pspp = 1;
00473     if (piscmap) {
00474         if (colortype & 1)  /* palette: see png.h, PNG_COLOR_TYPE_... */
00475             *piscmap = 1;
00476         else
00477             *piscmap = 0;
00478     }
00479 
00480     return 0;
00481 }
00482 
00483 
00484 /*
00485  *  fgetPngResolution()
00486  *
00487  *      Input:  stream (opened for read)
00488  *              &xres, &yres (<return> resolution in ppi)
00489  *      Return: 0 if OK; 0 on error
00490  *
00491  *  Notes:
00492  *      (1) If neither resolution field is set, this is not an error;
00493  *          the returned resolution values are 0 (designating 'unknown').
00494  *      (2) Side-effect: this rewinds the stream.
00495  */
00496 l_int32
00497 fgetPngResolution(FILE     *fp,
00498                   l_int32  *pxres,
00499                   l_int32  *pyres)
00500 {
00501 png_uint_32  xres, yres;
00502 png_structp  png_ptr;
00503 png_infop    info_ptr;
00504 
00505     PROCNAME("fgetPngResolution");
00506 
00507     if (!pxres || !pyres)
00508         return ERROR_INT("&xres and &yres not both defined", procName, 1);
00509     *pxres = *pyres = 0;
00510     if (!fp)
00511         return ERROR_INT("stream not opened", procName, 1);
00512 
00513        /* Make the two required structs */
00514    if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
00515                    (png_voidp)NULL, NULL, NULL)) == NULL)
00516         return ERROR_INT("png_ptr not made", procName, 1);
00517     if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
00518         png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00519         return ERROR_INT("info_ptr not made", procName, 1);
00520     }
00521 
00522         /* Set up png setjmp error handling.
00523          * Without this, an error calls exit. */
00524     if (setjmp(png_jmpbuf(png_ptr))) {
00525         png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00526         return ERROR_INT("internal png error", procName, 1);
00527     }
00528 
00529         /* Read the metadata */
00530     rewind(fp);
00531     png_init_io(png_ptr, fp);
00532     png_read_png(png_ptr, info_ptr, 0, NULL);
00533 
00534     xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
00535     yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
00536     *pxres = (l_int32)((l_float32)xres / 39.37 + 0.5);  /* to ppi */
00537     *pyres = (l_int32)((l_float32)yres / 39.37 + 0.5);
00538 
00539     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00540     rewind(fp);
00541     return 0;
00542 }
00543 
00544 
00545 /*---------------------------------------------------------------------*
00546  *                              Writing png                            *
00547  *---------------------------------------------------------------------*/
00548 /*!
00549  *  pixWritePng()
00550  *
00551  *      Input:  filename
00552  *              pix
00553  *              gamma
00554  *      Return: 0 if OK; 1 on error
00555  *
00556  *  Notes:
00557  *      (1) Special version for writing png with a specified gamma.
00558  *          When using pixWrite(), no field is given for gamma.
00559  */
00560 l_int32
00561 pixWritePng(const char  *filename,
00562             PIX         *pix,
00563             l_float32    gamma)
00564 {
00565 FILE  *fp;
00566 
00567     PROCNAME("pixWritePng");
00568 
00569     if (!pix)
00570         return ERROR_INT("pix not defined", procName, 1);
00571     if (!filename)
00572         return ERROR_INT("filename not defined", procName, 1);
00573 
00574     if ((fp = fopenWriteStream(filename, "wb+")) == NULL)
00575         return ERROR_INT("stream not opened", procName, 1);
00576 
00577     if (pixWriteStreamPng(fp, pix, gamma)) {
00578         fclose(fp);
00579         return ERROR_INT("pix not written to stream", procName, 1);
00580     }
00581 
00582     fclose(fp);
00583     return 0;
00584 }
00585 
00586 
00587 /*!
00588  *  pixWriteStreamPng()
00589  *
00590  *      Input:  stream
00591  *              pix
00592  *              gamma (use 0.0 if gamma is not defined)
00593  *      Return: 0 if OK; 1 on error
00594  *
00595  *  Notes:
00596  *      (1) If called from pixWriteStream(), the stream is positioned
00597  *          at the beginning of the file.
00598  *      (2) To do sequential writes of png format images to a stream,
00599  *          use pixWriteStreamPng() directly.
00600  *      (3) gamma is an optional png chunk.  If no gamma value is to be
00601  *          placed into the file, use gamma = 0.0.  Otherwise, if
00602  *          gamma > 0.0, its value is written into the header.
00603  *      (4) The use of gamma in png is highly problematic.  For an illuminating
00604  *          discussion, see:  http://hsivonen.iki.fi/png-gamma/
00605  *      (5) What is the effect/meaning of gamma in the png file?  This
00606  *          gamma, which we can call the 'source' gamma, is the
00607  *          inverse of the gamma that was used in enhance.c to brighten
00608  *          or darken images.  The 'source' gamma is supposed to indicate
00609  *          the intensity mapping that was done at the time the
00610  *          image was captured.  Display programs typically apply a
00611  *          'display' gamma of 2.2 to the output, which is intended
00612  *          to linearize the intensity based on the response of
00613  *          thermionic tubes (CRTs).  Flat panel LCDs have typically
00614  *          been designed to give a similar response as CRTs (call it
00615  *          "backward compatibility").  The 'display' gamma is
00616  *          in some sense the inverse of the 'source' gamma.
00617  *          jpeg encoders attached to scanners and cameras will lighten
00618  *          the pixels, applying a gamma corresponding to approximately
00619  *          a square-root relation of output vs input:
00620  *                output = input^(gamma)
00621  *          where gamma is often set near 0.4545  (1/gamma is 2.2).
00622  *          This is stored in the image file.  Then if the display
00623  *          program reads the gamma, it will apply a display gamma,
00624  *          typically about 2.2; the product is 1.0, and the
00625  *          display program produces a linear output.  This works because
00626  *          the dark colors were appropriately boosted by the scanner,
00627  *          as described by the 'source' gamma, so they should not
00628  *          be further boosted by the display program.
00629  *      (6) As an example, with xv and display, if no gamma is stored,
00630  *          the program acts as if gamma were 0.4545, multiplies this by 2.2,
00631  *          and does a linear rendering.  Taking this as a baseline
00632  *          brightness, if the stored gamma is:
00633  *              > 0.4545, the image is rendered lighter than baseline
00634  *              < 0.4545, the image is rendered darker than baseline
00635  *          In contrast, gqview seems to ignore the gamma chunk in png.
00636  *      (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16
00637  *          and 32.  However, it is possible, and in some cases desirable,
00638  *          to write out a png file using an rgb pix that has 24 bpp.
00639  *          For example, the open source xpdf SplashBitmap class generates
00640  *          24 bpp rgb images.  Consequently, we anble writing 24 bpp pix.
00641  *          To generate such a pix, you can make a 24 bpp pix without data
00642  *          and assign the data array to the pix; e.g.,
00643  *              pix = pixCreateHeader(w, h, 24);
00644  *              pixSetData(pix, rgbdata);
00645  *          See pixConvert32To24() for an example, where we get rgbdata
00646  *          from the 32 bpp pix.  Caution: do not call pixSetPadBits(),
00647  *          because the alignment is wrong and you may erase part of the
00648  *          last pixel on each line.
00649  */
00650 l_int32
00651 pixWriteStreamPng(FILE      *fp,
00652                   PIX       *pix,
00653                   l_float32  gamma)
00654 {
00655 char         commentstring[] = "Comment";
00656 l_int32      i, j, k;
00657 l_int32      wpl, d, cmflag;
00658 l_int32      ncolors;
00659 l_int32     *rmap, *gmap, *bmap;
00660 l_uint32    *data, *ppixel;
00661 png_byte     bit_depth, color_type;
00662 png_uint_32  w, h;
00663 png_uint_32  xres, yres;
00664 png_bytep   *row_pointers;
00665 png_bytep    rowbuffer;
00666 png_structp  png_ptr;
00667 png_infop    info_ptr;
00668 png_colorp   palette;
00669 PIX         *pixt;
00670 PIXCMAP     *cmap;
00671 char        *text;
00672 
00673     PROCNAME("pixWriteStreamPng");
00674 
00675     if (!fp)
00676         return ERROR_INT("stream not open", procName, 1);
00677     if (!pix)
00678         return ERROR_INT("pix not defined", procName, 1);
00679 
00680         /* Allocate the 2 data structures */
00681     if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
00682                    (png_voidp)NULL, NULL, NULL)) == NULL)
00683         return ERROR_INT("png_ptr not made", procName, 1);
00684 
00685     if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
00686         png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
00687         return ERROR_INT("info_ptr not made", procName, 1);
00688     }
00689 
00690         /* Set up png setjmp error handling */
00691     if (setjmp(png_jmpbuf(png_ptr))) {
00692         png_destroy_write_struct(&png_ptr, &info_ptr);
00693         return ERROR_INT("internal png error", procName, 1);
00694     }
00695 
00696     png_init_io(png_ptr, fp);
00697 
00698         /* With best zlib compression (9), get between 1 and 10% improvement
00699          * over default (5), but the compression is 3 to 10 times slower.
00700          * Our default compression is the zlib default (5). */
00701     png_set_compression_level(png_ptr, var_ZLIB_COMPRESSION);
00702 
00703     w = pixGetWidth(pix);
00704     h = pixGetHeight(pix);
00705     d = pixGetDepth(pix);
00706     if ((cmap = pixGetColormap(pix)))
00707         cmflag = 1;
00708     else
00709         cmflag = 0;
00710 
00711         /* Set the color type and bit depth. */
00712     if (d == 32 && var_PNG_WRITE_ALPHA == 1) {
00713         bit_depth = 8;
00714         color_type = PNG_COLOR_TYPE_RGBA;   /* 6 */
00715         cmflag = 0;  /* ignore if it exists */
00716     }
00717     else if (d == 24 || d == 32) {
00718         bit_depth = 8;
00719         color_type = PNG_COLOR_TYPE_RGB;   /* 2 */
00720         cmflag = 0;  /* ignore if it exists */
00721     }
00722     else {
00723         bit_depth = d;
00724         color_type = PNG_COLOR_TYPE_GRAY;  /* 0 */
00725     }
00726     if (cmflag)
00727         color_type = PNG_COLOR_TYPE_PALETTE;  /* 3 */
00728 
00729 #if  DEBUG
00730     fprintf(stderr, "cmflag = %d, bit_depth = %d, color_type = %d\n",
00731             cmflag, bit_depth, color_type);
00732 #endif  /* DEBUG */
00733 
00734     png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type,
00735                  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
00736                  PNG_FILTER_TYPE_BASE);
00737 
00738         /* Store resolution in ppm, if known */
00739     xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);
00740     yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);
00741     if ((xres == 0) || (yres == 0))
00742         png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN);
00743     else
00744         png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER);
00745 
00746     if (cmflag) {
00747         pixcmapToArrays(cmap, &rmap, &gmap, &bmap);
00748         ncolors = pixcmapGetCount(cmap);
00749 
00750             /* Make and save the palette */
00751         if ((palette = (png_colorp)(CALLOC(ncolors, sizeof(png_color))))
00752                 == NULL)
00753             return ERROR_INT("palette not made", procName, 1);
00754 
00755         for (i = 0; i < ncolors; i++) {
00756             palette[i].red = (png_byte)rmap[i];
00757             palette[i].green = (png_byte)gmap[i];
00758             palette[i].blue = (png_byte)bmap[i];
00759         }
00760 
00761         png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors);
00762         FREE(rmap);
00763         FREE(gmap);
00764         FREE(bmap);
00765     }
00766 
00767         /* 0.4545 is treated as the default by some image
00768          * display programs (not gqview).  A value > 0.4545 will
00769          * lighten an image as displayed by xv, display, etc. */
00770     if (gamma > 0.0)
00771         png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma);
00772 
00773     if ((text = pixGetText(pix))) {
00774         png_text text_chunk;
00775         text_chunk.compression = PNG_TEXT_COMPRESSION_NONE;
00776         text_chunk.key = commentstring;
00777         text_chunk.text = text;
00778         text_chunk.text_length = strlen(text);
00779 #ifdef PNG_ITXT_SUPPORTED
00780         text_chunk.itxt_length = 0;
00781         text_chunk.lang = NULL;
00782         text_chunk.lang_key = NULL;
00783 #endif
00784         png_set_text(png_ptr, info_ptr, &text_chunk, 1);
00785     }
00786 
00787         /* Write header and palette info */
00788     png_write_info(png_ptr, info_ptr);
00789 
00790     if ((d != 32) && (d != 24)) {  /* not rgb color */
00791             /* Generate a temporary pix with bytes swapped.
00792              * For a binary image, there are two conditions in
00793              * which you must first invert the data for writing png:
00794              *    (a) no colormap
00795              *    (b) colormap with BLACK set to 0
00796              * png writes binary with BLACK = 0, unless contradicted
00797              * by a colormap.  If the colormap has BLACK = "1"
00798              * (typ. about 255), do not invert the data.  If there
00799              * is no colormap, you must invert the data to store
00800              * in default BLACK = 0 state.  */
00801         if (d == 1 &&
00802             (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) {
00803             pixt = pixInvert(NULL, pix);
00804             pixEndianByteSwap(pixt);
00805         }
00806         else 
00807             pixt = pixEndianByteSwapNew(pix);
00808         if (!pixt) {
00809             png_destroy_write_struct(&png_ptr, &info_ptr);
00810             return ERROR_INT("pixt not made", procName, 1);
00811         }
00812 
00813             /* Make and assign array of image row pointers */
00814         if ((row_pointers = (png_bytep *)CALLOC(h, sizeof(png_bytep))) == NULL)
00815             return ERROR_INT("row-pointers not made", procName, 1);
00816         wpl = pixGetWpl(pixt);
00817         data = pixGetData(pixt);
00818         for (i = 0; i < h; i++)
00819             row_pointers[i] = (png_bytep)(data + i * wpl);
00820         png_set_rows(png_ptr, info_ptr, row_pointers);
00821 
00822             /* Transfer the data */
00823         png_write_image(png_ptr, row_pointers);
00824         png_write_end(png_ptr, info_ptr);
00825 
00826         if (cmflag)
00827             FREE(palette);
00828         FREE(row_pointers);
00829         pixDestroy(&pixt);
00830         png_destroy_write_struct(&png_ptr, &info_ptr);
00831         return 0;
00832     }
00833 
00834         /* For rgb, compose and write a row at a time */
00835     data = pixGetData(pix);
00836     wpl = pixGetWpl(pix);
00837     if (d == 24) {  /* See note 7 above: special case of 24 bpp rgb */
00838         for (i = 0; i < h; i++) {
00839             ppixel = data + i * wpl;
00840             png_write_rows(png_ptr, (png_bytepp)&ppixel, 1);
00841         }
00842     }
00843     else {  /* 32 bpp rgb and rgba */
00844         if ((rowbuffer = (png_bytep)CALLOC(w, 4)) == NULL)
00845             return ERROR_INT("rowbuffer not made", procName, 1);
00846         for (i = 0; i < h; i++) {
00847             ppixel = data + i * wpl;
00848             for (j = k = 0; j < w; j++) {
00849                 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
00850                 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
00851                 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
00852                 if (var_PNG_WRITE_ALPHA == 1)
00853                     rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL);
00854                 ppixel++;
00855             }
00856 
00857             png_write_rows(png_ptr, &rowbuffer, 1);
00858         }
00859         FREE(rowbuffer);
00860     }
00861 
00862     png_write_end(png_ptr, info_ptr);
00863 
00864     if (cmflag)
00865         FREE(palette);
00866     png_destroy_write_struct(&png_ptr, &info_ptr);
00867     return 0;
00868 
00869 }
00870 
00871 
00872 /*---------------------------------------------------------------------*
00873  *                    Read and write of png to RGBA                    *
00874  *---------------------------------------------------------------------*/
00875 extern const char *ImageFileFormatExtensions[];
00876 
00877 /*!
00878  *  pixReadRGBAPng()
00879  *
00880  *      Input:  filename (of png file)
00881  *      Return: pix, or null on error
00882  *
00883  *  Notes:
00884  *      (1) Wrapper to keep the alpha channel of a png, if it exists.
00885  *      (2) The default behavior of pix read functions is to ignore
00886  *          the alpha channel.
00887  *      (3) This always leaves alpha stripping in the same mode as
00888  *          when this function begins.  So if alpha stripping is in
00889  *          default mode, this disables it, reads the file (including
00890  *          the alpha channel), and resets back to stripping.  Otherwise,
00891  *          it leaves stripping disabled.
00892  */
00893 PIX *
00894 pixReadRGBAPng(const char  *filename)
00895 {
00896 l_int32  format;
00897 PIX     *pix;
00898 
00899     PROCNAME("pixReadRGBAPng");
00900 
00901     if (!filename)
00902         return (PIX *)ERROR_PTR("filename not defined", procName, NULL);
00903 
00904         /* Make sure it's a png file */
00905     findFileFormat(filename, &format);
00906     if (format != IFF_PNG) {
00907         L_ERROR_STRING("file format is %s, not png", procName,
00908                        ImageFileFormatExtensions[format]);
00909         return NULL;
00910     }
00911 
00912         /* If alpha channel reading is enabled, just read it */
00913     if (var_PNG_STRIP_ALPHA == FALSE)
00914         return pixRead(filename);
00915 
00916     l_pngSetStripAlpha(0);
00917     pix = pixRead(filename);
00918     l_pngSetStripAlpha(1);  /* reset to default */
00919     if (!pix) L_ERROR("pix not read", procName);
00920     return pix;
00921 }
00922 
00923 
00924 /*!
00925  *  pixWriteRGBAPng()
00926  *
00927  *      Input:  filename
00928  *              pix (rgba)
00929  *      Return: 0 if OK, 1 on error
00930  *
00931  *  Notes:
00932  *      (1) Wrapper to write the alpha sample of a 32 bpp pix to
00933  *          a png file in rgba format.
00934  *      (2) The default behavior of pix write to png is to ignore
00935  *          the alpha sample.
00936  *      (3) This always leaves alpha writing in the same mode as
00937  *          when this function begins.  So if alpha writing is in
00938  *          default mode, this enables it, writes out a rgba png file
00939  *          that includes the alpha channel, and resets to default.
00940  *          Otherwise, it leaves alpha writing enabled.
00941  */
00942 l_int32
00943 pixWriteRGBAPng(const char *filename,
00944                 PIX        *pix)
00945 {
00946 l_int32  ret;
00947 
00948     PROCNAME("pixWriteRGBAPng");
00949 
00950     if (!pix)
00951         return ERROR_INT("pix not defined", procName, 1);
00952     if (!filename)
00953         return ERROR_INT("filename not defined", procName, 1);
00954 
00955         /* If alpha channel writing is enabled, just write it */
00956     if (var_PNG_WRITE_ALPHA == TRUE)
00957         return pixWrite(filename, pix, IFF_PNG);
00958 
00959     l_pngSetWriteAlpha(1);
00960     ret = pixWritePng(filename, pix, 0.0);
00961     l_pngSetWriteAlpha(0);  /* reset to default */
00962     return ret;
00963 }
00964 
00965 
00966 /*---------------------------------------------------------------------*
00967  *                   Setting flags for special modes                   *
00968  *---------------------------------------------------------------------*/
00969 /*!
00970  *  l_pngSetStrip16To8()
00971  *
00972  *      Input:  flag (1 for stripping 16 bpp to 8 bpp; 0 for leaving 16 bpp)
00973  *      Return: void
00974  */
00975 void
00976 l_pngSetStrip16To8(l_int32  flag)
00977 {
00978     var_PNG_STRIP_16_TO_8 = flag;
00979 }
00980 
00981 
00982 /*!
00983  *  l_pngSetStripAlpha()
00984  *
00985  *      Input:  flag (1 for stripping alpha channel on read; 0 for leaving it)
00986  *      Return: void
00987  */
00988 void
00989 l_pngSetStripAlpha(l_int32  flag)
00990 {
00991     var_PNG_STRIP_ALPHA = flag;
00992 }
00993 
00994 
00995 /*!
00996  *  l_pngSetWriteAlpha()
00997  *
00998  *      Input:  flag (1 for writing alpha channel; 0 for just writing rgb)
00999  *      Return: void
01000  */
01001 void
01002 l_pngSetWriteAlpha(l_int32  flag)
01003 {
01004     var_PNG_WRITE_ALPHA = flag;
01005 }
01006 
01007 
01008 /*!
01009  *  l_pngSetZlibCompression()
01010  *
01011  *      Input:  val (zlib compression value)
01012  *      Return: void
01013  *
01014  *  Notes:
01015  *      (1) Valid zlib compression values are in the interval [0 ... 9],
01016  *          where, as defined in zlib.h:
01017  *            0         Z_NO_COMPRESSION
01018  *            1         Z_BEST_SPEED    (poorest compression)
01019  *            9         Z_BEST_COMPRESSION
01020  *          For the default value, use either of these:
01021  *            6         Z_DEFAULT_COMPRESSION
01022  *           -1         (resolves to Z_DEFAULT_COMPRESSION)
01023  *      (2) If you use the defined constants in zlib.h instead of the
01024  *          compression integers given above, you must include zlib.h.
01025  */
01026 void
01027 l_pngSetZlibCompression(l_int32  val)
01028 {
01029     PROCNAME("l_pngSetZlibCompression");
01030     if (val < -1 || val > 9) {
01031         L_ERROR("Invalid zlib comp val; using default", procName);
01032         val = Z_DEFAULT_COMPRESSION;
01033     }
01034     var_ZLIB_COMPRESSION = val;
01035 }
01036 
01037 
01038 
01039 /*---------------------------------------------------------------------*
01040  *                         Read/write to memory                        *
01041  *---------------------------------------------------------------------*/
01042 #if HAVE_FMEMOPEN
01043 
01044 extern FILE *open_memstream(char **data, size_t *size);
01045 extern FILE *fmemopen(void *data, size_t size, const char *mode);
01046 
01047 /*!
01048  *  pixReadMemPng()
01049  *
01050  *      Input:  cdata (const; png-encoded)
01051  *              size (of data)
01052  *      Return: pix, or null on error
01053  *
01054  *  Notes:
01055  *      (1) The @size byte of @data must be a null character.
01056  */
01057 PIX *
01058 pixReadMemPng(const l_uint8  *cdata,
01059               size_t          size)
01060 {
01061 l_uint8  *data;
01062 FILE     *fp;
01063 PIX      *pix;
01064 
01065     PROCNAME("pixReadMemPng");
01066 
01067     if (!cdata)
01068         return (PIX *)ERROR_PTR("cdata not defined", procName, NULL);
01069 
01070     data = (l_uint8 *)cdata;  /* we're really not going to change this */
01071     if ((fp = fmemopen(data, size, "r")) == NULL)
01072         return (PIX *)ERROR_PTR("stream not opened", procName, NULL);
01073     pix = pixReadStreamPng(fp);
01074     fclose(fp);
01075     return pix;
01076 }
01077 
01078 
01079 /*!
01080  *  pixWriteMemPng()
01081  *
01082  *      Input:  &data (<return> data of tiff compressed image)
01083  *              &size (<return> size of returned data)
01084  *              pix
01085  *              gamma (use 0.0 if gamma is not defined)
01086  *      Return: 0 if OK, 1 on error
01087  *
01088  *  Notes:
01089  *      (1) See pixWriteStreamPng() for usage.  This version writes to
01090  *          memory instead of to a file stream.
01091  */
01092 l_int32
01093 pixWriteMemPng(l_uint8  **pdata,
01094                size_t    *psize,
01095                PIX       *pix,
01096                l_float32  gamma)
01097 {
01098 l_int32  ret;
01099 FILE    *fp;
01100 
01101     PROCNAME("pixWriteMemPng");
01102 
01103     if (!pdata)
01104         return ERROR_INT("&data not defined", procName, 1 );
01105     if (!psize)
01106         return ERROR_INT("&size not defined", procName, 1 );
01107     if (!pix)
01108         return ERROR_INT("&pix not defined", procName, 1 );
01109 
01110     if ((fp = open_memstream((char **)pdata, psize)) == NULL)
01111         return ERROR_INT("stream not opened", procName, 1);
01112     ret = pixWriteStreamPng(fp, pix, gamma);
01113     fclose(fp);
01114     return ret;
01115 }
01116 
01117 #else
01118 
01119 PIX *
01120 pixReadMemPng(const l_uint8  *cdata,
01121               size_t          size)
01122 {
01123     return (PIX *)ERROR_PTR(
01124         "png read from memory not implemented on this platform",
01125         "pixReadMemPng", NULL);
01126 }
01127 
01128 
01129 l_int32
01130 pixWriteMemPng(l_uint8  **pdata,
01131                size_t    *psize,
01132                PIX       *pix,
01133                l_float32  gamma)
01134 {
01135     return ERROR_INT(
01136         "png write to memory not implemented on this platform",
01137         "pixWriteMemPng", 1);
01138 }
01139 
01140 #endif  /* HAVE_FMEMOPEN */
01141 
01142 /* --------------------------------------------*/
01143 #endif  /* HAVE_LIBPNG */
01144 /* --------------------------------------------*/
01145 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines