Leptonica  1.83.1
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 #include "pix_internal.h"
127 
128 /* --------------------------------------------*/
129 #if HAVE_LIBPNG /* defined in environ.h */
130 /* --------------------------------------------*/
131 
132 #include "png.h"
133 
134 #if HAVE_LIBZ
135 #include "zlib.h"
136 #else
137 #define Z_DEFAULT_COMPRESSION (-1)
138 #endif /* HAVE_LIBZ */
139 
140 /* ------------------ Set default for read option -------------------- */
141  /* Strip 16 bpp --> 8 bpp on reading png; default is for stripping.
142  * If you don't strip, you can't read the gray-alpha spp = 2 images. */
143 static l_int32 var_PNG_STRIP_16_TO_8 = 1;
144 
145 #ifndef NO_CONSOLE_IO
146 #define DEBUG_READ 0
147 #define DEBUG_WRITE 0
148 #endif /* ~NO_CONSOLE_IO */
149 
150 
151 /*---------------------------------------------------------------------*
152  * Reading png through stream *
153  *---------------------------------------------------------------------*/
187 PIX *
189 {
190 l_uint8 byte;
191 l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
192 l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
193 l_uint32 png_transforms;
194 l_uint32 *data, *line, *ppixel;
195 int num_palette, num_text, num_trans;
196 png_byte bit_depth, color_type, channels;
197 png_uint_32 w, h, rowbytes, xres, yres;
198 png_bytep rowptr, trans;
199 png_bytep *row_pointers;
200 png_structp png_ptr;
201 png_infop info_ptr, end_info;
202 png_colorp palette;
203 png_textp text_ptr; /* ptr to text_chunk */
204 PIX *pix, *pix1;
205 PIXCMAP *cmap;
206 
207  if (!fp)
208  return (PIX *)ERROR_PTR("fp not defined", __func__, NULL);
209  pix = NULL;
210 
211  /* Allocate the 3 data structures */
212  if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
213  (png_voidp)NULL, NULL, NULL)) == NULL)
214  return (PIX *)ERROR_PTR("png_ptr not made", __func__, NULL);
215 
216  if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
217  png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
218  return (PIX *)ERROR_PTR("info_ptr not made", __func__, NULL);
219  }
220 
221  if ((end_info = png_create_info_struct(png_ptr)) == NULL) {
222  png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
223  return (PIX *)ERROR_PTR("end_info not made", __func__, NULL);
224  }
225 
226  /* Set up png setjmp error handling */
227  if (setjmp(png_jmpbuf(png_ptr))) {
228  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
229  return (PIX *)ERROR_PTR("internal png error", __func__, NULL);
230  }
231 
232  png_init_io(png_ptr, fp);
233 
234  /* ---------------------------------------------------------- *
235  * - Set the transforms flags. Whatever happens here,
236  * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO.
237  * - Do not use PNG_TRANSFORM_EXPAND, which would
238  * expand all images with bpp < 8 to 8 bpp.
239  * - Strip 16 --> 8 if reading 16-bit gray+alpha
240  * ---------------------------------------------------------- */
241  /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */
242  if (var_PNG_STRIP_16_TO_8 == 1) { /* our default */
243  png_transforms = PNG_TRANSFORM_STRIP_16;
244  } else {
245  png_transforms = PNG_TRANSFORM_IDENTITY;
246  L_INFO("not stripping 16 --> 8 in png reading\n", __func__);
247  }
248 
249  /* Read it */
250  png_read_png(png_ptr, info_ptr, png_transforms, NULL);
251 
252  row_pointers = png_get_rows(png_ptr, info_ptr);
253  w = png_get_image_width(png_ptr, info_ptr);
254  h = png_get_image_height(png_ptr, info_ptr);
255  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
256  rowbytes = png_get_rowbytes(png_ptr, info_ptr);
257  color_type = png_get_color_type(png_ptr, info_ptr);
258  channels = png_get_channels(png_ptr, info_ptr);
259  spp = channels;
260  tRNS = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? 1 : 0;
261 
262  if (spp == 1) {
263  d = bit_depth;
264  } else { /* spp == 2 (gray + alpha), spp == 3 (rgb), spp == 4 (rgba) */
265  d = 4 * bit_depth;
266  }
267 
268  /* Remove if/when this is implemented for all bit_depths */
269  if (spp != 1 && bit_depth != 8) {
270  L_ERROR("spp = %d and bps = %d != 8\n"
271  "turn on 16 --> 8 stripping\n", __func__, spp, bit_depth);
272  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
273  return (PIX *)ERROR_PTR("not implemented for this image",
274  __func__, NULL);
275  }
276 
277  cmap = NULL;
278  if (color_type == PNG_COLOR_TYPE_PALETTE ||
279  color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */
280  png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
281  cmap = pixcmapCreate(d); /* spp == 1 */
282  for (cindex = 0; cindex < num_palette; cindex++) {
283  rval = palette[cindex].red;
284  gval = palette[cindex].green;
285  bval = palette[cindex].blue;
286  pixcmapAddColor(cmap, rval, gval, bval);
287  }
288  }
289 
290  if ((pix = pixCreate(w, h, d)) == NULL) {
291  pixcmapDestroy(&cmap);
292  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
293  return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
294  }
295  pixSetInputFormat(pix, IFF_PNG);
296  wpl = pixGetWpl(pix);
297  data = pixGetData(pix);
298  pixSetSpp(pix, spp);
299  if (pixSetColormap(pix, cmap)) {
300  pixDestroy(&pix);
301  return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL);
302  }
303 
304  if (spp == 1 && !tRNS) { /* copy straight from buffer to pix */
305  for (i = 0; i < h; i++) {
306  line = data + i * wpl;
307  rowptr = row_pointers[i];
308  for (j = 0; j < rowbytes; j++) {
309  SET_DATA_BYTE(line, j, rowptr[j]);
310  }
311  }
312  } else if (spp == 2) { /* grayscale + alpha; convert to RGBA */
313  L_INFO("converting (gray + alpha) ==> RGBA\n", __func__);
314  for (i = 0; i < h; i++) {
315  ppixel = data + i * wpl;
316  rowptr = row_pointers[i];
317  for (j = k = 0; j < w; j++) {
318  /* Copy gray value into r, g and b */
319  SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k]);
320  SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k]);
321  SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
322  SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
323  ppixel++;
324  }
325  }
326  pixSetSpp(pix, 4); /* we do not support 2 spp pix */
327  } else if (spp == 3 || spp == 4) {
328  for (i = 0; i < h; i++) {
329  ppixel = data + i * wpl;
330  rowptr = row_pointers[i];
331  for (j = k = 0; j < w; j++) {
332  SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]);
333  SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]);
334  SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]);
335  if (spp == 3) /* set to opaque; some readers are buggy */
336  SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, 255);
337  else /* spp == 4 */
338  SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]);
339  ppixel++;
340  }
341  }
342  }
343 
344  /* Special spp == 1 cases with transparency:
345  * (1) 8 bpp without colormap; assume full transparency
346  * (2) 1 bpp with colormap + trans array (for alpha)
347  * (3) 2 bpp with colormap + trans array (for alpha)
348  * (4) 4 bpp with colormap + trans array (for alpha)
349  * (5) 8 bpp with colormap + trans array (for alpha)
350  * These all require converting to RGBA */
351  if (spp == 1 && tRNS) {
352  if (!cmap) {
353  /* Case 1: make fully transparent RGBA image */
354  L_INFO("transparency, 1 spp, no colormap, no transparency array: "
355  "convention is fully transparent image\n", __func__);
356  L_INFO("converting (fully transparent 1 spp) ==> RGBA\n", __func__);
357  pixDestroy(&pix);
358  pix = pixCreate(w, h, 32); /* init to alpha = 0 (transparent) */
359  pixSetSpp(pix, 4);
360  } else {
361  L_INFO("converting (cmap + alpha) ==> RGBA\n", __func__);
362 
363  /* Grab the transparency array */
364  png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
365  if (!trans) { /* invalid png file */
366  pixDestroy(&pix);
367  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
368  return (PIX *)ERROR_PTR("cmap, tRNS, but no transparency array",
369  __func__, NULL);
370  }
371 
372  /* Save the cmap and destroy the pix */
373  cmap = pixcmapCopy(pixGetColormap(pix));
374  ncolors = pixcmapGetCount(cmap);
375  pixDestroy(&pix);
376 
377  /* Start over with 32 bit RGBA */
378  pix = pixCreate(w, h, 32);
379  wpl = pixGetWpl(pix);
380  data = pixGetData(pix);
381  pixSetSpp(pix, 4);
382 
383 #if DEBUG_READ
384  lept_stderr("ncolors = %d, num_trans = %d\n",
385  ncolors, num_trans);
386  for (i = 0; i < ncolors; i++) {
387  pixcmapGetColor(cmap, i, &rval, &gval, &bval);
388  if (i < num_trans) {
389  lept_stderr("(r,g,b,a) = (%d,%d,%d,%d)\n",
390  rval, gval, bval, trans[i]);
391  } else {
392  lept_stderr("(r,g,b,a) = (%d,%d,%d,<<255>>)\n",
393  rval, gval, bval);
394  }
395  }
396 #endif /* DEBUG_READ */
397 
398  /* Extract the data and convert to RGBA */
399  if (d == 1) {
400  /* Case 2: 1 bpp with transparency (usually) behind white */
401  L_INFO("converting 1 bpp cmap with alpha ==> RGBA\n", __func__);
402  if (num_trans == 1)
403  L_INFO("num_trans = 1; second color opaque by default\n",
404  __func__);
405  for (i = 0; i < h; i++) {
406  ppixel = data + i * wpl;
407  rowptr = row_pointers[i];
408  for (j = 0, index = 0; j < rowbytes; j++) {
409  byte = rowptr[j];
410  for (k = 0; k < 8 && index < w; k++, index++) {
411  bitval = (byte >> (7 - k)) & 1;
412  pixcmapGetColor(cmap, bitval, &rval, &gval, &bval);
413  composeRGBPixel(rval, gval, bval, ppixel);
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__);
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  *---------------------------------------------------------------------*/
573 l_ok
574 readHeaderPng(const char *filename,
575  l_int32 *pw,
576  l_int32 *ph,
577  l_int32 *pbps,
578  l_int32 *pspp,
579  l_int32 *piscmap)
580 {
581 l_int32 ret;
582 FILE *fp;
583 
584  if (pw) *pw = 0;
585  if (ph) *ph = 0;
586  if (pbps) *pbps = 0;
587  if (pspp) *pspp = 0;
588  if (piscmap) *piscmap = 0;
589  if (!filename)
590  return ERROR_INT("filename not defined", __func__, 1);
591  if ((fp = fopenReadStream(filename)) == NULL)
592  return ERROR_INT("image file not found", __func__, 1);
593  ret = freadHeaderPng(fp, pw, ph, pbps, pspp, piscmap);
594  fclose(fp);
595  return ret;
596 }
597 
598 
615 l_ok
616 freadHeaderPng(FILE *fp,
617  l_int32 *pw,
618  l_int32 *ph,
619  l_int32 *pbps,
620  l_int32 *pspp,
621  l_int32 *piscmap)
622 {
623 l_int32 nbytes, ret;
624 l_uint8 data[40];
625 
626  if (pw) *pw = 0;
627  if (ph) *ph = 0;
628  if (pbps) *pbps = 0;
629  if (pspp) *pspp = 0;
630  if (piscmap) *piscmap = 0;
631  if (!fp)
632  return ERROR_INT("stream not defined", __func__, 1);
633 
634  nbytes = fnbytesInFile(fp);
635  if (nbytes < 40)
636  return ERROR_INT("file too small to be png", __func__, 1);
637  if (fread(data, 1, 40, fp) != 40)
638  return ERROR_INT("error reading data", __func__, 1);
639  ret = readHeaderMemPng(data, 40, pw, ph, pbps, pspp, piscmap);
640  return ret;
641 }
642 
643 
670 l_ok
671 readHeaderMemPng(const l_uint8 *data,
672  size_t size,
673  l_int32 *pw,
674  l_int32 *ph,
675  l_int32 *pbps,
676  l_int32 *pspp,
677  l_int32 *piscmap)
678 {
679 l_uint16 twobytes;
680 l_uint16 *pshort;
681 l_int32 colortype, w, h, bps, spp;
682 l_uint32 *pword;
683 
684  if (pw) *pw = 0;
685  if (ph) *ph = 0;
686  if (pbps) *pbps = 0;
687  if (pspp) *pspp = 0;
688  if (piscmap) *piscmap = 0;
689  if (!data)
690  return ERROR_INT("data not defined", __func__, 1);
691  if (size < 40)
692  return ERROR_INT("size < 40", __func__, 1);
693 
694  /* Check password */
695  if (data[0] != 137 || data[1] != 80 || data[2] != 78 ||
696  data[3] != 71 || data[4] != 13 || data[5] != 10 ||
697  data[6] != 26 || data[7] != 10)
698  return ERROR_INT("not a valid png file", __func__, 1);
699 
700  pword = (l_uint32 *)data;
701  pshort = (l_uint16 *)data;
702  w = convertOnLittleEnd32(pword[4]);
703  h = convertOnLittleEnd32(pword[5]);
704  if (w < 1 || h < 1)
705  return ERROR_INT("invalid w or h", __func__, 1);
706  twobytes = convertOnLittleEnd16(pshort[12]); /* contains depth/sample */
707  /* and the color type */
708  colortype = twobytes & 0xff; /* color type */
709  bps = twobytes >> 8; /* bits/sample */
710 
711  /* Special case with alpha that is extracted as RGBA.
712  * Note that the cmap+alpha is also extracted as RGBA,
713  * but only if the tRNS chunk exists, which we can't tell
714  * by this simple parser.*/
715  if (colortype == 4)
716  L_INFO("gray + alpha: will extract as RGBA (spp = 4)\n", __func__);
717 
718  if (colortype == 2) { /* RGB */
719  spp = 3;
720  } else if (colortype == 6) { /* RGBA */
721  spp = 4;
722  } else if (colortype == 4) { /* gray + alpha */
723  spp = 2;
724  bps = 8; /* both the gray and alpha are 8-bit samples */
725  } else { /* gray (0) or cmap (3) or cmap+alpha (3) */
726  spp = 1;
727  }
728  if (bps < 1 || bps > 16) {
729  L_ERROR("invalid bps = %d\n", __func__, bps);
730  return 1;
731  }
732  if (pw) *pw = w;
733  if (ph) *ph = h;
734  if (pbps) *pbps = bps;
735  if (pspp) *pspp = spp;
736  if (piscmap) {
737  if (colortype & 1) /* palette */
738  *piscmap = 1;
739  else
740  *piscmap = 0;
741  }
742 
743  return 0;
744 }
745 
746 
747 /*---------------------------------------------------------------------*
748  * Reading png metadata *
749  *---------------------------------------------------------------------*/
750 /*
751  * fgetPngResolution()
752  *
753  * Input: fp (file stream opened for read)
754  * &xres, &yres (<return> resolution in ppi)
755  * Return: 0 if OK; 1 on error
756  *
757  * Notes:
758  * (1) If neither resolution field is set, this is not an error;
759  * the returned resolution values are 0 (designating 'unknown').
760  * (2) Side-effect: this rewinds the stream.
761  */
762 l_int32
763 fgetPngResolution(FILE *fp,
764  l_int32 *pxres,
765  l_int32 *pyres)
766 {
767 png_uint_32 xres, yres;
768 png_structp png_ptr;
769 png_infop info_ptr;
770 
771  if (pxres) *pxres = 0;
772  if (pyres) *pyres = 0;
773  if (!fp)
774  return ERROR_INT("stream not opened", __func__, 1);
775  if (!pxres || !pyres)
776  return ERROR_INT("&xres and &yres not both defined", __func__, 1);
777 
778  /* Make the two required structs */
779  if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
780  (png_voidp)NULL, NULL, NULL)) == NULL)
781  return ERROR_INT("png_ptr not made", __func__, 1);
782  if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
783  png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
784  return ERROR_INT("info_ptr not made", __func__, 1);
785  }
786 
787  /* Set up png setjmp error handling.
788  * Without this, an error calls exit. */
789  if (setjmp(png_jmpbuf(png_ptr))) {
790  png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
791  return ERROR_INT("internal png error", __func__, 1);
792  }
793 
794  /* Read the metadata */
795  rewind(fp);
796  png_init_io(png_ptr, fp);
797  png_read_info(png_ptr, info_ptr);
798 
799  xres = png_get_x_pixels_per_meter(png_ptr, info_ptr);
800  yres = png_get_y_pixels_per_meter(png_ptr, info_ptr);
801  *pxres = (l_int32)((l_float32)xres / 39.37 + 0.5); /* to ppi */
802  *pyres = (l_int32)((l_float32)yres / 39.37 + 0.5);
803 
804  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
805  rewind(fp);
806  return 0;
807 }
808 
809 
817 l_ok
818 isPngInterlaced(const char *filename,
819  l_int32 *pinterlaced)
820 {
821 l_uint8 buf[32];
822 FILE *fp;
823 
824  if (!pinterlaced)
825  return ERROR_INT("&interlaced not defined", __func__, 1);
826  *pinterlaced = 0;
827  if (!filename)
828  return ERROR_INT("filename not defined", __func__, 1);
829 
830  if ((fp = fopenReadStream(filename)) == NULL)
831  return ERROR_INT("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  */
859 l_ok
860 fgetPngColormapInfo(FILE *fp,
861  PIXCMAP **pcmap,
862  l_int32 *ptransparency)
863 {
864 l_int32 i, cindex, rval, gval, bval, num_palette, num_trans;
865 png_byte bit_depth, color_type;
866 png_bytep trans;
867 png_colorp palette;
868 png_structp png_ptr;
869 png_infop info_ptr;
870 
871  if (pcmap) *pcmap = NULL;
872  if (ptransparency) *ptransparency = 0;
873  if (!pcmap && !ptransparency)
874  return ERROR_INT("no output defined", __func__, 1);
875  if (!fp)
876  return ERROR_INT("stream not opened", __func__, 1);
877 
878  /* Make the two required structs */
879  if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
880  (png_voidp)NULL, NULL, NULL)) == NULL)
881  return ERROR_INT("png_ptr not made", __func__, 1);
882  if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) {
883  png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
884  return ERROR_INT("info_ptr not made", __func__, 1);
885  }
886 
887  /* Set up png setjmp error handling.
888  * Without this, an error calls exit. */
889  if (setjmp(png_jmpbuf(png_ptr))) {
890  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
891  if (pcmap && *pcmap) pixcmapDestroy(pcmap);
892  return ERROR_INT("internal png error", __func__, 1);
893  }
894 
895  /* Read the metadata and check if there is a colormap */
896  rewind(fp);
897  png_init_io(png_ptr, fp);
898  png_read_info(png_ptr, info_ptr);
899  color_type = png_get_color_type(png_ptr, info_ptr);
900  if (color_type != PNG_COLOR_TYPE_PALETTE &&
901  color_type != PNG_COLOR_MASK_PALETTE) {
902  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
903  return 0;
904  }
905 
906  /* Optionally, read the colormap */
907  if (pcmap) {
908  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
909  png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
910  *pcmap = pixcmapCreate(bit_depth); /* spp == 1 */
911  for (cindex = 0; cindex < num_palette; cindex++) {
912  rval = palette[cindex].red;
913  gval = palette[cindex].green;
914  bval = palette[cindex].blue;
915  pixcmapAddColor(*pcmap, rval, gval, bval);
916  }
917  }
918 
919  /* Optionally, look for transparency. Note that the colormap
920  * has been initialized to fully opaque. */
921  if (ptransparency && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
922  png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
923  if (trans) {
924  for (i = 0; i < num_trans; i++) {
925  if (trans[i] < 255) { /* not fully opaque */
926  *ptransparency = 1;
927  if (pcmap) pixcmapSetAlpha(*pcmap, i, trans[i]);
928  }
929  }
930  } else {
931  L_ERROR("transparency array not returned\n", __func__);
932  }
933  }
934 
935  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
936  rewind(fp);
937  return 0;
938 }
939 
940 
941 /*---------------------------------------------------------------------*
942  * Writing png through stream *
943  *---------------------------------------------------------------------*/
958 l_ok
959 pixWritePng(const char *filename,
960  PIX *pix,
961  l_float32 gamma)
962 {
963 FILE *fp;
964 
965  if (!pix)
966  return ERROR_INT("pix not defined", __func__, 1);
967  if (!filename)
968  return ERROR_INT("filename not defined", __func__, 1);
969 
970  if ((fp = fopenWriteStream(filename, "wb+")) == NULL)
971  return ERROR_INT("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 
1056 l_ok
1058  PIX *pix,
1059  l_float32 gamma)
1060 {
1061 char commentstring[] = "Comment";
1062 l_int32 i, j, k, wpl, d, spp, compval, valid;
1063 l_int32 cmflag, opaque, max_trans, ncolors;
1064 l_int32 *rmap, *gmap, *bmap, *amap;
1065 l_uint32 *data, *ppixel;
1066 png_byte bit_depth, color_type;
1067 png_byte alpha[256];
1068 png_uint_32 w, h;
1069 png_uint_32 xres, yres;
1070 png_bytep *row_pointers;
1071 png_bytep rowbuffer;
1072 png_structp png_ptr;
1073 png_infop info_ptr;
1074 png_colorp palette;
1075 PIX *pix1;
1076 PIXCMAP *cmap;
1077 char *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 
1307 l_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  *---------------------------------------------------------------------*/
1332 void
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;
1354  struct MemIOData *m_Next;
1356  struct MemIOData *m_Last;
1359 };
1360 typedef struct MemIOData MEMIODATA;
1361 
1362 static void memio_png_write_data(png_structp png_ptr, png_bytep data,
1363  png_size_t length);
1364 static void memio_png_flush(MEMIODATA* pthing);
1365 static void memio_png_read_data(png_structp png_ptr, png_bytep outBytes,
1366  png_size_t byteCountToRead);
1367 static void memio_free(MEMIODATA* pthing);
1368 
1369 static const l_int32 MEMIO_BUFFER_SIZE = 8192;
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  */
1384 static void
1385 memio_png_write_data(png_structp png_ptr,
1386  png_bytep data,
1387  png_size_t len)
1388 {
1389 MEMIODATA *thing, *last;
1390 l_int32 written = 0;
1391 l_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  */
1449 static void
1450 memio_png_flush(MEMIODATA *pthing)
1451 {
1452 l_int32 amount = 0;
1453 l_int32 copied = 0;
1454 MEMIODATA *buffer = 0;
1455 char *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  */
1512 static void
1513 memio_png_read_data(png_structp png_ptr,
1514  png_bytep outBytes,
1515  png_size_t byteCountToRead)
1516 {
1517 MEMIODATA *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  */
1539 static void
1540 memio_free(MEMIODATA* pthing)
1541 {
1542 MEMIODATA *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  *---------------------------------------------------------------------*/
1575 PIX *
1576 pixReadMemPng(const l_uint8 *filedata,
1577  size_t filesize)
1578 {
1579 l_uint8 byte;
1580 l_int32 i, j, k, index, ncolors, rval, gval, bval, valid;
1581 l_int32 wpl, d, spp, cindex, bitval, bival, quadval, tRNS;
1582 l_uint32 png_transforms;
1583 l_uint32 *data, *line, *ppixel;
1584 int num_palette, num_text, num_trans;
1585 png_byte bit_depth, color_type, channels;
1586 png_uint_32 w, h, rowbytes, xres, yres;
1587 png_bytep rowptr, trans;
1588 png_bytep *row_pointers;
1589 png_structp png_ptr;
1590 png_infop info_ptr, end_info;
1591 png_colorp palette;
1592 png_textp text_ptr; /* ptr to text_chunk */
1593 MEMIODATA state;
1594 PIX *pix, *pix1;
1595 PIXCMAP *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 {
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  *---------------------------------------------------------------------*/
1964 l_ok
1965 pixWriteMemPng(l_uint8 **pfiledata,
1966  size_t *pfilesize,
1967  PIX *pix,
1968  l_float32 gamma)
1969 {
1970 char commentstring[] = "Comment";
1971 l_int32 i, j, k, wpl, d, spp, cmflag, opaque, ncolors, compval, valid;
1972 l_int32 *rmap, *gmap, *bmap, *amap;
1973 l_uint32 *data, *ppixel;
1974 png_byte bit_depth, color_type;
1975 png_byte alpha[256];
1976 png_uint_32 w, h, xres, yres;
1977 png_bytep rowbuffer;
1978 png_structp png_ptr;
1979 png_infop info_ptr;
1980 png_colorp palette;
1981 PIX *pix1;
1982 PIXCMAP *cmap;
1983 char *text;
1984 MEMIODATA 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)
Definition: arrayaccess.h:188
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
l_ok pixcmapSetAlpha(PIXCMAP *cmap, l_int32 index, l_int32 aval)
pixcmapSetAlpha()
Definition: colormap.c:962
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:272
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:683
l_ok pixcmapIsValid(const PIXCMAP *cmap, PIX *pix, l_int32 *pvalid)
pixcmapIsValid()
Definition: colormap.c:308
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:126
l_ok pixcmapNonOpaqueColorsInfo(PIXCMAP *cmap, l_int32 *pntrans, l_int32 *pmax_trans, l_int32 *pmin_opaque)
pixcmapNonOpaqueColorsInfo()
Definition: colormap.c:1120
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:789
l_ok pixcmapIsOpaque(PIXCMAP *cmap, l_int32 *popaque)
pixcmapIsOpaque()
Definition: colormap.c:1063
l_ok pixcmapToArrays(const PIXCMAP *cmap, l_int32 **prmap, l_int32 **pgmap, l_int32 **pbmap, l_int32 **pamap)
pixcmapToArrays()
Definition: colormap.c:1981
PIXCMAP * pixcmapCopy(const PIXCMAP *cmaps)
pixcmapCopy()
Definition: colormap.c:243
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:403
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1642
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1582
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
char * pixGetText(PIX *pix)
pixGetText()
Definition: pix1.c:1408
l_ok pixSetText(PIX *pix, const char *textstring)
pixSetText()
Definition: pix1.c:1430
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixEndianByteSwapNew(PIX *pixs)
pixEndianByteSwapNew()
Definition: pix2.c:2979
l_ok pixSetPadBits(PIX *pix, l_int32 val)
pixSetPadBits()
Definition: pix2.c:1346
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2728
l_ok pixEndianByteSwap(PIX *pixs)
pixEndianByteSwap()
Definition: pix2.c:3041
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1481
@ 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
@ REMOVE_CMAP_BASED_ON_SRC
Definition: pix.h:384
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:324
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
PIX * pixReadMemPng(const l_uint8 *filedata, size_t filesize)
pixReadMemPng()
Definition: pngio.c:1576
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
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
void l_pngSetReadStrip16To8(l_int32 flag)
l_pngSetReadStrip16To8()
Definition: pngio.c:1333
PIX * pixReadStreamPng(FILE *fp)
pixReadStreamPng()
Definition: pngio.c:188
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
void * array
Definition: pix_internal.h:202
char * text
Definition: pix_internal.h:194
l_int32 special
Definition: pix_internal.h:193
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
size_t fnbytesInFile(FILE *fp)
fnbytesInFile()
Definition: utils2.c:1581
FILE * fopenWriteStream(const char *filename, const char *modestring)
fopenWriteStream()
Definition: utils2.c:1905
FILE * fopenReadStream(const char *filename)
fopenReadStream()
Definition: utils2.c:1864