1 /* libpnm1.c - pnm utility library part 1
2 **
3 ** Copyright (C) 1989 by Jef Poskanzer.
4 **
5 ** Permission to use, copy, modify, and distribute this software and its
6 ** documentation for any purpose and without fee is hereby granted, provided
7 ** that the above copyright notice appear in all copies and that both that
8 ** copyright notice and this permission notice appear in supporting
9 ** documentation.  This software is provided "as is" without express or
10 ** implied warranty.
11 */
12 
13 #include <string.h>
14 #include <errno.h>
15 
16 #include "netpbm/mallocvar.h"
17 
18 #include "pnm.h"
19 
20 #include "ppm.h"
21 #include "libppm.h"
22 
23 #include "pgm.h"
24 #include "libpgm.h"
25 
26 #include "pbm.h"
27 #include "libpbm.h"
28 
29 #include "pam.h"
30 #include "libpam.h"
31 
32 
33 
34 xel *
pnm_allocrow(unsigned int const cols)35 pnm_allocrow(unsigned int const cols) {
36 
37     xel * xelrow;
38 
39     MALLOCARRAY(xelrow, cols);
40 
41     if (xelrow == NULL)
42         pm_error("Unable to allocate space for a %u-column xel row", cols);
43 
44     return xelrow;
45 }
46 
47 
48 
49 void
pnm_init(int * const argcP,char ** const argv)50 pnm_init(int * const argcP, char ** const argv) {
51     ppm_init( argcP, argv );
52 }
53 
54 void
pnm_nextimage(FILE * const file,int * const eofP)55 pnm_nextimage(FILE * const file, int * const eofP) {
56     pm_nextimage(file, eofP);
57 }
58 
59 
60 
61 static void
validateComputableSize(unsigned int const cols,unsigned int const rows)62 validateComputableSize(unsigned int const cols,
63                        unsigned int const rows) {
64 /*----------------------------------------------------------------------------
65    Validate that the dimensions of the image are such that it can be
66    processed in typical ways on this machine without worrying about
67    overflows.  Note that in C, arithmetic is always modulus
68    arithmetic, so if your values are too big, the result is not what
69    you expect.  That failed expectation can be disastrous if you use
70    it to allocate memory.
71 
72    It is very normal to allocate space for a pixel row, so we make sure
73    the size of a pixel row, in bytes, can be represented by an 'int'.
74 
75    A common operation is adding 1 or 2 to the highest row or
76    column number in the image, so we make sure that's possible.
77 -----------------------------------------------------------------------------*/
78     if (cols > INT_MAX/(sizeof(pixval) * 3) || cols > INT_MAX - 2)
79         pm_error("image width (%u) too large to be processed", cols);
80     if (rows > INT_MAX - 2)
81         pm_error("image height (%u) too large to be processed", rows);
82 }
83 
84 
85 
86 void
pnm_readpnminit(FILE * const fileP,int * const colsP,int * const rowsP,xelval * const maxvalP,int * const formatP)87 pnm_readpnminit(FILE *   const fileP,
88                 int *    const colsP,
89                 int *    const rowsP,
90                 xelval * const maxvalP,
91                 int *    const formatP) {
92 
93     int realFormat;
94 
95     /* Check magic number. */
96     realFormat = pm_readmagicnumber(fileP);
97     switch (PAM_FORMAT_TYPE(realFormat)) {
98     case PPM_TYPE: {
99         pixval maxval;
100         *formatP = realFormat;
101         ppm_readppminitrest(fileP, colsP, rowsP, &maxval);
102         *maxvalP = maxval;
103     }
104     break;
105 
106     case PGM_TYPE: {
107         gray maxval;
108 
109         *formatP = realFormat;
110         pgm_readpgminitrest(fileP, colsP, rowsP, &maxval);
111         *maxvalP = maxval;
112     }
113     break;
114 
115     case PBM_TYPE:
116         *formatP = realFormat;
117         pbm_readpbminitrest(fileP, colsP, rowsP);
118         *maxvalP = 1;
119     break;
120 
121     case PAM_TYPE: {
122         gray maxval;
123         pnm_readpaminitrestaspnm(fileP, colsP, rowsP, &maxval, formatP);
124         *maxvalP = maxval;
125     }
126     break;
127 
128     default:
129         pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
130                  realFormat);
131     }
132     validateComputableSize(*colsP, *rowsP);
133 }
134 
135 
136 
137 static void
readpgmrow(FILE * const fileP,xel * const xelrow,int const cols,xelval const maxval,int const format)138 readpgmrow(FILE * const fileP,
139            xel *  const xelrow,
140            int    const cols,
141            xelval const maxval,
142            int    const format) {
143 
144     jmp_buf jmpbuf;
145     jmp_buf * origJmpbufP;
146     gray * grayrow;
147 
148     grayrow = pgm_allocrow(cols);
149 
150     if (setjmp(jmpbuf) != 0) {
151         pgm_freerow(grayrow);
152         pm_setjmpbuf(origJmpbufP);
153         pm_longjmp();
154     } else {
155         unsigned int col;
156 
157         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
158 
159         pgm_readpgmrow(fileP, grayrow, cols, (gray) maxval, format);
160 
161         for (col = 0; col < cols; ++col)
162             PNM_ASSIGN1(xelrow[col], grayrow[col]);
163 
164         pm_setjmpbuf(origJmpbufP);
165     }
166     pgm_freerow(grayrow);
167 }
168 
169 
170 
171 static void
readpbmrow(FILE * const fileP,xel * const xelrow,int const cols,xelval const maxval,int const format)172 readpbmrow(FILE * const fileP,
173                xel *  const xelrow,
174                int    const cols,
175                xelval const maxval,
176                int    const format) {
177 
178     jmp_buf jmpbuf;
179     jmp_buf * origJmpbufP;
180     bit * bitrow;
181 
182     bitrow = pbm_allocrow_packed(cols);
183 
184     if (setjmp(jmpbuf) != 0) {
185         pbm_freerow_packed(bitrow);
186         pm_setjmpbuf(origJmpbufP);
187         pm_longjmp();
188     } else {
189         unsigned int col;
190 
191         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
192 
193         pbm_readpbmrow_packed(fileP, bitrow, cols, format);
194 
195         for (col = 0; col < cols; ++col) {
196             pixval const g =
197                 ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ?
198                 maxval : 0;
199             PNM_ASSIGN1(xelrow[col], g);
200         }
201         pm_setjmpbuf(origJmpbufP);
202     }
203     pbm_freerow(bitrow);
204 }
205 
206 
207 
208 void
pnm_readpnmrow(FILE * const fileP,xel * const xelrow,int const cols,xelval const maxval,int const format)209 pnm_readpnmrow(FILE * const fileP,
210                xel *  const xelrow,
211                int    const cols,
212                xelval const maxval,
213                int    const format) {
214 
215     switch (PNM_FORMAT_TYPE(format)) {
216     case PPM_TYPE:
217         ppm_readppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, format);
218         break;
219 
220     case PGM_TYPE:
221         readpgmrow(fileP, xelrow, cols, maxval, format);
222         break;
223 
224     case PBM_TYPE:
225         readpbmrow(fileP, xelrow, cols, maxval, format);
226         break;
227 
228     default:
229         pm_error("INTERNAL ERROR.  Impossible format.");
230     }
231 }
232 
233 
234 
235 xel **
pnm_readpnm(FILE * const fileP,int * const colsP,int * const rowsP,xelval * const maxvalP,int * const formatP)236 pnm_readpnm(FILE *   const fileP,
237             int *    const colsP,
238             int *    const rowsP,
239             xelval * const maxvalP,
240             int *    const formatP) {
241 
242     jmp_buf jmpbuf;
243     jmp_buf * origJmpbufP;
244     int cols, rows;
245     xelval maxval;
246     int format;
247     xel ** xels;
248 
249     pnm_readpnminit(fileP, &cols, &rows, &maxval, &format);
250 
251     xels = pnm_allocarray(cols, rows);
252 
253     if (setjmp(jmpbuf) != 0) {
254         pnm_freearray(xels, rows);
255         pm_setjmpbuf(origJmpbufP);
256         pm_longjmp();
257     } else {
258         unsigned int row;
259 
260         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
261 
262         for (row = 0; row < rows; ++row)
263             pnm_readpnmrow(fileP, xels[row], cols, maxval, format);
264 
265         pm_setjmpbuf(origJmpbufP);
266     }
267     *colsP = cols;
268     *rowsP = rows;
269     *maxvalP = maxval;
270     *formatP = format;
271 
272     return xels;
273 }
274 
275 
276 
277 void
pnm_check(FILE * const fileP,enum pm_check_type const check_type,int const format,int const cols,int const rows,int const maxval,enum pm_check_code * const retvalP)278 pnm_check(FILE *               const fileP,
279           enum pm_check_type   const check_type,
280           int                  const format,
281           int                  const cols,
282           int                  const rows,
283           int                  const maxval,
284           enum pm_check_code * const retvalP) {
285 
286     switch (PNM_FORMAT_TYPE(format)) {
287     case PBM_TYPE:
288         pbm_check(fileP, check_type, format, cols, rows, retvalP);
289         break;
290     case PGM_TYPE:
291         pgm_check(fileP, check_type, format, cols, rows, maxval, retvalP);
292         break;
293     case PPM_TYPE:
294         ppm_check(fileP, check_type, format, cols, rows, maxval, retvalP);
295         break;
296     default:
297         pm_error("pnm_check() called with invalid format parameter");
298     }
299 }
300