1 /*
2 * PNG image routines for CUPS.
3 *
4 * Copyright 2007-2011 by Apple Inc.
5 * Copyright 1993-2007 by Easy Software Products.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "COPYING"
10 * which should have been included with this file.
11 *
12 * Contents:
13 *
14 * _cupsImageReadPNG() - Read a PNG image file.
15 */
16
17 /*
18 * Include necessary headers...
19 */
20
21 #include "image-private.h"
22
23 #if defined(HAVE_LIBPNG) && defined(HAVE_LIBZ)
24 # include <png.h> /* Portable Network Graphics (PNG) definitions */
25
26
27 /*
28 * '_cupsImageReadPNG()' - Read a PNG image file.
29 */
30
31 int /* O - Read status */
_cupsImageReadPNG(cups_image_t * img,FILE * fp,cups_icspace_t primary,cups_icspace_t secondary,int saturation,int hue,const cups_ib_t * lut)32 _cupsImageReadPNG(
33 cups_image_t *img, /* IO - cupsImage */
34 FILE *fp, /* I - cupsImage file */
35 cups_icspace_t primary, /* I - Primary choice for colorspace */
36 cups_icspace_t secondary, /* I - Secondary choice for colorspace */
37 int saturation, /* I - Color saturation (%) */
38 int hue, /* I - Color hue (degrees) */
39 const cups_ib_t *lut) /* I - Lookup table for gamma/brightness */
40 {
41 int y; /* Looping var */
42 png_structp pp; /* PNG read pointer */
43 png_infop info; /* PNG info pointers */
44 png_uint_32 width, /* Width of image */
45 height; /* Height of image */
46 int bit_depth, /* Bit depth */
47 color_type, /* Color type */
48 interlace_type, /* Interlace type */
49 compression_type, /* Compression type */
50 filter_type; /* Filter type */
51 png_uint_32 xppm, /* X pixels per meter */
52 yppm; /* Y pixels per meter */
53 int bpp; /* Bytes per pixel */
54 int pass, /* Current pass */
55 passes; /* Number of passes required */
56 cups_ib_t *in, /* Input pixels */
57 *inptr, /* Pointer into pixels */
58 *out; /* Output pixels */
59 png_color_16 bg; /* Background color */
60
61
62 /*
63 * Setup the PNG data structures...
64 */
65
66 pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
67 info = png_create_info_struct(pp);
68
69 /*
70 * Initialize the PNG read "engine"...
71 */
72
73 png_init_io(pp, fp);
74
75 /*
76 * Get the image dimensions and load the output image...
77 */
78
79 png_read_info(pp, info);
80
81 png_get_IHDR(pp, info, &width, &height, &bit_depth, &color_type,
82 &interlace_type, &compression_type, &filter_type);
83
84 fprintf(stderr, "DEBUG: PNG image: %dx%dx%d, color_type=%x (%s%s%s)\n",
85 (int)width, (int)height, bit_depth, color_type,
86 (color_type & PNG_COLOR_MASK_COLOR) ? "RGB" : "GRAYSCALE",
87 (color_type & PNG_COLOR_MASK_ALPHA) ? "+ALPHA" : "",
88 (color_type & PNG_COLOR_MASK_PALETTE) ? "+PALETTE" : "");
89
90 if (color_type & PNG_COLOR_MASK_PALETTE)
91 png_set_expand(pp);
92 else if (bit_depth < 8)
93 {
94 png_set_packing(pp);
95 png_set_expand(pp);
96 }
97 else if (bit_depth == 16)
98 png_set_strip_16(pp);
99
100 if (color_type & PNG_COLOR_MASK_COLOR)
101 img->colorspace = (primary == CUPS_IMAGE_RGB_CMYK) ? CUPS_IMAGE_RGB :
102 primary;
103 else
104 img->colorspace = secondary;
105
106 if (width == 0 || width > CUPS_IMAGE_MAX_WIDTH ||
107 height == 0 || height > CUPS_IMAGE_MAX_HEIGHT)
108 {
109 fprintf(stderr, "DEBUG: PNG image has invalid dimensions %ux%u!\n",
110 (unsigned)width, (unsigned)height);
111 fclose(fp);
112 return (1);
113 }
114
115 img->xsize = width;
116 img->ysize = height;
117
118
119 int temp = -1;
120
121 #ifdef HAVE_EXIF
122 /*
123 scan image file for exif data
124 */
125
126 temp = _cupsImageReadEXIF(img, fp);
127 #endif
128 /*
129 check headers only if EXIF contains no info about ppi
130 */
131
132 if (temp != 1 && (xppm = png_get_x_pixels_per_meter(pp, info)) != 0 &&
133 (yppm = png_get_y_pixels_per_meter(pp, info)) != 0)
134 {
135 img->xppi = (int)((float)xppm * 0.0254);
136 img->yppi = (int)((float)yppm * 0.0254);
137
138 if (img->xppi == 0 || img->yppi == 0)
139 {
140 fprintf(stderr, "DEBUG: PNG image has invalid resolution %dx%d PPI\n",
141 img->xppi, img->yppi);
142
143 img->xppi = img->yppi = 200;
144 }
145 }
146
147 cupsImageSetMaxTiles(img, 0);
148
149 passes = png_set_interlace_handling(pp);
150
151 /*
152 * Handle transparency...
153 */
154
155 if (png_get_valid(pp, info, PNG_INFO_tRNS))
156 png_set_tRNS_to_alpha(pp);
157
158 bg.red = 65535;
159 bg.green = 65535;
160 bg.blue = 65535;
161
162 png_set_background(pp, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
163
164 if (passes == 1)
165 {
166 /*
167 * Load one row at a time...
168 */
169
170 if (color_type == PNG_COLOR_TYPE_GRAY ||
171 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
172 in = malloc(img->xsize);
173 else
174 in = malloc(img->xsize * 3);
175 }
176 else
177 {
178 /*
179 * Interlaced images must be loaded all at once...
180 */
181
182 size_t bufsize; /* Size of buffer */
183
184
185 if (color_type == PNG_COLOR_TYPE_GRAY ||
186 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
187 {
188 bufsize = img->xsize * img->ysize;
189
190 if ((bufsize / img->xsize) != img->ysize)
191 {
192 fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
193 (unsigned)width, (unsigned)height);
194 fclose(fp);
195 return (1);
196 }
197 }
198 else
199 {
200 bufsize = img->xsize * img->ysize * 3;
201
202 if ((bufsize / (img->xsize * 3)) != img->ysize)
203 {
204 fprintf(stderr, "DEBUG: PNG image dimensions (%ux%u) too large!\n",
205 (unsigned)width, (unsigned)height);
206 fclose(fp);
207 return (1);
208 }
209 }
210
211 in = malloc(bufsize);
212 }
213
214 bpp = cupsImageGetDepth(img);
215 out = malloc(img->xsize * bpp);
216
217 if (!in || !out)
218 {
219 fputs("DEBUG: Unable to allocate memory for PNG image!\n", stderr);
220
221 if (in)
222 free(in);
223
224 if (out)
225 free(out);
226
227 fclose(fp);
228
229 return (1);
230 }
231
232 /*
233 * Read the image, interlacing as needed...
234 */
235
236 for (pass = 1; pass <= passes; pass ++)
237 for (inptr = in, y = 0; y < img->ysize; y ++)
238 {
239 png_read_row(pp, (png_bytep)inptr, NULL);
240
241 if (pass == passes)
242 {
243 /*
244 * Output this row...
245 */
246
247 if (color_type & PNG_COLOR_MASK_COLOR)
248 {
249 if ((saturation != 100 || hue != 0) && bpp > 1)
250 cupsImageRGBAdjust(inptr, img->xsize, saturation, hue);
251
252 switch (img->colorspace)
253 {
254 case CUPS_IMAGE_WHITE :
255 cupsImageRGBToWhite(inptr, out, img->xsize);
256 break;
257 case CUPS_IMAGE_RGB :
258 case CUPS_IMAGE_RGB_CMYK :
259 cupsImageRGBToRGB(inptr, out, img->xsize);
260 break;
261 case CUPS_IMAGE_BLACK :
262 cupsImageRGBToBlack(inptr, out, img->xsize);
263 break;
264 case CUPS_IMAGE_CMY :
265 cupsImageRGBToCMY(inptr, out, img->xsize);
266 break;
267 case CUPS_IMAGE_CMYK :
268 cupsImageRGBToCMYK(inptr, out, img->xsize);
269 break;
270 }
271 }
272 else
273 {
274 switch (img->colorspace)
275 {
276 case CUPS_IMAGE_WHITE :
277 memcpy(out, inptr, img->xsize);
278 break;
279 case CUPS_IMAGE_RGB :
280 case CUPS_IMAGE_RGB_CMYK :
281 cupsImageWhiteToRGB(inptr, out, img->xsize);
282 break;
283 case CUPS_IMAGE_BLACK :
284 cupsImageWhiteToBlack(inptr, out, img->xsize);
285 break;
286 case CUPS_IMAGE_CMY :
287 cupsImageWhiteToCMY(inptr, out, img->xsize);
288 break;
289 case CUPS_IMAGE_CMYK :
290 cupsImageWhiteToCMYK(inptr, out, img->xsize);
291 break;
292 }
293 }
294
295 if (lut)
296 cupsImageLut(out, img->xsize * bpp, lut);
297
298 _cupsImagePutRow(img, 0, y, img->xsize, out);
299 }
300
301 if (passes > 1)
302 {
303 if (color_type & PNG_COLOR_MASK_COLOR)
304 inptr += img->xsize * 3;
305 else
306 inptr += img->xsize;
307 }
308 }
309
310 png_read_end(pp, info);
311 png_destroy_read_struct(&pp, &info, NULL);
312
313 fclose(fp);
314 free(in);
315 free(out);
316
317 return (0);
318 }
319 #endif /* HAVE_LIBPNG && HAVE_LIBZ */
320
321