Leptonica 1.82.0
Image processing and image analysis suite
pngio.c
Go to the documentation of this file.
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
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
127/* --------------------------------------------*/
128#if HAVE_LIBPNG /* defined in environ.h */
129/* --------------------------------------------*/
130
131#include "png.h"
132
133#if HAVE_LIBZ
134#include "zlib.h"
135#else
136#define Z_DEFAULT_COMPRESSION (-1)
137#endif /* HAVE_LIBZ */
138
139/* ------------------ Set default for read option -------------------- */
140 /* Strip 16 bpp --> 8 bpp on reading png; default is for stripping.
141 * If you don't strip, you can't read the gray-alpha spp = 2 images. */
142static l_int32 var_PNG_STRIP_16_TO_8 = 1;
143
144#ifndef NO_CONSOLE_IO
145#define DEBUG_READ 0
146#define DEBUG_WRITE 0
147#endif /* ~NO_CONSOLE_IO */
148
149
150/*---------------------------------------------------------------------*
151 * Reading png through stream *
152 *---------------------------------------------------------------------*/
186PIX *
188{
189l_uint8 byte;
190l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
191l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
192l_uint32 png_transforms;
193l_uint32 *data, *line, *ppixel;
194int num_palette, num_text, num_trans;
195png_byte bit_depth, color_type, channels;
196png_uint_32 w, h, rowbytes, xres, yres;
197png_bytep rowptr, trans;
198png_bytep *row_pointers;
199png_structp png_ptr;
200png_infop info_ptr, end_info;
201png_colorp palette;
202png_textp text_ptr; /* ptr to text_chunk */
203PIX *pix, *pix1;
204PIXCMAP *cmap;
205
206 PROCNAME("pixReadStreamPng");
207
208 if (!fp)
209 return (PIX *)ERROR_PTR("fp not defined", procName, NULL);
210 pix = NULL;
211
212 /* Allocate the 3 data structures */
213 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
214 (png_voidp)NULL, NULL, NULL)) == NULL)
215 return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL);
216
217 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
218 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
219 return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL);
220 }
221
222 if ((end_info = png_create_info_struct(png_ptr)) == NULL) {
223 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
224 return (PIX *)ERROR_PTR("end_info not made", procName, NULL);
225 }
226
227 /* Set up png setjmp error handling */
228 if (setjmp(png_jmpbuf(png_ptr))) {
229 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
230 return (PIX *)ERROR_PTR("internal png error", procName, NULL);
231 }
232
233 png_init_io(png_ptr, fp);
234
235 /* ---------------------------------------------------------- *
236 * - Set the transforms flags. Whatever happens here,
237 * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO.
238 * - Do not use PNG_TRANSFORM_EXPAND, which would
239 * expand all images with bpp < 8 to 8 bpp.
240 * - Strip 16 --> 8 if reading 16-bit gray+alpha
241 * ---------------------------------------------------------- */
242 /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */
243 if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */
244 png_transforms = PNG_TRANSFORM_STRIP_16;
245 } else {
246 png_transforms = PNG_TRANSFORM_IDENTITY;
247 L_INFO("not stripping 16 --> 8 in png reading\n", procName);
248 }
249
250 /* Read it */
251 png_read_png(png_ptr, info_ptr, png_transforms, NULL);
252
253 row_pointers = png_get_rows(png_ptr, info_ptr);
254 w = png_get_image_width(png_ptr, info_ptr);
255 h = png_get_image_height(png_ptr, info_ptr);
256 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
257 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
258 color_type = png_get_color_type(png_ptr, info_ptr);
259 channels = png_get_channels(png_ptr, info_ptr);
260 spp = channels;
261 tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0;
262
263 if (spp == 1) {
264 d = bit_depth;
265 } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */
266 d = 4 * bit_depth;
267 }
268
269 /* Remove if/when this is implemented for all bit_depths */
270 if (spp != 1 && bit_depth != 8) {
271 L_ERROR("spp = %d and bps = %d != 8\n"
272 "turn on 16 --> 8 stripping\n", procName, spp, bit_depth);
273 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
274 return (PIX *)ERROR_PTR("not implemented for this image",
275 procName, NULL);
276 }
277
278 cmap = NULL;
279 if (color_type == PNG_COLOR_TYPE_PALETTE ||
280 color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */
281 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
282 cmap = pixcmapCreate(d); /* spp == 1 */
283 for (cindex = 0; cindex < num_palette; cindex++) {
284 rval = palette[cindex].red;
285 gval = palette[cindex].green;
286 bval = palette[cindex].blue;
287 pixcmapAddColor(cmap, rval, gval, bval);
288 }
289 }
290
291 if ((pix = pixCreate(w, h, d)) == NULL) {
292 pixcmapDestroy(&cmap);
293 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
294 return (PIX *)ERROR_PTR("pix not made", procName, NULL);
295 }
296 pixSetInputFormat(pix, IFF_PNG);
297 wpl = pixGetWpl(pix);
298 data = pixGetData(pix);
299 pixSetSpp(pix, spp);
300 if (pixSetColormap(pix, cmap)) {
301 pixDestroy(&pix);
302 return (PIX *)ERROR_PTR("invalid colormap", procName, NULL);
303 }
304
305 if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */
306 for (i = 0; i < h; i++) {
307 line = data + i * wpl;
308 rowptr = row_pointers[i];
309 for (j = 0; j < rowbytes; j++) {
310 SET_DATA_BYTE(line, j, rowptr[j]);
311 }
312 }
313 } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */
314 L_INFO("converting (gray + alpha) ==> RGBA\n", procName);
315 for (i = 0; i < h; i++) {
316 ppixel = data + i * wpl;
317 rowptr = row_pointers[i];
318 for (j = k = 0; j < w; j++) {
319 /* Copy gray value into r, g and b */
320 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]);
321 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]);
322 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
323 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
324 ppixel++;
325 }
326 }
327 pixSetSpp(pix, 4); /* we do not support 2 spp pix */
328 } else if (spp == 3 || spp == 4) {
329 for (i = 0; i < h; i++) {
330 ppixel = data + i * wpl;
331 rowptr = row_pointers[i];
332 for (j = k = 0; j < w; j++) {
333 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]);
334 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]);
335 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
336 if (spp == 3) /* set to opaque; some readers are buggy */
337 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, 255);
338 else /* spp == 4 */
339 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
340 ppixel++;
341 }
342 }
343 }
344
345 /* Special spp == 1 cases with transparency:
346 * (1) 8 bpp without colormap; assume full transparency
347 * (2) 1 bpp with colormap + trans array (for alpha)
348 * (3) 2 bpp with colormap + trans array (for alpha)
349 * (4) 4 bpp with colormap + trans array (for alpha)
350 * (5) 8 bpp with colormap + trans array (for alpha)
351 * These all require converting to RGBA */
352 if (spp == 1 && tRNS) {
353 if (!cmap) {
354 /* Case 1: make fully transparent RGBA image */
355 L_INFO("transparency, 1 spp, no colormap, no transparency array: "
356 "convention is fully transparent image\n", procName);
357 L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", procName);
358 pixDestroy(&pix);
359 pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */
360 pixSetSpp(pix, 4);
361 } else {
362 L_INFO("converting (cmap + alpha) ==> RGBA\n", procName);
363
364 /* Grab the transparency array */
365 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
366 if (!trans) { /* invalid png file */
367 pixDestroy(&pix);
368 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
369 return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array",
370 procName, NULL);
371 }
372
373 /* Save the cmap and destroy the pix */
374 cmap = pixcmapCopy(pixGetColormap(pix));
375 ncolors = pixcmapGetCount(cmap);
376 pixDestroy(&pix);
377
378 /* Start over with 32 bit RGBA */
379 pix = pixCreate(w, h, 32);
380 wpl = pixGetWpl(pix);
381 data = pixGetData(pix);
382 pixSetSpp(pix, 4);
383
384#if DEBUG_READ
385 lept_stderr("ncolors = %d, num_trans = %d\n",
386 ncolors, num_trans);
387 for (i = 0; i < ncolors; i++) {
388 pixcmapGetColor(cmap, i, &rval, &gval, &bval);
389 if (i < num_trans) {
390 lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n",
391 rval, gval, bval, trans[i]);
392 } else {
393 lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n",
394 rval, gval, bval);
395 }
396 }
397#endif /* DEBUG_READ */
398
399 /* Extract the data and convert to RGBA */
400 if (d == 1) {
401 /* Case 2: 1 bpp with transparency (usually) behind white */
402 L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", procName);
403 if (num_trans == 1)
404 L_INFO("num_trans = 1; second color opaque by default\n",
405 procName);
406 for (i = 0; i < h; i++) {
407 ppixel = data + i * wpl;
408 rowptr = row_pointers[i];
409 for (j = 0, index = 0; j < rowbytes; j++) {
410 byte = rowptr[j];
411 for (k = 0; k < 8 && index < w; k++, index++) {
412 bitval = (byte >> (7 - k)) & 1;
413 pixcmapGetColor(cmap, bitval, &rval, &gval, &bval);
414 composeRGBPixel(rval, gval, bval, ppixel);
416 bitval < num_trans ? trans[bitval] : 255);
417 ppixel++;
418 }
419 }
420 }
421 } else if (d == 2) {
422 /* Case 3: 2 bpp with cmap and associated transparency */
423 L_INFO("converting 2 bpp cmap with alpha ==> RGBA\n", procName);
424 for (i = 0; i < h; i++) {
425 ppixel = data + i * wpl;
426 rowptr = row_pointers[i];
427 for (j = 0, index = 0; j < rowbytes; j++) {
428 byte = rowptr[j];
429 for (k = 0; k < 4 && index < w; k++, index++) {
430 bival = (byte >> 2 * (3 - k)) & 3;
431 pixcmapGetColor(cmap, bival, &rval, &gval, &bval);
432 composeRGBPixel(rval, gval, bval, ppixel);
433 /* Assume missing entries to be 255 (opaque)
434 * according to the spec:
435 * http://www.w3.org/TR/PNG/#11tRNS */
437 bival < num_trans ? trans[bival] : 255);
438 ppixel++;
439 }
440 }
441 }
442 } else if (d == 4) {
443 /* Case 4: 4 bpp with cmap and associated transparency */
444 L_INFO("converting 4 bpp cmap with alpha ==> RGBA\n", procName);
445 for (i = 0; i < h; i++) {
446 ppixel = data + i * wpl;
447 rowptr = row_pointers[i];
448 for (j = 0, index = 0; j < rowbytes; j++) {
449 byte = rowptr[j];
450 for (k = 0; k < 2 && index < w; k++, index++) {
451 quadval = (byte >> 4 * (1 - k)) & 0xf;
452 pixcmapGetColor(cmap, quadval, &rval, &gval, &bval);
453 composeRGBPixel(rval, gval, bval, ppixel);
454 /* Assume missing entries to be 255 (opaque) */
456 quadval < num_trans ? trans[quadval] : 255);
457 ppixel++;
458 }
459 }
460 }
461 } else if (d == 8) {
462 /* Case 5: 8 bpp with cmap and associated transparency */
463 L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", procName);
464 for (i = 0; i < h; i++) {
465 ppixel = data + i * wpl;
466 rowptr = row_pointers[i];
467 for (j = 0; j < w; j++) {
468 index = rowptr[j];
469 pixcmapGetColor(cmap, index, &rval, &gval, &bval);
470 composeRGBPixel(rval, gval, bval, ppixel);
471 /* Assume missing entries to be 255 (opaque) */
473 index < num_trans ? trans[index] : 255);
474 ppixel++;
475 }
476 }
477 } else {
478 L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n",
479 procName, d);
480 }
481 pixcmapDestroy(&cmap);
482 }
483 }
484
485#if DEBUG_READ
486 if (cmap) {
487 for (i = 0; i < 16; i++) {
488 lept_stderr("[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]);
489 }
490 }
491#endif /* DEBUG_READ */
492
493 /* Final adjustments for bpp = 1.
494 * + If there is no colormap, the image must be inverted because
495 * png stores black pixels as 0.
496 * + We have already handled the case of cmapped, 1 bpp pix
497 * with transparency, where the output pix is 32 bpp RGBA.
498 * If there is no transparency but the pix has a colormap,
499 * we remove the colormap, because functions operating on
500 * 1 bpp images in leptonica assume no colormap.
501 * + The colormap must be removed in such a way that the pixel
502 * values are not changed. If the values are only black and
503 * white, we return a 1 bpp image; if gray, return an 8 bpp pix;
504 * otherwise, return a 32 bpp rgb pix.
505 *
506 * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag
507 * to do the inversion, because that flag (since version 1.0.9)
508 * inverts 8 bpp grayscale as well, which we don't want to do.
509 * (It also doesn't work if there is a colormap.)
510 *
511 * Note that if the input png is a 1-bit with colormap and
512 * transparency, it has already been rendered as a 32 bpp,
513 * spp = 4 rgba pix.
514 */
515 if (pixGetDepth(pix) == 1) {
516 if (!cmap) {
517 pixInvert(pix, pix);
518 } else {
519 L_INFO("removing opaque cmap from 1 bpp\n", procName);
521 pixDestroy(&pix);
522 pix = pix1;
523 }
524 }
525
526 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
527 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
528 pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */
529 pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */
530
531 /* Get the text if there is any */
532 png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
533 if (num_text && text_ptr)
534 pixSetText(pix, text_ptr->text);
535
536 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
537
538 /* Final validity check on the colormap */
539 if ((cmap = pixGetColormap(pix)) != NULL) {
540 pixcmapIsValid(cmap, pix, &valid);
541 if (!valid) {
542 pixDestroy(&pix);
543 return (PIX *)ERROR_PTR("colormap is not valid", procName, NULL);
544 }
545 }
546
547 pixSetPadBits(pix, 0);
548 return pix;
549}
550
551
552/*---------------------------------------------------------------------*
553 * Reading png header *
554 *---------------------------------------------------------------------*/
574l_ok
575readHeaderPng(const char *filename,
576 l_int32 *pw,
577 l_int32 *ph,
578 l_int32 *pbps,
579 l_int32 *pspp,
580 l_int32 *piscmap)
581{
582l_int32 ret;
583FILE *fp;
584
585 PROCNAME("readHeaderPng");
586
587 if (pw) *pw = 0;
588 if (ph) *ph = 0;
589 if (pbps) *pbps = 0;
590 if (pspp) *pspp = 0;
591 if (piscmap) *piscmap = 0;
592 if (!filename)
593 return ERROR_INT("filename not defined", procName, 1);
594 if ((fp = fopenReadStream(filename)) == NULL)
595 return ERROR_INT("image file not found", procName, 1);
596 ret = freadHeaderPng(fp, pw, ph, pbps, pspp, piscmap);
597 fclose(fp);
598 return ret;
599}
600
601
618l_ok
620 l_int32 *pw,
621 l_int32 *ph,
622 l_int32 *pbps,
623 l_int32 *pspp,
624 l_int32 *piscmap)
625{
626l_int32 nbytes, ret;
627l_uint8 data[40];
628
629 PROCNAME("freadHeaderPng");
630
631 if (pw) *pw = 0;
632 if (ph) *ph = 0;
633 if (pbps) *pbps = 0;
634 if (pspp) *pspp = 0;
635 if (piscmap) *piscmap = 0;
636 if (!fp)
637 return ERROR_INT("stream not defined", procName, 1);
638
639 nbytes = fnbytesInFile(fp);
640 if (nbytes < 40)
641 return ERROR_INT("file too small to be png", procName, 1);
642 if (fread(data, 1, 40, fp) != 40)
643 return ERROR_INT("error reading data", procName, 1);
644 ret = readHeaderMemPng(data, 40, pw, ph, pbps, pspp, piscmap);
645 return ret;
646}
647
648
675l_ok
676readHeaderMemPng(const l_uint8 *data,
677 size_t size,
678 l_int32 *pw,
679 l_int32 *ph,
680 l_int32 *pbps,
681 l_int32 *pspp,
682 l_int32 *piscmap)
683{
684l_uint16 twobytes;
685l_uint16 *pshort;
686l_int32 colortype, w, h, bps, spp;
687l_uint32 *pword;
688
689 PROCNAME("readHeaderMemPng");
690
691 if (pw) *pw = 0;
692 if (ph) *ph = 0;
693 if (pbps) *pbps = 0;
694 if (pspp) *pspp = 0;
695 if (piscmap) *piscmap = 0;
696 if (!data)
697 return ERROR_INT("data not defined", procName, 1);
698 if (size < 40)
699 return ERROR_INT("size < 40", procName, 1);
700
701 /* Check password */
702 if (data[0] != 137 || data[1] != 80 || data[2] != 78 ||
703 data[3] != 71 || data[4] != 13 || data[5] != 10 ||
704 data[6] != 26 || data[7] != 10)
705 return ERROR_INT("not a valid png file", procName, 1);
706
707 pword = (l_uint32 *)data;
708 pshort = (l_uint16 *)data;
709 w = convertOnLittleEnd32(pword[4]);
710 h = convertOnLittleEnd32(pword[5]);
711 if (w < 1 || h < 1)
712 return ERROR_INT("invalid w or h", procName, 1);
713 twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/sample */
714 /* and the color type */
715 colortype = twobytes & 0xff; /* color type */
716 bps = twobytes >> 8; /* bits/sample */
717
718 /* Special case with alpha that is extracted as RGBA.
719 * Note that the cmap+alpha is also extracted as RGBA,
720 * but only if the tRNS chunk exists, which we can't tell
721 * by this simple parser.*/
722 if (colortype == 4)
723 L_INFO("gray + alpha: will extract as RGBA (spp = 4)\n", procName);
724
725 if (colortype == 2) { /* RGB */
726 spp = 3;
727 } else if (colortype == 6) { /* RGBA */
728 spp = 4;
729 } else if (colortype == 4) { /* gray + alpha */
730 spp = 2;
731 bps = 8; /* both the gray and alpha are 8-bit samples */
732 } else { /* gray (0) or cmap (3) or cmap+alpha (3) */
733 spp = 1;
734 }
735 if (bps < 1 || bps > 16) {
736 L_ERROR("invalid bps = %d\n", procName, bps);
737 return 1;
738 }
739 if (pw) *pw = w;
740 if (ph) *ph = h;
741 if (pbps) *pbps = bps;
742 if (pspp) *pspp = spp;
743 if (piscmap) {
744 if (colortype & 1) /* palette */
745 *piscmap = 1;
746 else
747 *piscmap = 0;
748 }
749
750 return 0;
751}
752
753
754/*---------------------------------------------------------------------*
755 * Reading png metadata *
756 *---------------------------------------------------------------------*/
757/*
758 * fgetPngResolution()
759 *
760 * Input: fp (file stream opened for read)
761 * &xres, &yres (<return> resolution in ppi)
762 * Return: 0 if OK; 1 on error
763 *
764 * Notes:
765 * (1) If neither resolution field is set, this is not an error;
766 * the returned resolution values are 0 (designating 'unknown').
767 * (2) Side-effect: this rewinds the stream.
768 */
769l_int32
770fgetPngResolution(FILE *fp,
771 l_int32 *pxres,
772 l_int32 *pyres)
773{
774png_uint_32 xres, yres;
775png_structp png_ptr;
776png_infop info_ptr;
777
778 PROCNAME("fgetPngResolution");
779
780 if (pxres) *pxres = 0;
781 if (pyres) *pyres = 0;
782 if (!fp)
783 return ERROR_INT("stream not opened", procName, 1);
784 if (!pxres || !pyres)
785 return ERROR_INT("&xres and &yres not both defined", procName, 1);
786
787 /* Make the two required structs */
788 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
789 (png_voidp)NULL, NULL, NULL)) == NULL)
790 return ERROR_INT("png_ptr not made", procName, 1);
791 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
792 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
793 return ERROR_INT("info_ptr not made", procName, 1);
794 }
795
796 /* Set up png setjmp error handling.
797 * Without this, an error calls exit. */
798 if (setjmp(png_jmpbuf(png_ptr))) {
799 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
800 return ERROR_INT("internal png error", procName, 1);
801 }
802
803 /* Read the metadata */
804 rewind(fp);
805 png_init_io(png_ptr, fp);
806 png_read_info(png_ptr, info_ptr);
807
808 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
809 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
810 *pxres = (l_int32)((l_float32)xres / 39.37 + 0.5); /* to ppi */
811 *pyres = (l_int32)((l_float32)yres / 39.37 + 0.5);
812
813 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
814 rewind(fp);
815 return 0;
816}
817
818
826l_ok
827isPngInterlaced(const char *filename,
828 l_int32 *pinterlaced)
829{
830l_uint8 buf[32];
831FILE *fp;
832
833 PROCNAME("isPngInterlaced");
834
835 if (!pinterlaced)
836 return ERROR_INT("&interlaced not defined", procName, 1);
837 *pinterlaced = 0;
838 if (!filename)
839 return ERROR_INT("filename not defined", procName, 1);
840
841 if ((fp = fopenReadStream(filename)) == NULL)
842 return ERROR_INT("stream not opened", procName, 1);
843 if (fread(buf, 1, 32, fp) != 32) {
844 fclose(fp);
845 return ERROR_INT("data not read", procName, 1);
846 }
847 fclose(fp);
848
849 *pinterlaced = (buf[28] == 0) ? 0 : 1;
850 return 0;
851}
852
853
854/*
855 * \brief fgetPngColormapInfo()
856 *
857 * \param[in] fp file stream opened for read
858 * \param[out] pcmap optional; use NULL to skip
859 * \param[out] ptransparency optional; 1 if colormapped with
860 * transparency, 0 otherwise; use NULL to skip
861 * \return 0 if OK, 1 on error
862 *
863 * Notes:
864 * (1) The transparency information in a png is in the tRNA array,
865 * which is separate from the colormap. If this array exists
866 * and if any element is less than 255, there exists some
867 * transparency.
868 * (2) Side-effect: this rewinds the stream.
869 */
870l_ok
871fgetPngColormapInfo(FILE *fp,
872 PIXCMAP **pcmap,
873 l_int32 *ptransparency)
874{
875l_int32 i, cindex, rval, gval, bval, num_palette, num_trans;
876png_byte bit_depth, color_type;
877png_bytep trans;
878png_colorp palette;
879png_structp png_ptr;
880png_infop info_ptr;
881
882 PROCNAME("fgetPngColormapInfo");
883
884 if (pcmap) *pcmap = NULL;
885 if (ptransparency) *ptransparency = 0;
886 if (!pcmap && !ptransparency)
887 return ERROR_INT("no output defined", procName, 1);
888 if (!fp)
889 return ERROR_INT("stream not opened", procName, 1);
890
891 /* Make the two required structs */
892 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
893 (png_voidp)NULL, NULL, NULL)) == NULL)
894 return ERROR_INT("png_ptr not made", procName, 1);
895 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
896 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
897 return ERROR_INT("info_ptr not made", procName, 1);
898 }
899
900 /* Set up png setjmp error handling.
901 * Without this, an error calls exit. */
902 if (setjmp(png_jmpbuf(png_ptr))) {
903 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
904 if (pcmap && *pcmap) pixcmapDestroy(pcmap);
905 return ERROR_INT("internal png error", procName, 1);
906 }
907
908 /* Read the metadata and check if there is a colormap */
909 rewind(fp);
910 png_init_io(png_ptr, fp);
911 png_read_info(png_ptr, info_ptr);
912 color_type = png_get_color_type(png_ptr, info_ptr);
913 if (color_type != PNG_COLOR_TYPE_PALETTE &&
914 color_type != PNG_COLOR_MASK_PALETTE) {
915 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
916 return 0;
917 }
918
919 /* Optionally, read the colormap */
920 if (pcmap) {
921 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
922 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
923 *pcmap = pixcmapCreate(bit_depth); /* spp == 1 */
924 for (cindex = 0; cindex < num_palette; cindex++) {
925 rval = palette[cindex].red;
926 gval = palette[cindex].green;
927 bval = palette[cindex].blue;
928 pixcmapAddColor(*pcmap, rval, gval, bval);
929 }
930 }
931
932 /* Optionally, look for transparency. Note that the colormap
933 * has been initialized to fully opaque. */
934 if (ptransparency && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
935 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
936 if (trans) {
937 for (i = 0; i < num_trans; i++) {
938 if (trans[i] < 255) { /* not fully opaque */
939 *ptransparency = 1;
940 if (pcmap) pixcmapSetAlpha(*pcmap, i, trans[i]);
941 }
942 }
943 } else {
944 L_ERROR("transparency array not returned\n", procName);
945 }
946 }
947
948 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
949 rewind(fp);
950 return 0;
951}
952
953
954/*---------------------------------------------------------------------*
955 * Writing png through stream *
956 *---------------------------------------------------------------------*/
971l_ok
972pixWritePng(const char *filename,
973 PIX *pix,
974 l_float32 gamma)
975{
976FILE *fp;
977
978 PROCNAME("pixWritePng");
979
980 if (!pix)
981 return ERROR_INT("pix not defined", procName, 1);
982 if (!filename)
983 return ERROR_INT("filename not defined", procName, 1);
984
985 if ((fp = fopenWriteStream(filename, "wb+")) == NULL)
986 return ERROR_INT("stream not opened", procName, 1);
987
988 if (pixWriteStreamPng(fp, pix, gamma)) {
989 fclose(fp);
990 return ERROR_INT("pix not written to stream", procName, 1);
991 }
992
993 fclose(fp);
994 return 0;
995}
996
997
1071l_ok
1073 PIX *pix,
1074 l_float32 gamma)
1075{
1076char commentstring[] = "Comment";
1077l_int32 i, j, k, wpl, d, spp, compval, valid;
1078l_int32 cmflag, opaque, max_trans, ncolors;
1079l_int32 *rmap, *gmap, *bmap, *amap;
1080l_uint32 *data, *ppixel;
1081png_byte bit_depth, color_type;
1082png_byte alpha[256];
1083png_uint_32 w, h;
1084png_uint_32 xres, yres;
1085png_bytep *row_pointers;
1086png_bytep rowbuffer;
1087png_structp png_ptr;
1088png_infop info_ptr;
1089png_colorp palette;
1090PIX *pix1;
1091PIXCMAP *cmap;
1092char *text;
1093
1094 PROCNAME("pixWriteStreamPng");
1095
1096 if (!fp)
1097 return ERROR_INT("stream not open", procName, 1);
1098 if (!pix)
1099 return ERROR_INT("pix not defined", procName, 1);
1100
1101 w = pixGetWidth(pix);
1102 h = pixGetHeight(pix);
1103 d = pixGetDepth(pix);
1104 spp = pixGetSpp(pix);
1105
1106 /* A cmap validity check should prevent low-level colormap errors. */
1107 if ((cmap = pixGetColormap(pix))) {
1108 cmflag = 1;
1109 pixcmapIsValid(cmap, pix, &valid);
1110 if (!valid)
1111 return ERROR_INT("colormap is not valid", procName, 1);
1112 } else {
1113 cmflag = 0;
1114 }
1115 pixSetPadBits(pix, 0);
1116
1117 /* Set the color type and bit depth. */
1118 if (d == 32 && spp == 4) {
1119 bit_depth = 8;
1120 color_type = PNG_COLOR_TYPE_RGBA; /* 6 */
1121 cmflag = 0; /* ignore if it exists */
1122 } else if (d == 24 || d == 32) {
1123 bit_depth = 8;
1124 color_type = PNG_COLOR_TYPE_RGB; /* 2 */
1125 cmflag = 0; /* ignore if it exists */
1126 } else {
1127 bit_depth = d;
1128 color_type = PNG_COLOR_TYPE_GRAY; /* 0 */
1129 }
1130 if (cmflag)
1131 color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */
1132
1133#if DEBUG_WRITE
1134 lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n",
1135 cmflag, bit_depth, color_type);
1136#endif /* DEBUG_WRITE */
1137
1138 /* Allocate the 2 png data structures */
1139 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
1140 (png_voidp)NULL, NULL, NULL)) == NULL)
1141 return ERROR_INT("png_ptr not made", procName, 1);
1142 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
1143 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
1144 return ERROR_INT("info_ptr not made", procName, 1);
1145 }
1146
1147 /* Set up png setjmp error handling */
1148 pix1 = NULL;
1149 row_pointers = NULL;
1150 if (setjmp(png_jmpbuf(png_ptr))) {
1151 png_destroy_write_struct(&png_ptr, &info_ptr);
1152 LEPT_FREE(row_pointers);
1153 pixDestroy(&pix1);
1154 return ERROR_INT("internal png error", procName, 1);
1155 }
1156
1157 png_init_io(png_ptr, fp);
1158
1159 /* With best zlib compression (9), get between 1 and 10% improvement
1160 * over default (6), but the compression is 3 to 10 times slower.
1161 * Use the zlib default (6) as our default compression unless
1162 * pix->special falls in the range [10 ... 19]; then subtract 10
1163 * to get the compression value. */
1164 compval = Z_DEFAULT_COMPRESSION;
1165 if (pix->special >= 10 && pix->special < 20)
1166 compval = pix->special - 10;
1167 png_set_compression_level(png_ptr, compval);
1168
1169 png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type,
1170 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
1171 PNG_FILTER_TYPE_BASE);
1172
1173 /* Store resolution in ppm, if known */
1174 xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);
1175 yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);
1176 if ((xres == 0) || (yres == 0))
1177 png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN);
1178 else
1179 png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER);
1180
1181 if (cmflag) {
1182 /* Make and save the palette */
1183 ncolors = pixcmapGetCount(cmap);
1184 palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color));
1185 pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap);
1186 for (i = 0; i < ncolors; i++) {
1187 palette[i].red = (png_byte)rmap[i];
1188 palette[i].green = (png_byte)gmap[i];
1189 palette[i].blue = (png_byte)bmap[i];
1190 alpha[i] = (png_byte)amap[i];
1191 }
1192 LEPT_FREE(rmap);
1193 LEPT_FREE(gmap);
1194 LEPT_FREE(bmap);
1195 LEPT_FREE(amap);
1196 png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors);
1197 LEPT_FREE(palette);
1198
1199 /* Add the tRNS chunk. If the non-opaque colors are listed
1200 * first in the colormap, as in the spec, we can use that in
1201 * the 4th arg of png_set_tRNS. Otherwise, transparency will
1202 * be lost for some colors. To prevent that, see the comments
1203 * in pixcmapNonOpaqueColorsInfo(). */
1204 pixcmapIsOpaque(cmap, &opaque);
1205 if (!opaque) { /* alpha channel has some transparency; assume valid */
1206 pixcmapNonOpaqueColorsInfo(cmap, NULL, &max_trans, NULL);
1207 png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha,
1208 max_trans + 1, NULL);
1209 }
1210 }
1211
1212 /* 0.4545 is treated as the default by some image
1213 * display programs (not gqview). A value > 0.4545 will
1214 * lighten an image as displayed by xv, display, etc. */
1215 if (gamma > 0.0)
1216 png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma);
1217
1218 if ((text = pixGetText(pix))) {
1219 png_text text_chunk;
1220 text_chunk.compression = PNG_TEXT_COMPRESSION_NONE;
1221 text_chunk.key = commentstring;
1222 text_chunk.text = text;
1223 text_chunk.text_length = strlen(text);
1224#ifdef PNG_ITXT_SUPPORTED
1225 text_chunk.itxt_length = 0;
1226 text_chunk.lang = NULL;
1227 text_chunk.lang_key = NULL;
1228#endif
1229 png_set_text(png_ptr, info_ptr, &text_chunk, 1);
1230 }
1231
1232 /* Write header and palette info */
1233 png_write_info(png_ptr, info_ptr);
1234
1235 if ((d != 32) && (d != 24)) { /* not rgb color */
1236 /* Generate a temporary pix with bytes swapped.
1237 * For writing a 1 bpp image as png:
1238 * ~ if no colormap, invert the data, because png writes
1239 * black as 0
1240 * ~ if colormapped, do not invert the data; the two RGBA
1241 * colors can have any value. */
1242 if (d == 1 && !cmap) {
1243 pix1 = pixInvert(NULL, pix);
1244 pixEndianByteSwap(pix1);
1245 } else {
1246 pix1 = pixEndianByteSwapNew(pix);
1247 }
1248 if (!pix1) {
1249 png_destroy_write_struct(&png_ptr, &info_ptr);
1250 return ERROR_INT("pix1 not made", procName, 1);
1251 }
1252
1253 /* Make and assign array of image row pointers */
1254 row_pointers = (png_bytep *)LEPT_CALLOC(h, sizeof(png_bytep));
1255 wpl = pixGetWpl(pix1);
1256 data = pixGetData(pix1);
1257 for (i = 0; i < h; i++)
1258 row_pointers[i] = (png_bytep)(data + i * wpl);
1259 png_set_rows(png_ptr, info_ptr, row_pointers);
1260
1261 /* Transfer the data */
1262 png_write_image(png_ptr, row_pointers);
1263 png_write_end(png_ptr, info_ptr);
1264 LEPT_FREE(row_pointers);
1265 pixDestroy(&pix1);
1266 png_destroy_write_struct(&png_ptr, &info_ptr);
1267 return 0;
1268 }
1269
1270 /* For rgb, compose and write a row at a time */
1271 data = pixGetData(pix);
1272 wpl = pixGetWpl(pix);
1273 if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */
1274 for (i = 0; i < h; i++) {
1275 ppixel = data + i * wpl;
1276 png_write_rows(png_ptr, (png_bytepp)&ppixel, 1);
1277 }
1278 } else { /* 32 bpp rgb and rgba. Write out the alpha channel if either
1279 * the pix has 4 spp or writing it is requested anyway */
1280 rowbuffer = (png_bytep)LEPT_CALLOC(w, 4);
1281 for (i = 0; i < h; i++) {
1282 ppixel = data + i * wpl;
1283 for (j = k = 0; j < w; j++) {
1284 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
1285 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
1286 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
1287 if (spp == 4)
1288 rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL);
1289 ppixel++;
1290 }
1291
1292 png_write_rows(png_ptr, &rowbuffer, 1);
1293 }
1294 LEPT_FREE(rowbuffer);
1295 }
1296
1297 png_write_end(png_ptr, info_ptr);
1298 png_destroy_write_struct(&png_ptr, &info_ptr);
1299 return 0;
1300}
1301
1302
1324l_ok
1326 l_int32 compval)
1327{
1328 PROCNAME("pixSetZlibCompression");
1329
1330 if (!pix)
1331 return ERROR_INT("pix not defined", procName, 1);
1332 if (compval < 0 || compval > 9) {
1333 L_ERROR("Invalid zlib comp val; using default\n", procName);
1334 compval = Z_DEFAULT_COMPRESSION;
1335 }
1336 pixSetSpecial(pix, 10 + compval); /* valid range [10 ... 19] */
1337 return 0;
1338}
1339
1340
1341/*---------------------------------------------------------------------*
1342 * Set flag for stripping 16 bits on reading *
1343 *---------------------------------------------------------------------*/
1351void
1353{
1354 var_PNG_STRIP_16_TO_8 = flag;
1355}
1356
1357
1358/*-------------------------------------------------------------------------*
1359 * Memio utility *
1360 * libpng read/write callback replacements for performing memory I/O *
1361 * *
1362 * Copyright (C) 2017 Milner Technologies, Inc. This content is a *
1363 * component of leptonica and is provided under the terms of the *
1364 * Leptonica license. *
1365 *-------------------------------------------------------------------------*/
1366
1369{
1370 char* m_Buffer;
1371 l_int32 m_Count;
1372 l_int32 m_Size;
1378};
1379typedef struct MemIOData MEMIODATA;
1380
1381static void memio_png_write_data(png_structp png_ptr, png_bytep data,
1382 png_size_t length);
1383static void memio_png_flush(MEMIODATA* pthing);
1384static void memio_png_read_data(png_structp png_ptr, png_bytep outBytes,
1385 png_size_t byteCountToRead);
1386static void memio_free(MEMIODATA* pthing);
1387
1388static const l_int32 MEMIO_BUFFER_SIZE = 8192;
1390/*
1391 * \brief memio_png_write_data()
1392 *
1393 * \param[in] png_ptr
1394 * \param[in] data
1395 * \param[in] len size of array data in bytes
1396 *
1397 * <pre>
1398 * Notes:
1399 * (1) This is a libpng callback for writing an image into a
1400 * linked list of memory buffers.
1401 * </pre>
1402 */
1403static void
1404memio_png_write_data(png_structp png_ptr,
1405 png_bytep data,
1406 png_size_t len)
1407{
1408MEMIODATA *thing, *last;
1409l_int32 written = 0;
1410l_int32 remainingSpace, remainingToWrite;
1411
1412 thing = (struct MemIOData*)png_get_io_ptr(png_ptr);
1413 last = (struct MemIOData*)thing->m_Last;
1414 if (last->m_Buffer == NULL) {
1415 if (len > MEMIO_BUFFER_SIZE) {
1416 last->m_Buffer = (char *)LEPT_MALLOC(len);
1417 memcpy(last->m_Buffer, data, len);
1418 last->m_Size = last->m_Count = len;
1419 return;
1420 }
1421
1422 last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE);
1423 last->m_Size = MEMIO_BUFFER_SIZE;
1424 }
1425
1426 while (written < len) {
1427 if (last->m_Count == last->m_Size) {
1428 MEMIODATA* next = (MEMIODATA *)LEPT_MALLOC(sizeof(MEMIODATA));
1429 next->m_Next = NULL;
1430 next->m_Count = 0;
1431 next->m_Last = next;
1432
1433 last->m_Next = next;
1434 last = thing->m_Last = next;
1435
1436 last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE);
1437 last->m_Size = MEMIO_BUFFER_SIZE;
1438 }
1439
1440 remainingSpace = last->m_Size - last->m_Count;
1441 remainingToWrite = len - written;
1442 if (remainingSpace < remainingToWrite) {
1443 memcpy(last->m_Buffer + last->m_Count, data + written,
1444 remainingSpace);
1445 written += remainingSpace;
1446 last->m_Count += remainingSpace;
1447 } else {
1448 memcpy(last->m_Buffer + last->m_Count, data + written,
1449 remainingToWrite);
1450 written += remainingToWrite;
1451 last->m_Count += remainingToWrite;
1452 }
1453 }
1454}
1455
1456
1457/*
1458 * \brief memio_png_flush()
1459 *
1460 * \param[in] pthing
1461 *
1462 * <pre>
1463 * Notes:
1464 * (1) This consolidates write buffers into a single buffer at the
1465 * haed of the link list of buffers.
1466 * </pre>
1467 */
1468static void
1469memio_png_flush(MEMIODATA *pthing)
1470{
1471l_int32 amount = 0;
1472l_int32 copied = 0;
1473MEMIODATA *buffer = 0;
1474char *data = 0;
1475
1476 /* If the data is in one buffer, give the buffer to the user. */
1477 if (pthing->m_Next == NULL) return;
1478
1479 /* Consolidate multiple buffers into one new one; add the buffer
1480 * sizes together. */
1481 amount = pthing->m_Count;
1482 buffer = pthing->m_Next;
1483 while (buffer != NULL) {
1484 amount += buffer->m_Count;
1485 buffer = buffer->m_Next;
1486 }
1487
1488 /* Copy data to a new buffer. */
1489 data = (char *)LEPT_MALLOC(amount);
1490 memcpy(data, pthing->m_Buffer, pthing->m_Count);
1491 copied = pthing->m_Count;
1492
1493 LEPT_FREE(pthing->m_Buffer);
1494 pthing->m_Buffer = NULL;
1495
1496 /* Don't delete original "thing" because we don't control it. */
1497 buffer = pthing->m_Next;
1498 pthing->m_Next = NULL;
1499 while (buffer != NULL && copied < amount) {
1500 MEMIODATA* old;
1501 memcpy(data + copied, buffer->m_Buffer, buffer->m_Count);
1502 copied += buffer->m_Count;
1503
1504 old = buffer;
1505 buffer = buffer->m_Next;
1506
1507 LEPT_FREE(old->m_Buffer);
1508 LEPT_FREE(old);
1509 }
1510
1511 pthing->m_Buffer = data;
1512 pthing->m_Count = copied;
1513 pthing->m_Size = amount;
1514 return;
1515}
1516
1517
1518/*
1519 * \brief memio_png_read_data()
1520 *
1521 * \param[in] png_ptr
1522 * \param[in] outBytes
1523 * \param[in] byteCountToRead
1524 *
1525 * <pre>
1526 * Notes:
1527 * (1) This is a libpng callback that reads an image from a single
1528 * memory buffer.
1529 * </pre>
1530 */
1531static void
1532memio_png_read_data(png_structp png_ptr,
1533 png_bytep outBytes,
1534 png_size_t byteCountToRead)
1535{
1536MEMIODATA *thing;
1537
1538 thing = (MEMIODATA *)png_get_io_ptr(png_ptr);
1539 if (byteCountToRead > (thing->m_Size - thing->m_Count)) {
1540 png_error(png_ptr, "read error in memio_png_read_data");
1541 }
1542 memcpy(outBytes, thing->m_Buffer + thing->m_Count, byteCountToRead);
1543 thing->m_Count += byteCountToRead;
1544}
1545
1546
1547/*
1548 * \brief memio_free()
1549 *
1550 * \param[in] pthing
1551 *
1552 * <pre>
1553 * Notes:
1554 * (1) This frees all the write buffers in the linked list. It must
1555 * be done before exiting the pixWriteMemPng().
1556 * </pre>
1557 */
1558static void
1559memio_free(MEMIODATA* pthing)
1560{
1561MEMIODATA *buffer, *old;
1562
1563 if (pthing->m_Buffer != NULL)
1564 LEPT_FREE(pthing->m_Buffer);
1565
1566 pthing->m_Buffer = NULL;
1567 buffer = pthing->m_Next;
1568 while (buffer != NULL) {
1569 old = buffer;
1570 buffer = buffer->m_Next;
1571
1572 if (old->m_Buffer != NULL)
1573 LEPT_FREE(old->m_Buffer);
1574 LEPT_FREE(old);
1575 }
1576}
1577
1578
1579/*---------------------------------------------------------------------*
1580 * Reading png from memory *
1581 *---------------------------------------------------------------------*/
1594PIX *
1595pixReadMemPng(const l_uint8 *filedata,
1596 size_t filesize)
1597{
1598l_uint8 byte;
1599l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
1600l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
1601l_uint32 png_transforms;
1602l_uint32 *data, *line, *ppixel;
1603int num_palette, num_text, num_trans;
1604png_byte bit_depth, color_type, channels;
1605png_uint_32 w, h, rowbytes, xres, yres;
1606png_bytep rowptr, trans;
1607png_bytep *row_pointers;
1608png_structp png_ptr;
1609png_infop info_ptr, end_info;
1610png_colorp palette;
1611png_textp text_ptr; /* ptr to text_chunk */
1612MEMIODATA state;
1613PIX *pix, *pix1;
1614PIXCMAP *cmap;
1615
1616 PROCNAME("pixReadMemPng");
1617
1618 if (!filedata)
1619 return (PIX *)ERROR_PTR("filedata not defined", procName, NULL);
1620 if (filesize < 1)
1621 return (PIX *)ERROR_PTR("invalid filesize", procName, NULL);
1622
1623 state.m_Next = 0;
1624 state.m_Count = 0;
1625 state.m_Last = &state;
1626 state.m_Buffer = (char*)filedata;
1627 state.m_Size = filesize;
1628 pix = NULL;
1629
1630 /* Allocate the 3 data structures */
1631 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
1632 (png_voidp)NULL, NULL, NULL)) == NULL)
1633 return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL);
1634
1635 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
1636 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
1637 return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL);
1638 }
1639
1640 if ((end_info = png_create_info_struct(png_ptr)) == NULL) {
1641 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
1642 return (PIX *)ERROR_PTR("end_info not made", procName, NULL);
1643 }
1644
1645 /* Set up png setjmp error handling */
1646 if (setjmp(png_jmpbuf(png_ptr))) {
1647 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1648 return (PIX *)ERROR_PTR("internal png error", procName, NULL);
1649 }
1650
1651 png_set_read_fn(png_ptr, &state, memio_png_read_data);
1652
1653 /* ---------------------------------------------------------- *
1654 * Set the transforms flags. Whatever happens here,
1655 * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO.
1656 * Also, do not use PNG_TRANSFORM_EXPAND, which would
1657 * expand all images with bpp < 8 to 8 bpp.
1658 * ---------------------------------------------------------- */
1659 /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */
1660 if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */
1661 png_transforms = PNG_TRANSFORM_STRIP_16;
1662 } else {
1663 png_transforms = PNG_TRANSFORM_IDENTITY;
1664 L_INFO("not stripping 16 --> 8 in png reading\n", procName);
1665 }
1666
1667 /* Read it */
1668 png_read_png(png_ptr, info_ptr, png_transforms, NULL);
1669
1670 row_pointers = png_get_rows(png_ptr, info_ptr);
1671 w = png_get_image_width(png_ptr, info_ptr);
1672 h = png_get_image_height(png_ptr, info_ptr);
1673 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
1674 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
1675 color_type = png_get_color_type(png_ptr, info_ptr);
1676 channels = png_get_channels(png_ptr, info_ptr);
1677 spp = channels;
1678 tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0;
1679
1680 if (spp == 1) {
1681 d = bit_depth;
1682 } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */
1683 d = 4 * bit_depth;
1684 }
1685
1686 /* Remove if/when this is implemented for all bit_depths */
1687 if (spp == 3 && bit_depth != 8) {
1688 lept_stderr("Help: spp = 3 and depth = %d != 8\n!!", bit_depth);
1689 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1690 return (PIX *)ERROR_PTR("not implemented for this depth",
1691 procName, NULL);
1692 }
1693
1694 cmap = NULL;
1695 if (color_type == PNG_COLOR_TYPE_PALETTE ||
1696 color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */
1697 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
1698 cmap = pixcmapCreate(d); /* spp == 1 */
1699 for (cindex = 0; cindex < num_palette; cindex++) {
1700 rval = palette[cindex].red;
1701 gval = palette[cindex].green;
1702 bval = palette[cindex].blue;
1703 pixcmapAddColor(cmap, rval, gval, bval);
1704 }
1705 }
1706
1707 if ((pix = pixCreate(w, h, d)) == NULL) {
1708 pixcmapDestroy(&cmap);
1709 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1710 pixcmapDestroy(&cmap);
1711 return (PIX *)ERROR_PTR("pix not made", procName, NULL);
1712 }
1713 pixSetInputFormat(pix, IFF_PNG);
1714 wpl = pixGetWpl(pix);
1715 data = pixGetData(pix);
1716 pixSetSpp(pix, spp);
1717 if (pixSetColormap(pix, cmap)) {
1718 pixDestroy(&pix);
1719 return (PIX *)ERROR_PTR("invalid colormap", procName, NULL);
1720 }
1721
1722 if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */
1723 for (i = 0; i < h; i++) {
1724 line = data + i * wpl;
1725 rowptr = row_pointers[i];
1726 for (j = 0; j < rowbytes; j++) {
1727 SET_DATA_BYTE(line, j, rowptr[j]);
1728 }
1729 }
1730 } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */
1731 L_INFO("converting (gray + alpha) ==> RGBA\n", procName);
1732 for (i = 0; i < h; i++) {
1733 ppixel = data + i * wpl;
1734 rowptr = row_pointers[i];
1735 for (j = k = 0; j < w; j++) {
1736 /* Copy gray value into r, g and b */
1737 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]);
1738 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]);
1739 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
1740 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
1741 ppixel++;
1742 }
1743 }
1744 pixSetSpp(pix, 4); /* we do not support 2 spp pix */
1745 } else if (spp == 3 || spp == 4) {
1746 for (i = 0; i < h; i++) {
1747 ppixel = data + i * wpl;
1748 rowptr = row_pointers[i];
1749 for (j = k = 0; j < w; j++) {
1750 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]);
1751 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]);
1752 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
1753 if (spp == 4)
1754 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
1755 ppixel++;
1756 }
1757 }
1758 }
1759
1760 /* Special spp == 1 cases with transparency:
1761 * (1) 8 bpp without colormap; assume full transparency
1762 * (2) 1 bpp with colormap + trans array (for alpha)
1763 * (3) 2 bpp with colormap + trans array (for alpha)
1764 * (4) 4 bpp with colormap + trans array (for alpha)
1765 * (5) 8 bpp with colormap + trans array (for alpha)
1766 * These all require converting to RGBA */
1767 if (spp == 1 && tRNS) {
1768 if (!cmap) {
1769 /* Case 1: make fully transparent RGBA image */
1770 L_INFO("transparency, 1 spp, no colormap, no transparency array: "
1771 "convention is fully transparent image\n", procName);
1772 L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", procName);
1773 pixDestroy(&pix);
1774 pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */
1775 pixSetSpp(pix, 4);
1776 } else {
1777 L_INFO("converting (cmap + alpha) ==> RGBA\n", procName);
1778
1779 /* Grab the transparency array */
1780 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
1781 if (!trans) { /* invalid png file */
1782 pixDestroy(&pix);
1783 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1784 return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array",
1785 procName, NULL);
1786 }
1787
1788 /* Save the cmap and destroy the pix */
1789 cmap = pixcmapCopy(pixGetColormap(pix));
1790 ncolors = pixcmapGetCount(cmap);
1791 pixDestroy(&pix);
1792
1793 /* Start over with 32 bit RGBA */
1794 pix = pixCreate(w, h, 32);
1795 wpl = pixGetWpl(pix);
1796 data = pixGetData(pix);
1797 pixSetSpp(pix, 4);
1798
1799#if DEBUG_READ
1800 lept_stderr("ncolors = %d, num_trans = %d\n",
1801 ncolors, num_trans);
1802 for (i = 0; i < ncolors; i++) {
1803 pixcmapGetColor(cmap, i, &rval, &gval, &bval);
1804 if (i < num_trans) {
1805 lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n",
1806 rval, gval, bval, trans[i]);
1807 } else {
1808 lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n",
1809 rval, gval, bval);
1810 }
1811 }
1812#endif /* DEBUG_READ */
1813
1814 /* Extract the data and convert to RGBA */
1815 if (d == 1) {
1816 /* Case 2: 1 bpp with transparency (usually) behind white */
1817 L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", procName);
1818 if (num_trans == 1)
1819 L_INFO("num_trans = 1; second color opaque by default\n",
1820 procName);
1821 for (i = 0; i < h; i++) {
1822 ppixel = data + i * wpl;
1823 rowptr = row_pointers[i];
1824 for (j = 0, index = 0; j < rowbytes; j++) {
1825 byte = rowptr[j];
1826 for (k = 0; k < 8 && index < w; k++, index++) {
1827 bitval = (byte >> (7 - k)) & 1;
1828 pixcmapGetColor(cmap, bitval, &rval, &gval, &bval);
1829 composeRGBPixel(rval, gval, bval, ppixel);
1831 bitval < num_trans ? trans[bitval] : 255);
1832 ppixel++;
1833 }
1834 }
1835 }
1836 } else if (d == 2) {
1837 /* Case 3: 2 bpp with cmap and associated transparency */
1838 L_INFO("converting 2 bpp cmap with alpha ==> RGBA\n", procName);
1839 for (i = 0; i < h; i++) {
1840 ppixel = data + i * wpl;
1841 rowptr = row_pointers[i];
1842 for (j = 0, index = 0; j < rowbytes; j++) {
1843 byte = rowptr[j];
1844 for (k = 0; k < 4 && index < w; k++, index++) {
1845 bival = (byte >> 2 * (3 - k)) & 3;
1846 pixcmapGetColor(cmap, bival, &rval, &gval, &bval);
1847 composeRGBPixel(rval, gval, bval, ppixel);
1848 /* Assume missing entries to be 255 (opaque)
1849 * according to the spec:
1850 * http://www.w3.org/TR/PNG/#11tRNS */
1852 bival < num_trans ? trans[bival] : 255);
1853 ppixel++;
1854 }
1855 }
1856 }
1857 } else if (d == 4) {
1858 /* Case 4: 4 bpp with cmap and associated transparency */
1859 L_INFO("converting 4 bpp cmap with alpha ==> RGBA\n", procName);
1860 for (i = 0; i < h; i++) {
1861 ppixel = data + i * wpl;
1862 rowptr = row_pointers[i];
1863 for (j = 0, index = 0; j < rowbytes; j++) {
1864 byte = rowptr[j];
1865 for (k = 0; k < 2 && index < w; k++, index++) {
1866 quadval = (byte >> 4 * (1 - k)) & 0xf;
1867 pixcmapGetColor(cmap, quadval, &rval, &gval, &bval);
1868 composeRGBPixel(rval, gval, bval, ppixel);
1869 /* Assume missing entries to be 255 (opaque) */
1871 quadval < num_trans ? trans[quadval] : 255);
1872 ppixel++;
1873 }
1874 }
1875 }
1876 } else if (d == 8) {
1877 /* Case 5: 8 bpp with cmap and associated transparency */
1878 L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", procName);
1879 for (i = 0; i < h; i++) {
1880 ppixel = data + i * wpl;
1881 rowptr = row_pointers[i];
1882 for (j = 0; j < w; j++) {
1883 index = rowptr[j];
1884 pixcmapGetColor(cmap, index, &rval, &gval, &bval);
1885 composeRGBPixel(rval, gval, bval, ppixel);
1886 /* Assume missing entries to be 255 (opaque)
1887 * according to the spec:
1888 * http://www.w3.org/TR/PNG/#11tRNS */
1890 index < num_trans ? trans[index] : 255);
1891 ppixel++;
1892 }
1893 }
1894 } else {
1895 L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n",
1896 procName, d);
1897 }
1898 pixcmapDestroy(&cmap);
1899 }
1900 }
1901
1902#if DEBUG_READ
1903 if (cmap) {
1904 for (i = 0; i < 16; i++) {
1905 lept_stderr("[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]);
1906 }
1907 }
1908#endif /* DEBUG_READ */
1909
1910 /* Final adjustments for bpp = 1.
1911 * + If there is no colormap, the image must be inverted because
1912 * png stores black pixels as 0.
1913 * + We have already handled the case of cmapped, 1 bpp pix
1914 * with transparency, where the output pix is 32 bpp RGBA.
1915 * If there is no transparency but the pix has a colormap,
1916 * we remove the colormap, because functions operating on
1917 * 1 bpp images in leptonica assume no colormap.
1918 * + The colormap must be removed in such a way that the pixel
1919 * values are not changed. If the values are only black and
1920 * white, we return a 1 bpp image; if gray, return an 8 bpp pix;
1921 * otherwise, return a 32 bpp rgb pix.
1922 *
1923 * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag
1924 * to do the inversion, because that flag (since version 1.0.9)
1925 * inverts 8 bpp grayscale as well, which we don't want to do.
1926 * (It also doesn't work if there is a colormap.)
1927 *
1928 * Note that if the input png is a 1-bit with colormap and
1929 * transparency, it has already been rendered as a 32 bpp,
1930 * spp = 4 rgba pix.
1931 */
1932 if (pixGetDepth(pix) == 1) {
1933 if (!cmap) {
1934 pixInvert(pix, pix);
1935 } else {
1937 pixDestroy(&pix);
1938 pix = pix1;
1939 }
1940 }
1941
1942 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
1943 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
1944 pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */
1945 pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */
1946
1947 /* Get the text if there is any */
1948 png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
1949 if (num_text && text_ptr)
1950 pixSetText(pix, text_ptr->text);
1951
1952 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1953
1954 /* Final validity check on the colormap */
1955 if ((cmap = pixGetColormap(pix)) != NULL) {
1956 pixcmapIsValid(cmap, pix, &valid);
1957 if (!valid) {
1958 pixDestroy(&pix);
1959 return (PIX *)ERROR_PTR("colormap is not valid", procName, NULL);
1960 }
1961 }
1962
1963 pixSetPadBits(pix, 0);
1964 return pix;
1965}
1966
1967
1968/*---------------------------------------------------------------------*
1969 * Writing png to memory *
1970 *---------------------------------------------------------------------*/
1985l_ok
1986pixWriteMemPng(l_uint8 **pfiledata,
1987 size_t *pfilesize,
1988 PIX *pix,
1989 l_float32 gamma)
1990{
1991char commentstring[] = "Comment";
1992l_int32 i, j, k, wpl, d, spp, cmflag, opaque, ncolors, compval, valid;
1993l_int32 *rmap, *gmap, *bmap, *amap;
1994l_uint32 *data, *ppixel;
1995png_byte bit_depth, color_type;
1996png_byte alpha[256];
1997png_uint_32 w, h, xres, yres;
1998png_bytep rowbuffer;
1999png_structp png_ptr;
2000png_infop info_ptr;
2001png_colorp palette;
2002PIX *pix1;
2003PIXCMAP *cmap;
2004char *text;
2005MEMIODATA state;
2006
2007 PROCNAME("pixWriteMemPng");
2008
2009 if (pfiledata) *pfiledata = NULL;
2010 if (pfilesize) *pfilesize = 0;
2011 if (!pfiledata)
2012 return ERROR_INT("&filedata not defined", procName, 1);
2013 if (!pfilesize)
2014 return ERROR_INT("&filesize not defined", procName, 1);
2015 if (!pix)
2016 return ERROR_INT("pix not defined", procName, 1);
2017
2018 state.m_Buffer = 0;
2019 state.m_Size = 0;
2020 state.m_Next = 0;
2021 state.m_Count = 0;
2022 state.m_Last = &state;
2023
2024 w = pixGetWidth(pix);
2025 h = pixGetHeight(pix);
2026 d = pixGetDepth(pix);
2027 spp = pixGetSpp(pix);
2028
2029 /* A cmap validity check should prevent low-level colormap errors. */
2030 if ((cmap = pixGetColormap(pix))) {
2031 cmflag = 1;
2032 pixcmapIsValid(cmap, pix, &valid);
2033 if (!valid)
2034 return ERROR_INT("colormap is not valid", procName, 1);
2035 } else {
2036 cmflag = 0;
2037 }
2038
2039 pixSetPadBits(pix, 0);
2040
2041 /* Set the color type and bit depth. */
2042 if (d == 32 && spp == 4) {
2043 bit_depth = 8;
2044 color_type = PNG_COLOR_TYPE_RGBA; /* 6 */
2045 cmflag = 0; /* ignore if it exists */
2046 } else if (d == 24 || d == 32) {
2047 bit_depth = 8;
2048 color_type = PNG_COLOR_TYPE_RGB; /* 2 */
2049 cmflag = 0; /* ignore if it exists */
2050 } else {
2051 bit_depth = d;
2052 color_type = PNG_COLOR_TYPE_GRAY; /* 0 */
2053 }
2054 if (cmflag)
2055 color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */
2056
2057#if DEBUG_WRITE
2058 lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n",
2059 cmflag, bit_depth, color_type);
2060#endif /* DEBUG_WRITE */
2061
2062 /* Allocate the 2 data structures */
2063 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
2064 (png_voidp)NULL, NULL, NULL)) == NULL)
2065 return ERROR_INT("png_ptr not made", procName, 1);
2066
2067 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
2068 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
2069 return ERROR_INT("info_ptr not made", procName, 1);
2070 }
2071
2072 /* Set up png setjmp error handling */
2073 pix1 = NULL;
2074 if (setjmp(png_jmpbuf(png_ptr))) {
2075 png_destroy_write_struct(&png_ptr, &info_ptr);
2076 pixDestroy(&pix1);
2077 return ERROR_INT("internal png error", procName, 1);
2078 }
2079
2080 png_set_write_fn(png_ptr, &state, memio_png_write_data,
2081 (png_flush_ptr)NULL);
2082
2083 /* With best zlib compression (9), get between 1 and 10% improvement
2084 * over default (6), but the compression is 3 to 10 times slower.
2085 * Use the zlib default (6) as our default compression unless
2086 * pix->special falls in the range [10 ... 19]; then subtract 10
2087 * to get the compression value. */
2088 compval = Z_DEFAULT_COMPRESSION;
2089 if (pix->special >= 10 && pix->special < 20)
2090 compval = pix->special - 10;
2091 png_set_compression_level(png_ptr, compval);
2092
2093 png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type,
2094 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
2095 PNG_FILTER_TYPE_BASE);
2096
2097 /* Store resolution in ppm, if known */
2098 xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);
2099 yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);
2100 if ((xres == 0) || (yres == 0))
2101 png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN);
2102 else
2103 png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER);
2104
2105 if (cmflag) {
2106 /* Make and save the palette */
2107 ncolors = pixcmapGetCount(cmap);
2108 palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color));
2109 pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap);
2110 for (i = 0; i < ncolors; i++) {
2111 palette[i].red = (png_byte)rmap[i];
2112 palette[i].green = (png_byte)gmap[i];
2113 palette[i].blue = (png_byte)bmap[i];
2114 alpha[i] = (png_byte)amap[i];
2115 }
2116 LEPT_FREE(rmap);
2117 LEPT_FREE(gmap);
2118 LEPT_FREE(bmap);
2119 LEPT_FREE(amap);
2120 png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors);
2121 LEPT_FREE(palette);
2122
2123 pixcmapIsOpaque(cmap, &opaque);
2124 if (!opaque) /* alpha channel has some transparency; assume valid */
2125 png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha,
2126 (int)ncolors, NULL);
2127 }
2128
2129 /* 0.4545 is treated as the default by some image
2130 * display programs (not gqview). A value > 0.4545 will
2131 * lighten an image as displayed by xv, display, etc. */
2132 if (gamma > 0.0)
2133 png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma);
2134
2135 if ((text = pixGetText(pix))) {
2136 png_text text_chunk;
2137 text_chunk.compression = PNG_TEXT_COMPRESSION_NONE;
2138 text_chunk.key = commentstring;
2139 text_chunk.text = text;
2140 text_chunk.text_length = strlen(text);
2141#ifdef PNG_ITXT_SUPPORTED
2142 text_chunk.itxt_length = 0;
2143 text_chunk.lang = NULL;
2144 text_chunk.lang_key = NULL;
2145#endif
2146 png_set_text(png_ptr, info_ptr, &text_chunk, 1);
2147 }
2148
2149 /* Write header and palette info */
2150 png_write_info(png_ptr, info_ptr);
2151
2152 if ((d != 32) && (d != 24)) { /* not rgb color */
2153 /* Generate a temporary pix with bytes swapped.
2154 * For writing a 1 bpp image as png:
2155 * ~ if no colormap, invert the data, because png writes
2156 * black as 0
2157 * ~ if colormapped, do not invert the data; the two RGBA
2158 * colors can have any value. */
2159 if (d == 1 && !cmap) {
2160 pix1 = pixInvert(NULL, pix);
2161 pixEndianByteSwap(pix1);
2162 } else {
2163 pix1 = pixEndianByteSwapNew(pix);
2164 }
2165 if (!pix1) {
2166 png_destroy_write_struct(&png_ptr, &info_ptr);
2167 memio_free(&state);
2168 return ERROR_INT("pix1 not made", procName, 1);
2169 }
2170
2171 /* Transfer the data */
2172 wpl = pixGetWpl(pix1);
2173 data = pixGetData(pix1);
2174 for (i = 0; i < h; i++)
2175 png_write_row(png_ptr, (png_bytep)(data + i * wpl));
2176 png_write_end(png_ptr, info_ptr);
2177
2178 pixDestroy(&pix1);
2179 png_destroy_write_struct(&png_ptr, &info_ptr);
2180 memio_png_flush(&state);
2181 *pfiledata = (l_uint8 *)state.m_Buffer;
2182 state.m_Buffer = 0;
2183 *pfilesize = state.m_Count;
2184 memio_free(&state);
2185 return 0;
2186 }
2187
2188 /* For rgb, compose and write a row at a time */
2189 data = pixGetData(pix);
2190 wpl = pixGetWpl(pix);
2191 if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */
2192 for (i = 0; i < h; i++) {
2193 ppixel = data + i * wpl;
2194 png_write_rows(png_ptr, (png_bytepp)&ppixel, 1);
2195 }
2196 } else { /* 32 bpp rgb and rgba. Write out the alpha channel if either
2197 * the pix has 4 spp or writing it is requested anyway */
2198 rowbuffer = (png_bytep)LEPT_CALLOC(w, 4);
2199 for (i = 0; i < h; i++) {
2200 ppixel = data + i * wpl;
2201 for (j = k = 0; j < w; j++) {
2202 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
2203 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
2204 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
2205 if (spp == 4)
2206 rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL);
2207 ppixel++;
2208 }
2209
2210 png_write_rows(png_ptr, &rowbuffer, 1);
2211 }
2212 LEPT_FREE(rowbuffer);
2213 }
2214 png_write_end(png_ptr, info_ptr);
2215
2216 png_destroy_write_struct(&png_ptr, &info_ptr);
2217 memio_png_flush(&state);
2218 *pfiledata = (l_uint8 *)state.m_Buffer;
2219 state.m_Buffer = 0;
2220 *pfilesize = state.m_Count;
2221 memio_free(&state);
2222 return 0;
2223}
2224
2225/* --------------------------------------------*/
2226#endif /* HAVE_LIBPNG */
2227/* --------------------------------------------*/
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
l_ok pixcmapSetAlpha(PIXCMAP *cmap, l_int32 index, l_int32 aval)
pixcmapSetAlpha()
Definition: colormap.c:1007
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:279
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:708
l_ok pixcmapIsValid(const PIXCMAP *cmap, PIX *pix, l_int32 *pvalid)
pixcmapIsValid()
Definition: colormap.c:317
PIXCMAP * pixcmapCopy(const PIXCMAP *cmaps)
pixcmapCopy()
Definition: colormap.c:248
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:125
l_ok pixcmapNonOpaqueColorsInfo(PIXCMAP *cmap, l_int32 *pntrans, l_int32 *pmax_trans, l_int32 *pmin_opaque)
pixcmapNonOpaqueColorsInfo()
Definition: colormap.c:1173
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:824
l_ok pixcmapIsOpaque(PIXCMAP *cmap, l_int32 *popaque)
pixcmapIsOpaque()
Definition: colormap.c:1114
l_ok pixcmapToArrays(const PIXCMAP *cmap, l_int32 **prmap, l_int32 **pgmap, l_int32 **pbmap, l_int32 **pamap)
pixcmapToArrays()
Definition: colormap.c:2068
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
l_ok pixSetText(PIX *pix, const char *textstring)
pixSetText()
Definition: pix1.c:1536
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
char * pixGetText(PIX *pix)
pixGetText()
Definition: pix1.c:1512
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
PIX * pixEndianByteSwapNew(PIX *pixs)
pixEndianByteSwapNew()
Definition: pix2.c:3010
l_ok pixSetPadBits(PIX *pix, l_int32 val)
pixSetPadBits()
Definition: pix2.c:1382
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2751
l_ok pixEndianByteSwap(PIX *pixs)
pixEndianByteSwap()
Definition: pix2.c:3074
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
@ COLOR_BLUE
Definition: pix.h:206
@ COLOR_RED
Definition: pix.h:204
@ L_ALPHA_CHANNEL
Definition: pix.h:207
@ COLOR_GREEN
Definition: pix.h:205
@ REMOVE_CMAP_BASED_ON_SRC
Definition: pix.h:260
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
l_ok readHeaderPng(const char *filename, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap)
readHeaderPng()
Definition: pngio.c:575
l_ok isPngInterlaced(const char *filename, l_int32 *pinterlaced)
isPngInterlaced()
Definition: pngio.c:827
l_ok readHeaderMemPng(const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap)
readHeaderMemPng()
Definition: pngio.c:676
l_ok pixSetZlibCompression(PIX *pix, l_int32 compval)
pixSetZlibCompression()
Definition: pngio.c:1325
PIX * pixReadStreamPng(FILE *fp)
pixReadStreamPng()
Definition: pngio.c:187
static void memio_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
Definition: pngio.c:1404
l_ok pixWriteStreamPng(FILE *fp, PIX *pix, l_float32 gamma)
pixWriteStreamPng()
Definition: pngio.c:1072
l_ok freadHeaderPng(FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap)
freadHeaderPng()
Definition: pngio.c:619
PIX * pixReadMemPng(const l_uint8 *filedata, size_t filesize)
pixReadMemPng()
Definition: pngio.c:1595
void l_pngSetReadStrip16To8(l_int32 flag)
l_pngSetReadStrip16To8()
Definition: pngio.c:1352
l_ok pixWriteMemPng(l_uint8 **pfiledata, size_t *pfilesize, PIX *pix, l_float32 gamma)
pixWriteMemPng()
Definition: pngio.c:1986
l_ok pixWritePng(const char *filename, PIX *pix, l_float32 gamma)
pixWritePng()
Definition: pngio.c:972
struct MemIOData * m_Next
Definition: pngio.c:1373
l_int32 m_Size
Definition: pngio.c:1372
char * m_Buffer
Definition: pngio.c:1370
l_int32 m_Count
Definition: pngio.c:1371
struct MemIOData * m_Last
Definition: pngio.c:1375
void * array
Definition: pix.h:161
Definition: pix.h:139
char * text
Definition: pix.h:152
l_int32 special
Definition: pix.h:151
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
FILE * fopenWriteStream(const char *filename, const char *modestring)
fopenWriteStream()
Definition: utils2.c:1975
size_t fnbytesInFile(FILE *fp)
fnbytesInFile()
Definition: utils2.c:1635
FILE * fopenReadStream(const char *filename)
fopenReadStream()
Definition: utils2.c:1932