Leptonica 1.83.1
Image processing and image analysis suite
Loading...
Searching...
No Matches
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
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. */
143static 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 *---------------------------------------------------------------------*/
187PIX *
189{
190l_uint8 byte;
191l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
192l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
193l_uint32 png_transforms;
194l_uint32 *data, *line, *ppixel;
195int num_palette, num_text, num_trans;
196png_byte bit_depth, color_type, channels;
197png_uint_32 w, h, rowbytes, xres, yres;
198png_bytep rowptr, trans;
199png_bytep *row_pointers;
200png_structp png_ptr;
201png_infop info_ptr, end_info;
202png_colorp palette;
203png_textp text_ptr; /* ptr to text_chunk */
204PIX *pix, *pix1;
205PIXCMAP *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);
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 */
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) */
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) */
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 *---------------------------------------------------------------------*/
573l_ok
574readHeaderPng(const char *filename,
575 l_int32 *pw,
576 l_int32 *ph,
577 l_int32 *pbps,
578 l_int32 *pspp,
579 l_int32 *piscmap)
580{
581l_int32 ret;
582FILE *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("image file not found", __func__, 1);
593 ret = freadHeaderPng(fp, pw, ph, pbps, pspp, piscmap);
594 fclose(fp);
595 return ret;
596}
597
598
615l_ok
617 l_int32 *pw,
618 l_int32 *ph,
619 l_int32 *pbps,
620 l_int32 *pspp,
621 l_int32 *piscmap)
622{
623l_int32 nbytes, ret;
624l_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
670l_ok
671readHeaderMemPng(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{
679l_uint16 twobytes;
680l_uint16 *pshort;
681l_int32 colortype, w, h, bps, spp;
682l_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 */
762l_int32
763fgetPngResolution(FILE *fp,
764 l_int32 *pxres,
765 l_int32 *pyres)
766{
767png_uint_32 xres, yres;
768png_structp png_ptr;
769png_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
817l_ok
818isPngInterlaced(const char *filename,
819 l_int32 *pinterlaced)
820{
821l_uint8 buf[32];
822FILE *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("stream not opened", __func__, 1);
832 if (fread(buf, 1, 32, fp) != 32) {
833 fclose(fp);
834 return ERROR_INT("data not read", __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 */
859l_ok
860fgetPngColormapInfo(FILE *fp,
861 PIXCMAP **pcmap,
862 l_int32 *ptransparency)
863{
864l_int32 i, cindex, rval, gval, bval, num_palette, num_trans;
865png_byte bit_depth, color_type;
866png_bytep trans;
867png_colorp palette;
868png_structp png_ptr;
869png_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 *---------------------------------------------------------------------*/
958l_ok
959pixWritePng(const char *filename,
960 PIX *pix,
961 l_float32 gamma)
962{
963FILE *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("stream not opened", __func__, 1);
972
973 if (pixWriteStreamPng(fp, pix, gamma)) {
974 fclose(fp);
975 return ERROR_INT("pix not written to stream", __func__, 1);
976 }
977
978 fclose(fp);
979 return 0;
980}
981
982
1056l_ok
1058 PIX *pix,
1059 l_float32 gamma)
1060{
1061char commentstring[] = "Comment";
1062l_int32 i, j, k, wpl, d, spp, compval, valid;
1063l_int32 cmflag, opaque, max_trans, ncolors;
1064l_int32 *rmap, *gmap, *bmap, *amap;
1065l_uint32 *data, *ppixel;
1066png_byte bit_depth, color_type;
1067png_byte alpha[256];
1068png_uint_32 w, h;
1069png_uint_32 xres, yres;
1070png_bytep *row_pointers;
1071png_bytep rowbuffer;
1072png_structp png_ptr;
1073png_infop info_ptr;
1074png_colorp palette;
1075PIX *pix1;
1076PIXCMAP *cmap;
1077char *text;
1078
1079 if (!fp)
1080 return ERROR_INT("stream not open", __func__, 1);
1081 if (!pix)
1082 return ERROR_INT("pix not defined", __func__, 1);
1083
1084 w = pixGetWidth(pix);
1085 h = pixGetHeight(pix);
1086 d = pixGetDepth(pix);
1087 spp = pixGetSpp(pix);
1088
1089 /* A cmap validity check should prevent low-level colormap errors. */
1090 if ((cmap = pixGetColormap(pix))) {
1091 cmflag = 1;
1092 pixcmapIsValid(cmap, pix, &valid);
1093 if (!valid)
1094 return ERROR_INT("colormap is not valid", __func__, 1);
1095 } else {
1096 cmflag = 0;
1097 }
1098 pixSetPadBits(pix, 0);
1099
1100 /* Set the color type and bit depth. */
1101 if (d == 32 && spp == 4) {
1102 bit_depth = 8;
1103 color_type = PNG_COLOR_TYPE_RGBA; /* 6 */
1104 cmflag = 0; /* ignore if it exists */
1105 } else if (d == 24 || d == 32) {
1106 bit_depth = 8;
1107 color_type = PNG_COLOR_TYPE_RGB; /* 2 */
1108 cmflag = 0; /* ignore if it exists */
1109 } else {
1110 bit_depth = d;
1111 color_type = PNG_COLOR_TYPE_GRAY; /* 0 */
1112 }
1113 if (cmflag)
1114 color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */
1115
1116#if DEBUG_WRITE
1117 lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n",
1118 cmflag, bit_depth, color_type);
1119#endif /* DEBUG_WRITE */
1120
1121 /* Allocate the 2 png data structures */
1122 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
1123 (png_voidp)NULL, NULL, NULL)) == NULL)
1124 return ERROR_INT("png_ptr not made", __func__, 1);
1125 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
1126 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
1127 return ERROR_INT("info_ptr not made", __func__, 1);
1128 }
1129
1130 /* Set up png setjmp error handling */
1131 pix1 = NULL;
1132 row_pointers = NULL;
1133 if (setjmp(png_jmpbuf(png_ptr))) {
1134 png_destroy_write_struct(&png_ptr, &info_ptr);
1135 LEPT_FREE(row_pointers);
1136 pixDestroy(&pix1);
1137 return ERROR_INT("internal png error", __func__, 1);
1138 }
1139
1140 png_init_io(png_ptr, fp);
1141
1142 /* With best zlib compression (9), get between 1 and 10% improvement
1143 * over default (6), but the compression is 3 to 10 times slower.
1144 * Use the zlib default (6) as our default compression unless
1145 * pix->special falls in the range [10 ... 19]; then subtract 10
1146 * to get the compression value. */
1147 compval = Z_DEFAULT_COMPRESSION;
1148 if (pix->special >= 10 && pix->special < 20)
1149 compval = pix->special - 10;
1150 png_set_compression_level(png_ptr, compval);
1151
1152 png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type,
1153 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
1154 PNG_FILTER_TYPE_BASE);
1155
1156 /* Store resolution in ppm, if known */
1157 xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);
1158 yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);
1159 if ((xres == 0) || (yres == 0))
1160 png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN);
1161 else
1162 png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER);
1163
1164 if (cmflag) {
1165 /* Make and save the palette */
1166 ncolors = pixcmapGetCount(cmap);
1167 palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color));
1168 pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap);
1169 for (i = 0; i < ncolors; i++) {
1170 palette[i].red = (png_byte)rmap[i];
1171 palette[i].green = (png_byte)gmap[i];
1172 palette[i].blue = (png_byte)bmap[i];
1173 alpha[i] = (png_byte)amap[i];
1174 }
1175 LEPT_FREE(rmap);
1176 LEPT_FREE(gmap);
1177 LEPT_FREE(bmap);
1178 LEPT_FREE(amap);
1179 png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors);
1180 LEPT_FREE(palette);
1181
1182 /* Add the tRNS chunk. If the non-opaque colors are listed
1183 * first in the colormap, as in the spec, we can use that in
1184 * the 4th arg of png_set_tRNS. Otherwise, transparency will
1185 * be lost for some colors. To prevent that, see the comments
1186 * in pixcmapNonOpaqueColorsInfo(). */
1187 pixcmapIsOpaque(cmap, &opaque);
1188 if (!opaque) { /* alpha channel has some transparency; assume valid */
1189 pixcmapNonOpaqueColorsInfo(cmap, NULL, &max_trans, NULL);
1190 png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha,
1191 max_trans + 1, NULL);
1192 }
1193 }
1194
1195 /* 0.4545 is treated as the default by some image
1196 * display programs (not gqview). A value > 0.4545 will
1197 * lighten an image as displayed by xv, display, etc. */
1198 if (gamma > 0.0)
1199 png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma);
1200
1201 if ((text = pixGetText(pix))) {
1202 png_text text_chunk;
1203 text_chunk.compression = PNG_TEXT_COMPRESSION_NONE;
1204 text_chunk.key = commentstring;
1205 text_chunk.text = text;
1206 text_chunk.text_length = strlen(text);
1207#ifdef PNG_ITXT_SUPPORTED
1208 text_chunk.itxt_length = 0;
1209 text_chunk.lang = NULL;
1210 text_chunk.lang_key = NULL;
1211#endif
1212 png_set_text(png_ptr, info_ptr, &text_chunk, 1);
1213 }
1214
1215 /* Write header and palette info */
1216 png_write_info(png_ptr, info_ptr);
1217
1218 if ((d != 32) && (d != 24)) { /* not rgb color */
1219 /* Generate a temporary pix with bytes swapped.
1220 * For writing a 1 bpp image as png:
1221 * ~ if no colormap, invert the data, because png writes
1222 * black as 0
1223 * ~ if colormapped, do not invert the data; the two RGBA
1224 * colors can have any value. */
1225 if (d == 1 && !cmap) {
1226 pix1 = pixInvert(NULL, pix);
1227 pixEndianByteSwap(pix1);
1228 } else {
1229 pix1 = pixEndianByteSwapNew(pix);
1230 }
1231 if (!pix1) {
1232 png_destroy_write_struct(&png_ptr, &info_ptr);
1233 return ERROR_INT("pix1 not made", __func__, 1);
1234 }
1235
1236 /* Make and assign array of image row pointers */
1237 row_pointers = (png_bytep *)LEPT_CALLOC(h, sizeof(png_bytep));
1238 wpl = pixGetWpl(pix1);
1239 data = pixGetData(pix1);
1240 for (i = 0; i < h; i++)
1241 row_pointers[i] = (png_bytep)(data + i * wpl);
1242 png_set_rows(png_ptr, info_ptr, row_pointers);
1243
1244 /* Transfer the data */
1245 png_write_image(png_ptr, row_pointers);
1246 png_write_end(png_ptr, info_ptr);
1247 LEPT_FREE(row_pointers);
1248 pixDestroy(&pix1);
1249 png_destroy_write_struct(&png_ptr, &info_ptr);
1250 return 0;
1251 }
1252
1253 /* For rgb, compose and write a row at a time */
1254 data = pixGetData(pix);
1255 wpl = pixGetWpl(pix);
1256 if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */
1257 for (i = 0; i < h; i++) {
1258 ppixel = data + i * wpl;
1259 png_write_rows(png_ptr, (png_bytepp)&ppixel, 1);
1260 }
1261 } else { /* 32 bpp rgb and rgba. Write out the alpha channel if either
1262 * the pix has 4 spp or writing it is requested anyway */
1263 rowbuffer = (png_bytep)LEPT_CALLOC(w, 4);
1264 for (i = 0; i < h; i++) {
1265 ppixel = data + i * wpl;
1266 for (j = k = 0; j < w; j++) {
1267 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
1268 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
1269 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
1270 if (spp == 4)
1271 rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL);
1272 ppixel++;
1273 }
1274
1275 png_write_rows(png_ptr, &rowbuffer, 1);
1276 }
1277 LEPT_FREE(rowbuffer);
1278 }
1279
1280 png_write_end(png_ptr, info_ptr);
1281 png_destroy_write_struct(&png_ptr, &info_ptr);
1282 return 0;
1283}
1284
1285
1307l_ok
1309 l_int32 compval)
1310{
1311 if (!pix)
1312 return ERROR_INT("pix not defined", __func__, 1);
1313 if (compval < 0 || compval > 9) {
1314 L_ERROR("Invalid zlib comp val; using default\n", __func__);
1315 compval = Z_DEFAULT_COMPRESSION;
1316 }
1317 pixSetSpecial(pix, 10 + compval); /* valid range [10 ... 19] */
1318 return 0;
1319}
1320
1321
1322/*---------------------------------------------------------------------*
1323 * Set flag for stripping 16 bits on reading *
1324 *---------------------------------------------------------------------*/
1332void
1334{
1335 var_PNG_STRIP_16_TO_8 = flag;
1336}
1337
1338
1339/*-------------------------------------------------------------------------*
1340 * Memio utility *
1341 * libpng read/write callback replacements for performing memory I/O *
1342 * *
1343 * Copyright (C) 2017 Milner Technologies, Inc. This content is a *
1344 * component of leptonica and is provided under the terms of the *
1345 * Leptonica license. *
1346 *-------------------------------------------------------------------------*/
1347
1350{
1351 char* m_Buffer;
1352 l_int32 m_Count;
1353 l_int32 m_Size;
1359};
1360typedef struct MemIOData MEMIODATA;
1361
1362static void memio_png_write_data(png_structp png_ptr, png_bytep data,
1363 png_size_t length);
1364static void memio_png_flush(MEMIODATA* pthing);
1365static void memio_png_read_data(png_structp png_ptr, png_bytep outBytes,
1366 png_size_t byteCountToRead);
1367static void memio_free(MEMIODATA* pthing);
1368
1369static const l_int32 MEMIO_BUFFER_SIZE = 8192;
1370
1371/*
1372 * \brief memio_png_write_data()
1373 *
1374 * \param[in] png_ptr
1375 * \param[in] data
1376 * \param[in] len size of array data in bytes
1377 *
1378 * <pre>
1379 * Notes:
1380 * (1) This is a libpng callback for writing an image into a
1381 * linked list of memory buffers.
1382 * </pre>
1383 */
1384static void
1385memio_png_write_data(png_structp png_ptr,
1386 png_bytep data,
1387 png_size_t len)
1388{
1389MEMIODATA *thing, *last;
1390l_int32 written = 0;
1391l_int32 remainingSpace, remainingToWrite;
1392
1393 thing = (struct MemIOData*)png_get_io_ptr(png_ptr);
1394 last = (struct MemIOData*)thing->m_Last;
1395 if (last->m_Buffer == NULL) {
1396 if (len > MEMIO_BUFFER_SIZE) {
1397 last->m_Buffer = (char *)LEPT_MALLOC(len);
1398 memcpy(last->m_Buffer, data, len);
1399 last->m_Size = last->m_Count = len;
1400 return;
1401 }
1402
1403 last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE);
1404 last->m_Size = MEMIO_BUFFER_SIZE;
1405 }
1406
1407 while (written < len) {
1408 if (last->m_Count == last->m_Size) {
1409 MEMIODATA* next = (MEMIODATA *)LEPT_MALLOC(sizeof(MEMIODATA));
1410 next->m_Next = NULL;
1411 next->m_Count = 0;
1412 next->m_Last = next;
1413
1414 last->m_Next = next;
1415 last = thing->m_Last = next;
1416
1417 last->m_Buffer = (char *)LEPT_MALLOC(MEMIO_BUFFER_SIZE);
1418 last->m_Size = MEMIO_BUFFER_SIZE;
1419 }
1420
1421 remainingSpace = last->m_Size - last->m_Count;
1422 remainingToWrite = len - written;
1423 if (remainingSpace < remainingToWrite) {
1424 memcpy(last->m_Buffer + last->m_Count, data + written,
1425 remainingSpace);
1426 written += remainingSpace;
1427 last->m_Count += remainingSpace;
1428 } else {
1429 memcpy(last->m_Buffer + last->m_Count, data + written,
1430 remainingToWrite);
1431 written += remainingToWrite;
1432 last->m_Count += remainingToWrite;
1433 }
1434 }
1435}
1436
1437
1438/*
1439 * \brief memio_png_flush()
1440 *
1441 * \param[in] pthing
1442 *
1443 * <pre>
1444 * Notes:
1445 * (1) This consolidates write buffers into a single buffer at the
1446 * haed of the link list of buffers.
1447 * </pre>
1448 */
1449static void
1450memio_png_flush(MEMIODATA *pthing)
1451{
1452l_int32 amount = 0;
1453l_int32 copied = 0;
1454MEMIODATA *buffer = 0;
1455char *data = 0;
1456
1457 /* If the data is in one buffer, give the buffer to the user. */
1458 if (pthing->m_Next == NULL) return;
1459
1460 /* Consolidate multiple buffers into one new one; add the buffer
1461 * sizes together. */
1462 amount = pthing->m_Count;
1463 buffer = pthing->m_Next;
1464 while (buffer != NULL) {
1465 amount += buffer->m_Count;
1466 buffer = buffer->m_Next;
1467 }
1468
1469 /* Copy data to a new buffer. */
1470 data = (char *)LEPT_MALLOC(amount);
1471 memcpy(data, pthing->m_Buffer, pthing->m_Count);
1472 copied = pthing->m_Count;
1473
1474 LEPT_FREE(pthing->m_Buffer);
1475 pthing->m_Buffer = NULL;
1476
1477 /* Don't delete original "thing" because we don't control it. */
1478 buffer = pthing->m_Next;
1479 pthing->m_Next = NULL;
1480 while (buffer != NULL && copied < amount) {
1481 MEMIODATA* old;
1482 memcpy(data + copied, buffer->m_Buffer, buffer->m_Count);
1483 copied += buffer->m_Count;
1484
1485 old = buffer;
1486 buffer = buffer->m_Next;
1487
1488 LEPT_FREE(old->m_Buffer);
1489 LEPT_FREE(old);
1490 }
1491
1492 pthing->m_Buffer = data;
1493 pthing->m_Count = copied;
1494 pthing->m_Size = amount;
1495 return;
1496}
1497
1498
1499/*
1500 * \brief memio_png_read_data()
1501 *
1502 * \param[in] png_ptr
1503 * \param[in] outBytes
1504 * \param[in] byteCountToRead
1505 *
1506 * <pre>
1507 * Notes:
1508 * (1) This is a libpng callback that reads an image from a single
1509 * memory buffer.
1510 * </pre>
1511 */
1512static void
1513memio_png_read_data(png_structp png_ptr,
1514 png_bytep outBytes,
1515 png_size_t byteCountToRead)
1516{
1517MEMIODATA *thing;
1518
1519 thing = (MEMIODATA *)png_get_io_ptr(png_ptr);
1520 if (byteCountToRead > (thing->m_Size - thing->m_Count)) {
1521 png_error(png_ptr, "read error in memio_png_read_data");
1522 }
1523 memcpy(outBytes, thing->m_Buffer + thing->m_Count, byteCountToRead);
1524 thing->m_Count += byteCountToRead;
1525}
1526
1527
1528/*
1529 * \brief memio_free()
1530 *
1531 * \param[in] pthing
1532 *
1533 * <pre>
1534 * Notes:
1535 * (1) This frees all the write buffers in the linked list. It must
1536 * be done before exiting the pixWriteMemPng().
1537 * </pre>
1538 */
1539static void
1540memio_free(MEMIODATA* pthing)
1541{
1542MEMIODATA *buffer, *old;
1543
1544 if (pthing->m_Buffer != NULL)
1545 LEPT_FREE(pthing->m_Buffer);
1546
1547 pthing->m_Buffer = NULL;
1548 buffer = pthing->m_Next;
1549 while (buffer != NULL) {
1550 old = buffer;
1551 buffer = buffer->m_Next;
1552
1553 if (old->m_Buffer != NULL)
1554 LEPT_FREE(old->m_Buffer);
1555 LEPT_FREE(old);
1556 }
1557}
1558
1559
1560/*---------------------------------------------------------------------*
1561 * Reading png from memory *
1562 *---------------------------------------------------------------------*/
1575PIX *
1576pixReadMemPng(const l_uint8 *filedata,
1577 size_t filesize)
1578{
1579l_uint8 byte;
1580l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
1581l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
1582l_uint32 png_transforms;
1583l_uint32 *data, *line, *ppixel;
1584int num_palette, num_text, num_trans;
1585png_byte bit_depth, color_type, channels;
1586png_uint_32 w, h, rowbytes, xres, yres;
1587png_bytep rowptr, trans;
1588png_bytep *row_pointers;
1589png_structp png_ptr;
1590png_infop info_ptr, end_info;
1591png_colorp palette;
1592png_textp text_ptr; /* ptr to text_chunk */
1593MEMIODATA state;
1594PIX *pix, *pix1;
1595PIXCMAP *cmap;
1596
1597 if (!filedata)
1598 return (PIX *)ERROR_PTR("filedata not defined", __func__, NULL);
1599 if (filesize < 1)
1600 return (PIX *)ERROR_PTR("invalid filesize", __func__, NULL);
1601
1602 state.m_Next = 0;
1603 state.m_Count = 0;
1604 state.m_Last = &state;
1605 state.m_Buffer = (char*)filedata;
1606 state.m_Size = filesize;
1607 pix = NULL;
1608
1609 /* Allocate the 3 data structures */
1610 if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
1611 (png_voidp)NULL, NULL, NULL)) == NULL)
1612 return (PIX *)ERROR_PTR("png_ptr not made", __func__, NULL);
1613
1614 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
1615 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
1616 return (PIX *)ERROR_PTR("info_ptr not made", __func__, NULL);
1617 }
1618
1619 if ((end_info = png_create_info_struct(png_ptr)) == NULL) {
1620 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
1621 return (PIX *)ERROR_PTR("end_info not made", __func__, NULL);
1622 }
1623
1624 /* Set up png setjmp error handling */
1625 if (setjmp(png_jmpbuf(png_ptr))) {
1626 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1627 return (PIX *)ERROR_PTR("internal png error", __func__, NULL);
1628 }
1629
1630 png_set_read_fn(png_ptr, &state, memio_png_read_data);
1631
1632 /* ---------------------------------------------------------- *
1633 * Set the transforms flags. Whatever happens here,
1634 * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO.
1635 * Also, do not use PNG_TRANSFORM_EXPAND, which would
1636 * expand all images with bpp < 8 to 8 bpp.
1637 * ---------------------------------------------------------- */
1638 /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */
1639 if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */
1640 png_transforms = PNG_TRANSFORM_STRIP_16;
1641 } else {
1642 png_transforms = PNG_TRANSFORM_IDENTITY;
1643 L_INFO("not stripping 16 --> 8 in png reading\n", __func__);
1644 }
1645
1646 /* Read it */
1647 png_read_png(png_ptr, info_ptr, png_transforms, NULL);
1648
1649 row_pointers = png_get_rows(png_ptr, info_ptr);
1650 w = png_get_image_width(png_ptr, info_ptr);
1651 h = png_get_image_height(png_ptr, info_ptr);
1652 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
1653 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
1654 color_type = png_get_color_type(png_ptr, info_ptr);
1655 channels = png_get_channels(png_ptr, info_ptr);
1656 spp = channels;
1657 tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0;
1658
1659 if (spp == 1) {
1660 d = bit_depth;
1661 } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */
1662 d = 4 * bit_depth;
1663 }
1664
1665 /* Remove if/when this is implemented for all bit_depths */
1666 if (spp == 3 && bit_depth != 8) {
1667 lept_stderr("Help: spp = 3 and depth = %d != 8\n!!", bit_depth);
1668 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1669 return (PIX *)ERROR_PTR("not implemented for this depth",
1670 __func__, NULL);
1671 }
1672
1673 cmap = NULL;
1674 if (color_type == PNG_COLOR_TYPE_PALETTE ||
1675 color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */
1676 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
1677 cmap = pixcmapCreate(d); /* spp == 1 */
1678 for (cindex = 0; cindex < num_palette; cindex++) {
1679 rval = palette[cindex].red;
1680 gval = palette[cindex].green;
1681 bval = palette[cindex].blue;
1682 pixcmapAddColor(cmap, rval, gval, bval);
1683 }
1684 }
1685
1686 if ((pix = pixCreate(w, h, d)) == NULL) {
1687 pixcmapDestroy(&cmap);
1688 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1689 pixcmapDestroy(&cmap);
1690 return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
1691 }
1692 pixSetInputFormat(pix, IFF_PNG);
1693 wpl = pixGetWpl(pix);
1694 data = pixGetData(pix);
1695 pixSetSpp(pix, spp);
1696 if (pixSetColormap(pix, cmap)) {
1697 pixDestroy(&pix);
1698 return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL);
1699 }
1700
1701 if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */
1702 for (i = 0; i < h; i++) {
1703 line = data + i * wpl;
1704 rowptr = row_pointers[i];
1705 for (j = 0; j < rowbytes; j++) {
1706 SET_DATA_BYTE(line, j, rowptr[j]);
1707 }
1708 }
1709 } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */
1710 L_INFO("converting (gray + alpha) ==> RGBA\n", __func__);
1711 for (i = 0; i < h; i++) {
1712 ppixel = data + i * wpl;
1713 rowptr = row_pointers[i];
1714 for (j = k = 0; j < w; j++) {
1715 /* Copy gray value into r, g and b */
1716 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]);
1717 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]);
1718 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
1719 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
1720 ppixel++;
1721 }
1722 }
1723 pixSetSpp(pix, 4); /* we do not support 2 spp pix */
1724 } else if (spp == 3 || spp == 4) {
1725 for (i = 0; i < h; i++) {
1726 ppixel = data + i * wpl;
1727 rowptr = row_pointers[i];
1728 for (j = k = 0; j < w; j++) {
1729 SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]);
1730 SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]);
1731 SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
1732 if (spp == 4)
1733 SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
1734 ppixel++;
1735 }
1736 }
1737 }
1738
1739 /* Special spp == 1 cases with transparency:
1740 * (1) 8 bpp without colormap; assume full transparency
1741 * (2) 1 bpp with colormap + trans array (for alpha)
1742 * (3) 2 bpp with colormap + trans array (for alpha)
1743 * (4) 4 bpp with colormap + trans array (for alpha)
1744 * (5) 8 bpp with colormap + trans array (for alpha)
1745 * These all require converting to RGBA */
1746 if (spp == 1 && tRNS) {
1747 if (!cmap) {
1748 /* Case 1: make fully transparent RGBA image */
1749 L_INFO("transparency, 1 spp, no colormap, no transparency array: "
1750 "convention is fully transparent image\n", __func__);
1751 L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", __func__);
1752 pixDestroy(&pix);
1753 pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */
1754 pixSetSpp(pix, 4);
1755 } else {
1756 L_INFO("converting (cmap + alpha) ==> RGBA\n", __func__);
1757
1758 /* Grab the transparency array */
1759 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
1760 if (!trans) { /* invalid png file */
1761 pixDestroy(&pix);
1762 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1763 return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array",
1764 __func__, NULL);
1765 }
1766
1767 /* Save the cmap and destroy the pix */
1768 cmap = pixcmapCopy(pixGetColormap(pix));
1769 ncolors = pixcmapGetCount(cmap);
1770 pixDestroy(&pix);
1771
1772 /* Start over with 32 bit RGBA */
1773 pix = pixCreate(w, h, 32);
1774 wpl = pixGetWpl(pix);
1775 data = pixGetData(pix);
1776 pixSetSpp(pix, 4);
1777
1778#if DEBUG_READ
1779 lept_stderr("ncolors = %d, num_trans = %d\n",
1780 ncolors, num_trans);
1781 for (i = 0; i < ncolors; i++) {
1782 pixcmapGetColor(cmap, i, &rval, &gval, &bval);
1783 if (i < num_trans) {
1784 lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n",
1785 rval, gval, bval, trans[i]);
1786 } else {
1787 lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n",
1788 rval, gval, bval);
1789 }
1790 }
1791#endif /* DEBUG_READ */
1792
1793 /* Extract the data and convert to RGBA */
1794 if (d == 1) {
1795 /* Case 2: 1 bpp with transparency (usually) behind white */
1796 L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", __func__);
1797 if (num_trans == 1)
1798 L_INFO("num_trans = 1; second color opaque by default\n",
1799 __func__);
1800 for (i = 0; i < h; i++) {
1801 ppixel = data + i * wpl;
1802 rowptr = row_pointers[i];
1803 for (j = 0, index = 0; j < rowbytes; j++) {
1804 byte = rowptr[j];
1805 for (k = 0; k < 8 && index < w; k++, index++) {
1806 bitval = (byte >> (7 - k)) & 1;
1807 pixcmapGetColor(cmap, bitval, &rval, &gval, &bval);
1808 composeRGBPixel(rval, gval, bval, ppixel);
1810 bitval < num_trans ? trans[bitval] : 255);
1811 ppixel++;
1812 }
1813 }
1814 }
1815 } else if (d == 2) {
1816 /* Case 3: 2 bpp with cmap and associated transparency */
1817 L_INFO("converting 2 bpp cmap with alpha ==> RGBA\n", __func__);
1818 for (i = 0; i < h; i++) {
1819 ppixel = data + i * wpl;
1820 rowptr = row_pointers[i];
1821 for (j = 0, index = 0; j < rowbytes; j++) {
1822 byte = rowptr[j];
1823 for (k = 0; k < 4 && index < w; k++, index++) {
1824 bival = (byte >> 2 * (3 - k)) & 3;
1825 pixcmapGetColor(cmap, bival, &rval, &gval, &bval);
1826 composeRGBPixel(rval, gval, bval, ppixel);
1827 /* Assume missing entries to be 255 (opaque)
1828 * according to the spec:
1829 * http://www.w3.org/TR/PNG/#11tRNS */
1831 bival < num_trans ? trans[bival] : 255);
1832 ppixel++;
1833 }
1834 }
1835 }
1836 } else if (d == 4) {
1837 /* Case 4: 4 bpp with cmap and associated transparency */
1838 L_INFO("converting 4 bpp cmap with alpha ==> RGBA\n", __func__);
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 < 2 && index < w; k++, index++) {
1845 quadval = (byte >> 4 * (1 - k)) & 0xf;
1846 pixcmapGetColor(cmap, quadval, &rval, &gval, &bval);
1847 composeRGBPixel(rval, gval, bval, ppixel);
1848 /* Assume missing entries to be 255 (opaque) */
1850 quadval < num_trans ? trans[quadval] : 255);
1851 ppixel++;
1852 }
1853 }
1854 }
1855 } else if (d == 8) {
1856 /* Case 5: 8 bpp with cmap and associated transparency */
1857 L_INFO("converting 8 bpp cmap with alpha ==> RGBA\n", __func__);
1858 for (i = 0; i < h; i++) {
1859 ppixel = data + i * wpl;
1860 rowptr = row_pointers[i];
1861 for (j = 0; j < w; j++) {
1862 index = rowptr[j];
1863 pixcmapGetColor(cmap, index, &rval, &gval, &bval);
1864 composeRGBPixel(rval, gval, bval, ppixel);
1865 /* Assume missing entries to be 255 (opaque)
1866 * according to the spec:
1867 * http://www.w3.org/TR/PNG/#11tRNS */
1869 index < num_trans ? trans[index] : 255);
1870 ppixel++;
1871 }
1872 }
1873 } else {
1874 L_ERROR("spp == 1, cmap, trans array, invalid depth: %d\n",
1875 __func__, d);
1876 }
1877 pixcmapDestroy(&cmap);
1878 }
1879 }
1880
1881#if DEBUG_READ
1882 if (cmap) {
1883 for (i = 0; i < 16; i++) {
1884 lept_stderr("[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]);
1885 }
1886 }
1887#endif /* DEBUG_READ */
1888
1889 /* Final adjustments for bpp = 1.
1890 * + If there is no colormap, the image must be inverted because
1891 * png stores black pixels as 0.
1892 * + We have already handled the case of cmapped, 1 bpp pix
1893 * with transparency, where the output pix is 32 bpp RGBA.
1894 * If there is no transparency but the pix has a colormap,
1895 * we remove the colormap, because functions operating on
1896 * 1 bpp images in leptonica assume no colormap.
1897 * + The colormap must be removed in such a way that the pixel
1898 * values are not changed. If the values are only black and
1899 * white, we return a 1 bpp image; if gray, return an 8 bpp pix;
1900 * otherwise, return a 32 bpp rgb pix.
1901 *
1902 * Note that we cannot use the PNG_TRANSFORM_INVERT_MONO flag
1903 * to do the inversion, because that flag (since version 1.0.9)
1904 * inverts 8 bpp grayscale as well, which we don't want to do.
1905 * (It also doesn't work if there is a colormap.)
1906 *
1907 * Note that if the input png is a 1-bit with colormap and
1908 * transparency, it has already been rendered as a 32 bpp,
1909 * spp = 4 rgba pix.
1910 */
1911 if (pixGetDepth(pix) == 1) {
1912 if (!cmap) {
1913 pixInvert(pix, pix);
1914 } else {
1915 pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
1916 pixDestroy(&pix);
1917 pix = pix1;
1918 }
1919 }
1920
1921 xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
1922 yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
1923 pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */
1924 pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */
1925
1926 /* Get the text if there is any */
1927 png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
1928 if (num_text && text_ptr)
1929 pixSetText(pix, text_ptr->text);
1930
1931 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
1932
1933 /* Final validity check on the colormap */
1934 if ((cmap = pixGetColormap(pix)) != NULL) {
1935 pixcmapIsValid(cmap, pix, &valid);
1936 if (!valid) {
1937 pixDestroy(&pix);
1938 return (PIX *)ERROR_PTR("colormap is not valid", __func__, NULL);
1939 }
1940 }
1941
1942 pixSetPadBits(pix, 0);
1943 return pix;
1944}
1945
1946
1947/*---------------------------------------------------------------------*
1948 * Writing png to memory *
1949 *---------------------------------------------------------------------*/
1964l_ok
1965pixWriteMemPng(l_uint8 **pfiledata,
1966 size_t *pfilesize,
1967 PIX *pix,
1968 l_float32 gamma)
1969{
1970char commentstring[] = "Comment";
1971l_int32 i, j, k, wpl, d, spp, cmflag, opaque, ncolors, compval, valid;
1972l_int32 *rmap, *gmap, *bmap, *amap;
1973l_uint32 *data, *ppixel;
1974png_byte bit_depth, color_type;
1975png_byte alpha[256];
1976png_uint_32 w, h, xres, yres;
1977png_bytep rowbuffer;
1978png_structp png_ptr;
1979png_infop info_ptr;
1980png_colorp palette;
1981PIX *pix1;
1982PIXCMAP *cmap;
1983char *text;
1984MEMIODATA state;
1985
1986 if (pfiledata) *pfiledata = NULL;
1987 if (pfilesize) *pfilesize = 0;
1988 if (!pfiledata)
1989 return ERROR_INT("&filedata not defined", __func__, 1);
1990 if (!pfilesize)
1991 return ERROR_INT("&filesize not defined", __func__, 1);
1992 if (!pix)
1993 return ERROR_INT("pix not defined", __func__, 1);
1994
1995 state.m_Buffer = 0;
1996 state.m_Size = 0;
1997 state.m_Next = 0;
1998 state.m_Count = 0;
1999 state.m_Last = &state;
2000
2001 w = pixGetWidth(pix);
2002 h = pixGetHeight(pix);
2003 d = pixGetDepth(pix);
2004 spp = pixGetSpp(pix);
2005
2006 /* A cmap validity check should prevent low-level colormap errors. */
2007 if ((cmap = pixGetColormap(pix))) {
2008 cmflag = 1;
2009 pixcmapIsValid(cmap, pix, &valid);
2010 if (!valid)
2011 return ERROR_INT("colormap is not valid", __func__, 1);
2012 } else {
2013 cmflag = 0;
2014 }
2015
2016 pixSetPadBits(pix, 0);
2017
2018 /* Set the color type and bit depth. */
2019 if (d == 32 && spp == 4) {
2020 bit_depth = 8;
2021 color_type = PNG_COLOR_TYPE_RGBA; /* 6 */
2022 cmflag = 0; /* ignore if it exists */
2023 } else if (d == 24 || d == 32) {
2024 bit_depth = 8;
2025 color_type = PNG_COLOR_TYPE_RGB; /* 2 */
2026 cmflag = 0; /* ignore if it exists */
2027 } else {
2028 bit_depth = d;
2029 color_type = PNG_COLOR_TYPE_GRAY; /* 0 */
2030 }
2031 if (cmflag)
2032 color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */
2033
2034#if DEBUG_WRITE
2035 lept_stderr("cmflag = %d, bit_depth = %d, color_type = %d\n",
2036 cmflag, bit_depth, color_type);
2037#endif /* DEBUG_WRITE */
2038
2039 /* Allocate the 2 data structures */
2040 if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
2041 (png_voidp)NULL, NULL, NULL)) == NULL)
2042 return ERROR_INT("png_ptr not made", __func__, 1);
2043
2044 if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
2045 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
2046 return ERROR_INT("info_ptr not made", __func__, 1);
2047 }
2048
2049 /* Set up png setjmp error handling */
2050 pix1 = NULL;
2051 if (setjmp(png_jmpbuf(png_ptr))) {
2052 png_destroy_write_struct(&png_ptr, &info_ptr);
2053 pixDestroy(&pix1);
2054 return ERROR_INT("internal png error", __func__, 1);
2055 }
2056
2057 png_set_write_fn(png_ptr, &state, memio_png_write_data,
2058 (png_flush_ptr)NULL);
2059
2060 /* With best zlib compression (9), get between 1 and 10% improvement
2061 * over default (6), but the compression is 3 to 10 times slower.
2062 * Use the zlib default (6) as our default compression unless
2063 * pix->special falls in the range [10 ... 19]; then subtract 10
2064 * to get the compression value. */
2065 compval = Z_DEFAULT_COMPRESSION;
2066 if (pix->special >= 10 && pix->special < 20)
2067 compval = pix->special - 10;
2068 png_set_compression_level(png_ptr, compval);
2069
2070 png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type,
2071 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
2072 PNG_FILTER_TYPE_BASE);
2073
2074 /* Store resolution in ppm, if known */
2075 xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);
2076 yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);
2077 if ((xres == 0) || (yres == 0))
2078 png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN);
2079 else
2080 png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER);
2081
2082 if (cmflag) {
2083 /* Make and save the palette */
2084 ncolors = pixcmapGetCount(cmap);
2085 palette = (png_colorp)LEPT_CALLOC(ncolors, sizeof(png_color));
2086 pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap);
2087 for (i = 0; i < ncolors; i++) {
2088 palette[i].red = (png_byte)rmap[i];
2089 palette[i].green = (png_byte)gmap[i];
2090 palette[i].blue = (png_byte)bmap[i];
2091 alpha[i] = (png_byte)amap[i];
2092 }
2093 LEPT_FREE(rmap);
2094 LEPT_FREE(gmap);
2095 LEPT_FREE(bmap);
2096 LEPT_FREE(amap);
2097 png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors);
2098 LEPT_FREE(palette);
2099
2100 pixcmapIsOpaque(cmap, &opaque);
2101 if (!opaque) /* alpha channel has some transparency; assume valid */
2102 png_set_tRNS(png_ptr, info_ptr, (png_bytep)alpha,
2103 (int)ncolors, NULL);
2104 }
2105
2106 /* 0.4545 is treated as the default by some image
2107 * display programs (not gqview). A value > 0.4545 will
2108 * lighten an image as displayed by xv, display, etc. */
2109 if (gamma > 0.0)
2110 png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma);
2111
2112 if ((text = pixGetText(pix))) {
2113 png_text text_chunk;
2114 text_chunk.compression = PNG_TEXT_COMPRESSION_NONE;
2115 text_chunk.key = commentstring;
2116 text_chunk.text = text;
2117 text_chunk.text_length = strlen(text);
2118#ifdef PNG_ITXT_SUPPORTED
2119 text_chunk.itxt_length = 0;
2120 text_chunk.lang = NULL;
2121 text_chunk.lang_key = NULL;
2122#endif
2123 png_set_text(png_ptr, info_ptr, &text_chunk, 1);
2124 }
2125
2126 /* Write header and palette info */
2127 png_write_info(png_ptr, info_ptr);
2128
2129 if ((d != 32) && (d != 24)) { /* not rgb color */
2130 /* Generate a temporary pix with bytes swapped.
2131 * For writing a 1 bpp image as png:
2132 * ~ if no colormap, invert the data, because png writes
2133 * black as 0
2134 * ~ if colormapped, do not invert the data; the two RGBA
2135 * colors can have any value. */
2136 if (d == 1 && !cmap) {
2137 pix1 = pixInvert(NULL, pix);
2138 pixEndianByteSwap(pix1);
2139 } else {
2140 pix1 = pixEndianByteSwapNew(pix);
2141 }
2142 if (!pix1) {
2143 png_destroy_write_struct(&png_ptr, &info_ptr);
2144 memio_free(&state);
2145 return ERROR_INT("pix1 not made", __func__, 1);
2146 }
2147
2148 /* Transfer the data */
2149 wpl = pixGetWpl(pix1);
2150 data = pixGetData(pix1);
2151 for (i = 0; i < h; i++)
2152 png_write_row(png_ptr, (png_bytep)(data + i * wpl));
2153 png_write_end(png_ptr, info_ptr);
2154
2155 pixDestroy(&pix1);
2156 png_destroy_write_struct(&png_ptr, &info_ptr);
2157 memio_png_flush(&state);
2158 *pfiledata = (l_uint8 *)state.m_Buffer;
2159 state.m_Buffer = 0;
2160 *pfilesize = state.m_Count;
2161 memio_free(&state);
2162 return 0;
2163 }
2164
2165 /* For rgb, compose and write a row at a time */
2166 data = pixGetData(pix);
2167 wpl = pixGetWpl(pix);
2168 if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */
2169 for (i = 0; i < h; i++) {
2170 ppixel = data + i * wpl;
2171 png_write_rows(png_ptr, (png_bytepp)&ppixel, 1);
2172 }
2173 } else { /* 32 bpp rgb and rgba. Write out the alpha channel if either
2174 * the pix has 4 spp or writing it is requested anyway */
2175 rowbuffer = (png_bytep)LEPT_CALLOC(w, 4);
2176 for (i = 0; i < h; i++) {
2177 ppixel = data + i * wpl;
2178 for (j = k = 0; j < w; j++) {
2179 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED);
2180 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN);
2181 rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE);
2182 if (spp == 4)
2183 rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL);
2184 ppixel++;
2185 }
2186
2187 png_write_rows(png_ptr, &rowbuffer, 1);
2188 }
2189 LEPT_FREE(rowbuffer);
2190 }
2191 png_write_end(png_ptr, info_ptr);
2192
2193 png_destroy_write_struct(&png_ptr, &info_ptr);
2194 memio_png_flush(&state);
2195 *pfiledata = (l_uint8 *)state.m_Buffer;
2196 state.m_Buffer = 0;
2197 *pfilesize = state.m_Count;
2198 memio_free(&state);
2199 return 0;
2200}
2201
2202/* --------------------------------------------*/
2203#endif /* HAVE_LIBPNG */
2204/* --------------------------------------------*/
#define GET_DATA_BYTE(pdata, n)
#define SET_DATA_BYTE(pdata, n, val)
@ REMOVE_CMAP_BASED_ON_SRC
Definition pix.h:384
struct Pix PIX
Definition pix.h:228
struct PixColormap PIXCMAP
Definition pix.h:231
@ COLOR_BLUE
Definition pix.h:330
@ COLOR_RED
Definition pix.h:328
@ L_ALPHA_CHANNEL
Definition pix.h:331
@ COLOR_GREEN
Definition pix.h:329
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:574
l_ok isPngInterlaced(const char *filename, l_int32 *pinterlaced)
isPngInterlaced()
Definition pngio.c:818
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:671
l_ok pixSetZlibCompression(PIX *pix, l_int32 compval)
pixSetZlibCompression()
Definition pngio.c:1308
PIX * pixReadStreamPng(FILE *fp)
pixReadStreamPng()
Definition pngio.c:188
static void memio_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
Definition pngio.c:1385
l_ok pixWriteStreamPng(FILE *fp, PIX *pix, l_float32 gamma)
pixWriteStreamPng()
Definition pngio.c:1057
l_ok freadHeaderPng(FILE *fp, l_int32 *pw, l_int32 *ph, l_int32 *pbps, l_int32 *pspp, l_int32 *piscmap)
freadHeaderPng()
Definition pngio.c:616
PIX * pixReadMemPng(const l_uint8 *filedata, size_t filesize)
pixReadMemPng()
Definition pngio.c:1576
void l_pngSetReadStrip16To8(l_int32 flag)
l_pngSetReadStrip16To8()
Definition pngio.c:1333
l_ok pixWriteMemPng(l_uint8 **pfiledata, size_t *pfilesize, PIX *pix, l_float32 gamma)
pixWriteMemPng()
Definition pngio.c:1965
l_ok pixWritePng(const char *filename, PIX *pix, l_float32 gamma)
pixWritePng()
Definition pngio.c:959
struct MemIOData * m_Next
Definition pngio.c:1354
l_int32 m_Size
Definition pngio.c:1353
char * m_Buffer
Definition pngio.c:1351
l_int32 m_Count
Definition pngio.c:1352
struct MemIOData * m_Last
Definition pngio.c:1356
char * text
l_int32 special