]> git.imager.perl.org - imager.git/blob - pnm.c
make write errors for jpegs cause errors at the top level
[imager.git] / pnm.c
1 #include "image.h"
2 #include "io.h"
3 #include "log.h"
4 #include "iolayer.h"
5
6 #include <stdlib.h>
7 #include <errno.h>
8
9
10 /*
11 =head1 NAME
12
13 pnm.c - implements reading and writing ppm/pnm/pbm files, uses io layer.
14
15 =head1 SYNOPSIS
16
17    io_glue *ig = io_new_fd( fd );
18    i_img *im   = i_readpnm_wiol(ig, -1); // no limit on how much is read
19    // or 
20    io_glue *ig = io_new_fd( fd );
21    return_code = i_writepnm_wiol(im, ig); 
22
23 =head1 DESCRIPTION
24
25 pnm.c implements the basic functions to read and write portable 
26 anymap files.  It uses the iolayer and needs either a seekable source
27 or an entire memory mapped buffer.
28
29 =head1 FUNCTION REFERENCE
30
31 Some of these functions are internal.
32
33 =over 4
34
35 =cut
36 */
37
38
39 #define BSIZ 1024
40 #define misspace(x) (x==' ' || x=='\n' || x=='\r' || x=='\t' || x=='\f' || x=='\v')
41 #define misnumber(x) (x <= '9' && x>='0')
42
43 static char *typenames[]={"ascii pbm", "ascii pgm", "ascii ppm", "binary pbm", "binary pgm", "binary ppm"};
44
45 /*
46  * Type to encapsulate the local buffer
47  * management skipping over in a file 
48  */
49
50 typedef struct {
51   io_glue *ig;
52   int len;
53   int cp;
54   char buf[BSIZ];
55 } mbuf;
56
57
58 static
59 void init_buf(mbuf *mb, io_glue *ig) {
60   mb->len = 0;
61   mb->cp  = 0;
62   mb->ig  = ig;
63 }
64
65
66
67 /*
68 =item gnext(mbuf *mb)
69
70 Fetches a character and advances in stream by one character.  
71 Returns a pointer to the byte or NULL on failure (internal).
72
73    mb - buffer object
74
75 =cut
76 */
77
78 static
79 char *
80 gnext(mbuf *mb) {
81   io_glue *ig = mb->ig;
82   if (mb->cp == mb->len) {
83     mb->cp = 0;
84     mb->len = ig->readcb(ig, mb->buf, BSIZ);
85     if (mb->len == -1) {
86       i_push_error(errno, "file read error");
87       mm_log((1, "i_readpnm: read error\n"));
88       return NULL;
89     }
90     if (mb->len == 0) {
91       i_push_error(errno, "unexpected end of file");
92       mm_log((1, "i_readpnm: end of file\n"));
93       return NULL;
94     }
95   }
96   return &mb->buf[mb->cp++];
97 }
98
99
100 /*
101 =item gnext(mbuf *mb)
102
103 Fetches a character but does NOT advance.  Returns a pointer to
104 the byte or NULL on failure (internal).
105
106    mb - buffer object
107
108 =cut
109 */
110
111 static
112 char *
113 gpeek(mbuf *mb) {
114   io_glue *ig = mb->ig;
115   if (mb->cp == mb->len) {
116     mb->cp = 0;
117     mb->len = ig->readcb(ig, mb->buf, BSIZ);
118     if (mb->len == -1) {
119       i_push_error(errno, "read error");
120       mm_log((1, "i_readpnm: read error\n"));
121       return NULL;
122     }
123     if (mb->len == 0) {
124       i_push_error(0, "unexpected end of file");
125       mm_log((1, "i_readpnm: end of file\n"));
126       return NULL;
127     }
128   }
129   return &mb->buf[mb->cp];
130 }
131
132
133
134
135 /*
136 =item skip_spaces(mb)
137
138 Advances in stream until it is positioned at a
139 non white space character. (internal)
140
141    mb - buffer object
142
143 =cut
144 */
145
146 static
147 int
148 skip_spaces(mbuf *mb) {
149   char *cp;
150   while( (cp = gpeek(mb)) && misspace(*cp) ) if ( !gnext(mb) ) break;
151   if (!cp) return 0;
152   return 1;
153 }
154
155
156 /*
157 =item skip_spaces(mb)
158
159 Advances in stream over whitespace and a comment if one is found. (internal)
160
161    mb - buffer object
162
163 =cut
164 */
165
166 static
167 int
168 skip_comment(mbuf *mb) {
169   char *cp;
170
171   if (!skip_spaces(mb)) return 0;
172
173   if (!(cp = gpeek(mb))) return 0;
174   if (*cp == '#') {
175     while( (cp = gpeek(mb)) && (*cp != '\n' && *cp != '\r') ) {
176       if ( !gnext(mb) ) break;
177     }
178   }
179   if (!cp) return 0;
180   
181   return 1;
182 }
183
184
185 /*
186 =item gnum(mb, i)
187
188 Fetches the next number from stream and stores in i, returns true
189 on success else false.
190
191    mb - buffer object
192    i  - integer to store result in
193
194 =cut
195 */
196
197 static
198 int
199 gnum(mbuf *mb, int *i) {
200   char *cp;
201   *i = 0;
202
203   if (!skip_spaces(mb)) return 0; 
204
205   while( (cp = gpeek(mb)) && misnumber(*cp) ) {
206     *i = *i*10+(*cp-'0');
207     cp = gnext(mb);
208   }
209   return 1;
210 }
211
212
213 /*
214 =item i_readpnm_wiol(ig, length)
215
216 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
217
218    ig     - io_glue object
219    length - maximum length to read from data source, before closing it -1 
220             signifies no limit.
221
222 =cut
223 */
224
225
226 i_img *
227 i_readpnm_wiol(io_glue *ig, int length) {
228   i_img* im;
229   int type;
230   int x, y, ch;
231   int width, height, maxval, channels, pcount;
232   char *cp;
233   unsigned char *uc;
234   mbuf buf;
235   i_color val;
236   int mult;
237
238   i_clear_error();
239
240   /*  char *pp; */
241
242   mm_log((1,"i_readpnm(ig %p, length %d)\n", ig, length));
243
244   /*
245   pp = mymalloc(20);
246   
247   pp[-1]= 'c';
248   pp[-2]= 'c';
249   
250   bndcheck_all();
251
252   myfree(pp);
253
254   mm_log((1, "Hack is exiting\n"));
255
256 */
257   
258   io_glue_commit_types(ig);
259   init_buf(&buf, ig);
260
261   cp = gnext(&buf);
262
263   if (!cp || *cp != 'P') {
264     i_push_error(0, "bad header magic, not a PNM file");
265     mm_log((1, "i_readpnm: Could not read header of file\n"));
266     return NULL;
267   }
268
269   if ( !(cp = gnext(&buf)) ) {
270     mm_log((1, "i_readpnm: Could not read header of file\n"));
271     return NULL;
272   }
273   
274   type = *cp-'0';
275
276   if (type < 1 || type > 6) {
277     i_push_error(0, "unknown PNM file type, not a PNM file");
278     mm_log((1, "i_readpnm: Not a pnm file\n"));
279     return NULL;
280   }
281
282   if ( !(cp = gnext(&buf)) ) {
283     mm_log((1, "i_readpnm: Could not read header of file\n"));
284     return NULL;
285   }
286   
287   if ( !misspace(*cp) ) {
288     i_push_error(0, "unexpected character, not a PNM file");
289     mm_log((1, "i_readpnm: Not a pnm file\n"));
290     return NULL;
291   }
292   
293   mm_log((1, "i_readpnm: image is a %s\n", typenames[type-1] ));
294
295   
296   /* Read sizes and such */
297
298   if (!skip_comment(&buf)) {
299     i_push_error(0, "while skipping to width");
300     mm_log((1, "i_readpnm: error reading before width\n"));
301     return NULL;
302   }
303   
304   if (!gnum(&buf, &width)) {
305     i_push_error(0, "could not read image width");
306     mm_log((1, "i_readpnm: error reading width\n"));
307     return NULL;
308   }
309
310   if (!skip_comment(&buf)) {
311     i_push_error(0, "while skipping to height");
312     mm_log((1, "i_readpnm: error reading before height\n"));
313     return NULL;
314   }
315
316   if (!gnum(&buf, &height)) {
317     i_push_error(0, "could not read image height");
318     mm_log((1, "i_readpnm: error reading height\n"));
319     return NULL;
320   }
321   
322   if (!(type == 1 || type == 4)) {
323     if (!skip_comment(&buf)) {
324       i_push_error(0, "while skipping to maxval");
325       mm_log((1, "i_readpnm: error reading before maxval\n"));
326       return NULL;
327     }
328
329     if (!gnum(&buf, &maxval)) {
330       i_push_error(0, "could not read maxval");
331       mm_log((1, "i_readpnm: error reading maxval\n"));
332       return NULL;
333     }
334   } else maxval=1;
335
336   if (!(cp = gnext(&buf)) || !misspace(*cp)) {
337     i_push_error(0, "garbage in header, invalid PNM file");
338     mm_log((1, "i_readpnm: garbage in header\n"));
339     return NULL;
340   }
341
342   channels = (type == 3 || type == 6) ? 3:1;
343   pcount = width*height*channels;
344
345   mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval));
346   
347   im = i_img_empty_ch(NULL, width, height, channels);
348
349   switch (type) {
350   case 1: /* Ascii types */
351   case 2:
352   case 3:
353     mult = type == 1 ? 255 : 1;
354     for(y=0;y<height;y++) for(x=0; x<width; x++) {
355       for(ch=0; ch<channels; ch++) {
356         int t;
357         if (gnum(&buf, &t)) val.channel[ch] = t;
358         else {
359           mm_log((1,"i_readpnm: gnum() returned false in data\n"));
360           return im;
361         }
362       }
363       i_ppix(im, x, y, &val);
364     }
365     break;
366     
367   case 4: /* binary pbm */
368     for(y=0;y<height;y++) for(x=0; x<width; x+=8) {
369       if ( (uc = gnext(&buf)) ) {
370         int xt;
371         int pc = width-x < 8 ? width-x : 8;
372         /*      mm_log((1,"i_readpnm: y=%d x=%d pc=%d\n", y, x, pc)); */
373         for(xt = 0; xt<pc; xt++) {
374           val.channel[0] = (*uc & (128>>xt)) ? 0 : 255; 
375           i_ppix(im, x+xt, y, &val);
376         }
377       } else {
378         mm_log((1,"i_readpnm: gnext() returned false in data\n"));
379         return im;
380       }
381     }
382     break;
383
384   case 5: /* binary pgm */
385   case 6: /* binary ppm */
386     for(y=0;y<height;y++) for(x=0; x<width; x++) {
387       for(ch=0; ch<channels; ch++) {
388         if ( (uc = gnext(&buf)) ) val.channel[ch] = *uc;
389         else {
390           mm_log((1,"i_readpnm: gnext() returned false in data\n"));
391           return im;
392         }
393       }
394       i_ppix(im, x, y, &val);
395     }
396     break;
397   default:
398     mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type));
399     return NULL;
400   }
401   return im;
402 }
403
404 undef_int
405 i_writeppm(i_img *im,int fd) {
406   char header[255];
407   int rc;
408
409   mm_log((1,"i_writeppm(im* 0x%x,fd %d)\n",im,fd));
410   
411   i_clear_error();
412
413   if (im->channels==3) {
414     sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
415
416     if (mywrite(fd,header,strlen(header))<0) {
417       i_push_error(errno, "could not write ppm header");
418       mm_log((1,"i_writeppm: unable to write ppm header.\n"));
419       return(0);
420     }
421     
422     if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
423       rc=mywrite(fd,im->idata,im->bytes);
424     }
425     else {
426       unsigned char *data = mymalloc(3 * im->xsize);
427       if (data != NULL) {
428         int y = 0;
429         int x, ch;
430         unsigned char *p;
431         static int rgb_chan[3] = { 0, 1, 2 };
432
433         rc = 0;
434         while (y < im->ysize && rc >= 0) {
435           i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3);
436           rc = mywrite(fd, data, im->xsize * 3);
437         }
438         myfree(data);
439       }
440       else {
441         i_push_error(0, "Out of memory");
442         return 0;
443       }
444     }
445     if (rc<0) {
446       i_push_error(errno, "could not write ppm data");
447       mm_log((1,"i_writeppm: unable to write ppm data.\n"));
448       return(0);
449     }
450   }
451   else if (im->channels == 1) {
452     sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
453             im->xsize, im->ysize);
454     if (mywrite(fd,header, strlen(header)) < 0) {
455       i_push_error(errno, "could not write pgm header");
456       mm_log((1,"i_writeppm: unable to write pgm header.\n"));
457       return(0);
458     }
459
460     if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
461       rc=mywrite(fd,im->idata,im->bytes);
462     }
463     else {
464       unsigned char *data = mymalloc(im->xsize);
465       if (data != NULL) {
466         int y = 0;
467         int x, ch;
468         int chan = 0;
469         unsigned char *p;
470
471         rc = 0;
472         while (y < im->ysize && rc >= 0) {
473           i_gsamp(im, 0, im->xsize, y, data, &chan, 1);
474           rc = mywrite(fd, data, im->xsize);
475         }
476         myfree(data);
477       }
478       else {
479         i_push_error(0, "Out of memory");
480         return 0;
481       }
482     }
483     if (rc<0) {
484       i_push_error(errno, "could not write pgm data");
485       mm_log((1,"i_writeppm: unable to write pgm data.\n"));
486       return(0);
487     }
488   }
489   else {
490     i_push_error(0, "can only save 1 or 3 channel images to pnm");
491     mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
492     return(0);
493   }
494   
495   return(1);
496 }
497
498
499 undef_int
500 i_writeppm_wiol(i_img *im, io_glue *ig) {
501   char header[255];
502   int rc;
503   writep write_func;
504
505   mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig));
506   i_clear_error();
507
508   /* Add code to get the filename info from the iolayer */
509   /* Also add code to check for mmapped code */
510
511   io_glue_commit_types(ig);
512
513   if (im->channels == 3) {
514     sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
515     if (ig->writecb(ig,header,strlen(header))<0) {
516       i_push_error(errno, "could not write ppm header");
517       mm_log((1,"i_writeppm: unable to write ppm header.\n"));
518       return(0);
519     }
520
521     if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
522       rc = ig->writecb(ig,im->idata,im->bytes);
523     }
524     else {
525       unsigned char *data = mymalloc(3 * im->xsize);
526       if (data != NULL) {
527         int y = 0;
528         int x, ch;
529         unsigned char *p;
530         static int rgb_chan[3] = { 0, 1, 2 };
531
532         rc = 0;
533         while (y < im->ysize && rc >= 0) {
534           i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3);
535           rc = ig->writecb(ig, data, im->xsize * 3);
536         }
537         myfree(data);
538       }
539       else {
540         i_push_error(0, "Out of memory");
541         return 0;
542       }
543     }
544     if (rc<0) {
545       i_push_error(errno, "could not write ppm data");
546       mm_log((1,"i_writeppm: unable to write ppm data.\n"));
547       return(0);
548     }
549   }
550   else if (im->channels == 1) {
551     sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
552             im->xsize, im->ysize);
553     if (ig->writecb(ig,header, strlen(header)) < 0) {
554       i_push_error(errno, "could not write pgm header");
555       mm_log((1,"i_writeppm: unable to write pgm header.\n"));
556       return(0);
557     }
558
559     if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
560       rc=ig->writecb(ig,im->idata,im->bytes);
561     }
562     else {
563       unsigned char *data = mymalloc(im->xsize);
564       if (data != NULL) {
565         int y = 0;
566         int x, ch;
567         int chan = 0;
568         unsigned char *p;
569
570         rc = 0;
571         while (y < im->ysize && rc >= 0) {
572           i_gsamp(im, 0, im->xsize, y, data, &chan, 1);
573           rc = ig->writecb(ig, data, im->xsize);
574         }
575         myfree(data);
576       }
577       else {
578         i_push_error(0, "Out of memory");
579         return 0;
580       }
581     }
582     if (rc<0) {
583       i_push_error(errno, "could not write pgm data");
584       mm_log((1,"i_writeppm: unable to write pgm data.\n"));
585       return(0);
586     }
587   }
588   else {
589     i_push_error(0, "can only save 1 or 3 channel images to pnm");
590     mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
591     return(0);
592   }
593
594   return(1);
595 }