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, 0); // 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).
78 #define gnext(mb) (((mb)->cp == (mb)->len) ? gnextf(mb) : (mb)->buf + (mb)->cp++)
84 if (mb->cp == mb->len) {
86 mb->len = ig->readcb(ig, mb->buf, BSIZ);
88 i_push_error(errno, "file read error");
89 mm_log((1, "i_readpnm: read error\n"));
93 mm_log((1, "i_readpnm: end of file\n"));
97 return &mb->buf[mb->cp++];
102 =item gpeek(mbuf *mb)
104 Fetches a character but does NOT advance. Returns a pointer to
105 the byte or NULL on failure (internal).
112 #define gpeek(mb) ((mb)->cp == (mb)->len ? gpeekf(mb) : (mb)->buf + (mb)->cp)
117 io_glue *ig = mb->ig;
118 if (mb->cp == mb->len) {
120 mb->len = ig->readcb(ig, mb->buf, BSIZ);
122 i_push_error(errno, "read error");
123 mm_log((1, "i_readpnm: read error\n"));
127 mm_log((1, "i_readpnm: end of file\n"));
131 return &mb->buf[mb->cp];
135 gread(mbuf *mb, unsigned char *buf, size_t read_size) {
137 if (mb->cp != mb->len) {
138 int avail_size = mb->len - mb->cp;
139 int use_size = read_size > avail_size ? avail_size : read_size;
140 memcpy(buf, mb->buf+mb->cp, use_size);
142 total_read += use_size;
143 read_size -= use_size;
147 io_glue *ig = mb->ig;
148 int read_res = i_io_read(ig, buf, read_size);
150 total_read += read_res;
158 =item skip_spaces(mb)
160 Advances in stream until it is positioned at a
161 non white space character. (internal)
170 skip_spaces(mbuf *mb) {
172 while( (cp = gpeek(mb)) && misspace(*cp) ) if ( !gnext(mb) ) break;
179 =item skip_comment(mb)
181 Advances in stream over whitespace and a comment if one is found. (internal)
190 skip_comment(mbuf *mb) {
193 if (!skip_spaces(mb)) return 0;
195 if (!(cp = gpeek(mb))) return 0;
197 while( (cp = gpeek(mb)) && (*cp != '\n' && *cp != '\r') ) {
198 if ( !gnext(mb) ) break;
210 Fetches the next number from stream and stores in i, returns true
211 on success else false.
214 i - integer to store result in
221 gnum(mbuf *mb, int *i) {
225 if (!skip_spaces(mb)) return 0;
227 if (!(cp = gpeek(mb)))
231 while( (cp = gpeek(mb)) && misnumber(*cp) ) {
232 int work = *i*10+(*cp-'0');
235 i_push_error(0, "integer overflow");
246 read_pgm_ppm_bin8(mbuf *mb, i_img *im, int width, int height,
247 int channels, int maxval, int allow_incomplete) {
248 i_color *line, *linep;
250 unsigned char *read_buf, *readp;
252 int rounder = maxval / 2;
254 line = mymalloc(width * sizeof(i_color));
255 read_size = channels * width;
256 read_buf = mymalloc(read_size);
257 for(y=0;y<height;y++) {
260 if (gread(mb, read_buf, read_size) != read_size) {
263 if (allow_incomplete) {
264 i_tags_setn(&im->tags, "i_incomplete", 1);
265 i_tags_setn(&im->tags, "i_lines_read", y);
269 i_push_error(0, "short read - file truncated?");
275 for(x=0; x<width; x++) {
276 for(ch=0; ch<channels; ch++) {
277 linep->channel[ch] = *readp++;
283 for(x=0; x<width; x++) {
284 for(ch=0; ch<channels; ch++) {
285 /* we just clamp samples to the correct range */
286 unsigned sample = *readp++;
289 linep->channel[ch] = (sample * 255 + rounder) / maxval;
294 i_plin(im, 0, width, y, line);
304 read_pgm_ppm_bin16(mbuf *mb, i_img *im, int width, int height,
305 int channels, int maxval, int allow_incomplete) {
306 i_fcolor *line, *linep;
308 unsigned char *read_buf, *readp;
310 double maxvalf = maxval;
312 line = mymalloc(width * sizeof(i_fcolor));
313 read_size = channels * width * 2;
314 read_buf = mymalloc(read_size);
315 for(y=0;y<height;y++) {
318 if (gread(mb, read_buf, read_size) != read_size) {
321 if (allow_incomplete) {
322 i_tags_setn(&im->tags, "i_incomplete", 1);
323 i_tags_setn(&im->tags, "i_lines_read", y);
327 i_push_error(0, "short read - file truncated?");
332 for(x=0; x<width; x++) {
333 for(ch=0; ch<channels; ch++) {
334 unsigned sample = (readp[0] << 8) + readp[1];
338 linep->channel[ch] = sample / maxvalf;
342 i_plinf(im, 0, width, y, line);
352 read_pbm_bin(mbuf *mb, i_img *im, int width, int height, int allow_incomplete) {
353 i_palidx *line, *linep;
355 unsigned char *read_buf, *readp;
359 line = mymalloc(width * sizeof(i_palidx));
360 read_size = (width + 7) / 8;
361 read_buf = mymalloc(read_size);
362 for(y = 0; y < height; y++) {
363 if (gread(mb, read_buf, read_size) != read_size) {
366 if (allow_incomplete) {
367 i_tags_setn(&im->tags, "i_incomplete", 1);
368 i_tags_setn(&im->tags, "i_lines_read", y);
372 i_push_error(0, "short read - file truncated?");
380 for(x = 0; x < width; ++x) {
381 *linep++ = *readp & mask ? 1 : 0;
388 i_ppal(im, 0, width, y, line);
396 /* unlike pgm/ppm pbm:
397 - doesn't require spaces between samples (bits)
398 - 1 (maxval) is black instead of white
402 read_pbm_ascii(mbuf *mb, i_img *im, int width, int height, int allow_incomplete) {
403 i_palidx *line, *linep;
406 line = mymalloc(width * sizeof(i_palidx));
407 for(y = 0; y < height; y++) {
409 for(x = 0; x < width; ++x) {
412 if (!(cp = gnext(mb)) || (*cp != '0' && *cp != '1')) {
414 if (allow_incomplete) {
415 i_tags_setn(&im->tags, "i_incomplete", 1);
416 i_tags_setn(&im->tags, "i_lines_read", y);
421 i_push_error(0, "invalid data for ascii pnm");
423 i_push_error(0, "short read - file truncated?");
428 *linep++ = *cp == '0' ? 0 : 1;
430 i_ppal(im, 0, width, y, line);
439 read_pgm_ppm_ascii(mbuf *mb, i_img *im, int width, int height, int channels,
440 int maxval, int allow_incomplete) {
441 i_color *line, *linep;
443 int rounder = maxval / 2;
445 line = mymalloc(width * sizeof(i_color));
446 for(y=0;y<height;y++) {
448 for(x=0; x<width; x++) {
449 for(ch=0; ch<channels; ch++) {
452 if (!gnum(mb, &sample)) {
454 if (allow_incomplete) {
455 i_tags_setn(&im->tags, "i_incomplete", 1);
456 i_tags_setn(&im->tags, "i_lines_read", 1);
461 i_push_error(0, "invalid data for ascii pnm");
463 i_push_error(0, "short read - file truncated?");
470 linep->channel[ch] = (sample * 255 + rounder) / maxval;
474 i_plin(im, 0, width, y, line);
483 read_pgm_ppm_ascii_16(mbuf *mb, i_img *im, int width, int height,
484 int channels, int maxval, int allow_incomplete) {
485 i_fcolor *line, *linep;
487 double maxvalf = maxval;
489 line = mymalloc(width * sizeof(i_fcolor));
490 for(y=0;y<height;y++) {
492 for(x=0; x<width; x++) {
493 for(ch=0; ch<channels; ch++) {
496 if (!gnum(mb, &sample)) {
498 if (allow_incomplete) {
499 i_tags_setn(&im->tags, "i_incomplete", 1);
500 i_tags_setn(&im->tags, "i_lines_read", y);
505 i_push_error(0, "invalid data for ascii pnm");
507 i_push_error(0, "short read - file truncated?");
514 linep->channel[ch] = sample / maxvalf;
518 i_plinf(im, 0, width, y, line);
526 =item i_readpnm_wiol(ig, allow_incomplete)
528 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
531 allow_incomplete - allows a partial file to be read successfully
535 static i_img *i_readpnm_wiol_low( mbuf*, int);
538 i_readpnm_wiol(io_glue *ig, int allow_incomplete) {
540 io_glue_commit_types(ig);
543 return i_readpnm_wiol_low( &buf, allow_incomplete );
547 i_readpnm_wiol_low( mbuf *buf, int allow_incomplete) {
550 int width, height, maxval, channels;
555 mm_log((1,"i_readpnm(ig %p, allow_incomplete %d)\n", buf->ig, allow_incomplete));
559 if (!cp || *cp != 'P') {
560 i_push_error(0, "bad header magic, not a PNM file");
561 mm_log((1, "i_readpnm: Could not read header of file\n"));
565 if ( !(cp = gnext(buf)) ) {
566 mm_log((1, "i_readpnm: Could not read header of file\n"));
572 if (type < 1 || type > 6) {
573 i_push_error(0, "unknown PNM file type, not a PNM file");
574 mm_log((1, "i_readpnm: Not a pnm file\n"));
578 if ( !(cp = gnext(buf)) ) {
579 mm_log((1, "i_readpnm: Could not read header of file\n"));
583 if ( !misspace(*cp) ) {
584 i_push_error(0, "unexpected character, not a PNM file");
585 mm_log((1, "i_readpnm: Not a pnm file\n"));
589 mm_log((1, "i_readpnm: image is a %s\n", typenames[type-1] ));
592 /* Read sizes and such */
594 if (!skip_comment(buf)) {
595 i_push_error(0, "while skipping to width");
596 mm_log((1, "i_readpnm: error reading before width\n"));
600 if (!gnum(buf, &width)) {
601 i_push_error(0, "could not read image width");
602 mm_log((1, "i_readpnm: error reading width\n"));
606 if (!skip_comment(buf)) {
607 i_push_error(0, "while skipping to height");
608 mm_log((1, "i_readpnm: error reading before height\n"));
612 if (!gnum(buf, &height)) {
613 i_push_error(0, "could not read image height");
614 mm_log((1, "i_readpnm: error reading height\n"));
618 if (!(type == 1 || type == 4)) {
619 if (!skip_comment(buf)) {
620 i_push_error(0, "while skipping to maxval");
621 mm_log((1, "i_readpnm: error reading before maxval\n"));
625 if (!gnum(buf, &maxval)) {
626 i_push_error(0, "could not read maxval");
627 mm_log((1, "i_readpnm: error reading maxval\n"));
632 i_push_error(0, "maxval is zero - invalid pnm file");
633 mm_log((1, "i_readpnm: maxval is zero, invalid pnm file\n"));
636 else if (maxval > 65535) {
637 i_push_errorf(0, "maxval of %d is over 65535 - invalid pnm file",
639 mm_log((1, "i_readpnm: maxval of %d is over 65535 - invalid pnm file\n", maxval));
643 rounder = maxval / 2;
645 if (!(cp = gnext(buf)) || !misspace(*cp)) {
646 i_push_error(0, "garbage in header, invalid PNM file");
647 mm_log((1, "i_readpnm: garbage in header\n"));
651 channels = (type == 3 || type == 6) ? 3:1;
653 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
654 mm_log((1, "i_readpnm: image size exceeds limits\n"));
658 mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval));
660 if (type == 1 || type == 4) {
662 pbm_pal[0].channel[0] = 255;
663 pbm_pal[1].channel[0] = 0;
665 im = i_img_pal_new(width, height, 1, 256);
666 i_addcolors(im, pbm_pal, 2);
670 im = i_img_16_new(width, height, channels);
672 im = i_img_8_new(width, height, channels);
676 case 1: /* Ascii types */
677 im = read_pbm_ascii(buf, im, width, height, allow_incomplete);
683 im = read_pgm_ppm_ascii_16(buf, im, width, height, channels, maxval, allow_incomplete);
685 im = read_pgm_ppm_ascii(buf, im, width, height, channels, maxval, allow_incomplete);
688 case 4: /* binary pbm */
689 im = read_pbm_bin(buf, im, width, height, allow_incomplete);
692 case 5: /* binary pgm */
693 case 6: /* binary ppm */
695 im = read_pgm_ppm_bin16(buf, im, width, height, channels, maxval, allow_incomplete);
697 im = read_pgm_ppm_bin8(buf, im, width, height, channels, maxval, allow_incomplete);
701 mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type));
708 i_tags_add(&im->tags, "i_format", 0, "pnm", -1, 0);
709 i_tags_setn(&im->tags, "pnm_maxval", maxval);
710 i_tags_setn(&im->tags, "pnm_type", type);
715 static void free_images(i_img **imgs, int count) {
719 for (i = 0; i < count; ++i)
720 i_img_destroy(imgs[i]);
725 i_img **i_readpnm_multi_wiol(io_glue *ig, int *count, int allow_incomplete) {
726 i_img **results = NULL;
730 int result_alloc = 0,
734 io_glue_commit_types(ig);
737 mm_log((1, "read image %i\n", 1+*count));
738 img = i_readpnm_wiol_low( &buf, allow_incomplete );
740 free_images( results, *count );
744 if (*count > result_alloc) {
745 if (result_alloc == 0) {
747 results = mymalloc(result_alloc * sizeof(i_img *));
750 /* myrealloc never fails (it just dies if it can't allocate) */
752 results = myrealloc(results, result_alloc * sizeof(i_img *));
755 results[*count-1] = img;
758 if( i_tags_get_int(&img->tags, "i_incomplete", 0, &value ) && value) {
761 else if( skip_spaces( &buf ) && ( cp=gpeek( &buf ) ) && *cp == 'P' ) {
775 write_pbm(i_img *im, io_glue *ig, int zero_is_white) {
778 i_img_dim write_size;
779 unsigned char *write_buf;
780 unsigned char *writep;
784 sprintf(header, "P4\012# CREATOR: Imager\012%" i_DF " %" i_DF "\012",
785 i_DFc(im->xsize), i_DFc(im->ysize));
786 if (i_io_write(ig, header, strlen(header)) < 0) {
787 i_push_error(0, "could not write pbm header");
790 write_size = (im->xsize + 7) / 8;
791 line = mymalloc(sizeof(i_palidx) * im->xsize);
792 write_buf = mymalloc(write_size);
793 for (y = 0; y < im->ysize; ++y) {
794 i_gpal(im, 0, im->xsize, y, line);
797 memset(write_buf, 0, write_size);
798 for (x = 0; x < im->xsize; ++x) {
799 if (zero_is_white ? line[x] : !line[x])
807 if (i_io_write(ig, write_buf, write_size) != write_size) {
808 i_push_error(0, "write failure");
822 write_ppm_data_8(i_img *im, io_glue *ig, int want_channels) {
823 size_t write_size = im->xsize * want_channels;
824 size_t buf_size = im->xsize * im->channels;
825 unsigned char *data = mymalloc(buf_size);
830 i_get_file_background(im, &bg);
831 while (y < im->ysize && rc >= 0) {
832 i_gsamp_bg(im, 0, im->xsize, y, data, want_channels, &bg);
833 if (i_io_write(ig, data, write_size) != write_size) {
834 i_push_error(errno, "could not write ppm data");
847 write_ppm_data_16(i_img *im, io_glue *ig, int want_channels) {
848 size_t line_size = im->channels * im->xsize * sizeof(i_fsample_t);
849 size_t sample_count = want_channels * im->xsize;
850 size_t write_size = sample_count * 2;
851 i_fsample_t *line_buf = mymalloc(line_size);
852 i_fsample_t *samplep;
853 unsigned char *write_buf = mymalloc(write_size);
854 unsigned char *writep;
860 i_get_file_backgroundf(im, &bg);
862 while (y < im->ysize) {
863 i_gsampf_bg(im, 0, im->xsize, y, line_buf, want_channels, &bg);
866 for (sample_num = 0; sample_num < sample_count; ++sample_num) {
867 unsigned sample16 = SampleFTo16(*samplep++);
868 *writep++ = sample16 >> 8;
869 *writep++ = sample16 & 0xFF;
871 if (i_io_write(ig, write_buf, write_size) != write_size) {
872 i_push_error(errno, "could not write ppm data");
885 i_writeppm_wiol(i_img *im, io_glue *ig) {
890 mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig));
893 /* Add code to get the filename info from the iolayer */
894 /* Also add code to check for mmapped code */
896 io_glue_commit_types(ig);
898 if (i_img_is_monochrome(im, &zero_is_white)) {
899 return write_pbm(im, ig, zero_is_white);
904 int want_channels = im->channels;
906 if (want_channels == 2 || want_channels == 4)
909 if (!i_tags_get_int(&im->tags, "pnm_write_wide_data", 0, &wide_data))
912 if (want_channels == 3) {
915 else if (want_channels == 1) {
919 i_push_error(0, "can only save 1 or 3 channel images to pnm");
920 mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
923 if (im->bits <= 8 || !wide_data)
928 sprintf(header,"P%d\n#CREATOR: Imager\n%" i_DF " %" i_DF"\n%d\n",
929 type, i_DFc(im->xsize), i_DFc(im->ysize), maxval);
931 if (ig->writecb(ig,header,strlen(header)) != strlen(header)) {
932 i_push_error(errno, "could not write ppm header");
933 mm_log((1,"i_writeppm: unable to write ppm header.\n"));
937 if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type
938 && im->channels == want_channels) {
939 if (ig->writecb(ig,im->idata,im->bytes) != im->bytes) {
940 i_push_error(errno, "could not write ppm data");
944 else if (maxval == 255) {
945 if (!write_ppm_data_8(im, ig, want_channels))
949 if (!write_ppm_data_16(im, ig, want_channels))
963 Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook <tonyc@cpan.org>,
964 Philip Gwyn <gwyn@cpan.org>.