Initial revision
[imager.git] / pnm.c
CommitLineData
02d1d628
AMH
1#include "image.h"
2#include "io.h"
3#include "log.h"
4
5#include <stdlib.h>
6
7
8/*
9=head1 NAME
10
11pnm.c - implements reading and writing ppm/pnm/pbm files, uses io layer.
12
13=head1 SYNOPSIS
14
15 io_glue *ig = io_new_fd( fd );
16 i_img *im = i_readpnm_wiol(ig, -1); // no limit on how much is read
17 // or
18 io_glue *ig = io_new_fd( fd );
19 return_code = i_writepnm_wiol(im, ig);
20
21=head1 DESCRIPTION
22
23pnm.c implements the basic functions to read and write portable
24anymap files. It uses the iolayer and needs either a seekable source
25or an entire memory mapped buffer.
26
27=head1 FUNCTION REFERENCE
28
29Some of these functions are internal.
30
31=over 4
32
33=cut
34*/
35
36
37#define BSIZ 1024
38#define misspace(x) (x==' ' || x=='\n' || x=='\r' || x=='\t' || x=='\f' || x=='\v')
39#define misnumber(x) (x <= '9' && x>='0')
40
41static char *typenames[]={"ascii pbm", "ascii pgm", "ascii ppm", "binary pbm", "binary pgm", "binary ppm"};
42
43/*
44 * Type to encapsulate the local buffer
45 * management skipping over in a file
46 */
47
48typedef struct {
49 io_glue *ig;
50 int len;
51 int cp;
52 char buf[BSIZ];
53} mbuf;
54
55
56static
57void init_buf(mbuf *mb, io_glue *ig) {
58 mb->len = 0;
59 mb->cp = 0;
60 mb->ig = ig;
61}
62
63
64
65/*
66=item gnext(mbuf *mb)
67
68Fetches a character and advances in stream by one character.
69Returns a pointer to the byte or NULL on failure (internal).
70
71 mb - buffer object
72
73=cut
74*/
75
76static
77char *
78gnext(mbuf *mb) {
79 io_glue *ig = mb->ig;
80 if (mb->cp == mb->len) {
81 mb->cp = 0;
82 mb->len = ig->readcb(ig, mb->buf, BSIZ);
83 if (mb->len == -1) {
84 mm_log((1, "i_readpnm: read error\n"));
85 return NULL;
86 }
87 if (mb->len == 0) {
88 mm_log((1, "i_readpnm: end of file\n"));
89 return NULL;
90 }
91 }
92 return &mb->buf[mb->cp++];
93}
94
95
96/*
97=item gnext(mbuf *mb)
98
99Fetches a character but does NOT advance. Returns a pointer to
100the byte or NULL on failure (internal).
101
102 mb - buffer object
103
104=cut
105*/
106
107static
108char *
109gpeek(mbuf *mb) {
110 io_glue *ig = mb->ig;
111 if (mb->cp == mb->len) {
112 mb->cp = 0;
113 mb->len = ig->readcb(ig, mb->buf, BSIZ);
114 if (mb->len == -1) {
115 mm_log((1, "i_readpnm: read error\n"));
116 return NULL;
117 }
118 if (mb->len == 0) {
119 mm_log((1, "i_readpnm: end of file\n"));
120 return NULL;
121 }
122 }
123 return &mb->buf[mb->cp];
124}
125
126
127
128
129/*
130=item skip_spaces(mb)
131
132Advances in stream until it is positioned at a
133non white space character. (internal)
134
135 mb - buffer object
136
137=cut
138*/
139
140static
141int
142skip_spaces(mbuf *mb) {
143 char *cp;
144 while( (cp = gpeek(mb)) && misspace(*cp) ) if ( !gnext(mb) ) break;
145 if (!cp) return 0;
146 return 1;
147}
148
149
150/*
151=item skip_spaces(mb)
152
153Advances in stream over whitespace and a comment if one is found. (internal)
154
155 mb - buffer object
156
157=cut
158*/
159
160static
161int
162skip_comment(mbuf *mb) {
163 char *cp;
164
165 if (!skip_spaces(mb)) return 0;
166
167 if (!(cp = gpeek(mb))) return 0;
168 if (*cp == '#') {
169 while( (cp = gpeek(mb)) && (*cp != '\n' && *cp != '\r') ) {
170 if ( !gnext(mb) ) break;
171 }
172 }
173 if (!cp) return 0;
174
175 return 1;
176}
177
178
179/*
180=item gnum(mb, i)
181
182Fetches the next number from stream and stores in i, returns true
183on success else false.
184
185 mb - buffer object
186 i - integer to store result in
187
188=cut
189*/
190
191static
192int
193gnum(mbuf *mb, int *i) {
194 char *cp;
195 *i = 0;
196
197 if (!skip_spaces(mb)) return 0;
198
199 while( (cp = gpeek(mb)) && misnumber(*cp) ) {
200 *i = *i*10+(*cp-'0');
201 cp = gnext(mb);
202 }
203 return 1;
204}
205
206
207/*
208=item i_readpnm_wiol(ig, length)
209
210Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
211
212 ig - io_glue object
213 length - maximum length to read from data source, before closing it -1
214 signifies no limit.
215
216=cut
217*/
218
219
220i_img *
221i_readpnm_wiol(io_glue *ig, int length) {
222 i_img* im;
223 int type;
224 int x, y, ch;
225 int width, height, maxval, channels, pcount;
226 char *cp;
227 unsigned char *uc;
228 mbuf buf;
229 i_color val;
230 int mult;
231
232 /* char *pp; */
233
234 mm_log((1,"i_readpnm(ig %p, length %d)\n", ig, length));
235
236 /*
237 pp = mymalloc(20);
238
239 pp[-1]= 'c';
240 pp[-2]= 'c';
241
242 bndcheck_all();
243
244 myfree(pp);
245
246 mm_log((1, "Hack is exiting\n"));
247
248*/
249
250 io_glue_commit_types(ig);
251 init_buf(&buf, ig);
252
253 cp = gnext(&buf);
254
255 if (!cp || *cp != 'P') {
256 mm_log((1, "i_readpnm: Could not read header of file\n"));
257 return NULL;
258 }
259
260 if ( !(cp = gnext(&buf)) ) {
261 mm_log((1, "i_readpnm: Could not read header of file\n"));
262 return NULL;
263 }
264
265 type = *cp-'0';
266
267 if (type < 1 || type > 6) {
268 mm_log((1, "i_readpnm: Not a pnm file\n"));
269 return NULL;
270 }
271
272 if ( !(cp = gnext(&buf)) ) {
273 mm_log((1, "i_readpnm: Could not read header of file\n"));
274 return NULL;
275 }
276
277 if ( !misspace(*cp) ) {
278 mm_log((1, "i_readpnm: Not a pnm file\n"));
279 return NULL;
280 }
281
282 mm_log((1, "i_readpnm: image is a %s\n", typenames[type-1] ));
283
284
285 /* Read sizes and such */
286
287 if (!skip_comment(&buf)) {
288 mm_log((1, "i_readpnm: error reading before width\n"));
289 return NULL;
290 }
291
292 if (!gnum(&buf, &width)) {
293 mm_log((1, "i_readpnm: error reading width\n"));
294 return NULL;
295 }
296
297 if (!skip_comment(&buf)) {
298 mm_log((1, "i_readpnm: error reading before height\n"));
299 return NULL;
300 }
301
302 if (!gnum(&buf, &height)) {
303 mm_log((1, "i_readpnm: error reading height\n"));
304 return NULL;
305 }
306
307 if (!(type == 1 || type == 4)) {
308 if (!skip_comment(&buf)) {
309 mm_log((1, "i_readpnm: error reading before maxval\n"));
310 return NULL;
311 }
312
313 if (!gnum(&buf, &maxval)) {
314 mm_log((1, "i_readpnm: error reading maxval\n"));
315 return NULL;
316 }
317 } else maxval=1;
318
319 if (!(cp = gnext(&buf)) || !misspace(*cp)) {
320 mm_log((1, "i_readpnm: garbage in header\n"));
321 return NULL;
322 }
323
324 channels = (type == 3 || type == 6) ? 3:1;
325 pcount = width*height*channels;
326
327 mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval));
328
329 im = i_img_empty_ch(NULL, width, height, channels);
330
331 switch (type) {
332 case 1: /* Ascii types */
333 case 2:
334 case 3:
335 mult = type == 1 ? 255 : 1;
336 for(y=0;y<height;y++) for(x=0; x<width; x++) {
337 for(ch=0; ch<channels; ch++) {
338 int t;
339 if (gnum(&buf, &t)) val.channel[ch] = t;
340 else {
341 mm_log((1,"i_readpnm: gnum() returned false in data\n"));
342 return im;
343 }
344 }
345 i_ppix(im, x, y, &val);
346 }
347 break;
348
349 case 4: /* binary pbm */
350 for(y=0;y<height;y++) for(x=0; x<width; x+=8) {
351 if ( (uc = gnext(&buf)) ) {
352 int xt;
353 int pc = width-x < 8 ? width-x : 8;
354 /* mm_log((1,"i_readpnm: y=%d x=%d pc=%d\n", y, x, pc)); */
355 for(xt = 0; xt<pc; xt++) {
356 val.channel[0] = (*uc & (128>>xt)) ? 0 : 255;
357 i_ppix(im, x+xt, y, &val);
358 }
359 } else {
360 mm_log((1,"i_readpnm: gnext() returned false in data\n"));
361 return im;
362 }
363 }
364 break;
365
366 case 5: /* binary pgm */
367 case 6: /* binary ppm */
368 for(y=0;y<height;y++) for(x=0; x<width; x++) {
369 for(ch=0; ch<channels; ch++) {
370 if ( (uc = gnext(&buf)) ) val.channel[ch] = *uc;
371 else {
372 mm_log((1,"i_readpnm: gnext() returned false in data\n"));
373 return im;
374 }
375 }
376 i_ppix(im, x, y, &val);
377 }
378 break;
379 default:
380 mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type));
381 return NULL;
382 }
383 return im;
384}
385
386undef_int
387i_writeppm(i_img *im,int fd) {
388 char header[255];
389 int rc;
390
391 mm_log((1,"i_writeppm(im* 0x%x,fd %d)\n",im,fd));
392 if (im->channels!=3) {
393 mm_log((1,"i_writeppm: ppm is 3 channel only (current image is %d)\n",im->channels));
394 return(0);
395 }
396
397 sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
398
399 if (mywrite(fd,header,strlen(header))<0) {
400 mm_log((1,"i_writeppm: unable to write ppm header.\n"));
401 return(0);
402 }
403
404 rc=mywrite(fd,im->data,im->bytes);
405 if (rc<0) {
406 mm_log((1,"i_writeppm: unable to write ppm data.\n"));
407 return(0);
408 }
409 return(1);
410}
411
412
413
414
415
416
417