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