blob: 2c914a8b0059e99610785bb310c249b6e4453f78 [file] [log] [blame]
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
- This software is distributed in the hope that it will be
- useful, but with NO WARRANTY OF ANY KIND.
- No author or distributor accepts responsibility to anyone for the
- consequences of using this software, or for whether it serves any
- particular purpose or works at all, unless he or she says so in
- writing. Everyone is granted permission to copy, modify and
- redistribute this source code, for commercial or non-commercial
- purposes, with the following restrictions: (1) the origin of this
- source code must not be misrepresented; (2) modified versions must
- be plainly marked as such; and (3) this notice may not be removed
- or altered from any source or modified source distribution.
*====================================================================*/
/*
* pngio.c
*
* Read png from file
* PIX *pixReadStreamPng()
* l_int32 readHeaderPng()
* l_int32 freadHeaderPng()
* l_int32 sreadHeaderPng()
*
* Write png to file
* l_int32 pixWritePng() [ special top level ]
* l_int32 pixWriteStreamPng()
*
* Setting flags for special modes
* void l_pngSetStrip16To8()
* void l_pngSetStripAlpha()
* void l_pngSetZlibCompression()
*
* Read/write to memory [not on windows]
* PIX *pixReadMemPng()
* l_int32 pixWriteMemPng()
*
* Documentation: libpng.txt and example.c
*
* On input (decompression from file), palette color images
* are read into an 8 bpp Pix with a colormap, and 24 bpp
* 3 component color images are read into a 32 bpp Pix with
* rgb samples. On output (compression to file), palette color
* images are written as 8 bpp with the colormap, and 32 bpp
* full color images are written compressed as a 24 bpp,
* 3 component color image.
*
* In the following, we use these abbreviations:
* bpc == bit/component
* cpp == component/pixel
* bpp == bits/pixel of image in Pix (memory)
*
* Flags can be set for special reading from png compression
* into a pix, with either 16 bpc or 32 bpp with alpha channel.
* Otherwise, the default reading values are as follows:
* (1) 16 bpc input images are read into a Pix with 8 bpc.
* - For 16 bpc rgb (16 bpc, 3 cpp) --> 32 bpp rgb Pix
* - For 16 bpc gray (16 bpc, 1 cpp) --> 8 bpp grayscale Pix
* (2) The alpha layer is stripped out
* - For 8 bpc rgba (8 bpc, 4 cpp) --> 32 bpp rgb Pix
*
* The zlib compression value can be set [0 ... 9], with
* 0 no compression (huge files)
* 1 fastest compression
* 6 default compression
* 9 best compression
* If not set, we use the default compression in zlib.
* Note that if you are using the defined constants in zlib instead
* of the compression integers given above, you must include zlib.h.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"
#ifdef HAVE_CONFIG_H
#include "config_auto.h"
#endif /* HAVE_CONFIG_H */
/* --------------------------------------------*/
#if HAVE_LIBPNG /* defined in environ.h */
/* --------------------------------------------*/
#include "png.h"
/* ----------------Set defaults for read/write options ----------------- */
/* strip 16 bpp --> 8 bpp on reading png; default is for stripping */
static l_int32 L_PNG_STRIP_16_TO_8 = 1;
/* strip alpha on reading png; default is for stripping */
static l_int32 L_PNG_STRIP_ALPHA = 1;
/* zlib compression in png; default (5) is for standard compression */
static l_int32 L_ZLIB_COMPRESSION = Z_DEFAULT_COMPRESSION;
#ifndef NO_CONSOLE_IO
#define DEBUG 0
#endif /* ~NO_CONSOLE_IO */
/*---------------------------------------------------------------------*
* Reading png *
*---------------------------------------------------------------------*/
/*!
* pixReadStreamPng()
*
* Input: stream
* Return: pix, or null on error
*
* Notes:
* (1) If called from pixReadStream(), the stream is positioned
* at the beginning of the file.
* (2) To do sequential reads of png format images from a stream,
* use pixReadStreamPng()
*/
PIX *
pixReadStreamPng(FILE *fp)
{
l_uint8 rval, gval, bval;
l_int32 i, j, k;
l_int32 wpl, d, spp, cindex;
l_uint32 png_transforms;
l_uint32 *data, *line, *ppixel;
int num_palette, num_text;
png_byte bit_depth, color_type, channels;
png_uint_32 w, h, rowbytes;
png_uint_32 xres, yres;
png_bytep rowptr;
png_bytep *row_pointers;
png_structp png_ptr;
png_infop info_ptr, end_info;
png_colorp palette;
png_textp text_ptr; /* ptr to text_chunk */
PIX *pix;
PIXCMAP *cmap;
PROCNAME("pixReadStreamPng");
if (!fp)
return (PIX *)ERROR_PTR("fp not defined", procName, NULL);
pix = NULL;
/* Allocate the 3 data structures */
if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
(png_voidp)NULL, NULL, NULL)) == NULL)
return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL);
if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL);
}
if ((end_info = png_create_info_struct(png_ptr)) == NULL) {
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
return (PIX *)ERROR_PTR("end_info not made", procName, NULL);
}
/* Set up png setjmp error handling */
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return (PIX *)ERROR_PTR("internal png error", procName, NULL);
}
png_init_io(png_ptr, fp);
/* ---------------------------------------------------------- *
* Set the transforms flags. Whatever happens here,
* NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO.
* ---------------------------------------------------------- */
/* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */
if (L_PNG_STRIP_16_TO_8 == 1) /* our default */
png_transforms = PNG_TRANSFORM_STRIP_16;
else
png_transforms = PNG_TRANSFORM_IDENTITY;
/* To remove alpha channel, use PNG_TRANSFORM_STRIP_ALPHA */
if (L_PNG_STRIP_ALPHA == 1) /* our default */
png_transforms |= PNG_TRANSFORM_STRIP_ALPHA;
/* Read it */
png_read_png(png_ptr, info_ptr, png_transforms, NULL);
row_pointers = png_get_rows(png_ptr, info_ptr);
w = png_get_image_width(png_ptr, info_ptr);
h = png_get_image_height(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
channels = png_get_channels(png_ptr, info_ptr);
spp = channels;
if (spp == 1)
d = bit_depth;
else if (spp == 2) {
d = 2 * bit_depth;
L_WARNING("there shouldn't be 2 spp!", procName);
}
else if (spp == 3)
d = 4 * bit_depth;
else { /* spp == 4 */
d = 4 * bit_depth;
L_WARNING("there shouldn't be 4 spp!", procName);
}
/* Remove if/when this is implemented for all bit_depths */
if (spp == 3 && bit_depth != 8) {
fprintf(stderr, "Help: spp = 3 and depth = %d != 8\n!!", bit_depth);
return (PIX *)ERROR_PTR("not implemented for this depth",
procName, NULL);
}
if (color_type == PNG_COLOR_TYPE_PALETTE ||
color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */
png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
cmap = pixcmapCreate(d); /* spp == 1 */
for (cindex = 0; cindex < num_palette; cindex++) {
rval = palette[cindex].red;
gval = palette[cindex].green;
bval = palette[cindex].blue;
pixcmapAddColor(cmap, rval, gval, bval);
}
}
else
cmap = NULL;
if ((pix = pixCreate(w, h, d)) == NULL) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return (PIX *)ERROR_PTR("pix not made", procName, NULL);
}
wpl = pixGetWpl(pix);
data = pixGetData(pix);
pixSetColormap(pix, cmap);
if (spp == 1) { /* copy straight from buffer to pix */
for (i = 0; i < h; i++) {
line = data + i * wpl;
rowptr = row_pointers[i];
for (j = 0; j < rowbytes; j++) {
SET_DATA_BYTE(line, j, rowptr[j]);
}
}
}
else { /* spp == 3 */
for (i = 0; i < h; i++) {
ppixel = data + i * wpl;
rowptr = row_pointers[i];
for (j = k = 0; j < w; j++) {
SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]);
SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]);
SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
ppixel++;
}
}
}
#if DEBUG
if (cmap) {
for (i = 0; i < 16; i++) {
fprintf(stderr, "[%d] = %d\n", i,
((l_uint8 *)(cmap->array))[i]);
}
}
#endif /* DEBUG */
/* If there is no colormap, PNG defines black = 0 and
* white = 1 by default for binary monochrome. Therefore,
* since we use the opposite definition, we must invert
* the image in either of these cases:
* (i) there is no colormap (default)
* (ii) there is a colormap which defines black to
* be 0 and white to be 1.
* We cannot use the PNG_TRANSFORM_INVERT_MONO flag
* because that flag (since version 1.0.9) inverts 8 bpp
* grayscale as well, which we don't want to do.
* (It also doesn't work if there is a colormap.)
* If there is a colormap that defines black = 1 and
* white = 0, we don't need to do anything.
*
* How do we check the polarity of the colormap?
* The colormap determines the values of black and
* white pixels in the following way:
* if black = 1 (255), white = 0
* 255, 255, 255, 0, 0, 0, 0, 0, 0
* if black = 0, white = 1 (255)
* 0, 0, 0, 0, 255, 255, 255, 0
* So we test the first byte to see if it is 0;
* if so, invert the data. */
if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) {
/* fprintf(stderr, "Inverting binary data on png read\n"); */
pixInvert(pix, pix);
}
xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */
pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */
/* Get the text if there is any */
png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
if (num_text && text_ptr)
pixSetText(pix, text_ptr->text);
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return pix;
}
/*!
* readHeaderPng()
*
* Input: filename
* &width (<return>)
* &height (<return>)
* &bpc (<return>, bits/component)
* &cpp (<return>, components/pixel)
* &iscmap (<optional return>; input NULL to ignore)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) If there is a colormap, iscmap is returned as 1; else 0.
*/
l_int32
readHeaderPng(const char *filename,
l_int32 *pwidth,
l_int32 *pheight,
l_int32 *pbpc,
l_int32 *pcpp,
l_int32 *piscmap)
{
l_int32 ret;
FILE *fp;
PROCNAME("readHeaderPng");
if (!filename)
return ERROR_INT("filename not defined", procName, 1);
if (!pwidth || !pheight || !pbpc || !pcpp)
return ERROR_INT("input ptr(s) not defined", procName, 1);
if ((fp = fopenReadStream(filename)) == NULL)
return ERROR_INT("image file not found", procName, 1);
ret = freadHeaderPng(fp, pwidth, pheight, pbpc, pcpp, piscmap);
fclose(fp);
return ret;
}
/*!
* freadHeaderPng()
*
* Input: stream
* &width (<return>)
* &height (<return>)
* &bpc (<return>, bits/component)
* &cpp (<return>, components/pixel)
* &iscmap (<optional return>; input NULL to ignore)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) If there is a colormap, iscmap is returned as 1; else 0.
*/
l_int32
freadHeaderPng(FILE *fp,
l_int32 *pwidth,
l_int32 *pheight,
l_int32 *pbpc,
l_int32 *pcpp,
l_int32 *piscmap)
{
l_int32 nbytes, ret;
l_uint8 *data;
PROCNAME("freadHeaderPng");
if (!fp)
return ERROR_INT("stream not defined", procName, 1);
if (!pwidth || !pheight || !pbpc || !pcpp)
return ERROR_INT("input ptr(s) not defined", procName, 1);
nbytes = fnbytesInFile(fp);
if (nbytes < 40)
return ERROR_INT("file too small to be png", procName, 1);
if ((data = (l_uint8 *)CALLOC(40, sizeof(l_uint8))) == NULL)
return ERROR_INT("CALLOC fail for data", procName, 1);
fread(data, 40, 1, fp);
ret = sreadHeaderPng(data, pwidth, pheight, pbpc, pcpp, piscmap);
FREE(data);
return ret;
}
/*!
* sreadHeaderPng()
*
* Input: data
* &width (<return>)
* &height (<return>)
* &bpc (<return>, bits/component)
* &cpp (<return>, components/pixel)
* &iscmap (<optional return>; input NULL to ignore)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) If there is a colormap, iscmap is returned as 1; else 0.
*/
l_int32
sreadHeaderPng(const l_uint8 *data,
l_int32 *pwidth,
l_int32 *pheight,
l_int32 *pbpc,
l_int32 *pcpp,
l_int32 *piscmap)
{
l_uint8 colortype, bpc;
l_uint16 twobytes;
l_uint16 *pshort;
l_uint32 *pword;
PROCNAME("sreadHeaderPng");
if (!data)
return ERROR_INT("data not defined", procName, 1);
if (!pwidth || !pheight || !pbpc || !pcpp)
return ERROR_INT("input ptr(s) not defined", procName, 1);
*pwidth = *pheight = *pbpc = *pcpp = 0;
if (piscmap)
*piscmap = 0;
/* Check password */
if (data[0] != 137 || data[1] != 80 || data[2] != 78 ||
data[3] != 71 || data[4] != 13 || data[5] != 10 ||
data[6] != 26 || data[7] != 10)
return ERROR_INT("not a valid png file", procName, 1);
pword = (l_uint32 *)data;
pshort = (l_uint16 *)data;
*pwidth = convertOnLittleEnd32(pword[4]);
*pheight = convertOnLittleEnd32(pword[5]);
twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/component */
/* and the color type */
colortype = twobytes & 0xff; /* color type */
bpc = twobytes >> 8; /* bits/component */
*pbpc = bpc;
if (colortype == 2) /* RGB */
*pcpp = 3;
else if (colortype == 6) /* RGBA */
*pcpp = 4;
else /* palette or gray */
*pcpp = 1;
if (piscmap) {
if (colortype & 1) /* palette: see png.h, PNG_COLOR_TYPE_... */
*piscmap = 1;
else
*piscmap = 0;
}
return 0;
}
/*---------------------------------------------------------------------*
* Writing png *
*---------------------------------------------------------------------*/
/*!
* pixWritePng()
*
* Input: filename
* pix
* gamma
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) Special version for writing png with a specified gamma.
* When using pixWrite(), no field is given for gamma.
*/
l_int32
pixWritePng(const char *filename,
PIX *pix,
l_float32 gamma)
{
FILE *fp;
PROCNAME("pixWritePng");
if (!pix)
return ERROR_INT("pix not defined", procName, 1);
if (!filename)
return ERROR_INT("filename not defined", procName, 1);
if ((fp = fopen(filename, "wb+")) == NULL)
return ERROR_INT("stream not opened", procName, 1);
if (pixWriteStreamPng(fp, pix, gamma)) {
fclose(fp);
return ERROR_INT("pix not written to stream", procName, 1);
}
fclose(fp);
return 0;
}
/*!
* pixWriteStreamPng()
*
* Input: stream
* pix
* gamma (use 0.0 if gamma is not defined)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) If called from pixWriteStream(), the stream is positioned
* at the beginning of the file.
* (2) To do sequential writes of png format images to a stream,
* use pixWriteStreamPng() directly.
* (3) gamma is an optional png chunk. If no gamma value is to be
* placed into the file, use gamma = 0.0. Otherwise, if
* gamma > 0.0, its value is written into the header.
* (4) The use of gamma in png is highly problematic. For an illuminating
* discussion, see: http://hsivonen.iki.fi/png-gamma/
* (5) What is the effect/meaning of gamma in the png file? This
* gamma, which we can call the 'source' gamma, is the
* inverse of the gamma that was used in enhance.c to brighten
* or darken images. The 'source' gamma is supposed to indicate
* the intensity mapping that was done at the time the
* image was captured. Display programs typically apply a
* 'display' gamma of 2.2 to the output, which is intended
* to linearize the intensity based on the response of
* thermionic tubes (CRTs). Flat panel LCDs have typically
* been designed to give a similar response as CRTs (call it
* "backward compatibility"). The 'display' gamma is
* in some sense the inverse of the 'source' gamma.
* jpeg encoders attached to scanners and cameras will lighten
* the pixels, applying a gamma corresponding to approximately
* a square-root relation of output vs input:
* output = input^(gamma)
* where gamma is often set near 0.4545 (1/gamma is 2.2).
* This is stored in the image file. Then if the display
* program reads the gamma, it will apply a display gamma,
* typically about 2.2; the product is 1.0, and the
* display program produces a linear output. This works because
* the dark colors were appropriately boosted by the scanner,
* as described by the 'source' gamma, so they should not
* be further boosted by the display program.
* (6) As an example, with xv and display, if no gamma is stored,
* the program acts as if gamma were 0.4545, multiplies this by 2.2,
* and does a linear rendering. Taking this as a baseline
* brightness, if the stored gamma is:
* > 0.4545, the image is rendered lighter than baseline
* < 0.4545, the image is rendered darker than baseline
* In contrast, gqview seems to ignore the gamma chunk in png.
* (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16
* and 32. However, it is possible, and in some cases desirable,
* to write out a png file using an rgb pix that has 24 bpp.
* For example, the open source xpdf SplashBitmap class generates
* 24 bpp rgb images. To write these directly to file in leptonica,
* you can make a 24 bpp pix without data and assign the data array
* to the pix; e.g.,
* pix = pixCreateHeader(w, h, 24);
* pixSetData(pix, rgbdata);
* pixSetPadBits(pix, 0);
* Consequently, we enable writing 24 bpp pix.
*/
l_int32
pixWriteStreamPng(FILE *fp,
PIX *pix,
l_float32 gamma)
{
char commentstring[] = "Comment";
l_int32 i, j, k;
l_int32 wpl, d, cmflag;
l_int32 ncolors;
l_int32 *rmap, *gmap, *bmap;
l_uint32 *data, *ppixel;
png_byte bit_depth, color_type;
png_uint_32 w, h;
png_uint_32 xres, yres;
png_bytep *row_pointers;
png_bytep rowbuffer;
png_structp png_ptr;
png_infop info_ptr;
png_colorp palette;
PIX *pixt;
PIXCMAP *cmap;
char *text;
PROCNAME("pixWriteStreamPng");
if (!fp)
return ERROR_INT("stream not open", procName, 1);
if (!pix)
return ERROR_INT("pix not defined", procName, 1);
/* Allocate the 2 data structures */
if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
(png_voidp)NULL, NULL, NULL)) == NULL)
return ERROR_INT("png_ptr not made", procName, 1);
if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
return ERROR_INT("info_ptr not made", procName, 1);
}
/* Set up png setjmp error handling */
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
return ERROR_INT("internal png error", procName, 1);
}
png_init_io(png_ptr, fp);
/* With best zlib compression (9), get between 1 and 10% improvement
* over default (5), but the compression is 3 to 10 times slower.
* Our default compression is the zlib default (5). */
png_set_compression_level(png_ptr, L_ZLIB_COMPRESSION);
w = pixGetWidth(pix);
h = pixGetHeight(pix);
d = pixGetDepth(pix);
if ((cmap = pixGetColormap(pix)))
cmflag = 1;
else
cmflag = 0;
if ((d == 32) || (d == 24)) {
bit_depth = 8;
color_type = PNG_COLOR_TYPE_RGB;
cmflag = 0; /* ignore if it exists */
}
else {
bit_depth = d;
color_type = PNG_COLOR_TYPE_GRAY;
}
if (cmflag)
color_type = PNG_COLOR_TYPE_PALETTE;
#if DEBUG
fprintf(stderr, "cmflag = %d, bit_depth = %d, color_type = %d\n",
cmflag, bit_depth, color_type);
#endif /* DEBUG */
png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
/* Store resolution in ppm, if known */
xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);
yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);
if ((xres == 0) || (yres == 0))
png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN);
else
png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER);
if (cmflag) {
pixcmapToArrays(cmap, &rmap, &gmap, &bmap);
ncolors = pixcmapGetCount(cmap);
/* Make and save the palette */
if ((palette = (png_colorp)(CALLOC(ncolors, sizeof(png_color))))
== NULL)
return ERROR_INT("palette not made", procName, 1);
for (i = 0; i < ncolors; i++) {
palette[i].red = (png_byte)rmap[i];
palette[i].green = (png_byte)gmap[i];
palette[i].blue = (png_byte)bmap[i];
}
png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors);
FREE(rmap);
FREE(gmap);
FREE(bmap);
}
/* 0.4545 is treated as the default by some image
* display programs (not gqview). A value > 0.4545 will
* lighten an image as displayed by xv, display, etc. */
if (gamma > 0.0)
png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma);
if ((text = pixGetText(pix))) {
png_text text_chunk;
text_chunk.compression = PNG_TEXT_COMPRESSION_NONE;
text_chunk.key = commentstring;
text_chunk.text = text;
text_chunk.text_length = strlen(text);
#ifdef PNG_ITXT_SUPPORTED
text_chunk.itxt_length = 0;
text_chunk.lang = NULL;
text_chunk.lang_key = NULL;
#endif
png_set_text(png_ptr, info_ptr, &text_chunk, 1);
}
/* Write header and palette info */
png_write_info(png_ptr, info_ptr);
if ((d != 32) && (d != 24)) { /* not rgb color */
/* Generate a temporary pix with bytes swapped.
* For a binary image, there are two conditions in
* which you must first invert the data for writing png:
* (a) no colormap
* (b) colormap with BLACK set to 0
* png writes binary with BLACK = 0, unless contradicted
* by a colormap. If the colormap has BLACK = "1"
* (typ. about 255), do not invert the data. If there
* is no colormap, you must invert the data to store
* in default BLACK = 0 state. */
if (d == 1 &&
(!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) {
pixt = pixInvert(NULL, pix);
pixEndianByteSwap(pixt);
}
else
pixt = pixEndianByteSwapNew(pix);
if (!pixt) {
png_destroy_write_struct(&png_ptr, &info_ptr);
return ERROR_INT("pixt not made", procName, 1);
}
/* Make and assign array of image row pointers */
if ((row_pointers = (png_bytep *)CALLOC(h, sizeof(png_bytep))) == NULL)
return ERROR_INT("row-pointers not made", procName, 1);
wpl = pixGetWpl(pixt);
data = pixGetData(pixt);
for (i = 0; i < h; i++)
row_pointers[i] = (png_bytep)(data + i * wpl);
png_set_rows(png_ptr, info_ptr, row_pointers);
/* Transfer the data */
png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr);
if (cmflag)
FREE(palette);
FREE(row_pointers);
pixDestroy(&pixt);
png_destroy_write_struct(&png_ptr, &info_ptr);
return 0;
}
/* For rgb, compose and write a row at a time */
data = pixGetData(pix);
wpl = pixGetWpl(pix);
if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */
for (i = 0; i < h; i++) {
ppixel = data + i * wpl;
png_write_rows(png_ptr, (png_bytepp)&ppixel, 1);
}
}
else { /* standard 32 bpp rgb */
if ((rowbuffer = (png_bytep)CALLOC(w, 3)) == NULL)
return ERROR_INT("rowbuffer not made", procName, 1);
for (i = 0; i < h; i++) {
ppixel = data + i * wpl;
for (j = k = 0; j < w; j++) {
rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
ppixel++;
}
png_write_rows(png_ptr, &rowbuffer, 1);
}
FREE(rowbuffer);
}
png_write_end(png_ptr, info_ptr);
if (cmflag)
FREE(palette);
png_destroy_write_struct(&png_ptr, &info_ptr);
return 0;
}
/*---------------------------------------------------------------------*
* Setting flags for special modes *
*---------------------------------------------------------------------*/
/*!
* l_pngSetStrip16To8()
*
* Input: flag (1 for stripping 16 bpp to 8 bpp; 0 for leaving 16 bpp)
* Return: void
*/
void
l_pngSetStrip16To8(l_int32 flag)
{
if (flag == 1)
L_PNG_STRIP_16_TO_8 = 1;
else
L_PNG_STRIP_16_TO_8 = 0;
}
/*!
* l_pngSetStripAlpha()
*
* Input: flag (1 for stripping alpha channel on read; 0 for leaving it)
* Return: void
*/
void
l_pngSetStripAlpha(l_int32 flag)
{
if (flag == 1)
L_PNG_STRIP_ALPHA = 1;
else
L_PNG_STRIP_ALPHA = 0;
}
/*!
* l_pngSetZlibCompression()
*
* Input: val (zlib compression value)
* Return: void
*
* Notes:
* (1) Valid zlib compression values are in the interval [0 ... 9],
* where, as defined in zlib.h:
* 0 Z_NO_COMPRESSION
* 1 Z_BEST_SPEED (poorest compression)
* 9 Z_BEST_COMPRESSION
* For the default value, use either of these:
* 6 Z_DEFAULT_COMPRESSION
* -1 (resolves to Z_DEFAULT_COMPRESSION)
* (2) If you use the defined constants in zlib.h instead of the
* compression integers given above, you must include zlib.h.
*/
void
l_pngSetZlibCompression(l_int32 val)
{
PROCNAME("l_pngSetZlibCompression");
if (val < -1 || val > 9) {
L_ERROR("Invalid zlib comp val; using default", procName);
val = Z_DEFAULT_COMPRESSION;
}
L_ZLIB_COMPRESSION = val;
}
/*---------------------------------------------------------------------*
* Read/write to memory *
*---------------------------------------------------------------------*/
#if HAVE_FMEMOPEN
#include "_stdio.h"
/* extern FILE *open_memstream(char **data, size_t *size); */
/* extern FILE *fmemopen(void *data, size_t size, const char *mode); */
/*!
* pixReadMemPng()
*
* Input: cdata (const; png-encoded)
* size (of data)
* Return: pix, or null on error
*
* Notes:
* (1) The @size byte of @data must be a null character.
*/
PIX *
pixReadMemPng(const l_uint8 *cdata,
size_t size)
{
l_uint8 *data;
FILE *fp;
PIX *pix;
PROCNAME("pixReadMemPng");
if (!cdata)
return (PIX *)ERROR_PTR("cdata not defined", procName, NULL);
data = (l_uint8 *)cdata; /* we're really not going to change this */
if ((fp = fmemopen(data, size, "r")) == NULL)
return (PIX *)ERROR_PTR("stream not opened", procName, NULL);
pix = pixReadStreamPng(fp);
fclose(fp);
return pix;
}
/*!
* pixWriteMemPng()
*
* Input: &data (<return> data of tiff compressed image)
* &size (<return> size of returned data)
* pix
* gamma (use 0.0 if gamma is not defined)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) See pixWriteStreamPng() for usage. This version writes to
* memory instead of to a file stream.
*/
l_int32
pixWriteMemPng(l_uint8 **pdata,
size_t *psize,
PIX *pix,
l_float32 gamma)
{
l_int32 ret;
FILE *fp;
PROCNAME("pixWriteMemPng");
if (!pdata)
return ERROR_INT("&data not defined", procName, 1 );
if (!psize)
return ERROR_INT("&size not defined", procName, 1 );
if (!pix)
return ERROR_INT("&pix not defined", procName, 1 );
if ((fp = open_memstream((char **)pdata, psize)) == NULL)
return ERROR_INT("stream not opened", procName, 1);
ret = pixWriteStreamPng(fp, pix, gamma);
fclose(fp);
return ret;
}
#else
PIX *
pixReadMemPng(const l_uint8 *cdata,
size_t size)
{
return (PIX *)ERROR_PTR(
"png read from memory not implemented on this platform",
"pixReadMemPng", NULL);
}
l_int32
pixWriteMemPng(l_uint8 **pdata,
size_t *psize,
PIX *pix,
l_float32 gamma)
{
return ERROR_INT(
"png write to memory not implemented on this platform",
"pixWriteMemPng", 1);
}
#endif /* HAVE_FMEMOPEN */
/* --------------------------------------------*/
#endif /* HAVE_LIBPNG */
/* --------------------------------------------*/