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