13 pnm.c - implements reading and writing ppm/pnm/pbm files, uses io layer.
17 io_glue *ig = io_new_fd( fd );
18 i_img *im = i_readpnm_wiol(ig, -1); // no limit on how much is read
20 io_glue *ig = io_new_fd( fd );
21 return_code = i_writepnm_wiol(im, ig);
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.
29 =head1 FUNCTION REFERENCE
31 Some of these functions are internal.
40 #define misspace(x) (x==' ' || x=='\n' || x=='\r' || x=='\t' || x=='\f' || x=='\v')
41 #define misnumber(x) (x <= '9' && x>='0')
43 static char *typenames[]={"ascii pbm", "ascii pgm", "ascii ppm", "binary pbm", "binary pgm", "binary ppm"};
46 * Type to encapsulate the local buffer
47 * management skipping over in a file
59 void init_buf(mbuf *mb, io_glue *ig) {
70 Fetches a character and advances in stream by one character.
71 Returns a pointer to the byte or NULL on failure (internal).
82 if (mb->cp == mb->len) {
84 mb->len = ig->readcb(ig, mb->buf, BSIZ);
86 i_push_error(errno, "file read error");
87 mm_log((1, "i_readpnm: read error\n"));
91 i_push_error(errno, "unexpected end of file");
92 mm_log((1, "i_readpnm: end of file\n"));
96 return &mb->buf[mb->cp++];
101 =item gnext(mbuf *mb)
103 Fetches a character but does NOT advance. Returns a pointer to
104 the byte or NULL on failure (internal).
114 io_glue *ig = mb->ig;
115 if (mb->cp == mb->len) {
117 mb->len = ig->readcb(ig, mb->buf, BSIZ);
119 i_push_error(errno, "read error");
120 mm_log((1, "i_readpnm: read error\n"));
124 i_push_error(0, "unexpected end of file");
125 mm_log((1, "i_readpnm: end of file\n"));
129 return &mb->buf[mb->cp];
136 =item skip_spaces(mb)
138 Advances in stream until it is positioned at a
139 non white space character. (internal)
148 skip_spaces(mbuf *mb) {
150 while( (cp = gpeek(mb)) && misspace(*cp) ) if ( !gnext(mb) ) break;
157 =item skip_spaces(mb)
159 Advances in stream over whitespace and a comment if one is found. (internal)
168 skip_comment(mbuf *mb) {
171 if (!skip_spaces(mb)) return 0;
173 if (!(cp = gpeek(mb))) return 0;
175 while( (cp = gpeek(mb)) && (*cp != '\n' && *cp != '\r') ) {
176 if ( !gnext(mb) ) break;
188 Fetches the next number from stream and stores in i, returns true
189 on success else false.
192 i - integer to store result in
199 gnum(mbuf *mb, int *i) {
203 if (!skip_spaces(mb)) return 0;
205 while( (cp = gpeek(mb)) && misnumber(*cp) ) {
206 *i = *i*10+(*cp-'0');
214 =item i_readpnm_wiol(ig, length)
216 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
219 length - maximum length to read from data source, before closing it -1
227 i_readpnm_wiol(io_glue *ig, int length) {
231 int width, height, maxval, channels, pcount;
242 mm_log((1,"i_readpnm(ig %p, length %d)\n", ig, length));
254 mm_log((1, "Hack is exiting\n"));
258 io_glue_commit_types(ig);
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"));
269 if ( !(cp = gnext(&buf)) ) {
270 mm_log((1, "i_readpnm: Could not read header of file\n"));
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"));
282 if ( !(cp = gnext(&buf)) ) {
283 mm_log((1, "i_readpnm: Could not read header of file\n"));
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"));
293 mm_log((1, "i_readpnm: image is a %s\n", typenames[type-1] ));
296 /* Read sizes and such */
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"));
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"));
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"));
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"));
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"));
329 if (!gnum(&buf, &maxval)) {
330 i_push_error(0, "could not read maxval");
331 mm_log((1, "i_readpnm: error reading maxval\n"));
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"));
342 channels = (type == 3 || type == 6) ? 3:1;
343 pcount = width*height*channels;
345 mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval));
347 im = i_img_empty_ch(NULL, width, height, channels);
350 case 1: /* Ascii types */
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++) {
357 if (gnum(&buf, &t)) val.channel[ch] = t;
359 mm_log((1,"i_readpnm: gnum() returned false in data\n"));
363 i_ppix(im, x, y, &val);
367 case 4: /* binary pbm */
368 for(y=0;y<height;y++) for(x=0; x<width; x+=8) {
369 if ( (uc = gnext(&buf)) ) {
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);
378 mm_log((1,"i_readpnm: gnext() returned false in data\n"));
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;
390 mm_log((1,"i_readpnm: gnext() returned false in data\n"));
394 i_ppix(im, x, y, &val);
398 mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type));
405 i_writeppm(i_img *im,int fd) {
409 mm_log((1,"i_writeppm(im* 0x%x,fd %d)\n",im,fd));
413 if (im->channels==3) {
414 sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
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"));
422 rc=mywrite(fd,im->data,im->bytes);
424 i_push_error(errno, "could not write ppm data");
425 mm_log((1,"i_writeppm: unable to write ppm data.\n"));
429 else if (im->channels == 1) {
430 sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
431 im->xsize, im->ysize);
432 if (mywrite(fd,header, strlen(header)) < 0) {
433 i_push_error(errno, "could not write pgm header");
434 mm_log((1,"i_writeppm: unable to write pgm header.\n"));
438 rc=mywrite(fd,im->data,im->bytes);
440 i_push_error(errno, "could not write pgm data");
441 mm_log((1,"i_writeppm: unable to write pgm data.\n"));
446 i_push_error(0, "can only save 1 or 3 channel images to pnm");
447 mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
456 i_writeppm_wiol(i_img *im, io_glue *ig) {
461 mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig));
464 /* Add code to get the filename info from the iolayer */
465 /* Also add code to check for mmapped code */
467 io_glue_commit_types(ig);
469 if (im->channels==3) {
470 sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
472 if (ig->writecb(ig, header, strlen(header) )<0) {
473 i_push_error(errno, "could not write ppm header");
474 mm_log((1,"i_writeppm: unable to write ppm header.\n"));
478 rc = ig->writecb(ig, im->data, im->bytes);
480 i_push_error(errno, "could not write ppm data");
481 mm_log((1,"i_writeppm: unable to write ppm data.\n"));
485 else if (im->channels == 1) {
486 sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
487 im->xsize, im->ysize);
488 if (ig->writecb(ig, header, strlen(header)) < 0) {
489 i_push_error(errno, "could not write pgm header");
490 mm_log((1,"i_writeppm: unable to write pgm header.\n"));
494 rc = ig->writecb(ig, im->data, im->bytes);
496 i_push_error(errno, "could not write pgm data");
497 mm_log((1,"i_writeppm: unable to write pgm data.\n"));
502 i_push_error(0, "can only save 1 or 3 channel images to pnm");
503 mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));