#include "image-private.h"
#if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ)
# include <png.h>
int
_cupsImageReadPNG(
cups_image_t *img,
FILE *fp,
cups_icspace_t primary,
cups_icspace_t secondary,
int saturation,
int hue,
const cups_ib_t *lut)
{
int y;
png_structp pp;
png_infop info;
png_uint_32 width,
height;
int bit_depth,
color_type,
interlace_type,
compression_type,
filter_type;
png_uint_32 xppm,
yppm;
int bpp;
int pass,
passes;
cups_ib_t *in,
*inptr,
*out;
png_color_16 bg;
pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info = png_create_info_struct(pp);
png_init_io(pp, fp);
png_read_info(pp, info);
png_get_IHDR(pp, info, &width, &height, &bit_depth, &color_type,
&interlace_type, &compression_type, &filter_type);
fprintf(stderr, "DEBUG: PNG image: %dx%dx%d, color_type=%x (%s%s%s)\n",
(int)width, (int)height, bit_depth, color_type,
(color_type & PNG_COLOR_MASK_COLOR) ? "RGB" : "GRAYSCALE",
(color_type & PNG_COLOR_MASK_ALPHA) ? "+ALPHA" : "",
(color_type & PNG_COLOR_MASK_PALETTE) ? "+PALETTE" : "");
if (color_type & PNG_COLOR_MASK_PALETTE)
png_set_expand(pp);
else if (bit_depth < 8)
{
png_set_packing(pp);
png_set_expand(pp);
}
else if (bit_depth == 16)
png_set_strip_16(pp);
if (color_type & PNG_COLOR_MASK_COLOR)
img->colorspace = (primary == CUPS_IMAGE_RGB_CMYK) ? CUPS_IMAGE_RGB :
primary;
else
img->colorspace = secondary;
if (width == 0 || width > CUPS_IMAGE_MAX_WIDTH ||
height == 0 || height > CUPS_IMAGE_MAX_HEIGHT)
{
fprintf(stderr, "DEBUG: PNG image has invalid dimensions %ux%u!\n",
(unsigned)width, (unsigned)height);
fclose(fp);
return (1);
}
img->xsize = width;
img->ysize = height;
if ((xppm = png_get_x_pixels_per_meter(pp, info)) != 0 &&
(yppm = png_get_y_pixels_per_meter(pp, info)) != 0)
{
img->xppi = (int)((float)xppm * 0.0254);
img->yppi = (int)((float)yppm * 0.0254);
if (img->xppi == 0 || img->yppi == 0)
{
fprintf(stderr, "DEBUG: PNG image has invalid resolution %dx%d PPI\n",
img->xppi, img->yppi);
img->xppi = img->yppi = 128;
}
}
cupsImageSetMaxTiles(img, 0);
passes = png_set_interlace_handling(pp);
if (png_get_valid(pp, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(pp);
bg.red = 65535;
bg.green = 65535;
bg.blue = 65535;
png_set_background(pp, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
if (passes == 1)
{
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
in = malloc(img->xsize);
else
in = malloc(img->xsize * 3);
}
else
{
size_t bufsize;
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
bufsize = img->xsize * img->ysize;
if ((bufsize / img->ysize) != img->xsize)
{
fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
(unsigned)width, (unsigned)height);
fclose(fp);
return (1);
}
}
else
{
bufsize = img->xsize * img->ysize * 3;
if ((bufsize / (img->ysize * 3)) != img->xsize)
{
fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
(unsigned)width, (unsigned)height);
fclose(fp);
return (1);
}
}
in = malloc(bufsize);
}
bpp = cupsImageGetDepth(img);
out = malloc(img->xsize * bpp);
if (!in || !out)
{
fputs("DEBUG: Unable to allocate memory for PNG image!\n", stderr);
if (in)
free(in);
if (out)
free(out);
fclose(fp);
return (1);
}
for (pass = 1; pass <= passes; pass ++)
for (inptr = in, y = 0; y < img->ysize; y ++)
{
png_read_row(pp, (png_bytep)inptr, NULL);
if (pass == passes)
{
if (color_type & PNG_COLOR_MASK_COLOR)
{
if ((saturation != 100 || hue != 0) && bpp > 1)
cupsImageRGBAdjust(inptr, img->xsize, saturation, hue);
switch (img->colorspace)
{
case CUPS_IMAGE_WHITE :
cupsImageRGBToWhite(inptr, out, img->xsize);
break;
case CUPS_IMAGE_RGB :
case CUPS_IMAGE_RGB_CMYK :
cupsImageRGBToRGB(inptr, out, img->xsize);
break;
case CUPS_IMAGE_BLACK :
cupsImageRGBToBlack(inptr, out, img->xsize);
break;
case CUPS_IMAGE_CMY :
cupsImageRGBToCMY(inptr, out, img->xsize);
break;
case CUPS_IMAGE_CMYK :
cupsImageRGBToCMYK(inptr, out, img->xsize);
break;
}
}
else
{
switch (img->colorspace)
{
case CUPS_IMAGE_WHITE :
memcpy(out, inptr, img->xsize);
break;
case CUPS_IMAGE_RGB :
case CUPS_IMAGE_RGB_CMYK :
cupsImageWhiteToRGB(inptr, out, img->xsize);
break;
case CUPS_IMAGE_BLACK :
cupsImageWhiteToBlack(inptr, out, img->xsize);
break;
case CUPS_IMAGE_CMY :
cupsImageWhiteToCMY(inptr, out, img->xsize);
break;
case CUPS_IMAGE_CMYK :
cupsImageWhiteToCMYK(inptr, out, img->xsize);
break;
}
}
if (lut)
cupsImageLut(out, img->xsize * bpp, lut);
_cupsImagePutRow(img, 0, y, img->xsize, out);
}
if (passes > 1)
{
if (color_type & PNG_COLOR_MASK_COLOR)
inptr += img->xsize * 3;
else
inptr += img->xsize;
}
}
png_read_end(pp, info);
png_destroy_read_struct(&pp, &info, NULL);
fclose(fp);
free(in);
free(out);
return (0);
}
#endif