12 pnm.c - implements reading and writing ppm/pnm/pbm files, uses io layer.
16 io_glue *ig = io_new_fd( fd );
17 i_img *im = i_readpnm_wiol(ig, -1); // no limit on how much is read
19 io_glue *ig = io_new_fd( fd );
20 return_code = i_writepnm_wiol(im, ig);
24 pnm.c implements the basic functions to read and write portable
25 anymap files. It uses the iolayer and needs either a seekable source
26 or an entire memory mapped buffer.
28 =head1 FUNCTION REFERENCE
30 Some of these functions are internal.
39 #define misspace(x) (x==' ' || x=='\n' || x=='\r' || x=='\t' || x=='\f' || x=='\v')
40 #define misnumber(x) (x <= '9' && x>='0')
42 static char *typenames[]={"ascii pbm", "ascii pgm", "ascii ppm", "binary pbm", "binary pgm", "binary ppm"};
45 * Type to encapsulate the local buffer
46 * management skipping over in a file
58 void init_buf(mbuf *mb, io_glue *ig) {
69 Fetches a character and advances in stream by one character.
70 Returns a pointer to the byte or NULL on failure (internal).
81 if (mb->cp == mb->len) {
83 mb->len = ig->readcb(ig, mb->buf, BSIZ);
85 i_push_error(errno, "file read error");
86 mm_log((1, "i_readpnm: read error\n"));
90 i_push_error(errno, "unexpected end of file");
91 mm_log((1, "i_readpnm: end of file\n"));
95 return &mb->buf[mb->cp++];
100 =item gnext(mbuf *mb)
102 Fetches a character but does NOT advance. Returns a pointer to
103 the byte or NULL on failure (internal).
113 io_glue *ig = mb->ig;
114 if (mb->cp == mb->len) {
116 mb->len = ig->readcb(ig, mb->buf, BSIZ);
118 i_push_error(errno, "read error");
119 mm_log((1, "i_readpnm: read error\n"));
123 i_push_error(0, "unexpected end of file");
124 mm_log((1, "i_readpnm: end of file\n"));
128 return &mb->buf[mb->cp];
135 =item skip_spaces(mb)
137 Advances in stream until it is positioned at a
138 non white space character. (internal)
147 skip_spaces(mbuf *mb) {
149 while( (cp = gpeek(mb)) && misspace(*cp) ) if ( !gnext(mb) ) break;
156 =item skip_spaces(mb)
158 Advances in stream over whitespace and a comment if one is found. (internal)
167 skip_comment(mbuf *mb) {
170 if (!skip_spaces(mb)) return 0;
172 if (!(cp = gpeek(mb))) return 0;
174 while( (cp = gpeek(mb)) && (*cp != '\n' && *cp != '\r') ) {
175 if ( !gnext(mb) ) break;
187 Fetches the next number from stream and stores in i, returns true
188 on success else false.
191 i - integer to store result in
198 gnum(mbuf *mb, int *i) {
202 if (!skip_spaces(mb)) return 0;
204 while( (cp = gpeek(mb)) && misnumber(*cp) ) {
205 *i = *i*10+(*cp-'0');
213 =item i_readpnm_wiol(ig, length)
215 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
218 length - maximum length to read from data source, before closing it -1
226 i_readpnm_wiol(io_glue *ig, int length) {
230 int width, height, maxval, channels, pcount;
241 mm_log((1,"i_readpnm(ig %p, length %d)\n", ig, length));
253 mm_log((1, "Hack is exiting\n"));
257 io_glue_commit_types(ig);
262 if (!cp || *cp != 'P') {
263 i_push_error(0, "bad header magic, not a PNM file");
264 mm_log((1, "i_readpnm: Could not read header of file\n"));
268 if ( !(cp = gnext(&buf)) ) {
269 mm_log((1, "i_readpnm: Could not read header of file\n"));
275 if (type < 1 || type > 6) {
276 i_push_error(0, "unknown PNM file type, not a PNM file");
277 mm_log((1, "i_readpnm: Not a pnm file\n"));
281 if ( !(cp = gnext(&buf)) ) {
282 mm_log((1, "i_readpnm: Could not read header of file\n"));
286 if ( !misspace(*cp) ) {
287 i_push_error(0, "unexpected character, not a PNM file");
288 mm_log((1, "i_readpnm: Not a pnm file\n"));
292 mm_log((1, "i_readpnm: image is a %s\n", typenames[type-1] ));
295 /* Read sizes and such */
297 if (!skip_comment(&buf)) {
298 i_push_error(0, "while skipping to width");
299 mm_log((1, "i_readpnm: error reading before width\n"));
303 if (!gnum(&buf, &width)) {
304 i_push_error(0, "could not read image width");
305 mm_log((1, "i_readpnm: error reading width\n"));
309 if (!skip_comment(&buf)) {
310 i_push_error(0, "while skipping to height");
311 mm_log((1, "i_readpnm: error reading before height\n"));
315 if (!gnum(&buf, &height)) {
316 i_push_error(0, "could not read image height");
317 mm_log((1, "i_readpnm: error reading height\n"));
321 if (!(type == 1 || type == 4)) {
322 if (!skip_comment(&buf)) {
323 i_push_error(0, "while skipping to maxval");
324 mm_log((1, "i_readpnm: error reading before maxval\n"));
328 if (!gnum(&buf, &maxval)) {
329 i_push_error(0, "could not read maxval");
330 mm_log((1, "i_readpnm: error reading maxval\n"));
335 i_push_error(0, "maxval is zero - invalid pnm file");
336 mm_log((1, "i_readpnm: maxval is zero, invalid pnm file\n"));
339 else if (maxval > 65535) {
340 i_push_errorf(0, "maxval of %d is over 65535 - invalid pnm file",
342 mm_log((1, "i_readpnm: maxval of %d is over 65535 - invalid pnm file\n"));
345 else if (type >= 4 && maxval > 255) {
346 i_push_errorf(0, "maxval of %d is over 255 - not currently supported by Imager for binary pnm", maxval);
347 mm_log((1, "i_readpnm: maxval of %d is over 255 - not currently supported by Imager for binary pnm\n", maxval));
351 rounder = maxval / 2;
353 if (!(cp = gnext(&buf)) || !misspace(*cp)) {
354 i_push_error(0, "garbage in header, invalid PNM file");
355 mm_log((1, "i_readpnm: garbage in header\n"));
359 channels = (type == 3 || type == 6) ? 3:1;
360 pcount = width*height*channels;
362 mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval));
364 im = i_img_empty_ch(NULL, width, height, channels);
366 i_tags_add(&im->tags, "i_format", 0, "pnm", -1, 0);
369 case 1: /* Ascii types */
372 for(y=0;y<height;y++) for(x=0; x<width; x++) {
373 for(ch=0; ch<channels; ch++) {
375 if (gnum(&buf, &t)) val.channel[ch] = (t * 255 + rounder) / maxval;
377 mm_log((1,"i_readpnm: gnum() returned false in data\n"));
381 i_ppix(im, x, y, &val);
385 case 4: /* binary pbm */
386 for(y=0;y<height;y++) for(x=0; x<width; x+=8) {
387 if ( (uc = (unsigned char*)gnext(&buf)) ) {
389 int pc = width-x < 8 ? width-x : 8;
390 /* mm_log((1,"i_readpnm: y=%d x=%d pc=%d\n", y, x, pc)); */
391 for(xt = 0; xt<pc; xt++) {
392 val.channel[0] = (*uc & (128>>xt)) ? 0 : 255;
393 i_ppix(im, x+xt, y, &val);
396 mm_log((1,"i_readpnm: gnext() returned false in data\n"));
402 case 5: /* binary pgm */
403 case 6: /* binary ppm */
404 for(y=0;y<height;y++) for(x=0; x<width; x++) {
405 for(ch=0; ch<channels; ch++) {
406 if ( (uc = (unsigned char*)gnext(&buf)) )
407 val.channel[ch] = (*uc * 255 + rounder) / maxval;
409 mm_log((1,"i_readpnm: gnext() returned false in data\n"));
413 i_ppix(im, x, y, &val);
417 mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type));
425 i_writeppm_wiol(i_img *im, io_glue *ig) {
430 mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig));
433 /* Add code to get the filename info from the iolayer */
434 /* Also add code to check for mmapped code */
436 io_glue_commit_types(ig);
438 if (im->channels == 3) {
439 sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
440 if (ig->writecb(ig,header,strlen(header))<0) {
441 i_push_error(errno, "could not write ppm header");
442 mm_log((1,"i_writeppm: unable to write ppm header.\n"));
446 if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
447 rc = ig->writecb(ig,im->idata,im->bytes);
450 unsigned char *data = mymalloc(3 * im->xsize);
455 static int rgb_chan[3] = { 0, 1, 2 };
458 while (y < im->ysize && rc >= 0) {
459 i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3);
460 rc = ig->writecb(ig, data, im->xsize * 3);
466 i_push_error(0, "Out of memory");
471 i_push_error(errno, "could not write ppm data");
472 mm_log((1,"i_writeppm: unable to write ppm data.\n"));
476 else if (im->channels == 1) {
477 sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
478 im->xsize, im->ysize);
479 if (ig->writecb(ig,header, strlen(header)) < 0) {
480 i_push_error(errno, "could not write pgm header");
481 mm_log((1,"i_writeppm: unable to write pgm header.\n"));
485 if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
486 rc=ig->writecb(ig,im->idata,im->bytes);
489 unsigned char *data = mymalloc(im->xsize);
497 while (y < im->ysize && rc >= 0) {
498 i_gsamp(im, 0, im->xsize, y, data, &chan, 1);
499 rc = ig->writecb(ig, data, im->xsize);
505 i_push_error(0, "Out of memory");
510 i_push_error(errno, "could not write pgm data");
511 mm_log((1,"i_writeppm: unable to write pgm data.\n"));
516 i_push_error(0, "can only save 1 or 3 channel images to pnm");
517 mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
530 Arnar M. Hrafnkelsson <addi@umich.edu>