1 /* libpgm1.c - pgm 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 /* See pmfileio.c for the complicated explanation of this 32/64 bit file
14 offset stuff.
15 */
16 #define _FILE_OFFSET_BITS 64
17 #define _LARGE_FILES
18
19 #include <string.h>
20 #include <stdio.h>
21 #include <errno.h>
22
23 #include "netpbm/mallocvar.h"
24 #include "netpbm/nstring.h"
25
26 #include "pgm.h"
27 #include "libpgm.h"
28 #include "pbm.h"
29 #include "libpbm.h"
30 #include "pam.h"
31 #include "libpam.h"
32 #include "fileio.h"
33
34
35 gray *
pgm_allocrow(unsigned int const cols)36 pgm_allocrow(unsigned int const cols) {
37
38 gray * grayrow;
39
40 MALLOCARRAY(grayrow, cols);
41
42 if (grayrow == NULL)
43 pm_error("Unable to allocate space for a %u-column gray row", cols);
44
45 return grayrow;
46 }
47
48
49
50 void
pgm_init(int * const argcP,char ** const argv)51 pgm_init(int * const argcP,
52 char ** const argv) {
53
54 pbm_init(argcP, argv);
55 }
56
57
58
59 void
pgm_nextimage(FILE * const file,int * const eofP)60 pgm_nextimage(FILE * const file,
61 int * const eofP) {
62 pm_nextimage(file, eofP);
63 }
64
65
66
67 void
pgm_readpgminitrest(FILE * const fileP,int * const colsP,int * const rowsP,gray * const maxvalP)68 pgm_readpgminitrest(FILE * const fileP,
69 int * const colsP,
70 int * const rowsP,
71 gray * const maxvalP) {
72
73 gray maxval;
74
75 /* Read size. */
76 *colsP = (int)pm_getuint(fileP);
77 *rowsP = (int)pm_getuint(fileP);
78
79 /* Read maxval. */
80 maxval = pm_getuint(fileP);
81 if (maxval > PGM_OVERALLMAXVAL)
82 pm_error("maxval of input image (%u) is too large. "
83 "The maximum allowed by PGM is %u.",
84 maxval, PGM_OVERALLMAXVAL);
85 if (maxval == 0)
86 pm_error("maxval of input image is zero.");
87
88 *maxvalP = maxval;
89 }
90
91
92
93 static void
validateComputableSize(unsigned int const cols,unsigned int const rows)94 validateComputableSize(unsigned int const cols,
95 unsigned int const rows) {
96 /*----------------------------------------------------------------------------
97 Validate that the dimensions of the image are such that it can be
98 processed in typical ways on this machine without worrying about
99 overflows. Note that in C, arithmetic is always modulus
100 arithmetic, so if your values are too big, the result is not what
101 you expect. That failed expectation can be disastrous if you use
102 it to allocate memory.
103
104 It is very normal to allocate space for a pixel row, so we make sure
105 the size of a pixel row, in bytes, can be represented by an 'int'.
106
107 A common operation is adding 1 or 2 to the highest row or
108 column number in the image, so we make sure that's possible.
109 -----------------------------------------------------------------------------*/
110 if (cols > INT_MAX / (sizeof(gray)) || cols > INT_MAX - 2)
111 pm_error("image width (%u) too large to be processed", cols);
112 if (rows > INT_MAX - 2)
113 pm_error("image height (%u) too large to be processed", rows);
114 }
115
116
117
118 void
pgm_readpgminit(FILE * const fileP,int * const colsP,int * const rowsP,gray * const maxvalP,int * const formatP)119 pgm_readpgminit(FILE * const fileP,
120 int * const colsP,
121 int * const rowsP,
122 gray * const maxvalP,
123 int * const formatP) {
124
125 int realFormat;
126
127 /* Check magic number. */
128 realFormat = pm_readmagicnumber(fileP);
129 switch (PAM_FORMAT_TYPE(realFormat)) {
130 case PBM_TYPE:
131 *formatP = realFormat;
132 pbm_readpbminitrest(fileP, colsP, rowsP);
133
134 /* Mathematically, it makes the most sense for the maxval of a PBM
135 file seen as a PGM to be 1. But we tried this for a while and
136 found that it causes unexpected results and frequent need for a
137 Pnmdepth stage to convert the maxval to 255. You see, when you
138 transform a PGM file in a way that causes interpolated gray shades,
139 there's no in-between value to use when maxval is 1. It's really
140 hard even to discover that your lack of Pnmdepth is your problem.
141 So we pick 255, which is the most common PGM maxval, and the highest
142 resolution you can get without increasing the size of the PGM
143 image.
144
145 So this means some programs that are capable of exploiting the
146 bi-level nature of a PBM file must be PNM programs instead of PGM
147 programs.
148 */
149
150 *maxvalP = PGM_MAXMAXVAL;
151 break;
152
153 case PGM_TYPE:
154 *formatP = realFormat;
155 pgm_readpgminitrest(fileP, colsP, rowsP, maxvalP);
156 break;
157
158 case PPM_TYPE:
159 pm_error("Input file is a PPM, which this program cannot process. "
160 "You may want to convert it to PGM with 'ppmtopgm'");
161
162 case PAM_TYPE:
163 pnm_readpaminitrestaspnm(fileP, colsP, rowsP, maxvalP, formatP);
164
165 if (PAM_FORMAT_TYPE(*formatP) != PGM_TYPE)
166 pm_error("Format of PAM input is not consistent with PGM");
167
168 break;
169
170 default:
171 pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
172 realFormat);
173 }
174 validateComputableSize(*colsP, *rowsP);
175 }
176
177
178
179 static void
validateRpgmRow(gray * const grayrow,unsigned int const cols,gray const maxval,const char ** const errorP)180 validateRpgmRow(gray * const grayrow,
181 unsigned int const cols,
182 gray const maxval,
183 const char ** const errorP) {
184 /*----------------------------------------------------------------------------
185 Check for sample values above maxval in input.
186
187 Note: a program that wants to deal with invalid sample values itself can
188 simply make sure it uses a sufficiently high maxval on the read function
189 call, so this validation never fails.
190 -----------------------------------------------------------------------------*/
191 if (maxval == 255 || maxval == 65535) {
192 /* There's no way a sample can be invalid, so we don't need to look at
193 the samples individually.
194 */
195 *errorP = NULL;
196 } else {
197 unsigned int col;
198 for (col = 0; col < cols; ++col) {
199 if (grayrow[col] > maxval) {
200 pm_asprintf(errorP,
201 "gray value %u is greater than maxval (%u)",
202 grayrow[col], maxval);
203 return;
204 }
205 }
206 *errorP = NULL;
207 }
208 }
209
210
211
212 static void
readRpgmRow(FILE * const fileP,gray * const grayrow,int const cols,gray const maxval,int const format)213 readRpgmRow(FILE * const fileP,
214 gray * const grayrow,
215 int const cols,
216 gray const maxval,
217 int const format) {
218
219 unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
220 int const bytesPerRow = cols * bytesPerSample;
221
222 unsigned char * rowBuffer;
223 const char * error;
224
225 MALLOCARRAY(rowBuffer, bytesPerRow);
226 if (rowBuffer == NULL)
227 pm_asprintf(&error, "Unable to allocate memory for row buffer "
228 "for %u columns", cols);
229 else {
230 size_t rc;
231 rc = fread(rowBuffer, 1, bytesPerRow, fileP);
232 if (rc == 0)
233 pm_asprintf(&error, "Error reading row. fread() errno=%d (%s)",
234 errno, strerror(errno));
235 else if (rc != bytesPerRow)
236 pm_asprintf(&error, "Error reading row. Short read of %u bytes "
237 "instead of %u", (unsigned)rc, bytesPerRow);
238 else {
239 if (maxval < 256) {
240 unsigned int col;
241 for (col = 0; col < cols; ++col)
242 grayrow[col] = (gray)rowBuffer[col];
243 } else {
244 unsigned int bufferCursor;
245 unsigned int col;
246
247 bufferCursor = 0; /* Start at beginning of rowBuffer[] */
248
249 for (col = 0; col < cols; ++col) {
250 gray g;
251
252 g = rowBuffer[bufferCursor++] << 8;
253 g |= rowBuffer[bufferCursor++];
254
255 grayrow[col] = g;
256 }
257 }
258 validateRpgmRow(grayrow, cols, maxval, &error);
259 }
260 free(rowBuffer);
261 }
262 if (error) {
263 pm_errormsg("%s", error);
264 pm_strfree(error);
265 pm_longjmp();
266 }
267 }
268
269
270
271 static void
readPbmRow(FILE * const fileP,gray * const grayrow,int const cols,gray const maxval,int const format)272 readPbmRow(FILE * const fileP,
273 gray * const grayrow,
274 int const cols,
275 gray const maxval,
276 int const format) {
277
278 jmp_buf jmpbuf;
279 jmp_buf * origJmpbufP;
280 bit * bitrow;
281
282 bitrow = pbm_allocrow_packed(cols);
283 if (setjmp(jmpbuf) != 0) {
284 pbm_freerow(bitrow);
285 pm_setjmpbuf(origJmpbufP);
286 pm_longjmp();
287 } else {
288 unsigned int col;
289
290 pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
291
292 pbm_readpbmrow_packed(fileP, bitrow, cols, format);
293
294 for (col = 0; col < cols; ++col) {
295 grayrow[col] =
296 ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ?
297 maxval : 0
298 ;
299 }
300 pm_setjmpbuf(origJmpbufP);
301 }
302 pbm_freerow(bitrow);
303 }
304
305
306
307 void
pgm_readpgmrow(FILE * const fileP,gray * const grayrow,int const cols,gray const maxval,int const format)308 pgm_readpgmrow(FILE * const fileP,
309 gray * const grayrow,
310 int const cols,
311 gray const maxval,
312 int const format) {
313
314 switch (format) {
315 case PGM_FORMAT: {
316 unsigned int col;
317 for (col = 0; col < cols; ++col) {
318 grayrow[col] = pm_getuint(fileP);
319 if (grayrow[col] > maxval)
320 pm_error("value out of bounds (%u > %u)",
321 grayrow[col], maxval);
322 }
323 }
324 break;
325
326 case RPGM_FORMAT:
327 readRpgmRow(fileP, grayrow, cols, maxval, format);
328 break;
329
330 case PBM_FORMAT:
331 case RPBM_FORMAT:
332 readPbmRow(fileP, grayrow, cols, maxval, format);
333 break;
334
335 default:
336 pm_error("can't happen");
337 }
338 }
339
340
341
342 gray **
pgm_readpgm(FILE * const fileP,int * const colsP,int * const rowsP,gray * const maxvalP)343 pgm_readpgm(FILE * const fileP,
344 int * const colsP,
345 int * const rowsP,
346 gray * const maxvalP) {
347
348 gray ** grays;
349 int rows, cols;
350 gray maxval;
351 int format;
352 jmp_buf jmpbuf;
353 jmp_buf * origJmpbufP;
354
355 pgm_readpgminit(fileP, &cols, &rows, &maxval, &format);
356
357 grays = pgm_allocarray(cols, rows);
358
359 if (setjmp(jmpbuf) != 0) {
360 pgm_freearray(grays, rows);
361 pm_setjmpbuf(origJmpbufP);
362 pm_longjmp();
363 } else {
364 unsigned int row;
365
366 pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
367
368 for (row = 0; row < rows; ++row)
369 pgm_readpgmrow(fileP, grays[row], cols, maxval, format);
370
371 pm_setjmpbuf(origJmpbufP);
372 }
373 *colsP = cols;
374 *rowsP = rows;
375 *maxvalP = maxval;
376 return grays;
377 }
378
379
380
381 void
pgm_check(FILE * const file,enum pm_check_type const checkType,int const format,int const cols,int const rows,gray const maxval,enum pm_check_code * const retvalP)382 pgm_check(FILE * const file,
383 enum pm_check_type const checkType,
384 int const format,
385 int const cols,
386 int const rows,
387 gray const maxval,
388 enum pm_check_code * const retvalP) {
389
390 if (rows < 0)
391 pm_error("Invalid number of rows passed to pgm_check(): %d", rows);
392 if (cols < 0)
393 pm_error("Invalid number of columns passed to pgm_check(): %d", cols);
394
395 if (checkType != PM_CHECK_BASIC) {
396 if (retvalP)
397 *retvalP = PM_CHECK_UNKNOWN_TYPE;
398 } else if (PGM_FORMAT_TYPE(format) == PBM_TYPE) {
399 pbm_check(file, checkType, format, cols, rows, retvalP);
400 } else if (format != RPGM_FORMAT) {
401 if (retvalP)
402 *retvalP = PM_CHECK_UNCHECKABLE;
403 } else {
404 pm_filepos const bytesPerRow = cols * (maxval > 255 ? 2 : 1);
405 pm_filepos const needRasterSize = rows * bytesPerRow;
406
407 pm_check(file, checkType, needRasterSize, retvalP);
408 }
409 }
410