comparison mupdf-source/thirdparty/leptonica/src/pngio.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 - Copyright (C) 2017 Milner Technologies, Inc.
4 -
5 - Redistribution and use in source and binary forms, with or without
6 - modification, are permitted provided that the following conditions
7 - are met:
8 - 1. Redistributions of source code must retain the above copyright
9 - notice, this list of conditions and the following disclaimer.
10 - 2. Redistributions in binary form must reproduce the above
11 - copyright notice, this list of conditions and the following
12 - disclaimer in the documentation and/or other materials
13 - provided with the distribution.
14 -
15 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
19 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *====================================================================*/
27
28 /*!
29 * \file pngio.c
30 * <pre>
31 *
32 * Reading png through stream
33 * PIX *pixReadStreamPng()
34 *
35 * Reading png header
36 * l_int32 readHeaderPng()
37 * l_int32 freadHeaderPng()
38 * l_int32 readHeaderMemPng()
39 *
40 * Reading png metadata
41 * l_int32 fgetPngResolution()
42 * l_int32 isPngInterlaced()
43 * l_int32 fgetPngColormapInfo()
44 *
45 * Writing png through stream
46 * l_int32 pixWritePng() [ special top level ]
47 * l_int32 pixWriteStreamPng()
48 * l_int32 pixSetZlibCompression()
49 *
50 * Set flag for special read mode
51 * void l_pngSetReadStrip16To8()
52 *
53 * Low-level memio utility (thanks to T. D. Hintz)
54 * static void memio_png_write_data()
55 * static void memio_png_flush()
56 * static void memio_png_read_data()
57 * static void memio_free()
58 *
59 * Reading png from memory
60 * PIX *pixReadMemPng()
61 *
62 * Writing png to memory
63 * l_int32 pixWriteMemPng()
64 *
65 * Documentation: libpng.txt and example.c
66 *
67 * On input (decompression from file), palette color images
68 * are read into an 8 bpp Pix with a colormap, and 24 bpp
69 * 3 component color images are read into a 32 bpp Pix with
70 * rgb samples. On output (compression to file), palette color
71 * images are written as 8 bpp with the colormap, and 32 bpp
72 * full color images are written compressed as a 24 bpp,
73 * 3 component color image.
74 *
75 * In the following, we use these abbreviations:
76 * bps == bit/sample
77 * spp == samples/pixel
78 * bpp == bits/pixel of image in Pix (memory)
79 * where each component is referred to as a "sample".
80 *
81 * For reading and writing rgb and rgba images, we read and write
82 * alpha if it exists (spp == 4) and do not read or write if
83 * it doesn't (spp == 3). The alpha component can be 'removed'
84 * simply by setting spp to 3. In leptonica, we make relatively
85 * little explicit use of the alpha sample. Note that the alpha
86 * sample in the image is also called "alpha transparency",
87 * "alpha component" and "alpha layer."
88 *
89 * To change the zlib compression level, use pixSetZlibCompression()
90 * before writing the file. The default is for standard png compression.
91 * The zlib compression value can be set [0 ... 9], with
92 * 0 no compression (huge files)
93 * 1 fastest compression
94 * -1 default compression (equivalent to 6 in latest version)
95 * 9 best compression
96 * Note that if you are using the defined constants in zlib instead
97 * of the compression integers given above, you must include zlib.h.
98 *
99 * There is global for determining the size of retained samples:
100 * var_PNG_STRIP_16_to_8
101 * and a function l_pngSetReadStrip16To8() for setting it.
102 * The default is TRUE, which causes pixRead() to strip each 16 bit
103 * sample down to 8 bps:
104 * ~ For 16 bps rgb (16 bps, 3 spp) --> 32 bpp rgb Pix
105 * ~ For 16 bps gray (16 bps, 1 spp) --> 8 bpp grayscale Pix
106 * If the variable is set to FALSE, the 16 bit gray samples
107 * are saved when read; the 16 bit rgb samples return an error.
108 * Note: results can be non-deterministic if used with
109 * multi-threaded applications.
110 *
111 * Thanks to a memory buffering utility contributed by T. D. Hintz,
112 * encoding png directly into memory (and decoding from memory)
113 * is now enabled without the use of any temp files. Unlike with webp,
114 * it is necessary to preserve the stream interface to enable writing
115 * pixa to memory. So there are two independent but very similar
116 * implementations of png reading and writing.
117 * </pre>
118 */
119
120 #ifdef HAVE_CONFIG_H
121 #include <config_auto.h>
122 #endif /* HAVE_CONFIG_H */
123
124 #include <string.h>
125 #include "allheaders.h"
126 #include "pix_internal.h"
127
128 /* --------------------------------------------*/
129 #if HAVE_LIBPNG /* defined in environ.h */
130 /* --------------------------------------------*/
131
132 #include "png.h"
133
134 #if HAVE_LIBZ
135 #include "zlib.h"
136 #else
137 #define Z_DEFAULT_COMPRESSION (-1)
138 #endif /* HAVE_LIBZ */
139
140 /* ------------------ Set default for read option -------------------- */
141 /* Strip 16 bpp --> 8 bpp on reading png; default is for stripping.
142 * If you don't strip, you can't read the gray-alpha spp = 2 images. */
143 static l_int32 var_PNG_STRIP_16_TO_8 = 1;
144
145 #ifndef NO_CONSOLE_IO
146 #define DEBUG_READ 0
147 #define DEBUG_WRITE 0
148 #endif /* ~NO_CONSOLE_IO */
149
150
151 /*---------------------------------------------------------------------*
152 * Reading png through stream *
153 *---------------------------------------------------------------------*/
154 /*!
155 * \brief pixReadStreamPng()
156 *
157 * \param[in] fp file stream
158 * \return pix, or NULL on error
159 *
160 * <pre>
161 * Notes:
162 * (1) If called from pixReadStream(), the stream is positioned
163 * at the beginning of the file.
164 * (2) To do sequential reads of png format images from a stream,
165 * use pixReadStreamPng()
166 * (3) All images with alpha is converted to RGBA (spp = 4, with
167 * equal red, green and blue channels) on reading.
168 * There are three cases with alpha:
169 * (a) RGBA: spp = 4. The alpha value is the fourth byte
170 * (aka "channel, "component") in each 4-byte pixel.
171 * (b) grayscale-with-alpha (spp = 2), where bpp = 8, and each
172 * pixel has an associated alpha (transparency) value
173 * in the second component of the image data.
174 * (c) colormap (spp = 1) with alpha in the trans palette.
175 * d = 1, 2, 4, 8. The trans palette in writing is derived
176 * from the alpha components in the cmap. Transparency is
177 * often associated with a white background.
178 * (4) We use the high level png interface, where the transforms are set
179 * up in advance and the header and image are read with a single
180 * call. The more complicated interface, where the header is
181 * read first and the buffers for the raster image are user-
182 * allocated before reading the image, works for single images,
183 * but I could not get it to work properly for the successive
184 * png reads that are required by pixaReadStream().
185 * </pre>
186 */
187 PIX *
188 pixReadStreamPng(FILE *fp)
189 {
190 l_uint8 byte;
191 l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
192 l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
193 l_uint32 png_transforms;
194 l_uint32 *data, *line, *ppixel;
195 int num_palette, num_text, num_trans;
196 png_byte bit_depth, color_type, channels;
197 png_uint_32 w, h, rowbytes, xres, yres;
198 png_bytep rowptr, trans;
199 png_bytep *row_pointers;
200 png_structp png_ptr;
201 png_infop info_ptr, end_info;
202 png_colorp palette;
203 png_textp text_ptr; /* ptr to text_chunk */
204 PIX *pix, *pix1;
205 PIXCMAP *cmap;
206
207 if (!fp)
208 return (PIX *)ERROR_PTR("fp not defined", __func__, NULL);
209 pix = NULL;
210
211 /* Allocate the 3 data structures */
212 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
213 (png_voidp)NULL, NULL, NULL)) == NULL)
214 return (PIX *)ERROR_PTR("png_ptr not made", __func__, NULL);
215
216 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
217 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
218 return (PIX *)ERROR_PTR("info_ptr not made", __func__, NULL);
219 }
220
221 if ((end_info = png_create_info_struct(png_ptr)) == NULL) {
222 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
223 return (PIX *)ERROR_PTR("end_info not made", __func__, NULL);
224 }
225
226 /* Set up png setjmp error handling */
227 if (setjmp(png_jmpbuf(png_ptr))) {
228 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
229 return (PIX *)ERROR_PTR("internal png error", __func__, NULL);
230 }
231
232 png_init_io(png_ptr, fp);
233
234 /* ---------------------------------------------------------- *
235 * - Set the transforms flags. Whatever happens here,
236 * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO.
237 * - Do not use PNG_TRANSFORM_EXPAND, which would
238 * expand all images with bpp < 8 to 8 bpp.
239 * - Strip 16 --> 8 if reading 16-bit gray+alpha
240 * ---------------------------------------------------------- */
241 /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */
242 if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */
243 png_transforms = PNG_TRANSFORM_STRIP_16;
244 } else {
245 png_transforms = PNG_TRANSFORM_IDENTITY;
246 L_INFO("not stripping 16 --> 8 in png reading\n", __func__);
247 }
248
249 /* Read it */
250 png_read_png(png_ptr, info_ptr, png_transforms, NULL);
251
252 row_pointers = png_get_rows(png_ptr, info_ptr);
253 w = png_get_image_width(png_ptr, info_ptr);
254 h = png_get_image_height(png_ptr, info_ptr);
255 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
256 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
257 color_type = png_get_color_type(png_ptr, info_ptr);
258 channels = png_get_channels(png_ptr, info_ptr);
259 spp = channels;
260 tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0;
261
262 if (spp == 1) {
263 d = bit_depth;
264 } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */
265 d = 4 * bit_depth;
266 }
267
268 /* Remove if/when this is implemented for all bit_depths */
269 if (spp != 1 && bit_depth != 8) {
270 L_ERROR("spp = %d and bps = %d != 8\n"
271 "turn on 16 --> 8 stripping\n", __func__, spp, bit_depth);
272 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
273 return (PIX *)ERROR_PTR("not implemented for this image",
274 __func__, NULL);
275 }
276
277 cmap = NULL;
278 if (color_type == PNG_COLOR_TYPE_PALETTE ||
279 color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */
280 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
281 cmap = pixcmapCreate(d); /* spp == 1 */
282 for (cindex = 0; cindex < num_palette; cindex++) {
283 rval = palette[cindex].red;
284 gval = palette[cindex].green;
285 bval = palette[cindex].blue;
286 pixcmapAddColor(cmap, rval, gval, bval);
287 }
288 }
289
290 if ((pix = pixCreate(w, h, d)) == NULL) {
291 pixcmapDestroy(&cmap);
292 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
293 return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
294 }
295 pixSetInputFormat(pix, IFF_PNG);
296 wpl = pixGetWpl(pix);
297 data = pixGetData(pix);
298 pixSetSpp(pix, spp);
299 if (pixSetColormap(pix, cmap)) {
300 pixDestroy(&pix);
301 return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL);
302 }
303
304 if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */
305 for (i = 0; i < h; i++) {
306 line = data + i * wpl;
307 rowptr = row_pointers[i];
308 for (j = 0; j < rowbytes; j++) {
309 SET_DATA_BYTE(line, j, rowptr[j]);
310 }
311 }
312 } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */
313 L_INFO("converting (gray + alpha) ==> RGBA\n", __func__);
314 for (i = 0; i < h; i++) {
315 ppixel = data + i * wpl;
316 rowptr = row_pointers[i];
317 for (j = k = 0; j < w; j++) {
318 /* Copy gray value into r, g and b */
319 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]);
320 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]);
321 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
322 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
323 ppixel++;
324 }
325 }
326 pixSetSpp(pix, 4); /* we do not support 2 spp pix */
327 } else if (spp == 3 || spp == 4) {
328 for (i = 0; i < h; i++) {
329 ppixel = data + i * wpl;
330 rowptr = row_pointers[i];
331 for (j = k = 0; j < w; j++) {
332 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]);
333 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]);
334 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
335 if (spp == 3) /* set to opaque; some readers are buggy */
336 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, 255);
337 else /* spp == 4 */
338 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
339 ppixel++;
340 }
341 }
342 }
343
344 /* Special spp == 1 cases with transparency:
345 * (1) 8 bpp without colormap; assume full transparency
346 * (2) 1 bpp with colormap + trans array (for alpha)
347 * (3) 2 bpp with colormap + trans array (for alpha)
348 * (4) 4 bpp with colormap + trans array (for alpha)
349 * (5) 8 bpp with colormap + trans array (for alpha)
350 * These all require converting to RGBA */
351 if (spp == 1 && tRNS) {
352 if (!cmap) {
353 /* Case 1: make fully transparent RGBA image */
354 L_INFO("transparency, 1 spp, no colormap, no transparency array: "
355 "convention is fully transparent image\n", __func__);
356 L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", __func__);
357 pixDestroy(&pix);
358 pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */
359 pixSetSpp(pix, 4);
360 } else {
361 L_INFO("converting (cmap + alpha) ==> RGBA\n", __func__);
362
363 /* Grab the transparency array */
364 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
365 if (!trans) { /* invalid png file */
366 pixDestroy(&pix);
367 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
368 return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array",
369 __func__, NULL);
370 }
371
372 /* Save the cmap and destroy the pix */
373 cmap = pixcmapCopy(pixGetColormap(pix));
374 ncolors = pixcmapGetCount(cmap);
375 pixDestroy(&pix);
376
377 /* Start over with 32 bit RGBA */
378 pix = pixCreate(w, h, 32);
379 wpl = pixGetWpl(pix);
380 data = pixGetData(pix);
381 pixSetSpp(pix, 4);
382
383 #if DEBUG_READ
384 lept_stderr("ncolors = %d, num_trans = %d\n",
385 ncolors, num_trans);
386 for (i = 0; i < ncolors; i++) {
387 pixcmapGetColor(cmap, i, &rval, &gval, &bval);
388 if (i < num_trans) {
389 lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n",
390 rval, gval, bval, trans[i]);
391 } else {
392 lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n",
393 rval, gval, bval);
394 }
395 }
396 #endif /* DEBUG_READ */
397
398 /* Extract the data and convert to RGBA */
399 if (d == 1) {
400 /* Case 2: 1 bpp with transparency (usually) behind white */
401 L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", __func__);
402 if (num_trans == 1)
403 L_INFO("num_trans = 1; second color opaque by default\n",
404 __func__);
405 for (i = 0; i < h; i++) {
406 ppixel = data + i * wpl;
407 rowptr = row_pointers[i];
408 for (j = 0, index = 0; j < rowbytes; j++) {
409 byte = rowptr[j];
410 for (k = 0; k < 8 && index < w; k++, index++) {
411 bitval = (byte >> (7 - k)) & 1;
412 pixcmapGetColor(cmap, bitval, &rval, &gval, &bval);
413 composeRGBPixel(rval, gval, bval, ppixel);
414 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,
415 bitval < num_trans ? trans[bitval] : 255);
416 ppixel++;
417 }
418 }
419 }
420 } else if (d == 2) {
421 /* Case 3: 2 bpp with cmap and associated transparency */
422 L_INFO("converting 2 bpp cmap with alpha ==> RGBA\n", __func__);
423 for (i = 0; i < h; i++) {
424 ppixel = data + i * wpl;
425 rowptr = row_pointers[i];
426 for (j = 0, index = 0; j < rowbytes; j++) {
427 byte = rowptr[j];
428 for (k = 0; k < 4 && index < w; k++, index++) {
429 bival = (byte >> 2 * (3 - k)) & 3;
430 pixcmapGetColor(cmap, bival, &rval, &gval, &bval);
431 composeRGBPixel(rval, gval, bval, ppixel);
432 /* Assume missing entries to be 255 (opaque)
433 * according to the spec:
434 * http://www.w3.org/TR/PNG/#11tRNS */
435 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,
436 bival < num_trans ? trans[bival] : 255);
437 ppixel++;
438 }
439 }
440 }
441 } else if (d == 4) {
442 /* Case 4: 4 bpp with cmap and associated transparency */
443 L_INFO("converting 4 bpp cmap with alpha ==> RGBA\n", __func__);
444 for (i = 0; i < h; i++) {
445 ppixel = data + i * wpl;
446 rowptr = row_pointers[i];
447 for (j = 0, index = 0; j < rowbytes; j++) {
448 byte = rowptr[j];
449 for (k = 0; k < 2 && index < w; k++, index++) {
450 quadval = (byte >> 4 * (1 - k)) & 0xf;
451 pixcmapGetColor(cmap, quadval, &rval, &gval, &bval);
452 composeRGBPixel(rval, gval, bval, ppixel);
453 /* Assume missing entries to be 255 (opaque) */
454 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,
455 quadval < num_trans ? trans[quadval] : 255);
456 ppixel++;
457 }
458 }
459 }
460 } else if (d == 8) {
461 /* Case 5: 8 bpp with cmap and associated transparency */
462 L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", __func__);
463 for (i = 0; i < h; i++) {
464 ppixel = data + i * wpl;
465 rowptr = row_pointers[i];
466 for (j = 0; j < w; j++) {
467 index = rowptr[j];
468 pixcmapGetColor(cmap, index, &rval, &gval, &bval);
469 composeRGBPixel(rval, gval, bval, ppixel);
470 /* Assume missing entries to be 255 (opaque) */
471 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,
472 index < num_trans ? trans[index] : 255);
473 ppixel++;
474 }
475 }
476 } else {
477 L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n",
478 __func__, d);
479 }
480 pixcmapDestroy(&cmap);
481 }
482 }
483
484 #if DEBUG_READ
485 if (cmap) {
486 for (i = 0; i < 16; i++) {
487 lept_stderr("[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]);
488 }
489 }
490 #endif /* DEBUG_READ */
491
492 /* Final adjustments for bpp = 1.
493 * + If there is no colormap, the image must be inverted because
494 * png stores black pixels as 0.
495 * + We have already handled the case of cmapped, 1 bpp pix
496 * with transparency, where the output pix is 32 bpp RGBA.
497 * If there is no transparency but the pix has a colormap,
498 * we remove the colormap, because functions operating on
499 * 1 bpp images in leptonica assume no colormap.
500 * + The colormap must be removed in such a way that the pixel
501 * values are not changed. If the values are only black and
502 * white, we return a 1 bpp image; if gray, return an 8 bpp pix;
503 * otherwise, return a 32 bpp rgb pix.
504 *
505 * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag
506 * to do the inversion, because that flag (since version 1.0.9)
507 * inverts 8 bpp grayscale as well, which we don't want to do.
508 * (It also doesn't work if there is a colormap.)
509 *
510 * Note that if the input png is a 1-bit with colormap and
511 * transparency, it has already been rendered as a 32 bpp,
512 * spp = 4 rgba pix.
513 */
514 if (pixGetDepth(pix) == 1) {
515 if (!cmap) {
516 pixInvert(pix, pix);
517 } else {
518 L_INFO("removing opaque cmap from 1 bpp\n", __func__);
519 pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
520 pixDestroy(&pix);
521 pix = pix1;
522 }
523 }
524
525 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
526 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
527 pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */
528 pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */
529
530 /* Get the text if there is any */
531 png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
532 if (num_text && text_ptr)
533 pixSetText(pix, text_ptr->text);
534
535 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
536
537 /* Final validity check on the colormap */
538 if ((cmap = pixGetColormap(pix)) != NULL) {
539 pixcmapIsValid(cmap, pix, &valid);
540 if (!valid) {
541 pixDestroy(&pix);
542 return (PIX *)ERROR_PTR("colormap is not valid", __func__, NULL);
543 }
544 }
545
546 pixSetPadBits(pix, 0);
547 return pix;
548 }
549
550
551 /*---------------------------------------------------------------------*
552 * Reading png header *
553 *---------------------------------------------------------------------*/
554 /*!
555 * \brief readHeaderPng()
556 *
557 * \param[in] filename
558 * \param[out] pw [optional]
559 * \param[out] ph [optional]
560 * \param[out] pbps [optional] bits/sample
561 * \param[out] pspp [optional] samples/pixel
562 * \param[out] piscmap [optional]
563 * \return 0 if OK, 1 on error
564 *
565 * <pre>
566 * Notes:
567 * (1) If there is a colormap, iscmap is returned as 1; else 0.
568 * (2) For gray+alpha, although the png records bps = 16, we
569 * consider this as two 8 bpp samples (gray and alpha).
570 * When a gray+alpha is read, it is converted to 32 bpp RGBA.
571 * </pre>
572 */
573 l_ok
574 readHeaderPng(const char *filename,
575 l_int32 *pw,
576 l_int32 *ph,
577 l_int32 *pbps,
578 l_int32 *pspp,
579 l_int32 *piscmap)
580 {
581 l_int32 ret;
582 FILE *fp;
583
584 if (pw) *pw = 0;
585 if (ph) *ph = 0;
586 if (pbps) *pbps = 0;
587 if (pspp) *pspp = 0;
588 if (piscmap) *piscmap = 0;
589 if (!filename)
590 return ERROR_INT("filename not defined", __func__, 1);
591 if ((fp = fopenReadStream(filename)) == NULL)
592 return ERROR_INT_1("image file not found", filename, __func__, 1);
593 ret = freadHeaderPng(fp, pw, ph, pbps, pspp, piscmap);
594 fclose(fp);
595 return ret;
596 }
597
598
599 /*!
600 * \brief freadHeaderPng()
601 *
602 * \param[in] fp file stream
603 * \param[out] pw [optional]
604 * \param[out] ph [optional]
605 * \param[out] pbps [optional] bits/sample
606 * \param[out] pspp [optional] samples/pixel
607 * \param[out] piscmap [optional]
608 * \return 0 if OK, 1 on error
609 *
610 * <pre>
611 * Notes:
612 * (1) See readHeaderPng(). We only need the first 40 bytes in the file.
613 * </pre>
614 */
615 l_ok
616 freadHeaderPng(FILE *fp,
617 l_int32 *pw,
618 l_int32 *ph,
619 l_int32 *pbps,
620 l_int32 *pspp,
621 l_int32 *piscmap)
622 {
623 l_int32 nbytes, ret;
624 l_uint8 data[40];
625
626 if (pw) *pw = 0;
627 if (ph) *ph = 0;
628 if (pbps) *pbps = 0;
629 if (pspp) *pspp = 0;
630 if (piscmap) *piscmap = 0;
631 if (!fp)
632 return ERROR_INT("stream not defined", __func__, 1);
633
634 nbytes = fnbytesInFile(fp);
635 if (nbytes < 40)
636 return ERROR_INT("file too small to be png", __func__, 1);
637 if (fread(data, 1, 40, fp) != 40)
638 return ERROR_INT("error reading data", __func__, 1);
639 ret = readHeaderMemPng(data, 40, pw, ph, pbps, pspp, piscmap);
640 return ret;
641 }
642
643
644 /*!
645 * \brief readHeaderMemPng()
646 *
647 * \param[in] data
648 * \param[in] size 40 bytes is sufficient
649 * \param[out] pw [optional]
650 * \param[out] ph [optional]
651 * \param[out] pbps [optional] bits/sample
652 * \param[out] pspp [optional] samples/pixel
653 * \param[out] piscmap [optional] input NULL to ignore
654 * \return 0 if OK, 1 on error
655 *
656 * <pre>
657 * Notes:
658 * (1) See readHeaderPng().
659 * (2) png colortypes (see png.h: PNG_COLOR_TYPE_*):
660 * 0: gray; fully transparent (with tRNS) (1 spp)
661 * 2: RGB (3 spp)
662 * 3: colormap; colormap+alpha (with tRNS) (1 spp)
663 * 4: gray + alpha (2 spp)
664 * 6: RGBA (4 spp)
665 * Note:
666 * 0 and 3 have the alpha information in a tRNS chunk
667 * 4 and 6 have separate alpha samples with each pixel.
668 * </pre>
669 */
670 l_ok
671 readHeaderMemPng(const l_uint8 *data,
672 size_t size,
673 l_int32 *pw,
674 l_int32 *ph,
675 l_int32 *pbps,
676 l_int32 *pspp,
677 l_int32 *piscmap)
678 {
679 l_uint16 twobytes;
680 l_uint16 *pshort;
681 l_int32 colortype, w, h, bps, spp;
682 l_uint32 *pword;
683
684 if (pw) *pw = 0;
685 if (ph) *ph = 0;
686 if (pbps) *pbps = 0;
687 if (pspp) *pspp = 0;
688 if (piscmap) *piscmap = 0;
689 if (!data)
690 return ERROR_INT("data not defined", __func__, 1);
691 if (size < 40)
692 return ERROR_INT("size < 40", __func__, 1);
693
694 /* Check password */
695 if (data[0] != 137 || data[1] != 80 || data[2] != 78 ||
696 data[3] != 71 || data[4] != 13 || data[5] != 10 ||
697 data[6] != 26 || data[7] != 10)
698 return ERROR_INT("not a valid png file", __func__, 1);
699
700 pword = (l_uint32 *)data;
701 pshort = (l_uint16 *)data;
702 w = convertOnLittleEnd32(pword[4]);
703 h = convertOnLittleEnd32(pword[5]);
704 if (w < 1 || h < 1)
705 return ERROR_INT("invalid w or h", __func__, 1);
706 twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/sample */
707 /* and the color type */
708 colortype = twobytes & 0xff; /* color type */
709 bps = twobytes >> 8; /* bits/sample */
710
711 /* Special case with alpha that is extracted as RGBA.
712 * Note that the cmap+alpha is also extracted as RGBA,
713 * but only if the tRNS chunk exists, which we can't tell
714 * by this simple parser.*/
715 if (colortype == 4)
716 L_INFO("gray + alpha: will extract as RGBA (spp = 4)\n", __func__);
717
718 if (colortype == 2) { /* RGB */
719 spp = 3;
720 } else if (colortype == 6) { /* RGBA */
721 spp = 4;
722 } else if (colortype == 4) { /* gray + alpha */
723 spp = 2;
724 bps = 8; /* both the gray and alpha are 8-bit samples */
725 } else { /* gray (0) or cmap (3) or cmap+alpha (3) */
726 spp = 1;
727 }
728 if (bps < 1 || bps > 16) {
729 L_ERROR("invalid bps = %d\n", __func__, bps);
730 return 1;
731 }
732 if (pw) *pw = w;
733 if (ph) *ph = h;
734 if (pbps) *pbps = bps;
735 if (pspp) *pspp = spp;
736 if (piscmap) {
737 if (colortype & 1) /* palette */
738 *piscmap = 1;
739 else
740 *piscmap = 0;
741 }
742
743 return 0;
744 }
745
746
747 /*---------------------------------------------------------------------*
748 * Reading png metadata *
749 *---------------------------------------------------------------------*/
750 /*
751 * fgetPngResolution()
752 *
753 * Input: fp (file stream opened for read)
754 * &xres, &yres (<return> resolution in ppi)
755 * Return: 0 if OK; 1 on error
756 *
757 * Notes:
758 * (1) If neither resolution field is set, this is not an error;
759 * the returned resolution values are 0 (designating 'unknown').
760 * (2) Side-effect: this rewinds the stream.
761 */
762 l_int32
763 fgetPngResolution(FILE *fp,
764 l_int32 *pxres,
765 l_int32 *pyres)
766 {
767 png_uint_32 xres, yres;
768 png_structp png_ptr;
769 png_infop info_ptr;
770
771 if (pxres) *pxres = 0;
772 if (pyres) *pyres = 0;
773 if (!fp)
774 return ERROR_INT("stream not opened", __func__, 1);
775 if (!pxres || !pyres)
776 return ERROR_INT("&xres and &yres not both defined", __func__, 1);
777
778 /* Make the two required structs */
779 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
780 (png_voidp)NULL, NULL, NULL)) == NULL)
781 return ERROR_INT("png_ptr not made", __func__, 1);
782 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
783 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
784 return ERROR_INT("info_ptr not made", __func__, 1);
785 }
786
787 /* Set up png setjmp error handling.
788 * Without this, an error calls exit. */
789 if (setjmp(png_jmpbuf(png_ptr))) {
790 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
791 return ERROR_INT("internal png error", __func__, 1);
792 }
793
794 /* Read the metadata */
795 rewind(fp);
796 png_init_io(png_ptr, fp);
797 png_read_info(png_ptr, info_ptr);
798
799 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
800 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
801 *pxres = (l_int32)((l_float32)xres / 39.37 + 0.5); /* to ppi */
802 *pyres = (l_int32)((l_float32)yres / 39.37 + 0.5);
803
804 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
805 rewind(fp);
806 return 0;
807 }
808
809
810 /*!
811 * \brief isPngInterlaced()
812 *
813 * \param[in] filename
814 * \param[out] pinterlaced 1 if interlaced png; 0 otherwise
815 * \return 0 if OK, 1 on error
816 */
817 l_ok
818 isPngInterlaced(const char *filename,
819 l_int32 *pinterlaced)
820 {
821 l_uint8 buf[32];
822 FILE *fp;
823
824 if (!pinterlaced)
825 return ERROR_INT("&interlaced not defined", __func__, 1);
826 *pinterlaced = 0;
827 if (!filename)
828 return ERROR_INT("filename not defined", __func__, 1);
829
830 if ((fp = fopenReadStream(filename)) == NULL)
831 return ERROR_INT_1("stream not opened", filename, __func__, 1);
832 if (fread(buf, 1, 32, fp) != 32) {
833 fclose(fp);
834 return ERROR_INT_1("data not read", filename, __func__, 1);
835 }
836 fclose(fp);
837
838 *pinterlaced = (buf[28] == 0) ? 0 : 1;
839 return 0;
840 }
841
842
843 /*
844 * \brief fgetPngColormapInfo()
845 *
846 * \param[in] fp file stream opened for read
847 * \param[out] pcmap optional; use NULL to skip
848 * \param[out] ptransparency optional; 1 if colormapped with
849 * transparency, 0 otherwise; use NULL to skip
850 * \return 0 if OK, 1 on error
851 *
852 * Notes:
853 * (1) The transparency information in a png is in the tRNA array,
854 * which is separate from the colormap. If this array exists
855 * and if any element is less than 255, there exists some
856 * transparency.
857 * (2) Side-effect: this rewinds the stream.
858 */
859 l_ok
860 fgetPngColormapInfo(FILE *fp,
861 PIXCMAP **pcmap,
862 l_int32 *ptransparency)
863 {
864 l_int32 i, cindex, rval, gval, bval, num_palette, num_trans;
865 png_byte bit_depth, color_type;
866 png_bytep trans;
867 png_colorp palette;
868 png_structp png_ptr;
869 png_infop info_ptr;
870
871 if (pcmap) *pcmap = NULL;
872 if (ptransparency) *ptransparency = 0;
873 if (!pcmap && !ptransparency)
874 return ERROR_INT("no output defined", __func__, 1);
875 if (!fp)
876 return ERROR_INT("stream not opened", __func__, 1);
877
878 /* Make the two required structs */
879 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
880 (png_voidp)NULL, NULL, NULL)) == NULL)
881 return ERROR_INT("png_ptr not made", __func__, 1);
882 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
883 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
884 return ERROR_INT("info_ptr not made", __func__, 1);
885 }
886
887 /* Set up png setjmp error handling.
888 * Without this, an error calls exit. */
889 if (setjmp(png_jmpbuf(png_ptr))) {
890 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
891 if (pcmap && *pcmap) pixcmapDestroy(pcmap);
892 return ERROR_INT("internal png error", __func__, 1);
893 }
894
895 /* Read the metadata and check if there is a colormap */
896 rewind(fp);
897 png_init_io(png_ptr, fp);
898 png_read_info(png_ptr, info_ptr);
899 color_type = png_get_color_type(png_ptr, info_ptr);
900 if (color_type != PNG_COLOR_TYPE_PALETTE &&
901 color_type != PNG_COLOR_MASK_PALETTE) {
902 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
903 return 0;
904 }
905
906 /* Optionally, read the colormap */
907 if (pcmap) {
908 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
909 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
910 *pcmap = pixcmapCreate(bit_depth); /* spp == 1 */
911 for (cindex = 0; cindex < num_palette; cindex++) {
912 rval = palette[cindex].red;
913 gval = palette[cindex].green;
914 bval = palette[cindex].blue;
915 pixcmapAddColor(*pcmap, rval, gval, bval);
916 }
917 }
918
919 /* Optionally, look for transparency. Note that the colormap
920 * has been initialized to fully opaque. */
921 if (ptransparency && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
922 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
923 if (trans) {
924 for (i = 0; i < num_trans; i++) {
925 if (trans[i] < 255) { /* not fully opaque */
926 *ptransparency = 1;
927 if (pcmap) pixcmapSetAlpha(*pcmap, i, trans[i]);
928 }
929 }
930 } else {
931 L_ERROR("transparency array not returned\n", __func__);
932 }
933 }
934
935 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
936 rewind(fp);
937 return 0;
938 }
939
940
941 /*---------------------------------------------------------------------*
942 * Writing png through stream *
943 *---------------------------------------------------------------------*/
944 /*!
945 * \brief pixWritePng()
946 *
947 * \param[in] filename
948 * \param[in] pix
949 * \param[in] gamma
950 * \return 0 if OK; 1 on error
951 *
952 * <pre>
953 * Notes:
954 * (1) Special version for writing png with a specified gamma.
955 * When using pixWrite(), no field is given for gamma.
956 * </pre>
957 */
958 l_ok
959 pixWritePng(const char *filename,
960 PIX *pix,
961 l_float32 gamma)
962 {
963 FILE *fp;
964
965 if (!pix)
966 return ERROR_INT("pix not defined", __func__, 1);
967 if (!filename)
968 return ERROR_INT("filename not defined", __func__, 1);
969
970 if ((fp = fopenWriteStream(filename, "wb+")) == NULL)
971 return ERROR_INT_1("stream not opened", filename, __func__, 1);
972
973 if (pixWriteStreamPng(fp, pix, gamma)) {
974 fclose(fp);
975 return ERROR_INT_1("pix not written to stream", filename, __func__, 1);
976 }
977
978 fclose(fp);
979 return 0;
980 }
981
982
983 /*!
984 * \brief pixWriteStreamPng()
985 *
986 * \param[in] fp file stream
987 * \param[in] pix
988 * \param[in] gamma use 0.0 if gamma is not defined
989 * \return 0 if OK; 1 on error
990 *
991 * <pre>
992 * Notes:
993 * (1) If called from pixWriteStream(), the stream is positioned
994 * at the beginning of the file.
995 * (2) To do sequential writes of png format images to a stream,
996 * use pixWriteStreamPng() directly.
997 * (3) gamma is an optional png chunk. If no gamma value is to be
998 * placed into the file, use gamma = 0.0. Otherwise, if
999 * gamma > 0.0, its value is written into the header.
1000 * (4) The use of gamma in png is highly problematic. For an illuminating
1001 * discussion, see: http://hsivonen.iki.fi/png-gamma/
1002 * (5) What is the effect/meaning of gamma in the png file? This
1003 * gamma, which we can call the 'source' gamma, is the
1004 * inverse of the gamma that was used in enhance.c to brighten
1005 * or darken images. The 'source' gamma is supposed to indicate
1006 * the intensity mapping that was done at the time the
1007 * image was captured. Display programs typically apply a
1008 * 'display' gamma of 2.2 to the output, which is intended
1009 * to linearize the intensity based on the response of
1010 * thermionic tubes (CRTs). Flat panel LCDs have typically
1011 * been designed to give a similar response as CRTs (call it
1012 * "backward compatibility"). The 'display' gamma is
1013 * in some sense the inverse of the 'source' gamma.
1014 * jpeg encoders attached to scanners and cameras will lighten
1015 * the pixels, applying a gamma corresponding to approximately
1016 * a square-root relation of output vs input:
1017 * output = input^(gamma)
1018 * where gamma is often set near 0.4545 (1/gamma is 2.2).
1019 * This is stored in the image file. Then if the display
1020 * program reads the gamma, it will apply a display gamma,
1021 * typically about 2.2; the product is 1.0, and the
1022 * display program produces a linear output. This works because
1023 * the dark colors were appropriately boosted by the scanner,
1024 * as described by the 'source' gamma, so they should not
1025 * be further boosted by the display program.
1026 * (6) As an example, with xv and display, if no gamma is stored,
1027 * the program acts as if gamma were 0.4545, multiplies this by 2.2,
1028 * and does a linear rendering. Taking this as a baseline
1029 * brightness, if the stored gamma is:
1030 * > 0.4545, the image is rendered lighter than baseline
1031 * < 0.4545, the image is rendered darker than baseline
1032 * In contrast, gqview seems to ignore the gamma chunk in png.
1033 * (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16
1034 * and 32. However, it is possible, and in some cases desirable,
1035 * to write out a png file using an rgb pix that has 24 bpp.
1036 * For example, the open source xpdf SplashBitmap class generates
1037 * 24 bpp rgb images. Consequently, we enable writing 24 bpp pix
1038 * without converting it to 32 bpp first. Caution: do not call
1039 * pixSetPadBits(), because the alignment is wrong and you may
1040 * erase part of the last pixel on each line.
1041 * (8) If the pix has a colormap, it is written to file. In most
1042 * situations, the alpha component is 255 for each colormap entry,
1043 * which is opaque and indicates that it should be ignored.
1044 * However, if any alpha component is not 255, it is assumed that
1045 * the alpha values are valid, and they are written to the png
1046 * file in a tRNS segment. On readback, the tRNS segment is
1047 * identified, and the colormapped image with alpha is converted
1048 * to a 4 spp rgba image.
1049 * </pre>
1050 */
1051 l_ok
1052 pixWriteStreamPng(FILE *fp,
1053 PIX *pix,
1054 l_float32 gamma)
1055 {
1056 char commentstring[] = "Comment";
1057 l_int32 i, j, k, wpl, d, spp, compval, valid;
1058 l_int32 cmflag, opaque, max_trans, ncolors;
1059 l_int32 *rmap, *gmap, *bmap, *amap;
1060 l_uint32 *data, *ppixel;
1061 png_byte bit_depth, color_type;
1062 png_byte alpha[256];
1063 png_uint_32 w, h;
1064 png_uint_32 xres, yres;
1065 png_bytep *row_pointers;
1066 png_bytep rowbuffer;
1067 png_structp png_ptr;
1068 png_infop info_ptr;
1069 png_colorp palette;
1070 PIX *pix1;
1071 PIXCMAP *cmap;
1072 char *text;
1073
1074 if (!fp)
1075 return ERROR_INT("stream not open", __func__, 1);
1076 if (!pix)
1077 return ERROR_INT("pix not defined", __func__, 1);
1078
1079 w = pixGetWidth(pix);
1080 h = pixGetHeight(pix);
1081 d = pixGetDepth(pix);
1082 spp = pixGetSpp(pix);
1083
1084 /* A cmap validity check should prevent low-level colormap errors. */
1085 if ((cmap = pixGetColormap(pix))) {
1086 cmflag = 1;
1087 pixcmapIsValid(cmap, pix, &valid);
1088 if (!valid)
1089 return ERROR_INT("colormap is not valid", __func__, 1);
1090 } else {
1091 cmflag = 0;
1092 }
1093
1094 /* Do not set pad bits for d = 24 ! */
1095 if (d != 24) pixSetPadBits(pix, 0);
1096
1097 /* Set the color type and bit depth. */
1098 if (d == 32 && spp == 4) {
1099 bit_depth = 8;
1100 color_type = PNG_COLOR_TYPE_RGBA; /* 6 */
1101 cmflag = 0; /* ignore if it exists */
1102 } else if (d == 24 || d == 32) {
1103 bit_depth = 8;
1104 color_type = PNG_COLOR_TYPE_RGB; /* 2 */
1105 cmflag = 0; /* ignore if it exists */
1106 } else {
1107 bit_depth = d;
1108 color_type = PNG_COLOR_TYPE_GRAY; /* 0 */
1109 }
1110 if (cmflag)
1111 color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */
1112
1113 #if DEBUG_WRITE
1114 lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n",
1115 cmflag, bit_depth, color_type);
1116 #endif /* DEBUG_WRITE */
1117
1118 /* Allocate the 2 png data structures */
1119 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
1120 (png_voidp)NULL, NULL, NULL)) == NULL)
1121 return ERROR_INT("png_ptr not made", __func__, 1);
1122 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
1123 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
1124 return ERROR_INT("info_ptr not made", __func__, 1);
1125 }
1126
1127 /* Set up png setjmp error handling */
1128 pix1 = NULL;
1129 row_pointers = NULL;
1130 if (setjmp(png_jmpbuf(png_ptr))) {
1131 png_destroy_write_struct(&png_ptr, &info_ptr);
1132 LEPT_FREE(row_pointers);
1133 pixDestroy(&pix1);
1134 return ERROR_INT("internal png error", __func__, 1);
1135 }
1136
1137 png_init_io(png_ptr, fp);
1138
1139 /* With best zlib compression (9), get between 1 and 10% improvement
1140 * over default (6), but the compression is 3 to 10 times slower.
1141 * Use the zlib default (6) as our default compression unless
1142 * pix->special falls in the range [10 ... 19]; then subtract 10
1143 * to get the compression value. */
1144 compval = Z_DEFAULT_COMPRESSION;
1145 if (pix->special >= 10 && pix->special < 20)
1146 compval = pix->special - 10;
1147 png_set_compression_level(png_ptr, compval);
1148
1149 png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type,
1150 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
1151 PNG_FILTER_TYPE_BASE);
1152
1153 /* Store resolution in ppm, if known */
1154 xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);
1155 yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);
1156 if ((xres == 0) || (yres == 0))
1157 png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN);
1158 else
1159 png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER);
1160
1161 if (cmflag) {
1162 /* Make and save the palette */
1163 ncolors = pixcmapGetCount(cmap);
1164 palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color));
1165 pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap);
1166 for (i = 0; i < ncolors; i++) {
1167 palette[i].red = (png_byte)rmap[i];
1168 palette[i].green = (png_byte)gmap[i];
1169 palette[i].blue = (png_byte)bmap[i];
1170 alpha[i] = (png_byte)amap[i];
1171 }
1172 LEPT_FREE(rmap);
1173 LEPT_FREE(gmap);
1174 LEPT_FREE(bmap);
1175 LEPT_FREE(amap);
1176 png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors);
1177 LEPT_FREE(palette);
1178
1179 /* Add the tRNS chunk. If the non-opaque colors are listed
1180 * first in the colormap, as in the spec, we can use that in
1181 * the 4th arg of png_set_tRNS. Otherwise, transparency will
1182 * be lost for some colors. To prevent that, see the comments
1183 * in pixcmapNonOpaqueColorsInfo(). */
1184 pixcmapIsOpaque(cmap, &opaque);
1185 if (!opaque) { /* alpha channel has some transparency; assume valid */
1186 pixcmapNonOpaqueColorsInfo(cmap, NULL, &max_trans, NULL);
1187 png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha,
1188 max_trans + 1, NULL);
1189 }
1190 }
1191
1192 /* 0.4545 is treated as the default by some image
1193 * display programs (not gqview). A value > 0.4545 will
1194 * lighten an image as displayed by xv, display, etc. */
1195 if (gamma > 0.0)
1196 png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma);
1197
1198 if ((text = pixGetText(pix))) {
1199 png_text text_chunk;
1200 text_chunk.compression = PNG_TEXT_COMPRESSION_NONE;
1201 text_chunk.key = commentstring;
1202 text_chunk.text = text;
1203 text_chunk.text_length = strlen(text);
1204 #ifdef PNG_ITXT_SUPPORTED
1205 text_chunk.itxt_length = 0;
1206 text_chunk.lang = NULL;
1207 text_chunk.lang_key = NULL;
1208 #endif
1209 png_set_text(png_ptr, info_ptr, &text_chunk, 1);
1210 }
1211
1212 /* Write header and palette info */
1213 png_write_info(png_ptr, info_ptr);
1214
1215 if ((d != 32) && (d != 24)) { /* not rgb color */
1216 /* Generate a temporary pix with bytes swapped.
1217 * For writing a 1 bpp image as png:
1218 * ~ if no colormap, invert the data, because png writes
1219 * black as 0
1220 * ~ if colormapped, do not invert the data; the two RGBA
1221 * colors can have any value. */
1222 if (d == 1 && !cmap) {
1223 pix1 = pixInvert(NULL, pix);
1224 pixEndianByteSwap(pix1);
1225 } else {
1226 pix1 = pixEndianByteSwapNew(pix);
1227 }
1228 if (!pix1) {
1229 png_destroy_write_struct(&png_ptr, &info_ptr);
1230 return ERROR_INT("pix1 not made", __func__, 1);
1231 }
1232
1233 /* Make and assign array of image row pointers */
1234 row_pointers = (png_bytep *)LEPT_CALLOC(h, sizeof(png_bytep));
1235 wpl = pixGetWpl(pix1);
1236 data = pixGetData(pix1);
1237 for (i = 0; i < h; i++)
1238 row_pointers[i] = (png_bytep)(data + i * wpl);
1239 png_set_rows(png_ptr, info_ptr, row_pointers);
1240
1241 /* Transfer the data */
1242 png_write_image(png_ptr, row_pointers);
1243 png_write_end(png_ptr, info_ptr);
1244 LEPT_FREE(row_pointers);
1245 pixDestroy(&pix1);
1246 png_destroy_write_struct(&png_ptr, &info_ptr);
1247 return 0;
1248 }
1249
1250 /* For rgb and rgba, compose and write a row at a time */
1251 data = pixGetData(pix);
1252 wpl = pixGetWpl(pix);
1253 if (d == 24) { /* See note 7 above */
1254 for (i = 0; i < h; i++) {
1255 ppixel = data + i * wpl;
1256 png_write_rows(png_ptr, (png_bytepp)&ppixel, 1);
1257 }
1258 } else { /* 32 bpp rgb and rgba. If spp = 4, write the alpha channel */
1259 rowbuffer = (png_bytep)LEPT_CALLOC(w, 4);
1260 for (i = 0; i < h; i++) {
1261 ppixel = data + i * wpl;
1262 for (j = k = 0; j < w; j++) {
1263 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
1264 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
1265 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
1266 if (spp == 4)
1267 rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL);
1268 ppixel++;
1269 }
1270
1271 png_write_rows(png_ptr, &rowbuffer, 1);
1272 }
1273 LEPT_FREE(rowbuffer);
1274 }
1275
1276 png_write_end(png_ptr, info_ptr);
1277 png_destroy_write_struct(&png_ptr, &info_ptr);
1278 return 0;
1279 }
1280
1281
1282 /*!
1283 * \brief pixSetZlibCompression()
1284 *
1285 * \param[in] pix
1286 * \param[in] compval zlib compression value
1287 * \return 0 if OK, 1 on error
1288 *
1289 * <pre>
1290 * Notes:
1291 * (1) Valid zlib compression values are in the interval [0 ... 9],
1292 * where, as defined in zlib.h:
1293 * 0 Z_NO_COMPRESSION
1294 * 1 Z_BEST_SPEED (poorest compression)
1295 * 9 Z_BEST_COMPRESSION
1296 * For the default value, use either of these:
1297 * 6 Z_DEFAULT_COMPRESSION
1298 * -1 (resolves to Z_DEFAULT_COMPRESSION)
1299 * (2) If you use the defined constants in zlib.h instead of the
1300 * compression integers given above, you must include zlib.h.
1301 * </pre>
1302 */
1303 l_ok
1304 pixSetZlibCompression(PIX *pix,
1305 l_int32 compval)
1306 {
1307 if (!pix)
1308 return ERROR_INT("pix not defined", __func__, 1);
1309 if (compval < 0 || compval > 9) {
1310 L_ERROR("Invalid zlib comp val; using default\n", __func__);
1311 compval = Z_DEFAULT_COMPRESSION;
1312 }
1313 pixSetSpecial(pix, 10 + compval); /* valid range [10 ... 19] */
1314 return 0;
1315 }
1316
1317
1318 /*---------------------------------------------------------------------*
1319 * Set flag for stripping 16 bits on reading *
1320 *---------------------------------------------------------------------*/
1321 /*!
1322 * \brief l_pngSetReadStrip16To8()
1323 *
1324 * \param[in] flag 1 for stripping 16 bpp to 8 bpp on reading;
1325 * 0 for leaving 16 bpp
1326 * \return void
1327 */
1328 void
1329 l_pngSetReadStrip16To8(l_int32 flag)
1330 {
1331 var_PNG_STRIP_16_TO_8 = flag;
1332 }
1333
1334
1335 /*-------------------------------------------------------------------------*
1336 * Memio utility *
1337 * libpng read/write callback replacements for performing memory I/O *
1338 * *
1339 * Copyright (C) 2017 Milner Technologies, Inc. This content is a *
1340 * component of leptonica and is provided under the terms of the *
1341 * Leptonica license. *
1342 *-------------------------------------------------------------------------*/
1343
1344 /*! A node in a linked list of memory buffers that hold I/O content */
1345 struct MemIOData
1346 {
1347 char* m_Buffer; /*!< pointer to this node's I/O content */
1348 l_int32 m_Count; /*!< number of I/O content bytes read or written */
1349 l_int32 m_Size; /*!< allocated size of m_buffer */
1350 struct MemIOData *m_Next; /*!< pointer to the next node in the list; */
1351 /*!< zero if this is the last node */
1352 struct MemIOData *m_Last; /*!< pointer to the last node in the linked */
1353 /*!< list. The last node is where new */
1354 /*!< content is written. */
1355 };
1356 typedef struct MemIOData MEMIODATA;
1357
1358 static void memio_png_write_data(png_structp png_ptr, png_bytep data,
1359 png_size_t length);
1360 static void memio_png_flush(MEMIODATA* pthing);
1361 static void memio_png_read_data(png_structp png_ptr, png_bytep outBytes,
1362 png_size_t byteCountToRead);
1363 static void memio_free(MEMIODATA* pthing);
1364
1365 static const l_int32 MEMIO_BUFFER_SIZE = 8192; /*! buffer alloc size */
1366
1367 /*
1368 * \brief memio_png_write_data()
1369 *
1370 * \param[in] png_ptr
1371 * \param[in] data
1372 * \param[in] len size of array data in bytes
1373 *
1374 * <pre>
1375 * Notes:
1376 * (1) This is a libpng callback for writing an image into a
1377 * linked list of memory buffers.
1378 * </pre>
1379 */
1380 static void
1381 memio_png_write_data(png_structp png_ptr,
1382 png_bytep data,
1383 png_size_t len)
1384 {
1385 MEMIODATA *thing, *last;
1386 l_int32 written = 0;
1387 l_int32 remainingSpace, remainingToWrite;
1388
1389 thing = (struct MemIOData*)png_get_io_ptr(png_ptr);
1390 last = (struct MemIOData*)thing->m_Last;
1391 if (last->m_Buffer == NULL) {
1392 if (len > MEMIO_BUFFER_SIZE) {
1393 last->m_Buffer = (char *)LEPT_MALLOC(len);
1394 memcpy(last->m_Buffer, data, len);
1395 last->m_Size = last->m_Count = len;
1396 return;
1397 }
1398
1399 last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE);
1400 last->m_Size = MEMIO_BUFFER_SIZE;
1401 }
1402
1403 while (written < len) {
1404 if (last->m_Count == last->m_Size) {
1405 MEMIODATA* next = (MEMIODATA *)LEPT_MALLOC(sizeof(MEMIODATA));
1406 next->m_Next = NULL;
1407 next->m_Count = 0;
1408 next->m_Last = next;
1409
1410 last->m_Next = next;
1411 last = thing->m_Last = next;
1412
1413 last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE);
1414 last->m_Size = MEMIO_BUFFER_SIZE;
1415 }
1416
1417 remainingSpace = last->m_Size - last->m_Count;
1418 remainingToWrite = len - written;
1419 if (remainingSpace < remainingToWrite) {
1420 memcpy(last->m_Buffer + last->m_Count, data + written,
1421 remainingSpace);
1422 written += remainingSpace;
1423 last->m_Count += remainingSpace;
1424 } else {
1425 memcpy(last->m_Buffer + last->m_Count, data + written,
1426 remainingToWrite);
1427 written += remainingToWrite;
1428 last->m_Count += remainingToWrite;
1429 }
1430 }
1431 }
1432
1433
1434 /*
1435 * \brief memio_png_flush()
1436 *
1437 * \param[in] pthing
1438 *
1439 * <pre>
1440 * Notes:
1441 * (1) This consolidates write buffers into a single buffer at the
1442 * haed of the link list of buffers.
1443 * </pre>
1444 */
1445 static void
1446 memio_png_flush(MEMIODATA *pthing)
1447 {
1448 l_int32 amount = 0;
1449 l_int32 copied = 0;
1450 MEMIODATA *buffer = 0;
1451 char *data = 0;
1452
1453 /* If the data is in one buffer, give the buffer to the user. */
1454 if (pthing->m_Next == NULL) return;
1455
1456 /* Consolidate multiple buffers into one new one; add the buffer
1457 * sizes together. */
1458 amount = pthing->m_Count;
1459 buffer = pthing->m_Next;
1460 while (buffer != NULL) {
1461 amount += buffer->m_Count;
1462 buffer = buffer->m_Next;
1463 }
1464
1465 /* Copy data to a new buffer. */
1466 data = (char *)LEPT_MALLOC(amount);
1467 memcpy(data, pthing->m_Buffer, pthing->m_Count);
1468 copied = pthing->m_Count;
1469
1470 LEPT_FREE(pthing->m_Buffer);
1471 pthing->m_Buffer = NULL;
1472
1473 /* Don't delete original "thing" because we don't control it. */
1474 buffer = pthing->m_Next;
1475 pthing->m_Next = NULL;
1476 while (buffer != NULL && copied < amount) {
1477 MEMIODATA* old;
1478 memcpy(data + copied, buffer->m_Buffer, buffer->m_Count);
1479 copied += buffer->m_Count;
1480
1481 old = buffer;
1482 buffer = buffer->m_Next;
1483
1484 LEPT_FREE(old->m_Buffer);
1485 LEPT_FREE(old);
1486 }
1487
1488 pthing->m_Buffer = data;
1489 pthing->m_Count = copied;
1490 pthing->m_Size = amount;
1491 return;
1492 }
1493
1494
1495 /*
1496 * \brief memio_png_read_data()
1497 *
1498 * \param[in] png_ptr
1499 * \param[in] outBytes
1500 * \param[in] byteCountToRead
1501 *
1502 * <pre>
1503 * Notes:
1504 * (1) This is a libpng callback that reads an image from a single
1505 * memory buffer.
1506 * </pre>
1507 */
1508 static void
1509 memio_png_read_data(png_structp png_ptr,
1510 png_bytep outBytes,
1511 png_size_t byteCountToRead)
1512 {
1513 MEMIODATA *thing;
1514
1515 thing = (MEMIODATA *)png_get_io_ptr(png_ptr);
1516 if (byteCountToRead > (thing->m_Size - thing->m_Count)) {
1517 png_error(png_ptr, "read error in memio_png_read_data");
1518 }
1519 memcpy(outBytes, thing->m_Buffer + thing->m_Count, byteCountToRead);
1520 thing->m_Count += byteCountToRead;
1521 }
1522
1523
1524 /*
1525 * \brief memio_free()
1526 *
1527 * \param[in] pthing
1528 *
1529 * <pre>
1530 * Notes:
1531 * (1) This frees all the write buffers in the linked list. It must
1532 * be done before exiting the pixWriteMemPng().
1533 * </pre>
1534 */
1535 static void
1536 memio_free(MEMIODATA* pthing)
1537 {
1538 MEMIODATA *buffer, *old;
1539
1540 if (pthing->m_Buffer != NULL)
1541 LEPT_FREE(pthing->m_Buffer);
1542
1543 pthing->m_Buffer = NULL;
1544 buffer = pthing->m_Next;
1545 while (buffer != NULL) {
1546 old = buffer;
1547 buffer = buffer->m_Next;
1548
1549 if (old->m_Buffer != NULL)
1550 LEPT_FREE(old->m_Buffer);
1551 LEPT_FREE(old);
1552 }
1553 }
1554
1555
1556 /*---------------------------------------------------------------------*
1557 * Reading png from memory *
1558 *---------------------------------------------------------------------*/
1559 /*!
1560 * \brief pixReadMemPng()
1561 *
1562 * \param[in] filedata png compressed data in memory
1563 * \param[in] filesize number of bytes in data
1564 * \return pix, or NULL on error
1565 *
1566 * <pre>
1567 * Notes:
1568 * (1) See pixReastreamPng().
1569 * </pre>
1570 */
1571 PIX *
1572 pixReadMemPng(const l_uint8 *filedata,
1573 size_t filesize)
1574 {
1575 l_uint8 byte;
1576 l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
1577 l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
1578 l_uint32 png_transforms;
1579 l_uint32 *data, *line, *ppixel;
1580 int num_palette, num_text, num_trans;
1581 png_byte bit_depth, color_type, channels;
1582 png_uint_32 w, h, rowbytes, xres, yres;
1583 png_bytep rowptr, trans;
1584 png_bytep *row_pointers;
1585 png_structp png_ptr;
1586 png_infop info_ptr, end_info;
1587 png_colorp palette;
1588 png_textp text_ptr; /* ptr to text_chunk */
1589 MEMIODATA state;
1590 PIX *pix, *pix1;
1591 PIXCMAP *cmap;
1592
1593 if (!filedata)
1594 return (PIX *)ERROR_PTR("filedata not defined", __func__, NULL);
1595 if (filesize < 1)
1596 return (PIX *)ERROR_PTR("invalid filesize", __func__, NULL);
1597
1598 state.m_Next = 0;
1599 state.m_Count = 0;
1600 state.m_Last = &state;
1601 state.m_Buffer = (char*)filedata;
1602 state.m_Size = filesize;
1603 pix = NULL;
1604
1605 /* Allocate the 3 data structures */
1606 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
1607 (png_voidp)NULL, NULL, NULL)) == NULL)
1608 return (PIX *)ERROR_PTR("png_ptr not made", __func__, NULL);
1609
1610 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
1611 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
1612 return (PIX *)ERROR_PTR("info_ptr not made", __func__, NULL);
1613 }
1614
1615 if ((end_info = png_create_info_struct(png_ptr)) == NULL) {
1616 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
1617 return (PIX *)ERROR_PTR("end_info not made", __func__, NULL);
1618 }
1619
1620 /* Set up png setjmp error handling */
1621 if (setjmp(png_jmpbuf(png_ptr))) {
1622 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1623 return (PIX *)ERROR_PTR("internal png error", __func__, NULL);
1624 }
1625
1626 png_set_read_fn(png_ptr, &state, memio_png_read_data);
1627
1628 /* ---------------------------------------------------------- *
1629 * Set the transforms flags. Whatever happens here,
1630 * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO.
1631 * Also, do not use PNG_TRANSFORM_EXPAND, which would
1632 * expand all images with bpp < 8 to 8 bpp.
1633 * ---------------------------------------------------------- */
1634 /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */
1635 if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */
1636 png_transforms = PNG_TRANSFORM_STRIP_16;
1637 } else {
1638 png_transforms = PNG_TRANSFORM_IDENTITY;
1639 L_INFO("not stripping 16 --> 8 in png reading\n", __func__);
1640 }
1641
1642 /* Read it */
1643 png_read_png(png_ptr, info_ptr, png_transforms, NULL);
1644
1645 row_pointers = png_get_rows(png_ptr, info_ptr);
1646 w = png_get_image_width(png_ptr, info_ptr);
1647 h = png_get_image_height(png_ptr, info_ptr);
1648 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
1649 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
1650 color_type = png_get_color_type(png_ptr, info_ptr);
1651 channels = png_get_channels(png_ptr, info_ptr);
1652 spp = channels;
1653 tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0;
1654
1655 if (spp == 1) {
1656 d = bit_depth;
1657 } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */
1658 d = 4 * bit_depth;
1659 }
1660
1661 /* Remove if/when this is implemented for all bit_depths */
1662 if (spp == 3 && bit_depth != 8) {
1663 lept_stderr("Help: spp = 3 and depth = %d != 8\n!!", bit_depth);
1664 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1665 return (PIX *)ERROR_PTR("not implemented for this depth",
1666 __func__, NULL);
1667 }
1668
1669 cmap = NULL;
1670 if (color_type == PNG_COLOR_TYPE_PALETTE ||
1671 color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */
1672 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
1673 cmap = pixcmapCreate(d); /* spp == 1 */
1674 for (cindex = 0; cindex < num_palette; cindex++) {
1675 rval = palette[cindex].red;
1676 gval = palette[cindex].green;
1677 bval = palette[cindex].blue;
1678 pixcmapAddColor(cmap, rval, gval, bval);
1679 }
1680 }
1681
1682 if ((pix = pixCreate(w, h, d)) == NULL) {
1683 pixcmapDestroy(&cmap);
1684 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1685 pixcmapDestroy(&cmap);
1686 return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
1687 }
1688 pixSetInputFormat(pix, IFF_PNG);
1689 wpl = pixGetWpl(pix);
1690 data = pixGetData(pix);
1691 pixSetSpp(pix, spp);
1692 if (pixSetColormap(pix, cmap)) {
1693 pixDestroy(&pix);
1694 return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL);
1695 }
1696
1697 if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */
1698 for (i = 0; i < h; i++) {
1699 line = data + i * wpl;
1700 rowptr = row_pointers[i];
1701 for (j = 0; j < rowbytes; j++) {
1702 SET_DATA_BYTE(line, j, rowptr[j]);
1703 }
1704 }
1705 } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */
1706 L_INFO("converting (gray + alpha) ==> RGBA\n", __func__);
1707 for (i = 0; i < h; i++) {
1708 ppixel = data + i * wpl;
1709 rowptr = row_pointers[i];
1710 for (j = k = 0; j < w; j++) {
1711 /* Copy gray value into r, g and b */
1712 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]);
1713 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]);
1714 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
1715 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
1716 ppixel++;
1717 }
1718 }
1719 pixSetSpp(pix, 4); /* we do not support 2 spp pix */
1720 } else if (spp == 3 || spp == 4) {
1721 for (i = 0; i < h; i++) {
1722 ppixel = data + i * wpl;
1723 rowptr = row_pointers[i];
1724 for (j = k = 0; j < w; j++) {
1725 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]);
1726 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]);
1727 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
1728 if (spp == 4)
1729 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
1730 ppixel++;
1731 }
1732 }
1733 }
1734
1735 /* Special spp == 1 cases with transparency:
1736 * (1) 8 bpp without colormap; assume full transparency
1737 * (2) 1 bpp with colormap + trans array (for alpha)
1738 * (3) 2 bpp with colormap + trans array (for alpha)
1739 * (4) 4 bpp with colormap + trans array (for alpha)
1740 * (5) 8 bpp with colormap + trans array (for alpha)
1741 * These all require converting to RGBA */
1742 if (spp == 1 && tRNS) {
1743 if (!cmap) {
1744 /* Case 1: make fully transparent RGBA image */
1745 L_INFO("transparency, 1 spp, no colormap, no transparency array: "
1746 "convention is fully transparent image\n", __func__);
1747 L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", __func__);
1748 pixDestroy(&pix);
1749 pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */
1750 pixSetSpp(pix, 4);
1751 } else {
1752 L_INFO("converting (cmap + alpha) ==> RGBA\n", __func__);
1753
1754 /* Grab the transparency array */
1755 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
1756 if (!trans) { /* invalid png file */
1757 pixDestroy(&pix);
1758 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1759 return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array",
1760 __func__, NULL);
1761 }
1762
1763 /* Save the cmap and destroy the pix */
1764 cmap = pixcmapCopy(pixGetColormap(pix));
1765 ncolors = pixcmapGetCount(cmap);
1766 pixDestroy(&pix);
1767
1768 /* Start over with 32 bit RGBA */
1769 pix = pixCreate(w, h, 32);
1770 wpl = pixGetWpl(pix);
1771 data = pixGetData(pix);
1772 pixSetSpp(pix, 4);
1773
1774 #if DEBUG_READ
1775 lept_stderr("ncolors = %d, num_trans = %d\n",
1776 ncolors, num_trans);
1777 for (i = 0; i < ncolors; i++) {
1778 pixcmapGetColor(cmap, i, &rval, &gval, &bval);
1779 if (i < num_trans) {
1780 lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n",
1781 rval, gval, bval, trans[i]);
1782 } else {
1783 lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n",
1784 rval, gval, bval);
1785 }
1786 }
1787 #endif /* DEBUG_READ */
1788
1789 /* Extract the data and convert to RGBA */
1790 if (d == 1) {
1791 /* Case 2: 1 bpp with transparency (usually) behind white */
1792 L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", __func__);
1793 if (num_trans == 1)
1794 L_INFO("num_trans = 1; second color opaque by default\n",
1795 __func__);
1796 for (i = 0; i < h; i++) {
1797 ppixel = data + i * wpl;
1798 rowptr = row_pointers[i];
1799 for (j = 0, index = 0; j < rowbytes; j++) {
1800 byte = rowptr[j];
1801 for (k = 0; k < 8 && index < w; k++, index++) {
1802 bitval = (byte >> (7 - k)) & 1;
1803 pixcmapGetColor(cmap, bitval, &rval, &gval, &bval);
1804 composeRGBPixel(rval, gval, bval, ppixel);
1805 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,
1806 bitval < num_trans ? trans[bitval] : 255);
1807 ppixel++;
1808 }
1809 }
1810 }
1811 } else if (d == 2) {
1812 /* Case 3: 2 bpp with cmap and associated transparency */
1813 L_INFO("converting 2 bpp cmap with alpha ==> RGBA\n", __func__);
1814 for (i = 0; i < h; i++) {
1815 ppixel = data + i * wpl;
1816 rowptr = row_pointers[i];
1817 for (j = 0, index = 0; j < rowbytes; j++) {
1818 byte = rowptr[j];
1819 for (k = 0; k < 4 && index < w; k++, index++) {
1820 bival = (byte >> 2 * (3 - k)) & 3;
1821 pixcmapGetColor(cmap, bival, &rval, &gval, &bval);
1822 composeRGBPixel(rval, gval, bval, ppixel);
1823 /* Assume missing entries to be 255 (opaque)
1824 * according to the spec:
1825 * http://www.w3.org/TR/PNG/#11tRNS */
1826 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,
1827 bival < num_trans ? trans[bival] : 255);
1828 ppixel++;
1829 }
1830 }
1831 }
1832 } else if (d == 4) {
1833 /* Case 4: 4 bpp with cmap and associated transparency */
1834 L_INFO("converting 4 bpp cmap with alpha ==> RGBA\n", __func__);
1835 for (i = 0; i < h; i++) {
1836 ppixel = data + i * wpl;
1837 rowptr = row_pointers[i];
1838 for (j = 0, index = 0; j < rowbytes; j++) {
1839 byte = rowptr[j];
1840 for (k = 0; k < 2 && index < w; k++, index++) {
1841 quadval = (byte >> 4 * (1 - k)) & 0xf;
1842 pixcmapGetColor(cmap, quadval, &rval, &gval, &bval);
1843 composeRGBPixel(rval, gval, bval, ppixel);
1844 /* Assume missing entries to be 255 (opaque) */
1845 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,
1846 quadval < num_trans ? trans[quadval] : 255);
1847 ppixel++;
1848 }
1849 }
1850 }
1851 } else if (d == 8) {
1852 /* Case 5: 8 bpp with cmap and associated transparency */
1853 L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", __func__);
1854 for (i = 0; i < h; i++) {
1855 ppixel = data + i * wpl;
1856 rowptr = row_pointers[i];
1857 for (j = 0; j < w; j++) {
1858 index = rowptr[j];
1859 pixcmapGetColor(cmap, index, &rval, &gval, &bval);
1860 composeRGBPixel(rval, gval, bval, ppixel);
1861 /* Assume missing entries to be 255 (opaque)
1862 * according to the spec:
1863 * http://www.w3.org/TR/PNG/#11tRNS */
1864 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,
1865 index < num_trans ? trans[index] : 255);
1866 ppixel++;
1867 }
1868 }
1869 } else {
1870 L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n",
1871 __func__, d);
1872 }
1873 pixcmapDestroy(&cmap);
1874 }
1875 }
1876
1877 #if DEBUG_READ
1878 if (cmap) {
1879 for (i = 0; i < 16; i++) {
1880 lept_stderr("[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]);
1881 }
1882 }
1883 #endif /* DEBUG_READ */
1884
1885 /* Final adjustments for bpp = 1.
1886 * + If there is no colormap, the image must be inverted because
1887 * png stores black pixels as 0.
1888 * + We have already handled the case of cmapped, 1 bpp pix
1889 * with transparency, where the output pix is 32 bpp RGBA.
1890 * If there is no transparency but the pix has a colormap,
1891 * we remove the colormap, because functions operating on
1892 * 1 bpp images in leptonica assume no colormap.
1893 * + The colormap must be removed in such a way that the pixel
1894 * values are not changed. If the values are only black and
1895 * white, we return a 1 bpp image; if gray, return an 8 bpp pix;
1896 * otherwise, return a 32 bpp rgb pix.
1897 *
1898 * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag
1899 * to do the inversion, because that flag (since version 1.0.9)
1900 * inverts 8 bpp grayscale as well, which we don't want to do.
1901 * (It also doesn't work if there is a colormap.)
1902 *
1903 * Note that if the input png is a 1-bit with colormap and
1904 * transparency, it has already been rendered as a 32 bpp,
1905 * spp = 4 rgba pix.
1906 */
1907 if (pixGetDepth(pix) == 1) {
1908 if (!cmap) {
1909 pixInvert(pix, pix);
1910 } else {
1911 pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
1912 pixDestroy(&pix);
1913 pix = pix1;
1914 }
1915 }
1916
1917 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
1918 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
1919 pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */
1920 pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */
1921
1922 /* Get the text if there is any */
1923 png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
1924 if (num_text && text_ptr)
1925 pixSetText(pix, text_ptr->text);
1926
1927 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1928
1929 /* Final validity check on the colormap */
1930 if ((cmap = pixGetColormap(pix)) != NULL) {
1931 pixcmapIsValid(cmap, pix, &valid);
1932 if (!valid) {
1933 pixDestroy(&pix);
1934 return (PIX *)ERROR_PTR("colormap is not valid", __func__, NULL);
1935 }
1936 }
1937
1938 pixSetPadBits(pix, 0);
1939 return pix;
1940 }
1941
1942
1943 /*---------------------------------------------------------------------*
1944 * Writing png to memory *
1945 *---------------------------------------------------------------------*/
1946 /*!
1947 * \brief pixWriteMemPng()
1948 *
1949 * \param[out] pfiledata png encoded data of pix
1950 * \param[out] pfilesize size of png encoded data
1951 * \param[in] pix
1952 * \param[in] gamma use 0.0 if gamma is not defined
1953 * \return 0 if OK; 1 on error
1954 *
1955 * <pre>
1956 * Notes:
1957 * (1) See pixWriteStreamPng()
1958 * </pre>
1959 */
1960 l_ok
1961 pixWriteMemPng(l_uint8 **pfiledata,
1962 size_t *pfilesize,
1963 PIX *pix,
1964 l_float32 gamma)
1965 {
1966 char commentstring[] = "Comment";
1967 l_int32 i, j, k, wpl, d, spp, cmflag, opaque, ncolors, compval, valid;
1968 l_int32 *rmap, *gmap, *bmap, *amap;
1969 l_uint32 *data, *ppixel;
1970 png_byte bit_depth, color_type;
1971 png_byte alpha[256];
1972 png_uint_32 w, h, xres, yres;
1973 png_bytep rowbuffer;
1974 png_structp png_ptr;
1975 png_infop info_ptr;
1976 png_colorp palette;
1977 PIX *pix1;
1978 PIXCMAP *cmap;
1979 char *text;
1980 MEMIODATA state;
1981
1982 if (pfiledata) *pfiledata = NULL;
1983 if (pfilesize) *pfilesize = 0;
1984 if (!pfiledata)
1985 return ERROR_INT("&filedata not defined", __func__, 1);
1986 if (!pfilesize)
1987 return ERROR_INT("&filesize not defined", __func__, 1);
1988 if (!pix)
1989 return ERROR_INT("pix not defined", __func__, 1);
1990
1991 state.m_Buffer = 0;
1992 state.m_Size = 0;
1993 state.m_Next = 0;
1994 state.m_Count = 0;
1995 state.m_Last = &state;
1996
1997 w = pixGetWidth(pix);
1998 h = pixGetHeight(pix);
1999 d = pixGetDepth(pix);
2000 spp = pixGetSpp(pix);
2001
2002 /* A cmap validity check should prevent low-level colormap errors. */
2003 if ((cmap = pixGetColormap(pix))) {
2004 cmflag = 1;
2005 pixcmapIsValid(cmap, pix, &valid);
2006 if (!valid)
2007 return ERROR_INT("colormap is not valid", __func__, 1);
2008 } else {
2009 cmflag = 0;
2010 }
2011
2012 /* Do not set pad bits for d = 24 ! */
2013 if (d != 24) pixSetPadBits(pix, 0);
2014
2015 /* Set the color type and bit depth. */
2016 if (d == 32 && spp == 4) {
2017 bit_depth = 8;
2018 color_type = PNG_COLOR_TYPE_RGBA; /* 6 */
2019 cmflag = 0; /* ignore if it exists */
2020 } else if (d == 24 || d == 32) {
2021 bit_depth = 8;
2022 color_type = PNG_COLOR_TYPE_RGB; /* 2 */
2023 cmflag = 0; /* ignore if it exists */
2024 } else {
2025 bit_depth = d;
2026 color_type = PNG_COLOR_TYPE_GRAY; /* 0 */
2027 }
2028 if (cmflag)
2029 color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */
2030
2031 #if DEBUG_WRITE
2032 lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n",
2033 cmflag, bit_depth, color_type);
2034 #endif /* DEBUG_WRITE */
2035
2036 /* Allocate the 2 data structures */
2037 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
2038 (png_voidp)NULL, NULL, NULL)) == NULL)
2039 return ERROR_INT("png_ptr not made", __func__, 1);
2040
2041 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
2042 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
2043 return ERROR_INT("info_ptr not made", __func__, 1);
2044 }
2045
2046 /* Set up png setjmp error handling */
2047 pix1 = NULL;
2048 if (setjmp(png_jmpbuf(png_ptr))) {
2049 png_destroy_write_struct(&png_ptr, &info_ptr);
2050 pixDestroy(&pix1);
2051 return ERROR_INT("internal png error", __func__, 1);
2052 }
2053
2054 png_set_write_fn(png_ptr, &state, memio_png_write_data,
2055 (png_flush_ptr)NULL);
2056
2057 /* With best zlib compression (9), get between 1 and 10% improvement
2058 * over default (6), but the compression is 3 to 10 times slower.
2059 * Use the zlib default (6) as our default compression unless
2060 * pix->special falls in the range [10 ... 19]; then subtract 10
2061 * to get the compression value. */
2062 compval = Z_DEFAULT_COMPRESSION;
2063 if (pix->special >= 10 && pix->special < 20)
2064 compval = pix->special - 10;
2065 png_set_compression_level(png_ptr, compval);
2066
2067 png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type,
2068 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
2069 PNG_FILTER_TYPE_BASE);
2070
2071 /* Store resolution in ppm, if known */
2072 xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);
2073 yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);
2074 if ((xres == 0) || (yres == 0))
2075 png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN);
2076 else
2077 png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER);
2078
2079 if (cmflag) {
2080 /* Make and save the palette */
2081 ncolors = pixcmapGetCount(cmap);
2082 palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color));
2083 pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap);
2084 for (i = 0; i < ncolors; i++) {
2085 palette[i].red = (png_byte)rmap[i];
2086 palette[i].green = (png_byte)gmap[i];
2087 palette[i].blue = (png_byte)bmap[i];
2088 alpha[i] = (png_byte)amap[i];
2089 }
2090 LEPT_FREE(rmap);
2091 LEPT_FREE(gmap);
2092 LEPT_FREE(bmap);
2093 LEPT_FREE(amap);
2094 png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors);
2095 LEPT_FREE(palette);
2096
2097 pixcmapIsOpaque(cmap, &opaque);
2098 if (!opaque) /* alpha channel has some transparency; assume valid */
2099 png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha,
2100 (int)ncolors, NULL);
2101 }
2102
2103 /* 0.4545 is treated as the default by some image
2104 * display programs (not gqview). A value > 0.4545 will
2105 * lighten an image as displayed by xv, display, etc. */
2106 if (gamma > 0.0)
2107 png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma);
2108
2109 if ((text = pixGetText(pix))) {
2110 png_text text_chunk;
2111 text_chunk.compression = PNG_TEXT_COMPRESSION_NONE;
2112 text_chunk.key = commentstring;
2113 text_chunk.text = text;
2114 text_chunk.text_length = strlen(text);
2115 #ifdef PNG_ITXT_SUPPORTED
2116 text_chunk.itxt_length = 0;
2117 text_chunk.lang = NULL;
2118 text_chunk.lang_key = NULL;
2119 #endif
2120 png_set_text(png_ptr, info_ptr, &text_chunk, 1);
2121 }
2122
2123 /* Write header and palette info */
2124 png_write_info(png_ptr, info_ptr);
2125
2126 if ((d != 32) && (d != 24)) { /* not rgb color */
2127 /* Generate a temporary pix with bytes swapped.
2128 * For writing a 1 bpp image as png:
2129 * ~ if no colormap, invert the data, because png writes
2130 * black as 0
2131 * ~ if colormapped, do not invert the data; the two RGBA
2132 * colors can have any value. */
2133 if (d == 1 && !cmap) {
2134 pix1 = pixInvert(NULL, pix);
2135 pixEndianByteSwap(pix1);
2136 } else {
2137 pix1 = pixEndianByteSwapNew(pix);
2138 }
2139 if (!pix1) {
2140 png_destroy_write_struct(&png_ptr, &info_ptr);
2141 memio_free(&state);
2142 return ERROR_INT("pix1 not made", __func__, 1);
2143 }
2144
2145 /* Transfer the data */
2146 wpl = pixGetWpl(pix1);
2147 data = pixGetData(pix1);
2148 for (i = 0; i < h; i++)
2149 png_write_row(png_ptr, (png_bytep)(data + i * wpl));
2150 png_write_end(png_ptr, info_ptr);
2151
2152 pixDestroy(&pix1);
2153 png_destroy_write_struct(&png_ptr, &info_ptr);
2154 memio_png_flush(&state);
2155 *pfiledata = (l_uint8 *)state.m_Buffer;
2156 state.m_Buffer = 0;
2157 *pfilesize = state.m_Count;
2158 memio_free(&state);
2159 return 0;
2160 }
2161
2162 /* For rgb and rgba, compose and write a row at a time */
2163 data = pixGetData(pix);
2164 wpl = pixGetWpl(pix);
2165 if (d == 24) { /* See note 7 in pixWriteStreamPng() */
2166 for (i = 0; i < h; i++) {
2167 ppixel = data + i * wpl;
2168 png_write_rows(png_ptr, (png_bytepp)&ppixel, 1);
2169 }
2170 } else { /* 32 bpp rgb and rgba. If spp = 4, write the alpha channel */
2171 rowbuffer = (png_bytep)LEPT_CALLOC(w, 4);
2172 for (i = 0; i < h; i++) {
2173 ppixel = data + i * wpl;
2174 for (j = k = 0; j < w; j++) {
2175 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
2176 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
2177 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
2178 if (spp == 4)
2179 rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL);
2180 ppixel++;
2181 }
2182
2183 png_write_rows(png_ptr, &rowbuffer, 1);
2184 }
2185 LEPT_FREE(rowbuffer);
2186 }
2187 png_write_end(png_ptr, info_ptr);
2188
2189 png_destroy_write_struct(&png_ptr, &info_ptr);
2190 memio_png_flush(&state);
2191 *pfiledata = (l_uint8 *)state.m_Buffer;
2192 state.m_Buffer = 0;
2193 *pfilesize = state.m_Count;
2194 memio_free(&state);
2195 return 0;
2196 }
2197
2198 /* --------------------------------------------*/
2199 #endif /* HAVE_LIBPNG */
2200 /* --------------------------------------------*/