1 /* libpbm2.c - pbm utility library part 2
2 **
3 ** Copyright (C) 1988 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 <assert.h>
14 #include <limits.h>
15 
16 #include "pbm.h"
17 #include "libpbm.h"
18 #include "fileio.h"
19 #include "pam.h"
20 
21 static bit
getbit(FILE * const file)22 getbit (FILE * const file) {
23     char ch;
24 
25     do {
26         ch = pm_getc( file );
27     } while ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' );
28 
29     if ( ch != '0' && ch != '1' )
30         pm_error( "junk in file where bits should be" );
31 
32     return ( ch == '1' ) ? 1 : 0;
33 }
34 
35 
36 
37 void
pbm_readpbminitrest(FILE * const file,int * const colsP,int * const rowsP)38 pbm_readpbminitrest( FILE * const file,
39                      int  * const colsP,
40                      int  * const rowsP ) {
41     /* Read size. */
42     *colsP = (int)pm_getuint( file );
43     *rowsP = (int)pm_getuint( file );
44 
45     /* *colsP and *rowsP really should be unsigned int, but they come
46        from the time before unsigned ints (or at least from a person
47        trained in that tradition), so they are int.  We could simply
48        consider negative numbers to mean values > INT_MAX/2 and much
49        code would just automatically work.  But some code would fail
50        miserably.  So we consider values that won't fit in an int to
51        be unprocessable.
52     */
53     if (*colsP < 0)
54         pm_error("Number of columns in header is too large.");
55     if (*rowsP < 0)
56         pm_error("Number of columns in header is too large.");
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    A common operation is adding 1 or 2 to the highest row or
73    column number in the image, so we make sure that's possible.
74 -----------------------------------------------------------------------------*/
75     if (cols > INT_MAX - 2)
76         pm_error("image width (%u) too large to be processed", cols);
77     if (rows > INT_MAX - 2)
78         pm_error("image height (%u) too large to be processed", rows);
79 }
80 
81 
82 
83 void
pbm_readpbminit(FILE * const ifP,int * const colsP,int * const rowsP,int * const formatP)84 pbm_readpbminit(FILE * const ifP,
85                 int *  const colsP,
86                 int *  const rowsP,
87                 int *  const formatP) {
88 
89     int realFormat;
90 
91     realFormat = pm_readmagicnumber(ifP);
92 
93     switch (PAM_FORMAT_TYPE(realFormat)) {
94     case PBM_TYPE:
95         *formatP = realFormat;
96         pbm_readpbminitrest(ifP, colsP, rowsP);
97         break;
98 
99     case PGM_TYPE:
100         pm_error("The input file is a PGM, not a PBM.  You may want to "
101                  "convert it to PBM with 'pamditherbw | pamtopnm' or "
102                  "'pamthreshold | pamtopnm'");
103 
104     case PPM_TYPE:
105         pm_error("The input file is a PPM, not a PBM.  You may want to "
106                  "convert it to PBM with 'ppmtopgm', 'pamditherbw', and "
107                  "'pamtopnm'");
108 
109     case PAM_TYPE:
110         pm_error("The input file is a PAM, not a PBM.  "
111                  "If it is a black and white image, you can convert it "
112                  "to PBM with 'pamtopnm'");
113         break;
114     default:
115         pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
116                  realFormat);
117     }
118     validateComputableSize(*colsP, *rowsP);
119 }
120 
121 
122 
123 void
pbm_readpbmrow(FILE * const file,bit * const bitrow,int const cols,int const format)124 pbm_readpbmrow( FILE * const file,
125                 bit * const bitrow,
126                 int const cols,
127                 int const format) {
128 
129     int col, bitshift;
130 
131     switch ( format )
132     {
133     case PBM_FORMAT:
134     for ( col = 0; col < cols; ++col )
135         bitrow[col] = getbit( file );
136     break;
137 
138     case RPBM_FORMAT: {
139         unsigned char item;
140         bitshift = -1;  item = 0;  /* item's value is meaningless here */
141         for ( col = 0; col < cols; ++col ) {
142               if ( bitshift == -1 ) {
143                     item = pm_getrawbyte( file );
144                     bitshift = 7;
145                 }
146               bitrow[col] = ( item >> bitshift ) & 1;
147               --bitshift;
148           }
149     }
150     break;
151 
152     default:
153     pm_error( "can't happen" );
154     }
155 }
156 
157 
158 
159 void
pbm_readpbmrow_packed(FILE * const fileP,unsigned char * const packedBits,int const cols,int const format)160 pbm_readpbmrow_packed(FILE *          const fileP,
161                       unsigned char * const packedBits,
162                       int             const cols,
163                       int             const format) {
164 
165     switch(format) {
166     case PBM_FORMAT: {
167         unsigned int col;
168         unsigned int byteIndex;
169 
170         /* We first clear the return buffer, then set ones where needed */
171         for (byteIndex = 0; byteIndex < pbm_packed_bytes(cols); ++byteIndex)
172             packedBits[byteIndex] = 0x00;
173 
174         for (col = 0; col < cols; ++col) {
175             unsigned char mask;
176             mask = getbit(fileP) << (7 - col % 8);
177             packedBits[col / 8] |= mask;
178         }
179     }
180     break;
181 
182     case RPBM_FORMAT: {
183         unsigned int bytesReadCt;
184         bytesReadCt = fread(packedBits, 1, pbm_packed_bytes(cols), fileP);
185 
186         if (bytesReadCt < pbm_packed_bytes(cols)) {
187             if (feof(fileP))
188                 if (bytesReadCt == 0)
189                     pm_error("Attempt to read a raw PBM image row, but "
190                              "no more rows left in file.");
191                 else
192                     pm_error("EOF in the middle of a raw PBM row.");
193             else
194                 pm_error("I/O error reading raw PBM row");
195         }
196     }
197     break;
198 
199     default:
200         pm_error("Internal error in pbm_readpbmrow_packed.");
201     }
202 }
203 
204 
205 
206 void
pbm_readpbmrow_bitoffset(FILE * const ifP,unsigned char * const packedBits,int const cols,int const format,unsigned int const offset)207 pbm_readpbmrow_bitoffset(FILE *          const ifP,
208                          unsigned char * const packedBits,
209                          int             const cols,
210                          int             const format,
211                          unsigned int    const offset) {
212 /*----------------------------------------------------------------------------
213    Read PBM packed bitrow from file 'ifP' (raster format given by
214    'cols' and 'format') and shift right 'offset' bits.
215 
216    Read it into packedBits[], preserving surrounding image data.
217 
218    Logic not tested for negative offsets.
219 -----------------------------------------------------------------------------*/
220     unsigned int const rsh = offset % 8;
221     unsigned int const lsh = (8 - rsh) % 8;
222     unsigned char * const window = &packedBits[offset/8];
223         /* Area of packed row buffer into which we read the image data.
224            Aligned to nearest byte boundary to the left, so the first
225            few bits might contain original data, not output.
226         */
227     unsigned int const last = pbm_packed_bytes(cols+rsh) - 1;
228         /* Position within window of rightmost byte after shift */
229 
230     /* The original leftmost and rightmost chars. */
231     unsigned char const origHead = window[0];
232     unsigned char const origEnd  = window[last];
233 
234     pbm_readpbmrow_packed(ifP, window, cols, format);
235 
236     if (rsh > 0) {
237         /* Target slot doesn't start on byte boundary; right-shift. */
238         unsigned char carryover;
239         unsigned int i;
240 
241         carryover = (origHead >> lsh) << lsh;
242 
243         for (i = 0; i <= last; ++i) {
244             unsigned char const t = window[i] << lsh;
245             window[i] = carryover | window[i] >> rsh;
246             carryover = t;
247         }
248     }
249 
250     if ((cols + rsh) % 8 > 0) {
251         /* Adjust rightmost char */
252         unsigned int  const trs = (cols + rsh) % 8;
253         unsigned int  const tls = 8 - trs;
254         unsigned char const rightBits =
255             ((unsigned char)(origEnd << trs) >> trs);
256         unsigned char const leftBits =
257             ((unsigned char)(window[last] >> tls) << tls);
258 
259         window[last] =  leftBits | rightBits;
260     }
261 }
262 
263 
264 
265 void
pbm_cleanrowend_packed(unsigned char * const packedBits,unsigned int const cols)266 pbm_cleanrowend_packed(unsigned char * const packedBits,
267                        unsigned int    const cols) {
268 /*----------------------------------------------------------------------------
269   Set fractional "don't care" bits at end of row to zero.
270 ----------------------------------------------------------------------------*/
271     unsigned int const bitsPerChar = 8;
272 
273     if (cols % bitsPerChar > 0) {
274         unsigned int const last = pbm_packed_bytes(cols) - 1;
275 
276         assert(pbm_packed_bytes(cols) > 0);
277 
278         packedBits[last] >>= bitsPerChar - cols % bitsPerChar;
279         packedBits[last] <<= bitsPerChar - cols % bitsPerChar;
280     }
281 }
282 
283 
284 
285 bit**
pbm_readpbm(FILE * const file,int * const colsP,int * const rowsP)286 pbm_readpbm( FILE * const file,
287              int  * const colsP,
288              int  * const rowsP) {
289 
290     bit ** bits;
291     int format, row;
292 
293     pbm_readpbminit( file, colsP, rowsP, &format );
294 
295     bits = pbm_allocarray( *colsP, *rowsP );
296 
297     for ( row = 0; row < *rowsP; ++row )
298         pbm_readpbmrow( file, bits[row], *colsP, format );
299 
300     return bits;
301 }
302