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