1 /*=============================================================================
2 libpamread.c
3 ===============================================================================
4 These are the library functions, which belong in the libnetpbm library,
5 that deal with reading the PAM (Portable Arbitrary Format) image format
6 raster (not the header).
7
8 This file was originally written by Bryan Henderson and is contributed
9 to the public domain by him and subsequent authors.
10 =============================================================================*/
11
12 /* See pmfileio.c for the complicated explanation of this 32/64 bit file
13 offset stuff.
14 */
15 #define _FILE_OFFSET_BITS 64
16 #define _LARGE_FILES
17
18 #include <string.h>
19 #include <limits.h>
20 #include <assert.h>
21
22 #include "netpbm/pm_config.h"
23 #include "netpbm/nstring.h"
24
25 #include "fileio.h"
26 #include "pam.h"
27
28
29 static void
readPbmRow(const struct pam * const pamP,tuple * const tuplerow)30 readPbmRow(const struct pam * const pamP,
31 tuple * const tuplerow) {
32
33 if (pamP->depth != 1)
34 pm_error("Invalid pam structure passed to pnm_readpamrow(). "
35 "It says PBM format, but 'depth' member is not 1.");
36 else {
37 jmp_buf jmpbuf;
38 jmp_buf * origJmpbufP;
39 unsigned char * bitrow;
40
41 bitrow = (unsigned char *) pbm_allocrow(pbm_packed_bytes(pamP->width));
42
43 if (setjmp(jmpbuf) != 0) {
44 pbm_freerow(bitrow);
45 pm_setjmpbuf(origJmpbufP);
46 pm_longjmp();
47 } else {
48 pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
49
50 pbm_readpbmrow_packed(pamP->file, bitrow, pamP->width,
51 pamP->format);
52
53 if (tuplerow) {
54 unsigned int col;
55 for (col = 0; col < pamP->width; ++col) {
56 tuplerow[col][0] =
57 ( ((bitrow[col/8] >> (7-col%8)) & 1 ) == PBM_BLACK)
58 ? PAM_PBM_BLACK : PAM_PBM_WHITE
59 ;
60 }
61 }
62 pm_setjmpbuf(origJmpbufP);
63 }
64 pbm_freerow(bitrow);
65 }
66 }
67
68
69
70 static void
readPlainNonPbmRow(const struct pam * const pamP,tuple * const tuplerow)71 readPlainNonPbmRow(const struct pam * const pamP,
72 tuple * const tuplerow) {
73
74 int col;
75
76 for (col = 0; col < pamP->width; ++col) {
77 unsigned int plane;
78 for (plane = 0; plane < pamP->depth; ++plane)
79 if (tuplerow) {
80 tuplerow[col][plane] = pm_getuint(pamP->file);
81
82 if (tuplerow[col][plane] > pamP->maxval)
83 pm_error("Plane %u sample value %lu exceeds the "
84 "image maxval of %lu",
85 plane, tuplerow[col][plane], pamP->maxval);
86 } else
87 pm_getuint(pamP->file); /* read data and discard */
88 }
89 }
90
91
92
93 /* Though it is possible to simplify the bytesToSampleN() and
94 parsexNBpsRow() functions into a single routine that handles all
95 sample widths, we value efficiency higher here. Earlier versions
96 of Netpbm (before 10.25) did that, with a loop, and performance
97 suffered visibly.
98 */
99
100 static __inline__ sample
bytes2ToSample(unsigned char buff[2])101 bytes2ToSample(unsigned char buff[2]) {
102
103 return (buff[0] << 8) + buff[1];
104 }
105
106
107
108 static __inline__ sample
bytes3ToSample(unsigned char buff[3])109 bytes3ToSample(unsigned char buff[3]) {
110 return (buff[0] << 16) + (buff[1] << 8) + buff[2];
111 }
112
113
114
115 static __inline__ sample
bytes4ToSample(unsigned char buff[4])116 bytes4ToSample(unsigned char buff[4]) {
117
118 return (buff[0] << 24) + (buff[1] << 16) + (buff[2] << 8) + buff[3];
119 }
120
121
122
123 static void
parse1BpsRow(const struct pam * const pamP,tuple * const tuplerow,const unsigned char * const inbuf)124 parse1BpsRow(const struct pam * const pamP,
125 tuple * const tuplerow,
126 const unsigned char * const inbuf) {
127
128 int col;
129 unsigned int bufferCursor;
130
131 bufferCursor = 0; /* initial value */
132
133 for (col = 0; col < pamP->width; ++col) {
134 unsigned int plane;
135 for (plane = 0; plane < pamP->depth; ++plane)
136 tuplerow[col][plane]= inbuf[bufferCursor++];
137 }
138 }
139
140
141
142 static void
parse2BpsRow(const struct pam * const pamP,tuple * const tuplerow,const unsigned char * const inbuf)143 parse2BpsRow(const struct pam * const pamP,
144 tuple * const tuplerow,
145 const unsigned char * const inbuf) {
146
147 unsigned char (* const ib)[2] = (unsigned char (*)[2]) inbuf;
148
149 int col;
150 unsigned int bufferCursor;
151
152 bufferCursor = 0; /* initial value */
153
154 for (col=0; col < pamP->width; ++col) {
155 unsigned int plane;
156 for (plane = 0; plane < pamP->depth; ++plane)
157 tuplerow[col][plane] = bytes2ToSample(ib[bufferCursor++]);
158 }
159 }
160
161
162
163 static void
parse3BpsRow(const struct pam * const pamP,tuple * const tuplerow,const unsigned char * const inbuf)164 parse3BpsRow(const struct pam * const pamP,
165 tuple * const tuplerow,
166 const unsigned char * const inbuf) {
167
168 unsigned char (* const ib)[3] = (unsigned char (*)[3]) inbuf;
169
170 int col;
171 unsigned int bufferCursor;
172
173 bufferCursor = 0; /* initial value */
174
175 for (col=0; col < pamP->width; ++col) {
176 unsigned int plane;
177 for (plane = 0; plane < pamP->depth; ++plane)
178 tuplerow[col][plane] = bytes3ToSample(ib[bufferCursor++]);
179 }
180 }
181
182
183
184 static void
parse4BpsRow(const struct pam * const pamP,tuple * const tuplerow,const unsigned char * const inbuf)185 parse4BpsRow(const struct pam * const pamP,
186 tuple * const tuplerow,
187 const unsigned char * const inbuf) {
188
189 unsigned char (* const ib)[4] = (unsigned char (*)[4]) inbuf;
190
191 int col;
192 unsigned int bufferCursor;
193
194 bufferCursor = 0; /* initial value */
195
196 for (col=0; col < pamP->width; ++col) {
197 unsigned int plane;
198 for (plane = 0; plane < pamP->depth; ++plane)
199 tuplerow[col][plane] = bytes4ToSample(ib[bufferCursor++]);
200 }
201 }
202
203
204
205 static void
validatePamRow(const struct pam * const pamP,tuple * const tuplerow,const char ** const errorP)206 validatePamRow(const struct pam * const pamP,
207 tuple * const tuplerow,
208 const char ** const errorP) {
209 /*----------------------------------------------------------------------------
210 Check for sample values above maxval in input.
211
212 Note: a program that wants to deal with invalid sample values itself can
213 simply make sure it sets pamP->maxval sufficiently high, so this validation
214 never fails.
215 -----------------------------------------------------------------------------*/
216 /* To save time, skip the test for if the maxval is a saturated value
217 (255, 65535) or format is PBM.
218
219 This is an expensive test, but is skipped in most cases: in practice
220 maxvals other than 255 or 65535 are uncommon. Thus we do this in a
221 separate pass through the row rather than while reading in the row.
222 */
223
224 if (pamP->maxval == (((sample) 0x1) << pamP->bytes_per_sample*8) - 1 ||
225 PAM_FORMAT_TYPE(pamP->format) == PBM_FORMAT) {
226 /* There's no way a sample can be invalid, so we don't need to
227 look at the samples individually.
228 */
229 *errorP = NULL;
230 } else {
231 unsigned int col;
232 for (col = 0; col < pamP->width; ++col) {
233 unsigned int plane;
234 for (plane = 0; plane < pamP->depth; ++plane) {
235 if (tuplerow[col][plane] > pamP->maxval) {
236 pm_asprintf(errorP,
237 "Plane %u sample value %lu exceeds the "
238 "image maxval of %lu",
239 plane, tuplerow[col][plane], pamP->maxval);
240 return;
241 }
242 }
243 }
244 *errorP = NULL;
245 }
246 }
247
248
249
250 static void
readRawNonPbmRow(const struct pam * const pamP,tuple * const tuplerow)251 readRawNonPbmRow(const struct pam * const pamP,
252 tuple * const tuplerow) {
253
254 unsigned int const rowImageSize =
255 pamP->width * pamP->bytes_per_sample * pamP->depth;
256
257 unsigned char * inbuf;
258 size_t bytesRead;
259 const char * error;
260
261 inbuf = pnm_allocrowimage(pamP);
262
263 bytesRead = fread(inbuf, 1, rowImageSize, pamP->file);
264
265 if (bytesRead != rowImageSize) {
266 if (feof(pamP->file))
267 pm_asprintf(&error, "End of file encountered "
268 "when trying to read a row from input file.");
269 else
270 pm_asprintf(&error, "Error reading a row from input file. "
271 "fread() fails with errno=%d (%s)",
272 errno, strerror(errno));
273 } else {
274 error = NULL; /* initial assumption */
275 if (tuplerow) {
276 switch (pamP->bytes_per_sample) {
277 case 1: parse1BpsRow(pamP, tuplerow, inbuf); break;
278 case 2: parse2BpsRow(pamP, tuplerow, inbuf); break;
279 case 3: parse3BpsRow(pamP, tuplerow, inbuf); break;
280 case 4: parse4BpsRow(pamP, tuplerow, inbuf); break;
281 default:
282 pm_asprintf(&error, "invalid bytes per sample passed to "
283 "pnm_formatpamrow(): %u", pamP->bytes_per_sample);
284 }
285 if (error == NULL)
286 validatePamRow(pamP, tuplerow, &error);
287 }
288 }
289 pnm_freerowimage(inbuf);
290
291 if (error) {
292 pm_errormsg("%s", error);
293 pm_strfree(error);
294 pm_longjmp();
295 }
296 }
297
298
299
300 void
pnm_readpamrow(const struct pam * const pamP,tuple * const tuplerow)301 pnm_readpamrow(const struct pam * const pamP,
302 tuple * const tuplerow) {
303 /*----------------------------------------------------------------------------
304 Read a row from the Netpbm image file into tuplerow[], at the
305 current file position. If 'tuplerow' is NULL, advance the file
306 pointer to the next row, but don't return the contents of the
307 current one.
308
309 We assume the file is positioned to the beginning of a row of the
310 image's raster.
311 -----------------------------------------------------------------------------*/
312 /* For speed, we don't check any of the inputs for consistency
313 here (unless it's necessary to avoid crashing). Any consistency
314 checking should have been done by a prior call to
315 pnm_readpaminit().
316 */
317
318 /* Need a special case for raw PBM because it has multiple tuples (8)
319 packed into one byte.
320 */
321
322 switch (pamP->format) {
323 case PAM_FORMAT:
324 case RPPM_FORMAT:
325 case RPGM_FORMAT:
326 readRawNonPbmRow(pamP, tuplerow);
327 break;
328 case PPM_FORMAT:
329 case PGM_FORMAT:
330 readPlainNonPbmRow(pamP, tuplerow);
331 break;
332 case RPBM_FORMAT:
333 case PBM_FORMAT:
334 readPbmRow(pamP, tuplerow);
335 break;
336 default:
337 pm_error("Invalid 'format' member in PAM structure: %u", pamP->format);
338 }
339 }
340
341
342
343 tuple **
pnm_readpam(FILE * const fileP,struct pam * const pamP,int const size)344 pnm_readpam(FILE * const fileP,
345 struct pam * const pamP,
346 int const size) {
347
348 jmp_buf jmpbuf;
349 jmp_buf * origJmpbufP;
350 tuple ** tuplearray;
351
352 pnm_readpaminit(fileP, pamP, size);
353
354 tuplearray = pnm_allocpamarray(pamP);
355
356 if (setjmp(jmpbuf) != 0) {
357 pnm_freepamarray(tuplearray, pamP);
358 pm_setjmpbuf(origJmpbufP);
359 pm_longjmp();
360 } else {
361 unsigned int row;
362
363 pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
364
365 for (row = 0; row < pamP->height; ++row)
366 pnm_readpamrow(pamP, tuplearray[row]);
367
368 pm_setjmpbuf(origJmpbufP);
369 }
370 return tuplearray;
371 }
372