Coverage Report

Created: 2024-07-27 06:28

/src/leptonica/src/pnmio.c
Line
Count
Source (jump to first uncovered line)
1
/*====================================================================*
2
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
3
 -
4
 -  Redistribution and use in source and binary forms, with or without
5
 -  modification, are permitted provided that the following conditions
6
 -  are met:
7
 -  1. Redistributions of source code must retain the above copyright
8
 -     notice, this list of conditions and the following disclaimer.
9
 -  2. Redistributions in binary form must reproduce the above
10
 -     copyright notice, this list of conditions and the following
11
 -     disclaimer in the documentation and/or other materials
12
 -     provided with the distribution.
13
 -
14
 -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
 -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16
 -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17
 -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18
 -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19
 -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
 -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21
 -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22
 -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23
 -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
 -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 *====================================================================*/
26
27
/*!
28
 * \file pnmio.c
29
 * <pre>
30
 *
31
 *      Stream interface
32
 *          PIX             *pixReadStreamPnm()
33
 *          l_int32          readHeaderPnm()
34
 *          l_int32          freadHeaderPnm()
35
 *          l_int32          pixWriteStreamPnm()
36
 *          l_int32          pixWriteStreamAsciiPnm()
37
 *          l_int32          pixWriteStreamPam()
38
 *
39
 *      Read/write to memory
40
 *          PIX             *pixReadMemPnm()
41
 *          l_int32          readHeaderMemPnm()
42
 *          l_int32          pixWriteMemPnm()
43
 *          l_int32          pixWriteMemPam()
44
 *
45
 *      Local helpers
46
 *          static l_int32   pnmReadNextAsciiValue();
47
 *          static l_int32   pnmReadNextNumber();
48
 *          static l_int32   pnmReadNextString();
49
 *          static l_int32   pnmSkipCommentLines();
50
 *
51
 *      These are here by popular demand, with the help of Mattias
52
 *      Kregert (mattias@kregert.se), who provided the first implementation.
53
 *
54
 *      The pnm formats are exceedingly simple, because they have
55
 *      no compression and no colormaps.  They support images that
56
 *      are 1 bpp; 2, 4, 8 and 16 bpp grayscale; and rgb.
57
 *
58
 *      The original pnm formats ("ASCII") are included for completeness,
59
 *      but their use is deprecated for all but tiny iconic images.
60
 *      They are extremely wasteful of memory; for example, the P1 binary
61
 *      ASCII format is 16 times as big as the packed uncompressed
62
 *      format, because 2 characters are used to represent every bit
63
 *      (pixel) in the image.  Reading is slow because we check for extra
64
 *      white space and EOL at every sample value.
65
 *
66
 *      The packed pnm formats ("raw") give file sizes similar to
67
 *      bmp files, which are uncompressed packed.  However, bmp
68
 *      are more flexible, because they can support colormaps.
69
 *
70
 *      We don't differentiate between the different types ("pbm",
71
 *      "pgm", "ppm") at the interface level, because this is really a
72
 *      "distinction without a difference."  You read a file, you get
73
 *      the appropriate Pix.  You write a file from a Pix, you get the
74
 *      appropriate type of file.  If there is a colormap on the Pix,
75
 *      and the Pix is more than 1 bpp, you get either an 8 bpp pgm
76
 *      or a 24 bpp RGB pnm, depending on whether the colormap colors
77
 *      are gray or rgb, respectively.
78
 *
79
 *      This follows the general policy that the I/O routines don't
80
 *      make decisions about the content of the image -- you do that
81
 *      with image processing before you write it out to file.
82
 *      The I/O routines just try to make the closest connection
83
 *      possible between the file and the Pix in memory.
84
 *
85
 *      On systems like Windows without fmemopen() and open_memstream(),
86
 *      we write data to a temp file and read it back for operations
87
 *      between pix and compressed-data, such as pixReadMemPnm() and
88
 *      pixWriteMemPnm().
89
 *
90
 *      The P7 format is new. It introduced a header with multiple
91
 *      lines containing distinct tags for the various fields.
92
 *      See: http://netpbm.sourceforge.net/doc/pam.html
93
 *
94
 *        WIDTH <int>         ; mandatory, exactly once
95
 *        HEIGHT <int>        ; mandatory, exactly once
96
 *        DEPTH <int>         ; mandatory, exactly once,
97
 *                            ; its meaning is equivalent to spp
98
 *        MAXVAL <int>        ; mandatory, one of 1, 3, 15, 255 or 65535
99
 *        TUPLTYPE <string>   ; optional; BLACKANDWHITE, GRAYSCALE, RGB
100
 *                            ; and optional suffix _ALPHA, e.g. RGB_ALPHA
101
 *        ENDHDR              ; mandatory, last header line
102
 *
103
 *      Reading BLACKANDWHITE_ALPHA and GRAYSCALE_ALPHA, which have a DEPTH
104
 *      value of 2, is supported. The original image is converted to a Pix
105
 *      with 32-bpp and alpha channel (spp == 4).
106
 *
107
 *      Writing P7 format is currently selected for 32-bpp with alpha
108
 *      channel, i.e. for Pix which have spp == 4, using pixWriteStreamPam().
109
 *
110
 *      Jürgen Buchmüller provided the implementation for the P7 (pam) format.
111
 *
112
 *      Giulio Lunati made an elegant reimplementation of the static helper
113
 *      functions using fscanf() instead of fseek(), so that it works with
114
 *      pnm data from stdin.
115
 * </pre>
116
 */
117
118
#ifdef HAVE_CONFIG_H
119
#include <config_auto.h>
120
#endif  /* HAVE_CONFIG_H */
121
122
#include <string.h>
123
#include <ctype.h>
124
#include "allheaders.h"
125
126
/* --------------------------------------------*/
127
#if  USE_PNMIO   /* defined in environ.h */
128
/* --------------------------------------------*/
129
130
static l_int32 pnmReadNextAsciiValue(FILE  *fp, l_int32 *pval);
131
static l_int32 pnmReadNextNumber(FILE *fp, l_int32 *pval);
132
static l_int32 pnmReadNextString(FILE *fp, char *buff, l_int32 size);
133
static l_int32 pnmSkipCommentLines(FILE  *fp);
134
135
    /* a sanity check on the size read from file */
136
static const l_int32  MAX_PNM_WIDTH = 100000;
137
static const l_int32  MAX_PNM_HEIGHT = 100000;
138
139
140
/*--------------------------------------------------------------------*
141
 *                          Stream interface                          *
142
 *--------------------------------------------------------------------*/
143
/*!
144
 * \brief   pixReadStreamPnm()
145
 *
146
 * \param[in]    fp   file stream opened for read
147
 * \return  pix, or NULL on error
148
 */
149
PIX *
150
pixReadStreamPnm(FILE  *fp)
151
0
{
152
0
l_uint8    val8, rval8, gval8, bval8, aval8, mask8;
153
0
l_uint16   val16, rval16, gval16, bval16, aval16;
154
0
l_int32    w, h, d, bps, spp, bpl, wpl, i, j, type;
155
0
l_int32    val, rval, gval, bval;
156
0
l_uint32   rgbval;
157
0
l_uint32  *line, *data;
158
0
PIX       *pix;
159
160
0
    if (!fp)
161
0
        return (PIX *)ERROR_PTR("fp not defined", __func__, NULL);
162
163
0
    if (freadHeaderPnm(fp, &w, &h, &d, &type, &bps, &spp))
164
0
        return (PIX *)ERROR_PTR("header read failed", __func__, NULL);
165
0
    if (bps < 1 || bps > 16)
166
0
        return (PIX *)ERROR_PTR("invalid bps", __func__, NULL);
167
0
    if (spp < 1 || spp > 4)
168
0
        return (PIX *)ERROR_PTR("invalid spp", __func__, NULL);
169
0
    if ((pix = pixCreate(w, h, d)) == NULL)
170
0
        return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
171
0
    pixSetInputFormat(pix, IFF_PNM);
172
0
    data = pixGetData(pix);
173
0
    wpl = pixGetWpl(pix);
174
175
        /* If type == 6 and bps == 16, we use the code in type 7
176
         * to read 6 bytes/pixel from the input file. */
177
0
    if (type == 6 && bps == 16)
178
0
        type = 7;
179
180
0
    switch (type) {
181
0
    case 1:
182
0
    case 2:
183
        /* Old "ASCII" binary or gray format */
184
0
        for (i = 0; i < h; i++) {
185
0
            for (j = 0; j < w; j++) {
186
0
                if (pnmReadNextAsciiValue(fp, &val)) {
187
0
                    pixDestroy(&pix);
188
0
                    return (PIX *)ERROR_PTR("read abend", __func__, NULL);
189
0
                }
190
0
                pixSetPixel(pix, j, i, val);
191
0
            }
192
0
        }
193
0
        break;
194
195
0
    case 3:
196
        /* Old "ASCII" rgb format */
197
0
        for (i = 0; i < h; i++) {
198
0
            for (j = 0; j < w; j++) {
199
0
                if (pnmReadNextAsciiValue(fp, &rval)) {
200
0
                    pixDestroy(&pix);
201
0
                    return (PIX *)ERROR_PTR("read abend", __func__, NULL);
202
0
                }
203
0
                if (pnmReadNextAsciiValue(fp, &gval)) {
204
0
                    pixDestroy(&pix);
205
0
                    return (PIX *)ERROR_PTR("read abend", __func__, NULL);
206
0
                }
207
0
                if (pnmReadNextAsciiValue(fp, &bval)) {
208
0
                    pixDestroy(&pix);
209
0
                    return (PIX *)ERROR_PTR("read abend", __func__, NULL);
210
0
                }
211
0
                composeRGBPixel(rval, gval, bval, &rgbval);
212
0
                pixSetPixel(pix, j, i, rgbval);
213
0
            }
214
0
        }
215
0
        break;
216
217
0
    case 4:
218
        /* "raw" format for 1 bpp */
219
0
        bpl = (d * w + 7) / 8;
220
0
        for (i = 0; i < h; i++) {
221
0
            line = data + i * wpl;
222
0
            for (j = 0; j < bpl; j++) {
223
0
                if (fread(&val8, 1, 1, fp) != 1) {
224
0
                    pixDestroy(&pix);
225
0
                    return (PIX *)ERROR_PTR("read error in 4", __func__, NULL);
226
0
                }
227
0
                SET_DATA_BYTE(line, j, val8);
228
0
            }
229
0
        }
230
0
        break;
231
232
0
    case 5:
233
        /* "raw" format for grayscale */
234
0
        for (i = 0; i < h; i++) {
235
0
            line = data + i * wpl;
236
0
            if (d != 16) {
237
0
                for (j = 0; j < w; j++) {
238
0
                    if (fread(&val8, 1, 1, fp) != 1) {
239
0
                        pixDestroy(&pix);
240
0
                        return (PIX *)ERROR_PTR("error in 5", __func__, NULL);
241
0
                    }
242
0
                    if (d == 2)
243
0
                        SET_DATA_DIBIT(line, j, val8);
244
0
                    else if (d == 4)
245
0
                        SET_DATA_QBIT(line, j, val8);
246
0
                    else  /* d == 8 */
247
0
                        SET_DATA_BYTE(line, j, val8);
248
0
                }
249
0
            } else {  /* d == 16 */
250
0
                for (j = 0; j < w; j++) {
251
0
                    if (fread(&val16, 2, 1, fp) != 1) {
252
0
                        pixDestroy(&pix);
253
0
                        return (PIX *)ERROR_PTR("16 bpp error", __func__, NULL);
254
0
                    }
255
0
                    SET_DATA_TWO_BYTES(line, j, val16);
256
0
                }
257
0
            }
258
0
        }
259
0
        break;
260
261
0
    case 6:
262
        /* "raw" format, type == 6; 8 bps, rgb */
263
0
        for (i = 0; i < h; i++) {
264
0
            line = data + i * wpl;
265
0
            for (j = 0; j < wpl; j++) {
266
0
                if (fread(&rval8, 1, 1, fp) != 1) {
267
0
                    pixDestroy(&pix);
268
0
                    return (PIX *)ERROR_PTR("read error type 6",
269
0
                                            __func__, NULL);
270
0
                }
271
0
                if (fread(&gval8, 1, 1, fp) != 1) {
272
0
                    pixDestroy(&pix);
273
0
                    return (PIX *)ERROR_PTR("read error type 6",
274
0
                                            __func__, NULL);
275
0
                }
276
0
                if (fread(&bval8, 1, 1, fp) != 1) {
277
0
                    pixDestroy(&pix);
278
0
                    return (PIX *)ERROR_PTR("read error type 6",
279
0
                                            __func__, NULL);
280
0
                }
281
0
                composeRGBPixel(rval8, gval8, bval8, &rgbval);
282
0
                line[j] = rgbval;
283
0
            }
284
0
        }
285
0
        break;
286
287
0
    case 7:
288
        /* "arbitrary" format; type == 7; */
289
0
        if (bps != 16) {
290
0
            mask8 = (1 << bps) - 1;
291
0
            switch (spp) {
292
0
            case 1: /* 1, 2, 4, 8 bpp grayscale */
293
0
                for (i = 0; i < h; i++) {
294
0
                    for (j = 0; j < w; j++) {
295
0
                        if (fread(&val8, 1, 1, fp) != 1) {
296
0
                            pixDestroy(&pix);
297
0
                            return (PIX *)ERROR_PTR("read error type 7",
298
0
                                                    __func__, NULL);
299
0
                        }
300
0
                        val8 = val8 & mask8;
301
0
                        if (bps == 1) val8 ^= 1;  /* white-is-1 photometry */
302
0
                        pixSetPixel(pix, j, i, val8);
303
0
                    }
304
0
                }
305
0
                break;
306
307
0
            case 2: /* 1, 2, 4, 8 bpp grayscale + alpha */
308
0
                for (i = 0; i < h; i++) {
309
0
                    for (j = 0; j < w; j++) {
310
0
                        if (fread(&val8, 1, 1, fp) != 1) {
311
0
                            pixDestroy(&pix);
312
0
                            return (PIX *)ERROR_PTR("read error type 7",
313
0
                                                    __func__, NULL);
314
0
                        }
315
0
                        if (fread(&aval8, 1, 1, fp) != 1) {
316
0
                            pixDestroy(&pix);
317
0
                            return (PIX *)ERROR_PTR("read error type 7",
318
0
                                                    __func__, NULL);
319
0
                        }
320
0
                        val8 = val8 & mask8;
321
0
                        aval8 = aval8 & mask8;
322
0
                        composeRGBAPixel(val8, val8, val8, aval8, &rgbval);
323
0
                        pixSetPixel(pix, j, i, rgbval);
324
0
                    }
325
0
                }
326
0
                pixSetSpp(pix, 4);
327
0
                break;
328
329
0
            case 3: /* rgb */
330
0
                for (i = 0; i < h; i++) {
331
0
                    line = data + i * wpl;
332
0
                    for (j = 0; j < wpl; j++) {
333
0
                        if (fread(&rval8, 1, 1, fp) != 1) {
334
0
                            pixDestroy(&pix);
335
0
                            return (PIX *)ERROR_PTR("read error type 7",
336
0
                                                    __func__, NULL);
337
0
                        }
338
0
                        if (fread(&gval8, 1, 1, fp) != 1) {
339
0
                            pixDestroy(&pix);
340
0
                            return (PIX *)ERROR_PTR("read error type 7",
341
0
                                                    __func__, NULL);
342
0
                        }
343
0
                        if (fread(&bval8, 1, 1, fp) != 1) {
344
0
                            pixDestroy(&pix);
345
0
                            return (PIX *)ERROR_PTR("read error type 7",
346
0
                                                    __func__, NULL);
347
0
                        }
348
0
                        rval8 = rval8 & mask8;
349
0
                        gval8 = gval8 & mask8;
350
0
                        bval8 = bval8 & mask8;
351
0
                        composeRGBPixel(rval8, gval8, bval8, &rgbval);
352
0
                        line[j] = rgbval;
353
0
                    }
354
0
                }
355
0
                break;
356
357
0
            case 4: /* rgba */
358
0
                for (i = 0; i < h; i++) {
359
0
                    line = data + i * wpl;
360
0
                    for (j = 0; j < wpl; j++) {
361
0
                        if (fread(&rval8, 1, 1, fp) != 1) {
362
0
                            pixDestroy(&pix);
363
0
                            return (PIX *)ERROR_PTR("read error type 7",
364
0
                                                    __func__, NULL);
365
0
                        }
366
0
                        if (fread(&gval8, 1, 1, fp) != 1) {
367
0
                            pixDestroy(&pix);
368
0
                            return (PIX *)ERROR_PTR("read error type 7",
369
0
                                                    __func__, NULL);
370
0
                        }
371
0
                        if (fread(&bval8, 1, 1, fp) != 1) {
372
0
                            pixDestroy(&pix);
373
0
                            return (PIX *)ERROR_PTR("read error type 7",
374
0
                                                    __func__, NULL);
375
0
                        }
376
0
                        if (fread(&aval8, 1, 1, fp) != 1) {
377
0
                            pixDestroy(&pix);
378
0
                            return (PIX *)ERROR_PTR("read error type 7",
379
0
                                                    __func__, NULL);
380
0
                        }
381
0
                        rval8 = rval8 & mask8;
382
0
                        gval8 = gval8 & mask8;
383
0
                        bval8 = bval8 & mask8;
384
0
                        aval8 = aval8 & mask8;
385
0
                        composeRGBAPixel(rval8, gval8, bval8, aval8, &rgbval);
386
0
                        line[j] = rgbval;
387
0
                    }
388
0
                }
389
0
                pixSetSpp(pix, 4);
390
0
                break;
391
0
            }
392
0
        } else {  /* bps == 16 */
393
                /* I have only seen one example that is type 6, 16 bps.
394
                 * It was 3 spp (rgb), and the 8 bps of real data was stored
395
                 * in the second byte.  In the following, I make the wild
396
                 * assumption that for all 16 bpp pnm/pam files, we can
397
                 * take the second byte. */
398
0
            switch (spp) {
399
0
            case 1: /* 16 bps grayscale */
400
0
                for (i = 0; i < h; i++) {
401
0
                    for (j = 0; j < w; j++) {
402
0
                        if (fread(&val16, 2, 1, fp) != 1) {
403
0
                            pixDestroy(&pix);
404
0
                            return (PIX *)ERROR_PTR("read error type 7",
405
0
                                                    __func__, NULL);
406
0
                        }
407
0
                        val8 = val16 & 0xff;
408
0
                        pixSetPixel(pix, j, i, val8);
409
0
                    }
410
0
                }
411
0
                break;
412
413
0
            case 2: /* 16 bps grayscale + alpha */
414
0
                for (i = 0; i < h; i++) {
415
0
                    for (j = 0; j < w; j++) {
416
0
                        if (fread(&val16, 2, 1, fp) != 1) {
417
0
                            pixDestroy(&pix);
418
0
                            return (PIX *)ERROR_PTR("read error type 7",
419
0
                                                    __func__, NULL);
420
0
                        }
421
0
                        if (fread(&aval16, 2, 1, fp) != 1) {
422
0
                            pixDestroy(&pix);
423
0
                            return (PIX *)ERROR_PTR("read error type 7",
424
0
                                                    __func__, NULL);
425
0
                        }
426
0
                        val8 = val16 & 0xff;
427
0
                        aval8 = aval16 & 0xff;
428
0
                        composeRGBAPixel(val8, val8, val8, aval8, &rgbval);
429
0
                        pixSetPixel(pix, j, i, rgbval);
430
0
                    }
431
0
                }
432
0
                pixSetSpp(pix, 4);
433
0
                break;
434
435
0
            case 3: /* 16bps rgb */
436
0
                for (i = 0; i < h; i++) {
437
0
                    line = data + i * wpl;
438
0
                    for (j = 0; j < wpl; j++) {
439
0
                        if (fread(&rval16, 2, 1, fp) != 1) {
440
0
                            pixDestroy(&pix);
441
0
                            return (PIX *)ERROR_PTR("read error type 7",
442
0
                                                    __func__, NULL);
443
0
                        }
444
0
                        if (fread(&gval16, 2, 1, fp) != 1) {
445
0
                            pixDestroy(&pix);
446
0
                            return (PIX *)ERROR_PTR("read error type 7",
447
0
                                                    __func__, NULL);
448
0
                        }
449
0
                        if (fread(&bval16, 2, 1, fp) != 1) {
450
0
                            pixDestroy(&pix);
451
0
                            return (PIX *)ERROR_PTR("read error type 7",
452
0
                                                    __func__, NULL);
453
0
                        }
454
0
                        rval8 = rval16 & 0xff;
455
0
                        gval8 = gval16 & 0xff;
456
0
                        bval8 = bval16 & 0xff;
457
0
                        composeRGBPixel(rval8, gval8, bval8, &rgbval);
458
0
                        line[j] = rgbval;
459
0
                    }
460
0
                }
461
0
                break;
462
463
0
            case 4: /* 16bps rgba */
464
0
                for (i = 0; i < h; i++) {
465
0
                    line = data + i * wpl;
466
0
                    for (j = 0; j < wpl; j++) {
467
0
                        if (fread(&rval16, 2, 1, fp) != 1) {
468
0
                            pixDestroy(&pix);
469
0
                            return (PIX *)ERROR_PTR("read error type 7",
470
0
                                                    __func__, NULL);
471
0
                        }
472
0
                        if (fread(&gval16, 2, 1, fp) != 1) {
473
0
                            pixDestroy(&pix);
474
0
                            return (PIX *)ERROR_PTR("read error type 7",
475
0
                                                    __func__, NULL);
476
0
                        }
477
0
                        if (fread(&bval16, 2, 1, fp) != 1) {
478
0
                            pixDestroy(&pix);
479
0
                            return (PIX *)ERROR_PTR("read error type 7",
480
0
                                                    __func__, NULL);
481
0
                        }
482
0
                        if (fread(&aval16, 2, 1, fp) != 1) {
483
0
                            pixDestroy(&pix);
484
0
                            return (PIX *)ERROR_PTR("read error type 7",
485
0
                                                    __func__, NULL);
486
0
                        }
487
0
                        rval8 = rval16 & 0xff;
488
0
                        gval8 = gval16 & 0xff;
489
0
                        bval8 = bval16 & 0xff;
490
0
                        aval8 = aval16 & 0xff;
491
0
                        composeRGBAPixel(rval8, gval8, bval8, aval8, &rgbval);
492
0
                        line[j] = rgbval;
493
0
                    }
494
0
                }
495
0
                pixSetSpp(pix, 4);
496
0
                break;
497
0
            }
498
0
        }
499
0
        break;
500
0
    }
501
0
    return pix;
502
0
}
503
504
505
/*!
506
 * \brief   readHeaderPnm()
507
 *
508
 * \param[in]    filename
509
 * \param[out]   pw       [optional]
510
 * \param[out]   ph       [optional]
511
 * \param[out]   pd       [optional]
512
 * \param[out]   ptype    [optional] pnm type
513
 * \param[out]   pbps     [optional] bits/sample
514
 * \param[out]   pspp     [optional] samples/pixel
515
 * \return  0 if OK, 1 on error
516
 */
517
l_ok
518
readHeaderPnm(const char *filename,
519
              l_int32    *pw,
520
              l_int32    *ph,
521
              l_int32    *pd,
522
              l_int32    *ptype,
523
              l_int32    *pbps,
524
              l_int32    *pspp)
525
0
{
526
0
l_int32  ret;
527
0
FILE    *fp;
528
529
0
    if (pw) *pw = 0;
530
0
    if (ph) *ph = 0;
531
0
    if (pd) *pd = 0;
532
0
    if (ptype) *ptype = 0;
533
0
    if (pbps) *pbps = 0;
534
0
    if (pspp) *pspp = 0;
535
0
    if (!filename)
536
0
        return ERROR_INT("filename not defined", __func__, 1);
537
538
0
    if ((fp = fopenReadStream(filename)) == NULL)
539
0
        return ERROR_INT_1("image file not found", filename, __func__, 1);
540
0
    ret = freadHeaderPnm(fp, pw, ph, pd, ptype, pbps, pspp);
541
0
    fclose(fp);
542
0
    return ret;
543
0
}
544
545
546
/*!
547
 * \brief   freadHeaderPnm()
548
 *
549
 * \param[in]    fp     file stream opened for read
550
 * \param[out]   pw     [optional]
551
 * \param[out]   ph     [optional]
552
 * \param[out]   pd     [optional]
553
 * \param[out]   ptype  [optional] pnm type
554
 * \param[out]   pbps   [optional]  bits/sample
555
 * \param[out]   pspp   [optional]  samples/pixel
556
 * \return  0 if OK, 1 on error
557
 */
558
l_ok
559
freadHeaderPnm(FILE     *fp,
560
               l_int32  *pw,
561
               l_int32  *ph,
562
               l_int32  *pd,
563
               l_int32  *ptype,
564
               l_int32  *pbps,
565
               l_int32  *pspp)
566
0
{
567
0
char     tag[16], tupltype[32];
568
0
l_int32  i, w, h, d, bps, spp, type;
569
0
l_int32  maxval;
570
0
l_int32  ch;
571
572
0
    if (pw) *pw = 0;
573
0
    if (ph) *ph = 0;
574
0
    if (pd) *pd = 0;
575
0
    if (ptype) *ptype = 0;
576
0
    if (pbps) *pbps = 0;
577
0
    if (pspp) *pspp = 0;
578
0
    if (!fp)
579
0
        return ERROR_INT("fp not defined", __func__, 1);
580
581
0
    if (fscanf(fp, "P%d\n", &type) != 1)
582
0
        return ERROR_INT("invalid read for type", __func__, 1);
583
0
    if (type < 1 || type > 7)
584
0
        return ERROR_INT("invalid pnm file", __func__, 1);
585
586
0
    if (pnmSkipCommentLines(fp))
587
0
        return ERROR_INT("no data in file", __func__, 1);
588
589
0
    if (type == 7) {
590
0
        w = h = d = bps = spp = maxval = 0;
591
0
        for (i = 0; i < 10; i++) {   /* limit to 10 lines of this header */
592
0
            if (pnmReadNextString(fp, tag, sizeof(tag)))
593
0
                return ERROR_INT("found no next tag", __func__, 1);
594
0
            if (!strcmp(tag, "WIDTH")) {
595
0
                if (pnmReadNextNumber(fp, &w))
596
0
                    return ERROR_INT("failed reading width", __func__, 1);
597
0
                continue;
598
0
            }
599
0
            if (!strcmp(tag, "HEIGHT")) {
600
0
                if (pnmReadNextNumber(fp, &h))
601
0
                    return ERROR_INT("failed reading height", __func__, 1);
602
0
                continue;
603
0
            }
604
0
            if (!strcmp(tag, "DEPTH")) {
605
0
                if (pnmReadNextNumber(fp, &spp))
606
0
                    return ERROR_INT("failed reading depth", __func__, 1);
607
0
                continue;
608
0
            }
609
0
            if (!strcmp(tag, "MAXVAL")) {
610
0
                if (pnmReadNextNumber(fp, &maxval))
611
0
                    return ERROR_INT("failed reading maxval", __func__, 1);
612
0
                continue;
613
0
            }
614
0
            if (!strcmp(tag, "TUPLTYPE")) {
615
0
                if (pnmReadNextString(fp, tupltype, sizeof(tupltype)))
616
0
                    return ERROR_INT("failed reading tuple type", __func__, 1);
617
0
                continue;
618
0
            }
619
0
            if (!strcmp(tag, "ENDHDR")) {
620
0
                if ('\n' != (ch = fgetc(fp)))
621
0
                    return ERROR_INT("missing LF after ENDHDR", __func__, 1);
622
0
                break;
623
0
            }
624
0
        }
625
0
        if (w <= 0 || h <= 0 || w > MAX_PNM_WIDTH || h > MAX_PNM_HEIGHT) {
626
0
            L_INFO("invalid size: w = %d, h = %d\n", __func__, w, h);
627
0
            return 1;
628
0
        }
629
0
        if (maxval == 1) {
630
0
            d = bps = 1;
631
0
        } else if (maxval == 3) {
632
0
            d = bps = 2;
633
0
        } else if (maxval == 15) {
634
0
            d = bps = 4;
635
0
        } else if (maxval == 255) {
636
0
            d = bps = 8;
637
0
        } else if (maxval == 0xffff) {
638
0
            d = bps = 16;
639
0
        } else {
640
0
            L_INFO("invalid maxval = %d\n", __func__, maxval);
641
0
            return 1;
642
0
        }
643
0
        switch (spp) {
644
0
        case 1:
645
            /* d and bps are already set */
646
0
            break;
647
0
        case 2:
648
0
        case 3:
649
0
        case 4:
650
            /* create a 32 bpp Pix */
651
0
            d = 32;
652
0
            break;
653
0
        default:
654
0
            L_INFO("invalid depth = %d\n", __func__, spp);
655
0
            return 1;
656
0
        }
657
0
    } else {
658
659
0
        if (fscanf(fp, "%d %d\n", &w, &h) != 2)
660
0
            return ERROR_INT("invalid read for w,h", __func__, 1);
661
0
        if (w <= 0 || h <= 0 || w > MAX_PNM_WIDTH || h > MAX_PNM_HEIGHT) {
662
0
            L_INFO("invalid size: w = %d, h = %d\n", __func__, w, h);
663
0
            return 1;
664
0
        }
665
666
       /* Get depth of pix.  For types 2 and 5, we use the maxval.
667
        * Important implementation note:
668
        *   - You can't use fscanf(), which throws away whitespace,
669
        *     and will discard binary data if it starts with whitespace(s).
670
        *   - You can't use fgets(), which stops at newlines, but this
671
        *     dumb format doesn't require a newline after the maxval
672
        *     number -- it just requires one whitespace character.
673
        *   - Which leaves repeated calls to fgetc, including swallowing
674
        *     the single whitespace character. */
675
0
        if (type == 1 || type == 4) {
676
0
            d = 1;
677
0
            spp = 1;
678
0
            bps = 1;
679
0
        } else if (type == 2 || type == 5) {
680
0
            if (pnmReadNextNumber(fp, &maxval))
681
0
                return ERROR_INT("invalid read for maxval (2,5)", __func__, 1);
682
0
            if (maxval == 3) {
683
0
                d = 2;
684
0
            } else if (maxval == 15) {
685
0
                d = 4;
686
0
            } else if (maxval == 255) {
687
0
                d = 8;
688
0
            } else if (maxval == 0xffff) {
689
0
                d = 16;
690
0
            } else {
691
0
                lept_stderr("maxval = %d\n", maxval);
692
0
                return ERROR_INT("invalid maxval", __func__, 1);
693
0
            }
694
0
            bps = d;
695
0
            spp = 1;
696
0
        } else {  /* type == 3 || type == 6; this is rgb  */
697
0
            if (pnmReadNextNumber(fp, &maxval))
698
0
                return ERROR_INT("invalid read for maxval (3,6)", __func__, 1);
699
0
            if (maxval != 255 && maxval != 0xffff) {
700
0
                L_ERROR("unexpected maxval = %d\n", __func__, maxval);
701
0
                return 1;
702
0
            }
703
0
            bps = (maxval == 255) ? 8 : 16;
704
0
            d = 32;
705
0
            spp = 3;
706
0
        }
707
0
    }
708
0
    if (pw) *pw = w;
709
0
    if (ph) *ph = h;
710
0
    if (pd) *pd = d;
711
0
    if (ptype) *ptype = type;
712
0
    if (pbps) *pbps = bps;
713
0
    if (pspp) *pspp = spp;
714
0
    return 0;
715
0
}
716
717
718
/*!
719
 * \brief   pixWriteStreamPnm()
720
 *
721
 * \param[in]   fp    file stream opened for write
722
 * \param[in]   pix
723
 * \return  0 if OK; 1 on error
724
 *
725
 * <pre>
726
 * Notes:
727
 *      (1) This writes "raw" packed format only:
728
 *          1 bpp --> pbm (P4)
729
 *          2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm (P5)
730
 *          2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm (P6)
731
 *      (2) 24 bpp rgb are not supported in leptonica, but this will
732
 *          write them out as a packed array of bytes (3 to a pixel).
733
 * </pre>
734
 */
735
l_ok
736
pixWriteStreamPnm(FILE  *fp,
737
                  PIX   *pix)
738
0
{
739
0
l_uint8    val8;
740
0
l_uint8    pel[4];
741
0
l_uint16   val16;
742
0
l_int32    h, w, d, ds, i, j, wpls, bpl, filebpl, writeerror, maxval;
743
0
l_uint32  *pword, *datas, *lines;
744
0
PIX       *pixs;
745
746
0
    if (!fp)
747
0
        return ERROR_INT("fp not defined", __func__, 1);
748
0
    if (!pix)
749
0
        return ERROR_INT("pix not defined", __func__, 1);
750
751
0
    pixGetDimensions(pix, &w, &h, &d);
752
0
    if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32)
753
0
        return ERROR_INT("d not in {1,2,4,8,16,24,32}", __func__, 1);
754
0
    if (d == 32 && pixGetSpp(pix) == 4)
755
0
        return pixWriteStreamPam(fp, pix);
756
757
        /* If a colormap exists, remove and convert to grayscale or rgb */
758
0
    if (pixGetColormap(pix) != NULL)
759
0
        pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
760
0
    else
761
0
        pixs = pixClone(pix);
762
0
    ds =  pixGetDepth(pixs);
763
0
    datas = pixGetData(pixs);
764
0
    wpls = pixGetWpl(pixs);
765
766
0
    writeerror = 0;
767
768
0
    if (ds == 1) {  /* binary */
769
0
        fprintf(fp, "P4\n# Raw PBM file written by leptonica "
770
0
                    "(www.leptonica.com)\n%d %d\n", w, h);
771
772
0
        bpl = (w + 7) / 8;
773
0
        for (i = 0; i < h; i++) {
774
0
            lines = datas + i * wpls;
775
0
            for (j = 0; j < bpl; j++) {
776
0
                val8 = GET_DATA_BYTE(lines, j);
777
0
                fwrite(&val8, 1, 1, fp);
778
0
            }
779
0
        }
780
0
    } else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) {  /* grayscale */
781
0
        maxval = (1 << ds) - 1;
782
0
        fprintf(fp, "P5\n# Raw PGM file written by leptonica "
783
0
                    "(www.leptonica.com)\n%d %d\n%d\n", w, h, maxval);
784
785
0
        if (ds != 16) {
786
0
            for (i = 0; i < h; i++) {
787
0
                lines = datas + i * wpls;
788
0
                for (j = 0; j < w; j++) {
789
0
                    if (ds == 2)
790
0
                        val8 = GET_DATA_DIBIT(lines, j);
791
0
                    else if (ds == 4)
792
0
                        val8 = GET_DATA_QBIT(lines, j);
793
0
                    else  /* ds == 8 */
794
0
                        val8 = GET_DATA_BYTE(lines, j);
795
0
                    fwrite(&val8, 1, 1, fp);
796
0
                }
797
0
            }
798
0
        } else {  /* ds == 16 */
799
0
            for (i = 0; i < h; i++) {
800
0
                lines = datas + i * wpls;
801
0
                for (j = 0; j < w; j++) {
802
0
                    val16 = GET_DATA_TWO_BYTES(lines, j);
803
0
                    fwrite(&val16, 2, 1, fp);
804
0
                }
805
0
            }
806
0
        }
807
0
    } else {  /* rgb color */
808
0
        fprintf(fp, "P6\n# Raw PPM file written by leptonica "
809
0
                    "(www.leptonica.com)\n%d %d\n255\n", w, h);
810
811
0
        if (d == 24) {   /* packed, 3 bytes to a pixel */
812
0
            filebpl = 3 * w;
813
0
            for (i = 0; i < h; i++) {  /* write out each raster line */
814
0
                lines = datas + i * wpls;
815
0
                if (fwrite(lines, 1, filebpl, fp) != filebpl)
816
0
                    writeerror = 1;
817
0
            }
818
0
        } else {  /* 32 bpp rgb */
819
0
            for (i = 0; i < h; i++) {
820
0
                lines = datas + i * wpls;
821
0
                for (j = 0; j < wpls; j++) {
822
0
                    pword = lines + j;
823
0
                    pel[0] = GET_DATA_BYTE(pword, COLOR_RED);
824
0
                    pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN);
825
0
                    pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE);
826
0
                    if (fwrite(pel, 1, 3, fp) != 3)
827
0
                        writeerror = 1;
828
0
                }
829
0
            }
830
0
        }
831
0
    }
832
833
0
    pixDestroy(&pixs);
834
0
    if (writeerror)
835
0
        return ERROR_INT("image write fail", __func__, 1);
836
0
    return 0;
837
0
}
838
839
840
/*!
841
 * \brief   pixWriteStreamAsciiPnm()
842
 *
843
 * \param[in]   fp    file stream opened for write
844
 * \param[in]   pix
845
 * \return  0 if OK; 1 on error
846
 *
847
 *  Writes "ASCII" format only:
848
 *      1 bpp --> pbm P1
849
 *      2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm P2
850
 *      2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm P3
851
 */
852
l_ok
853
pixWriteStreamAsciiPnm(FILE  *fp,
854
                       PIX   *pix)
855
0
{
856
0
char       buffer[256];
857
0
l_uint8    cval[3];
858
0
l_int32    h, w, d, ds, i, j, k, maxval, count;
859
0
l_uint32   val;
860
0
PIX       *pixs;
861
862
0
    if (!fp)
863
0
        return ERROR_INT("fp not defined", __func__, 1);
864
0
    if (!pix)
865
0
        return ERROR_INT("pix not defined", __func__, 1);
866
867
0
    pixGetDimensions(pix, &w, &h, &d);
868
0
    if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
869
0
        return ERROR_INT("d not in {1,2,4,8,16,32}", __func__, 1);
870
871
        /* If a colormap exists, remove and convert to grayscale or rgb */
872
0
    if (pixGetColormap(pix) != NULL)
873
0
        pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
874
0
    else
875
0
        pixs = pixClone(pix);
876
0
    ds =  pixGetDepth(pixs);
877
878
0
    if (ds == 1) {  /* binary */
879
0
        fprintf(fp, "P1\n# Ascii PBM file written by leptonica "
880
0
                    "(www.leptonica.com)\n%d %d\n", w, h);
881
882
0
        count = 0;
883
0
        for (i = 0; i < h; i++) {
884
0
            for (j = 0; j < w; j++) {
885
0
                pixGetPixel(pixs, j, i, &val);
886
0
                if (val == 0)
887
0
                    fputc('0', fp);
888
0
                else  /* val == 1 */
889
0
                    fputc('1', fp);
890
0
                fputc(' ', fp);
891
0
                count += 2;
892
0
                if (count >= 70) {
893
0
                    fputc('\n', fp);
894
0
                    count = 0;
895
0
                }
896
0
            }
897
0
        }
898
0
    } else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) {  /* grayscale */
899
0
        maxval = (1 << ds) - 1;
900
0
        fprintf(fp, "P2\n# Ascii PGM file written by leptonica "
901
0
                    "(www.leptonica.com)\n%d %d\n%d\n", w, h, maxval);
902
903
0
        count = 0;
904
0
        for (i = 0; i < h; i++) {
905
0
            for (j = 0; j < w; j++) {
906
0
                pixGetPixel(pixs, j, i, &val);
907
0
                if (ds == 2) {
908
0
                    snprintf(buffer, sizeof(buffer), "%1d ", val);
909
0
                    fwrite(buffer, 1, 2, fp);
910
0
                    count += 2;
911
0
                } else if (ds == 4) {
912
0
                    snprintf(buffer, sizeof(buffer), "%2d ", val);
913
0
                    fwrite(buffer, 1, 3, fp);
914
0
                    count += 3;
915
0
                } else if (ds == 8) {
916
0
                    snprintf(buffer, sizeof(buffer), "%3d ", val);
917
0
                    fwrite(buffer, 1, 4, fp);
918
0
                    count += 4;
919
0
                } else {  /* ds == 16 */
920
0
                    snprintf(buffer, sizeof(buffer), "%5d ", val);
921
0
                    fwrite(buffer, 1, 6, fp);
922
0
                    count += 6;
923
0
                }
924
0
                if (count >= 60) {
925
0
                    fputc('\n', fp);
926
0
                    count = 0;
927
0
                }
928
0
            }
929
0
        }
930
0
    } else {  /* rgb color */
931
0
        fprintf(fp, "P3\n# Ascii PPM file written by leptonica "
932
0
                    "(www.leptonica.com)\n%d %d\n255\n", w, h);
933
0
        count = 0;
934
0
        for (i = 0; i < h; i++) {
935
0
            for (j = 0; j < w; j++) {
936
0
                pixGetPixel(pixs, j, i, &val);
937
0
                cval[0] = GET_DATA_BYTE(&val, COLOR_RED);
938
0
                cval[1] = GET_DATA_BYTE(&val, COLOR_GREEN);
939
0
                cval[2] = GET_DATA_BYTE(&val, COLOR_BLUE);
940
0
                for (k = 0; k < 3; k++) {
941
0
                    snprintf(buffer, sizeof(buffer), "%3d ", cval[k]);
942
0
                    fwrite(buffer, 1, 4, fp);
943
0
                    count += 4;
944
0
                    if (count >= 60) {
945
0
                        fputc('\n', fp);
946
0
                        count = 0;
947
0
                    }
948
0
                }
949
0
            }
950
0
        }
951
0
    }
952
953
0
    pixDestroy(&pixs);
954
0
    return 0;
955
0
}
956
957
958
/*!
959
 * \brief   pixWriteStreamPam()
960
 *
961
 * \param[in]   fp    file stream opened for write
962
 * \param[in]   pix
963
 * \return  0 if OK; 1 on error
964
 *
965
 * <pre>
966
 * Notes:
967
 *      (1) This writes arbitrary PAM (P7) packed format.
968
 *      (2) 24 bpp rgb are not supported in leptonica, but this will
969
 *          write them out as a packed array of bytes (3 to a pixel).
970
 * </pre>
971
 */
972
l_ok
973
pixWriteStreamPam(FILE  *fp,
974
                  PIX   *pix)
975
0
{
976
0
l_uint8    val8;
977
0
l_uint8    pel[8];
978
0
l_uint16   val16;
979
0
l_int32    h, w, d, ds, i, j;
980
0
l_int32    wpls, spps, filebpl, writeerror, maxval;
981
0
l_uint32  *pword, *datas, *lines;
982
0
PIX       *pixs;
983
984
0
    if (!fp)
985
0
        return ERROR_INT("fp not defined", __func__, 1);
986
0
    if (!pix)
987
0
        return ERROR_INT("pix not defined", __func__, 1);
988
989
0
    pixGetDimensions(pix, &w, &h, &d);
990
0
    if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32)
991
0
        return ERROR_INT("d not in {1,2,4,8,16,24,32}", __func__, 1);
992
993
        /* If a colormap exists, remove and convert to grayscale or rgb */
994
0
    if (pixGetColormap(pix) != NULL)
995
0
        pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
996
0
    else
997
0
        pixs = pixClone(pix);
998
0
    ds = pixGetDepth(pixs);
999
0
    datas = pixGetData(pixs);
1000
0
    wpls = pixGetWpl(pixs);
1001
0
    spps = pixGetSpp(pixs);
1002
0
    if (ds < 24)
1003
0
        maxval = (1 << ds) - 1;
1004
0
    else
1005
0
        maxval = 255;
1006
1007
0
    writeerror = 0;
1008
0
    fprintf(fp, "P7\n# Arbitrary PAM file written by leptonica "
1009
0
                "(www.leptonica.com)\n");
1010
0
    fprintf(fp, "WIDTH %d\n", w);
1011
0
    fprintf(fp, "HEIGHT %d\n", h);
1012
0
    fprintf(fp, "DEPTH %d\n", spps);
1013
0
    fprintf(fp, "MAXVAL %d\n", maxval);
1014
0
    if (spps == 1 && ds == 1)
1015
0
        fprintf(fp, "TUPLTYPE BLACKANDWHITE\n");
1016
0
    else if (spps == 1)
1017
0
        fprintf(fp, "TUPLTYPE GRAYSCALE\n");
1018
0
    else if (spps == 3)
1019
0
        fprintf(fp, "TUPLTYPE RGB\n");
1020
0
    else if (spps == 4)
1021
0
        fprintf(fp, "TUPLTYPE RGB_ALPHA\n");
1022
0
    fprintf(fp, "ENDHDR\n");
1023
1024
0
    switch (d) {
1025
0
    case 1:
1026
0
        for (i = 0; i < h; i++) {
1027
0
            lines = datas + i * wpls;
1028
0
            for (j = 0; j < w; j++) {
1029
0
                val8 = GET_DATA_BIT(lines, j);
1030
0
                val8 ^= 1;  /* pam apparently uses white-is-1 photometry */
1031
0
                if (fwrite(&val8, 1, 1, fp) != 1)
1032
0
                    writeerror = 1;
1033
0
            }
1034
0
        }
1035
0
        break;
1036
1037
0
    case 2:
1038
0
        for (i = 0; i < h; i++) {
1039
0
            lines = datas + i * wpls;
1040
0
            for (j = 0; j < w; j++) {
1041
0
                val8 = GET_DATA_DIBIT(lines, j);
1042
0
                if (fwrite(&val8, 1, 1, fp) != 1)
1043
0
                    writeerror = 1;
1044
0
            }
1045
0
        }
1046
0
        break;
1047
1048
0
    case 4:
1049
0
        for (i = 0; i < h; i++) {
1050
0
            lines = datas + i * wpls;
1051
0
            for (j = 0; j < w; j++) {
1052
0
                val8 = GET_DATA_QBIT(lines, j);
1053
0
                if (fwrite(&val8, 1, 1, fp) != 1)
1054
0
                    writeerror = 1;
1055
0
            }
1056
0
        }
1057
0
        break;
1058
1059
0
    case 8:
1060
0
        for (i = 0; i < h; i++) {
1061
0
            lines = datas + i * wpls;
1062
0
            for (j = 0; j < w; j++) {
1063
0
                val8 = GET_DATA_BYTE(lines, j);
1064
0
                if (fwrite(&val8, 1, 1, fp) != 1)
1065
0
                    writeerror = 1;
1066
0
            }
1067
0
        }
1068
0
        break;
1069
1070
0
    case 16:
1071
0
        for (i = 0; i < h; i++) {
1072
0
            lines = datas + i * wpls;
1073
0
            for (j = 0; j < w; j++) {
1074
0
                val16 = GET_DATA_TWO_BYTES(lines, j);
1075
0
                if (fwrite(&val16, 2, 1, fp) != 1)
1076
0
                    writeerror = 1;
1077
0
            }
1078
0
        }
1079
0
        break;
1080
1081
0
    case 24:
1082
0
        filebpl = 3 * w;
1083
0
        for (i = 0; i < h; i++) {
1084
0
            lines = datas + i * wpls;
1085
0
            if (fwrite(lines, 1, filebpl, fp) != filebpl)
1086
0
                writeerror = 1;
1087
0
        }
1088
0
        break;
1089
1090
0
    case 32:
1091
0
        switch (spps) {
1092
0
        case 3:
1093
0
            for (i = 0; i < h; i++) {
1094
0
                lines = datas + i * wpls;
1095
0
                for (j = 0; j < wpls; j++) {
1096
0
                    pword = lines + j;
1097
0
                    pel[0] = GET_DATA_BYTE(pword, COLOR_RED);
1098
0
                    pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN);
1099
0
                    pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE);
1100
0
                    if (fwrite(pel, 1, 3, fp) != 3)
1101
0
                        writeerror = 1;
1102
0
                }
1103
0
            }
1104
0
            break;
1105
0
        case 4:
1106
0
            for (i = 0; i < h; i++) {
1107
0
                lines = datas + i * wpls;
1108
0
                for (j = 0; j < wpls; j++) {
1109
0
                    pword = lines + j;
1110
0
                    pel[0] = GET_DATA_BYTE(pword, COLOR_RED);
1111
0
                    pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN);
1112
0
                    pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE);
1113
0
                    pel[3] = GET_DATA_BYTE(pword, L_ALPHA_CHANNEL);
1114
0
                    if (fwrite(pel, 1, 4, fp) != 4)
1115
0
                        writeerror = 1;
1116
0
                }
1117
0
            }
1118
0
            break;
1119
0
        }
1120
0
        break;
1121
0
    }
1122
1123
0
    pixDestroy(&pixs);
1124
0
    if (writeerror)
1125
0
        return ERROR_INT("image write fail", __func__, 1);
1126
0
    return 0;
1127
0
}
1128
1129
1130
/*---------------------------------------------------------------------*
1131
 *                         Read/write to memory                        *
1132
 *---------------------------------------------------------------------*/
1133
1134
/*!
1135
 * \brief   pixReadMemPnm()
1136
 *
1137
 * \param[in]   data   const; pnm-encoded
1138
 * \param[in]   size   of data
1139
 * \return  pix, or NULL on error
1140
 *
1141
 * <pre>
1142
 * Notes:
1143
 *      (1) The %size byte of %data must be a null character.
1144
 * </pre>
1145
 */
1146
PIX *
1147
pixReadMemPnm(const l_uint8  *data,
1148
              size_t          size)
1149
0
{
1150
0
FILE  *fp;
1151
0
PIX   *pix;
1152
1153
0
    if (!data)
1154
0
        return (PIX *)ERROR_PTR("data not defined", __func__, NULL);
1155
0
    if ((fp = fopenReadFromMemory(data, size)) == NULL)
1156
0
        return (PIX *)ERROR_PTR("stream not opened", __func__, NULL);
1157
0
    pix = pixReadStreamPnm(fp);
1158
0
    fclose(fp);
1159
0
    if (!pix) L_ERROR("pix not read\n", __func__);
1160
0
    return pix;
1161
0
}
1162
1163
1164
/*!
1165
 * \brief   readHeaderMemPnm()
1166
 *
1167
 * \param[in]    data    const; pnm-encoded
1168
 * \param[in]    size    of data
1169
 * \param[out]   pw      [optional]
1170
 * \param[out]   ph      [optional]
1171
 * \param[out]   pd      [optional]
1172
 * \param[out]   ptype   [optional] pnm type
1173
 * \param[out]   pbps    [optional] bits/sample
1174
 * \param[out]   pspp    [optional] samples/pixel
1175
 * \return  0 if OK, 1 on error
1176
 */
1177
l_ok
1178
readHeaderMemPnm(const l_uint8  *data,
1179
                 size_t          size,
1180
                 l_int32        *pw,
1181
                 l_int32        *ph,
1182
                 l_int32        *pd,
1183
                 l_int32        *ptype,
1184
                 l_int32        *pbps,
1185
                 l_int32        *pspp)
1186
0
{
1187
0
l_int32  ret;
1188
0
FILE    *fp;
1189
1190
0
    if (!data)
1191
0
        return ERROR_INT("data not defined", __func__, 1);
1192
1193
0
    if ((fp = fopenReadFromMemory(data, size)) == NULL)
1194
0
        return ERROR_INT("stream not opened", __func__, 1);
1195
0
    ret = freadHeaderPnm(fp, pw, ph, pd, ptype, pbps, pspp);
1196
0
    fclose(fp);
1197
0
    if (ret)
1198
0
        return ERROR_INT("header data read failed", __func__, 1);
1199
0
    return 0;
1200
0
}
1201
1202
1203
/*!
1204
 * \brief   pixWriteMemPnm()
1205
 *
1206
 * \param[out]   pdata   data of PNM image
1207
 * \param[out]   psize   size of returned data
1208
 * \param[in]    pix
1209
 * \return  0 if OK, 1 on error
1210
 *
1211
 * <pre>
1212
 * Notes:
1213
 *      (1) See pixWriteStreamPnm() for usage.  This version writes to
1214
 *          memory instead of to a file stream.
1215
 * </pre>
1216
 */
1217
l_ok
1218
pixWriteMemPnm(l_uint8  **pdata,
1219
               size_t    *psize,
1220
               PIX       *pix)
1221
0
{
1222
0
l_int32  ret;
1223
0
FILE    *fp;
1224
1225
0
    if (pdata) *pdata = NULL;
1226
0
    if (psize) *psize = 0;
1227
0
    if (!pdata)
1228
0
        return ERROR_INT("&data not defined", __func__, 1 );
1229
0
    if (!psize)
1230
0
        return ERROR_INT("&size not defined", __func__, 1 );
1231
0
    if (!pix)
1232
0
        return ERROR_INT("&pix not defined", __func__, 1 );
1233
1234
0
#if HAVE_FMEMOPEN
1235
0
    if ((fp = open_memstream((char **)pdata, psize)) == NULL)
1236
0
        return ERROR_INT("stream not opened", __func__, 1);
1237
0
    ret = pixWriteStreamPnm(fp, pix);
1238
0
    fputc('\0', fp);
1239
0
    fclose(fp);
1240
0
    if (*psize > 0) *psize = *psize - 1;
1241
#else
1242
    L_INFO("no fmemopen API --> work-around: write to temp file\n", __func__);
1243
  #ifdef _WIN32
1244
    if ((fp = fopenWriteWinTempfile()) == NULL)
1245
        return ERROR_INT("tmpfile stream not opened", __func__, 1);
1246
  #else
1247
    if ((fp = tmpfile()) == NULL)
1248
        return ERROR_INT("tmpfile stream not opened", __func__, 1);
1249
  #endif  /* _WIN32 */
1250
    ret = pixWriteStreamPnm(fp, pix);
1251
    rewind(fp);
1252
    *pdata = l_binaryReadStream(fp, psize);
1253
    fclose(fp);
1254
#endif  /* HAVE_FMEMOPEN */
1255
0
    return ret;
1256
0
}
1257
1258
1259
/*!
1260
 * \brief   pixWriteMemPam()
1261
 *
1262
 * \param[out]   pdata   data of PAM image
1263
 * \param[out]   psize   size of returned data
1264
 * \param[in]    pix
1265
 * \return  0 if OK, 1 on error
1266
 *
1267
 * <pre>
1268
 * Notes:
1269
 *      (1) See pixWriteStreamPnm() for usage.  This version writes to
1270
 *          memory instead of to a file stream.
1271
 * </pre>
1272
 */
1273
l_ok
1274
pixWriteMemPam(l_uint8  **pdata,
1275
               size_t    *psize,
1276
               PIX       *pix)
1277
0
{
1278
0
l_int32  ret;
1279
0
FILE    *fp;
1280
1281
0
    if (pdata) *pdata = NULL;
1282
0
    if (psize) *psize = 0;
1283
0
    if (!pdata)
1284
0
        return ERROR_INT("&data not defined", __func__, 1 );
1285
0
    if (!psize)
1286
0
        return ERROR_INT("&size not defined", __func__, 1 );
1287
0
    if (!pix)
1288
0
        return ERROR_INT("&pix not defined", __func__, 1 );
1289
1290
0
#if HAVE_FMEMOPEN
1291
0
    if ((fp = open_memstream((char **)pdata, psize)) == NULL)
1292
0
        return ERROR_INT("stream not opened", __func__, 1);
1293
0
    ret = pixWriteStreamPam(fp, pix);
1294
0
    fputc('\0', fp);
1295
0
    fclose(fp);
1296
0
    if (*psize > 0) *psize = *psize - 1;
1297
#else
1298
    L_INFO("no fmemopen API --> work-around: write to temp file\n", __func__);
1299
  #ifdef _WIN32
1300
    if ((fp = fopenWriteWinTempfile()) == NULL)
1301
        return ERROR_INT("tmpfile stream not opened", __func__, 1);
1302
  #else
1303
    if ((fp = tmpfile()) == NULL)
1304
        return ERROR_INT("tmpfile stream not opened", __func__, 1);
1305
  #endif  /* _WIN32 */
1306
    ret = pixWriteStreamPam(fp, pix);
1307
    rewind(fp);
1308
    *pdata = l_binaryReadStream(fp, psize);
1309
    fclose(fp);
1310
#endif  /* HAVE_FMEMOPEN */
1311
0
    return ret;
1312
0
}
1313
1314
1315
/*--------------------------------------------------------------------*
1316
 *                          Static helpers                            *
1317
 *--------------------------------------------------------------------*/
1318
/*!
1319
 * \brief   pnmReadNextAsciiValue()
1320
 *
1321
 *      Return: 0 if OK, 1 on error or EOF.
1322
 *
1323
 *  Notes:
1324
 *      (1) This reads the next sample value in ASCII from the file.
1325
 */
1326
static l_int32
1327
pnmReadNextAsciiValue(FILE     *fp,
1328
                      l_int32  *pval)
1329
0
{
1330
0
l_int32  ignore;
1331
1332
0
    if (!pval)
1333
0
        return ERROR_INT("&val not defined", __func__, 1);
1334
0
    *pval = 0;
1335
0
    if (!fp)
1336
0
        return ERROR_INT("stream not open", __func__, 1);
1337
1338
0
    if (EOF == fscanf(fp, " "))
1339
0
        return 1;
1340
0
    if (1 != fscanf(fp, "%d", pval))
1341
0
        return 1;
1342
1343
0
    return 0;
1344
0
}
1345
1346
1347
/*!
1348
 * \brief   pnmReadNextNumber()
1349
 *
1350
 * \param[in]    fp    file stream
1351
 * \param[out]   pval  value as an integer
1352
 * \return  0 if OK, 1 on error or EOF.
1353
 *
1354
 * <pre>
1355
 * Notes:
1356
 *      (1) This reads the next set of numeric chars, returning
1357
 *          the value and swallowing initial whitespaces and ONE
1358
 *          trailing whitespace character.  This is needed to read
1359
 *          the maxval in the header, which precedes the binary data.
1360
 * </pre>
1361
 */
1362
static l_int32
1363
pnmReadNextNumber(FILE     *fp,
1364
                  l_int32  *pval)
1365
0
{
1366
0
char      buf[8];
1367
0
l_int32   i, c, foundws;
1368
1369
0
    if (!pval)
1370
0
        return ERROR_INT("&val not defined", __func__, 1);
1371
0
    *pval = 0;
1372
0
    if (!fp)
1373
0
        return ERROR_INT("stream not open", __func__, 1);
1374
1375
        /* Swallow whitespace */
1376
0
    if (fscanf(fp, " ") == EOF)
1377
0
        return ERROR_INT("end of file reached", __func__, 1);
1378
1379
        /* The ASCII characters for the number are followed by exactly
1380
         * one whitespace character. */
1381
0
    foundws = FALSE;
1382
0
    for (i = 0; i < 8; i++)
1383
0
        buf[i] = '\0';
1384
0
    for (i = 0; i < 8; i++) {
1385
0
        if ((c = fgetc(fp)) == EOF)
1386
0
            return ERROR_INT("end of file reached", __func__, 1);
1387
0
        if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
1388
0
            foundws = TRUE;
1389
0
            buf[i] = '\n';
1390
0
            break;
1391
0
        }
1392
0
        if (!isdigit(c))
1393
0
            return ERROR_INT("char read is not a digit", __func__, 1);
1394
0
        buf[i] = c;
1395
0
    }
1396
0
    if (!foundws)
1397
0
        return ERROR_INT("no whitespace found", __func__, 1);
1398
0
    if (sscanf(buf, "%d", pval) != 1)
1399
0
        return ERROR_INT("invalid read", __func__, 1);
1400
0
    return 0;
1401
0
}
1402
1403
/*!
1404
 * \brief   pnmReadNextString()
1405
 *
1406
 * \param[in]    fp    file stream
1407
 * \param[out]   buff  pointer to the string buffer
1408
 * \param[in]    size  max. number of characters in buffer
1409
 * \return  0 if OK, 1 on error or EOF.
1410
 *
1411
 * <pre>
1412
 * Notes:
1413
 *      (1) This reads the next set of alphanumeric chars, returning the string.
1414
 *          This is needed to read header lines, which precede the P7
1415
 *          format binary data.
1416
 * </pre>
1417
 */
1418
static l_int32
1419
pnmReadNextString(FILE    *fp,
1420
                  char    *buff,
1421
                  l_int32  size)
1422
0
{
1423
0
char  fmtString[7];  /* must contain "%9999s" [*] */
1424
1425
0
    if (!buff)
1426
0
        return ERROR_INT("buff not defined", __func__, 1);
1427
0
    *buff = '\0';
1428
0
    if (size > 10000)  /* size - 1 has > 4 digits [*]  */
1429
0
        return ERROR_INT("size is too big", __func__, 1);
1430
0
    if (size <= 0)
1431
0
        return ERROR_INT("size is too small", __func__, 1);
1432
0
    if (!fp)
1433
0
        return ERROR_INT("stream not open", __func__, 1);
1434
1435
        /* Skip whitespace */
1436
0
    if (fscanf(fp, " ") == EOF)
1437
0
        return 1;
1438
1439
        /* Comment lines are allowed to appear anywhere in the header lines */
1440
0
    if (pnmSkipCommentLines(fp))
1441
0
        return ERROR_INT("end of file reached", __func__, 1);
1442
1443
0
    snprintf(fmtString, 7, "%%%ds", size - 1);
1444
0
    if (fscanf(fp, fmtString, buff) == EOF)
1445
0
        return 1;
1446
1447
0
    return 0;
1448
0
}
1449
1450
1451
/*!
1452
 * \brief   pnmSkipCommentLines()
1453
 *
1454
 *      Return: 0 if OK, 1 on error or EOF
1455
 *
1456
 *  Notes:
1457
 *      (1) Comment lines begin with '#'
1458
 *      (2) Usage: caller should check return value for EOF
1459
 *      (3) The previous implementation used fseek(fp, -1L, SEEK_CUR)
1460
 *          to back up one character, which doesn't work with stdin.
1461
 */
1462
static l_int32
1463
pnmSkipCommentLines(FILE  *fp)
1464
0
{
1465
0
l_int32  i;
1466
0
char     c;
1467
1468
0
    if (!fp)
1469
0
        return ERROR_INT("stream not open", __func__, 1);
1470
0
    while ((i = fscanf(fp, "#%c", &c))) {
1471
0
        if (i == EOF) return 1;
1472
0
        while (c != '\n') {
1473
0
            if (fscanf(fp, "%c", &c) == EOF)
1474
0
                return 1;
1475
0
        }
1476
0
    }
1477
0
    return 0;
1478
0
}
1479
1480
1481
/* --------------------------------------------*/
1482
#endif  /* USE_PNMIO */
1483
/* --------------------------------------------*/