Coverage Report

Created: 2025-06-13 06:48

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