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