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. */
142 static 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  *---------------------------------------------------------------------*/
186 PIX *
188 {
189 l_uint8 byte;
190 l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
191 l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
192 l_uint32 png_transforms;
193 l_uint32 *data, *line, *ppixel;
194 int num_palette, num_text, num_trans;
195 png_byte bit_depth, color_type, channels;
196 png_uint_32 w, h, rowbytes, xres, yres;
197 png_bytep rowptr, trans;
198 png_bytep *row_pointers;
199 png_structp png_ptr;
200 png_infop info_ptr, end_info;
201 png_colorp palette;
202 png_textp text_ptr; /* ptr to text_chunk */
203 PIX *pix, *pix1;
204 PIXCMAP *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  *---------------------------------------------------------------------*/
574 l_ok
575 readHeaderPng(const char *filename,
576  l_int32 *pw,
577  l_int32 *ph,
578  l_int32 *pbps,
579  l_int32 *pspp,
580  l_int32 *piscmap)
581 {
582 l_int32 ret;
583 FILE *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 
618 l_ok
619 freadHeaderPng(FILE *fp,
620  l_int32 *pw,
621  l_int32 *ph,
622  l_int32 *pbps,
623  l_int32 *pspp,
624  l_int32 *piscmap)
625 {
626 l_int32 nbytes, ret;
627 l_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 
675 l_ok
676 readHeaderMemPng(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 {
684 l_uint16 twobytes;
685 l_uint16 *pshort;
686 l_int32 colortype, w, h, bps, spp;
687 l_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  */
769 l_int32
770 fgetPngResolution(FILE *fp,
771  l_int32 *pxres,
772  l_int32 *pyres)
773 {
774 png_uint_32 xres, yres;
775 png_structp png_ptr;
776 png_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 
826 l_ok
827 isPngInterlaced(const char *filename,
828  l_int32 *pinterlaced)
829 {
830 l_uint8 buf[32];
831 FILE *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  */
870 l_ok
871 fgetPngColormapInfo(FILE *fp,
872  PIXCMAP **pcmap,
873  l_int32 *ptransparency)
874 {
875 l_int32 i, cindex, rval, gval, bval, num_palette, num_trans;
876 png_byte bit_depth, color_type;
877 png_bytep trans;
878 png_colorp palette;
879 png_structp png_ptr;
880 png_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  *---------------------------------------------------------------------*/
971 l_ok
972 pixWritePng(const char *filename,
973  PIX *pix,
974  l_float32 gamma)
975 {
976 FILE *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 
1071 l_ok
1073  PIX *pix,
1074  l_float32 gamma)
1075 {
1076 char commentstring[] = "Comment";
1077 l_int32 i, j, k, wpl, d, spp, compval, valid;
1078 l_int32 cmflag, opaque, max_trans, ncolors;
1079 l_int32 *rmap, *gmap, *bmap, *amap;
1080 l_uint32 *data, *ppixel;
1081 png_byte bit_depth, color_type;
1082 png_byte alpha[256];
1083 png_uint_32 w, h;
1084 png_uint_32 xres, yres;
1085 png_bytep *row_pointers;
1086 png_bytep rowbuffer;
1087 png_structp png_ptr;
1088 png_infop info_ptr;
1089 png_colorp palette;
1090 PIX *pix1;
1091 PIXCMAP *cmap;
1092 char *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 
1324 l_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  *---------------------------------------------------------------------*/
1351 void
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;
1373  struct MemIOData *m_Next;
1375  struct MemIOData *m_Last;
1378 };
1379 typedef struct MemIOData MEMIODATA;
1380 
1381 static void memio_png_write_data(png_structp png_ptr, png_bytep data,
1382  png_size_t length);
1383 static void memio_png_flush(MEMIODATA* pthing);
1384 static void memio_png_read_data(png_structp png_ptr, png_bytep outBytes,
1385  png_size_t byteCountToRead);
1386 static void memio_free(MEMIODATA* pthing);
1387 
1388 static 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  */
1403 static void
1404 memio_png_write_data(png_structp png_ptr,
1405  png_bytep data,
1406  png_size_t len)
1407 {
1408 MEMIODATA *thing, *last;
1409 l_int32 written = 0;
1410 l_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  */
1468 static void
1469 memio_png_flush(MEMIODATA *pthing)
1470 {
1471 l_int32 amount = 0;
1472 l_int32 copied = 0;
1473 MEMIODATA *buffer = 0;
1474 char *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  */
1531 static void
1532 memio_png_read_data(png_structp png_ptr,
1533  png_bytep outBytes,
1534  png_size_t byteCountToRead)
1535 {
1536 MEMIODATA *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  */
1558 static void
1559 memio_free(MEMIODATA* pthing)
1560 {
1561 MEMIODATA *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  *---------------------------------------------------------------------*/
1594 PIX *
1595 pixReadMemPng(const l_uint8 *filedata,
1596  size_t filesize)
1597 {
1598 l_uint8 byte;
1599 l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
1600 l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
1601 l_uint32 png_transforms;
1602 l_uint32 *data, *line, *ppixel;
1603 int num_palette, num_text, num_trans;
1604 png_byte bit_depth, color_type, channels;
1605 png_uint_32 w, h, rowbytes, xres, yres;
1606 png_bytep rowptr, trans;
1607 png_bytep *row_pointers;
1608 png_structp png_ptr;
1609 png_infop info_ptr, end_info;
1610 png_colorp palette;
1611 png_textp text_ptr; /* ptr to text_chunk */
1612 MEMIODATA state;
1613 PIX *pix, *pix1;
1614 PIXCMAP *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  *---------------------------------------------------------------------*/
1985 l_ok
1986 pixWriteMemPng(l_uint8 **pfiledata,
1987  size_t *pfilesize,
1988  PIX *pix,
1989  l_float32 gamma)
1990 {
1991 char commentstring[] = "Comment";
1992 l_int32 i, j, k, wpl, d, spp, cmflag, opaque, ncolors, compval, valid;
1993 l_int32 *rmap, *gmap, *bmap, *amap;
1994 l_uint32 *data, *ppixel;
1995 png_byte bit_depth, color_type;
1996 png_byte alpha[256];
1997 png_uint_32 w, h, xres, yres;
1998 png_bytep rowbuffer;
1999 png_structp png_ptr;
2000 png_infop info_ptr;
2001 png_colorp palette;
2002 PIX *pix1;
2003 PIXCMAP *cmap;
2004 char *text;
2005 MEMIODATA 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 /* --------------------------------------------*/
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
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
PIX * pixReadStreamPng(FILE *fp)
pixReadStreamPng()
Definition: pngio.c:187
l_int32 special
Definition: pix.h:151
Definition: pix.h:204
static void memio_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
Definition: pngio.c:1404
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
size_t nbytes
Definition: pixalloc.c:124
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:279
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
void l_pngSetReadStrip16To8(l_int32 flag)
l_pngSetReadStrip16To8()
Definition: pngio.c:1352
l_ok pixWriteStreamPng(FILE *fp, PIX *pix, l_float32 gamma)
pixWriteStreamPng()
Definition: pngio.c:1072
struct MemIOData * m_Next
Definition: pngio.c:1373
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
l_ok pixSetText(PIX *pix, const char *textstring)
pixSetText()
Definition: pix1.c:1536
l_ok pixWritePng(const char *filename, PIX *pix, l_float32 gamma)
pixWritePng()
Definition: pngio.c:972
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:125
l_int32 m_Count
Definition: pngio.c:1371
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
char * text
Definition: pix.h:152
l_ok pixWriteMemPng(l_uint8 **pfiledata, size_t *pfilesize, PIX *pix, l_float32 gamma)
pixWriteMemPng()
Definition: pngio.c:1986
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 pixSetZlibCompression(PIX *pix, l_int32 compval)
pixSetZlibCompression()
Definition: pngio.c:1325
l_ok pixEndianByteSwap(PIX *pixs)
pixEndianByteSwap()
Definition: pix2.c:3074
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
char * m_Buffer
Definition: pngio.c:1370
l_ok pixSetPadBits(PIX *pix, l_int32 val)
pixSetPadBits()
Definition: pix2.c:1382
size_t fnbytesInFile(FILE *fp)
fnbytesInFile()
Definition: utils2.c:1635
l_int32 m_Size
Definition: pngio.c:1372
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
l_ok pixcmapIsOpaque(PIXCMAP *cmap, l_int32 *popaque)
pixcmapIsOpaque()
Definition: colormap.c:1114
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
l_ok pixcmapSetAlpha(PIXCMAP *cmap, l_int32 index, l_int32 aval)
pixcmapSetAlpha()
Definition: colormap.c:1007
l_ok pixcmapIsValid(const PIXCMAP *cmap, PIX *pix, l_int32 *pvalid)
pixcmapIsValid()
Definition: colormap.c:317
PIX * pixEndianByteSwapNew(PIX *pixs)
pixEndianByteSwapNew()
Definition: pix2.c:3010
FILE * fopenWriteStream(const char *filename, const char *modestring)
fopenWriteStream()
Definition: utils2.c:1975
FILE * fopenReadStream(const char *filename)
fopenReadStream()
Definition: utils2.c:1932
l_ok isPngInterlaced(const char *filename, l_int32 *pinterlaced)
isPngInterlaced()
Definition: pngio.c:827
char * pixGetText(PIX *pix)
pixGetText()
Definition: pix1.c:1512
void * array
Definition: pix.h:161
Definition: pix.h:138
PIX * pixReadMemPng(const l_uint8 *filedata, size_t filesize)
pixReadMemPng()
Definition: pngio.c:1595
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:708
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2751
PIXCMAP * pixcmapCopy(const PIXCMAP *cmaps)
pixcmapCopy()
Definition: colormap.c:248
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
struct MemIOData * m_Last
Definition: pngio.c:1375
l_ok pixcmapToArrays(const PIXCMAP *cmap, l_int32 **prmap, l_int32 **pgmap, l_int32 **pbmap, l_int32 **pamap)
pixcmapToArrays()
Definition: colormap.c:2068