• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -  This software is distributed in the hope that it will be
4  -  useful, but with NO WARRANTY OF ANY KIND.
5  -  No author or distributor accepts responsibility to anyone for the
6  -  consequences of using this software, or for whether it serves any
7  -  particular purpose or works at all, unless he or she says so in
8  -  writing.  Everyone is granted permission to copy, modify and
9  -  redistribute this source code, for commercial or non-commercial
10  -  purposes, with the following restrictions: (1) the origin of this
11  -  source code must not be misrepresented; (2) modified versions must
12  -  be plainly marked as such; and (3) this notice may not be removed
13  -  or altered from any source or modified source distribution.
14  *====================================================================*/
15 
16 /*
17  *  pnmio.c
18  *
19  *      Stream interface
20  *          PIX             *pixReadStreamPnm()
21  *          l_int32          pixWriteStreamPnm()
22  *          l_int32          pixWriteStreamAsciiPnm()
23  *
24  *      Read/write to memory   [not on windows]
25  *          PIX             *pixReadMemPnm()
26  *          l_int32          pixWriteMemPnm()
27  *
28  *      Local helpers
29  *          static l_int32   pnmReadNextAsciiValue();
30  *          static l_int32   pnmSkipCommentLines();
31  *
32  *      These are here by popular demand, with the help of Mattias
33  *      Kregert (mattias@kregert.se), who provided the first implementation.
34  *
35  *      The pnm formats are exceedingly simple, because they have
36  *      no compression and no colormaps.  They support images that
37  *      are 1 bpp; 2, 4, 8 and 16 bpp grayscale; and rgb.
38  *
39  *      The original pnm formats ("ascii") are included for completeness,
40  *      but their use is deprecated for all but tiny iconic images.
41  *      They are extremely wasteful of memory; for example, the P1 binary
42  *      ascii format is 16 times as big as the packed uncompressed
43  *      format, because 2 characters are used to represent every bit
44  *      (pixel) in the image.  Reading is slow because we check for extra
45  *      white space and EOL at every sample value.
46  *
47  *      The packed pnm formats ("raw") give file sizes similar to
48  *      bmp files, which are uncompressed packed.  However, bmp
49  *      are more flexible, because they can support colormaps.
50  *
51  *      We don't differentiate between the different types ("pbm",
52  *      "pgm", "ppm") at the interface level, because this is really a
53  *      "distinction without a difference."  You read a file, you get
54  *      the appropriate Pix.  You write a file from a Pix, you get the
55  *      appropriate type of file.  If there is a colormap on the Pix,
56  *      and the Pix is more than 1 bpp, you get either an 8 bpp pgm
57  *      or a 24 bpp RGB pnm, depending on whether the colormap colors
58  *      are gray or rgb, respectively.
59  *
60  *      This follows the general policy that the I/O routines don't
61  *      make decisions about the content of the image -- you do that
62  *      with image processing before you write it out to file.
63  *      The I/O routines just try to make the closest connection
64  *      possible between the file and the Pix in memory.
65  */
66 
67 #include <stdio.h>
68 #include <string.h>
69 #include <stdlib.h>
70 #include "allheaders.h"
71 
72 /* --------------------------------------------*/
73 #if  USE_PNMIO   /* defined in environ.h */
74 /* --------------------------------------------*/
75 
76 
77 static l_int32 pnmReadNextAsciiValue(FILE  *fp, l_int32 *pval);
78 static l_int32 pnmSkipCommentLines(FILE  *fp);
79 
80     /* a sanity check on the size read from file */
81 static const l_int32  MAX_PNM_WIDTH = 100000;
82 static const l_int32  MAX_PNM_HEIGHT = 100000;
83 
84 
85 /*--------------------------------------------------------------------*
86  *                          Stream interface                          *
87  *--------------------------------------------------------------------*/
88 /*!
89  *  pixReadStreamPnm()
90  *
91  *      Input:  stream opened for read
92  *      Return: pix, or null on error
93  */
94 PIX *
pixReadStreamPnm(FILE * fp)95 pixReadStreamPnm(FILE  *fp)
96 {
97 l_uint8    val8, rval8, gval8, bval8;
98 l_uint16   val16;
99 l_int32    w, h, d, bpl, wpl, i, j, type;
100 l_int32    maxval, val, rval, gval, bval;
101 l_uint32   rgbval;
102 l_uint32  *line, *data;
103 PIX       *pix;
104 
105     PROCNAME("pixReadStreamPnm");
106 
107     if (!fp)
108         return (PIX *)ERROR_PTR("fp not defined", procName, NULL);
109 
110     fscanf(fp, "P%d\n", &type);
111     if (type < 1 || type > 6)
112         return (PIX *)ERROR_PTR("invalid pnm file", procName, NULL);
113 
114     if (pnmSkipCommentLines(fp))
115         return (PIX *)ERROR_PTR("no data in file", procName, NULL);
116 
117     fscanf(fp, "%d %d\n", &w, &h);
118     if (w <= 0 || h <= 0 || w > MAX_PNM_WIDTH || h > MAX_PNM_HEIGHT)
119         return (PIX *)ERROR_PTR("invalid sizes", procName, NULL);
120 
121         /* Get depth of pix */
122     if (type == 1 || type == 4)
123         d = 1;
124     else if (type == 2 || type == 5) {
125         fscanf(fp, "%d\n", &maxval);
126         if (maxval == 3)
127             d = 2;
128         else if (maxval == 15)
129             d = 4;
130         else if (maxval == 255)
131             d = 8;
132         else if (maxval == 0xffff)
133             d = 16;
134         else {
135             fprintf(stderr, "maxval = %d\n", maxval);
136             return (PIX *)ERROR_PTR("invalid maxval", procName, NULL);
137         }
138     }
139     else {  /* type == 3 || type == 6; this is rgb  */
140         fscanf(fp, "%d\n", &maxval);
141         if (maxval != 255)
142             L_WARNING_INT("unexpected maxval = %d", procName, maxval);
143         d = 32;
144     }
145 
146     if ((pix = pixCreate(w, h, d)) == NULL)
147         return (PIX *)ERROR_PTR( "pix not made", procName, NULL);
148     data = pixGetData(pix);
149     wpl = pixGetWpl(pix);
150 
151         /* Old "ascii" format */
152     if (type <= 3) {
153         for (i = 0; i < h; i++) {
154             for (j = 0; j < w; j++) {
155                 if (type == 1 || type == 2) {
156                     if (pnmReadNextAsciiValue(fp, &val))
157                         return (PIX *)ERROR_PTR( "read abend", procName, pix);
158                     pixSetPixel(pix, j, i, val);
159                 }
160                 else {  /* type == 3 */
161                     if (pnmReadNextAsciiValue(fp, &rval))
162                         return (PIX *)ERROR_PTR( "read abend", procName, pix);
163                     if (pnmReadNextAsciiValue(fp, &gval))
164                         return (PIX *)ERROR_PTR( "read abend", procName, pix);
165                     if (pnmReadNextAsciiValue(fp, &bval))
166                         return (PIX *)ERROR_PTR( "read abend", procName, pix);
167                     composeRGBPixel(rval, gval, bval, &rgbval);
168                     pixSetPixel(pix, j, i, rgbval);
169                 }
170             }
171         }
172         return pix;
173     }
174 
175         /* "raw" format for 1 bpp */
176     if (type == 4) {
177         bpl = (d * w + 7) / 8;
178         for (i = 0; i < h; i++) {
179             line = data + i * wpl;
180             for (j = 0; j < bpl; j++) {
181                 fread(&val8, 1, 1, fp);
182                 SET_DATA_BYTE(line, j, val8);
183             }
184         }
185         return pix;
186     }
187 
188         /* "raw" format for grayscale */
189     if (type == 5) {
190         bpl = (d * w + 7) / 8;
191         for (i = 0; i < h; i++) {
192             line = data + i * wpl;
193             if (d != 16) {
194                 for (j = 0; j < w; j++) {
195                     fread(&val8, 1, 1, fp);
196                     if (d == 2)
197                         SET_DATA_DIBIT(line, j, val8);
198                     else if (d == 4)
199                         SET_DATA_QBIT(line, j, val8);
200                     else  /* d == 8 */
201                         SET_DATA_BYTE(line, j, val8);
202                 }
203             }
204             else {  /* d == 16 */
205                 for (j = 0; j < w; j++) {
206                     fread(&val16, 2, 1, fp);
207                     SET_DATA_TWO_BYTES(line, j, val16);
208                 }
209             }
210         }
211         return pix;
212     }
213 
214         /* "raw" format, type == 6; rgb */
215     for (i = 0; i < h; i++) {
216         line = data + i * wpl;
217         for (j = 0; j < wpl; j++) {
218             fread(&rval8, 1, 1, fp);
219             fread(&gval8, 1, 1, fp);
220             fread(&bval8, 1, 1, fp);
221             composeRGBPixel(rval8, gval8, bval8, &rgbval);
222             line[j] = rgbval;
223         }
224     }
225     return pix;
226 }
227 
228 
229 /*!
230  *  pixWriteStreamPnm()
231  *
232  *      Input:  stream opened for write
233  *              pix
234  *      Return: 0 if OK; 1 on error
235  *
236  *  Notes:
237  *      (1) This writes "raw" packed format only:
238  *          1 bpp --> pbm (P4)
239  *          2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm (P5)
240  *          2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm (P6)
241  *      (2) 24 bpp rgb are not supported in leptonica, but this will
242  *          write them out as a packed array of bytes (3 to a pixel).
243  */
244 l_int32
pixWriteStreamPnm(FILE * fp,PIX * pix)245 pixWriteStreamPnm(FILE  *fp,
246                   PIX   *pix)
247 {
248 l_uint8    val8;
249 l_uint8    pel[4];
250 l_uint16   val16;
251 l_int32    h, w, d, ds, i, j, wpls, bpl, filebpl, writeerror, maxval;
252 l_uint32  *pword, *datas, *lines;
253 PIX       *pixs;
254 
255     PROCNAME("pixWriteStreamPnm");
256 
257     if (!fp)
258         return ERROR_INT("fp not defined", procName, 1);
259     if (!pix)
260         return ERROR_INT("pix not defined", procName, 1);
261 
262     pixGetDimensions(pix, &w, &h, &d);
263     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32)
264         return ERROR_INT("d not in {1,2,4,8,16,24,32}", procName, 1);
265 
266         /* If a colormap exists, remove and convert to grayscale or rgb */
267     if (pixGetColormap(pix) != NULL)
268         pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
269     else
270         pixs = pixClone(pix);
271     ds =  pixGetDepth(pixs);
272     datas = pixGetData(pixs);
273     wpls = pixGetWpl(pixs);
274 
275     writeerror = 0;
276     if (ds == 1) {  /* binary */
277         fprintf(fp, "P4\n# Raw PBM file written by leptonlib (www.leptonica.com)\n%d %d\n", w, h);
278 
279         bpl = (w + 7) / 8;
280         for (i = 0; i < h; i++) {
281             lines = datas + i * wpls;
282             for (j = 0; j < bpl; j++) {
283                 val8 = GET_DATA_BYTE(lines, j);
284                 fwrite(&val8, 1, 1, fp);
285             }
286         }
287     }
288     else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) {  /* grayscale */
289         maxval = (1 << ds) - 1;
290         fprintf(fp, "P5\n# Raw PGM file written by leptonlib (www.leptonica.com)\n%d %d\n%d\n", w, h, maxval);
291 
292         if (ds != 16) {
293             for (i = 0; i < h; i++) {
294                 lines = datas + i * wpls;
295                 for (j = 0; j < w; j++) {
296                     if (ds == 2)
297                         val8 = GET_DATA_DIBIT(lines, j);
298                     else if (ds == 4)
299                         val8 = GET_DATA_QBIT(lines, j);
300                     else  /* ds == 8 */
301                         val8 = GET_DATA_BYTE(lines, j);
302                     fwrite(&val8, 1, 1, fp);
303                 }
304             }
305         }
306         else {  /* ds == 16 */
307             for (i = 0; i < h; i++) {
308                 lines = datas + i * wpls;
309                 for (j = 0; j < w; j++) {
310                     val16 = GET_DATA_TWO_BYTES(lines, j);
311                     fwrite(&val16, 2, 1, fp);
312                 }
313             }
314         }
315     }
316     else {  /* rgb color */
317         fprintf(fp, "P6\n# Raw PPM file written by leptonlib (www.leptonica.com)\n%d %d\n255\n", w, h);
318 
319         if (d == 24) {   /* packed, 3 bytes to a pixel */
320             filebpl = 3 * w;
321             for (i = 0; i < h; i++) {  /* write out each raster line */
322                 lines = datas + i * wpls;
323                 if (fwrite(lines, 1, filebpl, fp) != filebpl)
324                     writeerror = 1;
325             }
326         }
327         else {  /* 32 bpp rgb */
328             for (i = 0; i < h; i++) {
329                 lines = datas + i * wpls;
330                 for (j = 0; j < wpls; j++) {
331                     pword = lines + j;
332                     pel[0] = *((l_uint8 *)pword + 3);   /* red   */
333                     pel[1] = *((l_uint8 *)pword + 2);   /* green */
334                     pel[2] = *((l_uint8 *)pword + 1);   /* blue  */
335                     if (fwrite(&pel, 1, 3, fp) != 3)
336                         writeerror = 1;
337                 }
338             }
339         }
340     }
341 
342     pixDestroy(&pixs);
343     if (writeerror)
344         return ERROR_INT("image write fail", procName, 1);
345     return 0;
346 }
347 
348 
349 /*!
350  *  pixWriteStreamAsciiPnm()
351  *
352  *      Input:  stream opened for write
353  *              pix
354  *      Return: 0 if OK; 1 on error
355  *
356  *  Writes "ascii" format only:
357  *      1 bpp --> pbm (P1)
358  *      2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm (P2)
359  *      2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm (P3)
360  */
361 l_int32
pixWriteStreamAsciiPnm(FILE * fp,PIX * pix)362 pixWriteStreamAsciiPnm(FILE  *fp,
363                        PIX   *pix)
364 {
365 char       buffer[256];
366 l_uint8    cval[3];
367 l_int32    h, w, d, ds, i, j, k, maxval, count;
368 l_uint32   val;
369 PIX       *pixs;
370 
371     PROCNAME("pixWriteStreamAsciiPnm");
372 
373     if (!fp)
374         return ERROR_INT("fp not defined", procName, 1);
375     if (!pix)
376         return ERROR_INT("pix not defined", procName, 1);
377 
378     pixGetDimensions(pix, &w, &h, &d);
379     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
380         return ERROR_INT("d not in {1,2,4,8,16,32}", procName, 1);
381 
382         /* If a colormap exists, remove and convert to grayscale or rgb */
383     if (pixGetColormap(pix) != NULL)
384         pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
385     else
386         pixs = pixClone(pix);
387     ds =  pixGetDepth(pixs);
388 
389     if (ds == 1) {  /* binary */
390         fprintf(fp, "P1\n# Ascii PBM file written by leptonlib (www.leptonica.com)\n%d %d\n", w, h);
391 
392         count = 0;
393         for (i = 0; i < h; i++) {
394             for (j = 0; j < w; j++) {
395                 pixGetPixel(pixs, j, i, &val);
396                 if (val == 0)
397                     fputc('0', fp);
398                 else  /* val == 1 */
399                     fputc('1', fp);
400                 fputc(' ', fp);
401                 count += 2;
402                 if (count >= 70)
403                     fputc('\n', fp);
404             }
405         }
406     }
407     else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) {  /* grayscale */
408         maxval = (1 << ds) - 1;
409         fprintf(fp, "P2\n# Ascii PGM file written by leptonlib (www.leptonica.com)\n%d %d\n%d\n", w, h, maxval);
410 
411         count = 0;
412         for (i = 0; i < h; i++) {
413             for (j = 0; j < w; j++) {
414                 pixGetPixel(pixs, j, i, &val);
415                 if (ds == 2) {
416                     sprintf(buffer, "%1d ", val);
417                     fwrite(buffer, 1, 2, fp);
418                     count += 2;
419                 }
420                 else if (ds == 4) {
421                     sprintf(buffer, "%2d ", val);
422                     fwrite(buffer, 1, 3, fp);
423                     count += 3;
424                 }
425                 else if (ds == 8) {
426                     sprintf(buffer, "%3d ", val);
427                     fwrite(buffer, 1, 4, fp);
428                     count += 4;
429                 }
430                 else {  /* ds == 16 */
431                     sprintf(buffer, "%5d ", val);
432                     fwrite(buffer, 1, 6, fp);
433                     count += 6;
434                 }
435                 if (count >= 60) {
436                     fputc('\n', fp);
437                     count = 0;
438                 }
439             }
440         }
441     }
442     else {  /* rgb color */
443         fprintf(fp, "P3\n# Ascii PPM file written by leptonlib (www.leptonica.com)\n%d %d\n255\n", w, h);
444 
445         count = 0;
446         for (i = 0; i < h; i++) {
447             for (j = 0; j < w; j++) {
448                 pixGetPixel(pixs, j, i, &val);
449                 cval[0] = GET_DATA_BYTE(&val, COLOR_RED);
450                 cval[1] = GET_DATA_BYTE(&val, COLOR_GREEN);
451                 cval[2] = GET_DATA_BYTE(&val, COLOR_BLUE);
452                 for (k = 0; k < 3; k++) {
453                     sprintf(buffer, "%3d ", cval[k]);
454                     fwrite(buffer, 1, 4, fp);
455                     count += 4;
456                     if (count >= 60) {
457                         fputc('\n', fp);
458                         count = 0;
459                     }
460                 }
461             }
462         }
463     }
464 
465     pixDestroy(&pixs);
466     return 0;
467 }
468 
469 
470 /*---------------------------------------------------------------------*
471  *                         Read/write to memory                        *
472  *---------------------------------------------------------------------*/
473 #ifdef HAVE_CONFIG_H
474 #include "config_auto.h"
475 #endif  /* HAVE_CONFIG_H */
476 
477 #if HAVE_FMEMOPEN
478 
479 #include "_stdio.h"
480 /* extern FILE *open_memstream(char **data, size_t *size); */
481 /* extern FILE *fmemopen(void *data, size_t size, const char *mode); */
482 
483 /*!
484  *  pixReadMemPnm()
485  *
486  *      Input:  cdata (const; pnm-encoded)
487  *              size (of data)
488  *      Return: pix, or null on error
489  *
490  *  Notes:
491  *      (1) The @size byte of @data must be a null character.
492  */
493 PIX *
pixReadMemPnm(const l_uint8 * cdata,size_t size)494 pixReadMemPnm(const l_uint8  *cdata,
495               size_t          size)
496 {
497 l_uint8  *data;
498 FILE     *fp;
499 PIX      *pix;
500 
501     PROCNAME("pixReadMemPnm");
502 
503     if (!cdata)
504         return (PIX *)ERROR_PTR("cdata not defined", procName, NULL);
505 
506     data = (l_uint8 *)cdata;  /* we're really not going to change this */
507     if ((fp = fmemopen(data, size, "r")) == NULL)
508         return (PIX *)ERROR_PTR("stream not opened", procName, NULL);
509     pix = pixReadStreamPnm(fp);
510     fclose(fp);
511     return pix;
512 }
513 
514 
515 /*!
516  *  pixWriteMemPnm()
517  *
518  *      Input:  &data (<return> data of tiff compressed image)
519  *              &size (<return> size of returned data)
520  *              pix
521  *      Return: 0 if OK, 1 on error
522  *
523  *  Notes:
524  *      (1) See pixWriteStreamPnm() for usage.  This version writes to
525  *          memory instead of to a file stream.
526  */
527 l_int32
pixWriteMemPnm(l_uint8 ** pdata,size_t * psize,PIX * pix)528 pixWriteMemPnm(l_uint8  **pdata,
529                size_t    *psize,
530                PIX       *pix)
531 {
532 l_int32  ret;
533 FILE    *fp;
534 
535     PROCNAME("pixWriteMemPnm");
536 
537     if (!pdata)
538         return ERROR_INT("&data not defined", procName, 1 );
539     if (!psize)
540         return ERROR_INT("&size not defined", procName, 1 );
541     if (!pix)
542         return ERROR_INT("&pix not defined", procName, 1 );
543 
544     if ((fp = open_memstream((char **)pdata, psize)) == NULL)
545         return ERROR_INT("stream not opened", procName, 1);
546     ret = pixWriteStreamPnm(fp, pix);
547     fclose(fp);
548     return ret;
549 }
550 
551 #else
552 
553 PIX *
pixReadMemPnm(const l_uint8 * cdata,size_t size)554 pixReadMemPnm(const l_uint8  *cdata,
555               size_t          size)
556 {
557     return (PIX *)ERROR_PTR(
558         "pnm read from memory not implemented on this platform",
559         "pixReadMemPnm", NULL);
560 }
561 
562 
563 l_int32
pixWriteMemPnm(l_uint8 ** pdata,size_t * psize,PIX * pix)564 pixWriteMemPnm(l_uint8  **pdata,
565                size_t    *psize,
566                PIX       *pix)
567 {
568     return ERROR_INT(
569         "pnm write to memory not implemented on this platform",
570         "pixWriteMemPnm", 1);
571 }
572 
573 #endif  /* HAVE_FMEMOPEN */
574 
575 
576 /*--------------------------------------------------------------------*
577  *                          Static helpers                            *
578  *--------------------------------------------------------------------*/
579 /*!
580  *  pnmReadNextAsciiValue()
581  *
582  *      Return: 0 if OK, 1 on error or EOF.
583  *
584  *  Notes:
585  *      (1) This reads the next sample value in ascii from the the file.
586  */
587 static l_int32
pnmReadNextAsciiValue(FILE * fp,l_int32 * pval)588 pnmReadNextAsciiValue(FILE  *fp,
589                       l_int32 *pval)
590 {
591 l_int32   c;
592 
593     PROCNAME("pnmReadNextAsciiValue");
594 
595     if (!fp)
596         return ERROR_INT("stream not open", procName, 1);
597     if (!pval)
598         return ERROR_INT("&val not defined", procName, 1);
599     *pval = 0;
600     do {  /* skip whitespace */
601         if ((c = fgetc(fp)) == EOF)
602             return 1;
603     } while (c == ' ' || c == '\t' || c == '\n' || c == '\r');
604 
605     fseek(fp, -1L, SEEK_CUR);        /* back up one byte */
606     fscanf(fp, "%d", pval);
607     return 0;
608 }
609 
610 
611 /*!
612  *  pnmSkipCommentLines()
613  *
614  *      Return: 0 if OK, 1 on error or EOF
615  *
616  *  Notes:
617  *      (1) Comment lines begin with '#'
618  *      (2) Usage: caller should check return value for EOF
619  */
620 static l_int32
pnmSkipCommentLines(FILE * fp)621 pnmSkipCommentLines(FILE  *fp)
622 {
623 l_int32  c;
624 
625     PROCNAME("pnmSkipCommentLines");
626 
627     if (!fp)
628         return ERROR_INT("stream not open", procName, 1);
629     if ((c = fgetc(fp)) == EOF)
630         return 1;
631     if (c == '#') {
632         do {  /* each line starting with '#' */
633             do {  /* this entire line */
634                 if ((c = fgetc(fp)) == EOF)
635                     return 1;
636             } while (c != '\n');
637             if ((c = fgetc(fp)) == EOF)
638                 return 1;
639         } while (c == '#');
640     }
641 
642         /* Back up one byte */
643     fseek(fp, -1L, SEEK_CUR);
644     return 0;
645 }
646 
647 /* --------------------------------------------*/
648 #endif  /* USE_PNMIO */
649 /* --------------------------------------------*/
650