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.
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"};
47 Advances in stream until it is positioned at a
48 non white space character. (internal)
57 skip_spaces(io_glue *ig) {
59 while( (c = i_io_peekc(ig)) != EOF && misspace(c) ) {
60 if ( i_io_getc(ig) == EOF )
71 =item skip_comment(ig)
73 Advances in stream over whitespace and a comment if one is found. (internal)
82 skip_comment(io_glue *ig) {
88 if ((c = i_io_peekc(ig)) == EOF)
92 while( (c = i_io_peekc(ig)) != EOF && (c != '\n' && c != '\r') ) {
93 if ( i_io_getc(ig) == EOF )
107 Fetches the next number from stream and stores in i, returns true
108 on success else false.
111 i - integer to store result in
118 gnum(io_glue *ig, int *i) {
122 if (!skip_spaces(ig)) return 0;
124 if ((c = i_io_peekc(ig)) == EOF)
128 while( (c = i_io_peekc(ig)) != EOF && misnumber(c) ) {
129 int work = *i * 10 + (c - '0');
132 i_push_error(0, "integer overflow");
144 read_pgm_ppm_bin8(io_glue *ig, i_img *im, int width, int height,
145 int channels, int maxval, int allow_incomplete) {
146 i_color *line, *linep;
148 unsigned char *read_buf, *readp;
150 int rounder = maxval / 2;
152 line = mymalloc(width * sizeof(i_color));
153 read_size = channels * width;
154 read_buf = mymalloc(read_size);
155 for(y=0;y<height;y++) {
158 if (i_io_read(ig, read_buf, read_size) != read_size) {
161 if (allow_incomplete) {
162 i_tags_setn(&im->tags, "i_incomplete", 1);
163 i_tags_setn(&im->tags, "i_lines_read", y);
167 i_push_error(0, "short read - file truncated?");
173 for(x=0; x<width; x++) {
174 for(ch=0; ch<channels; ch++) {
175 linep->channel[ch] = *readp++;
181 for(x=0; x<width; x++) {
182 for(ch=0; ch<channels; ch++) {
183 /* we just clamp samples to the correct range */
184 unsigned sample = *readp++;
187 linep->channel[ch] = (sample * 255 + rounder) / maxval;
192 i_plin(im, 0, width, y, line);
202 read_pgm_ppm_bin16(io_glue *ig, i_img *im, int width, int height,
203 int channels, int maxval, int allow_incomplete) {
204 i_fcolor *line, *linep;
206 unsigned char *read_buf, *readp;
208 double maxvalf = maxval;
210 line = mymalloc(width * sizeof(i_fcolor));
211 read_size = channels * width * 2;
212 read_buf = mymalloc(read_size);
213 for(y=0;y<height;y++) {
216 if (i_io_read(ig, read_buf, read_size) != read_size) {
219 if (allow_incomplete) {
220 i_tags_setn(&im->tags, "i_incomplete", 1);
221 i_tags_setn(&im->tags, "i_lines_read", y);
225 i_push_error(0, "short read - file truncated?");
230 for(x=0; x<width; x++) {
231 for(ch=0; ch<channels; ch++) {
232 unsigned sample = (readp[0] << 8) + readp[1];
236 linep->channel[ch] = sample / maxvalf;
240 i_plinf(im, 0, width, y, line);
250 read_pbm_bin(io_glue *ig, i_img *im, int width, int height, int allow_incomplete) {
251 i_palidx *line, *linep;
253 unsigned char *read_buf, *readp;
257 line = mymalloc(width * sizeof(i_palidx));
258 read_size = (width + 7) / 8;
259 read_buf = mymalloc(read_size);
260 for(y = 0; y < height; y++) {
261 if (i_io_read(ig, read_buf, read_size) != read_size) {
264 if (allow_incomplete) {
265 i_tags_setn(&im->tags, "i_incomplete", 1);
266 i_tags_setn(&im->tags, "i_lines_read", y);
270 i_push_error(0, "short read - file truncated?");
278 for(x = 0; x < width; ++x) {
279 *linep++ = *readp & mask ? 1 : 0;
286 i_ppal(im, 0, width, y, line);
294 /* unlike pgm/ppm pbm:
295 - doesn't require spaces between samples (bits)
296 - 1 (maxval) is black instead of white
300 read_pbm_ascii(io_glue *ig, i_img *im, int width, int height, int allow_incomplete) {
301 i_palidx *line, *linep;
304 line = mymalloc(width * sizeof(i_palidx));
305 for(y = 0; y < height; y++) {
307 for(x = 0; x < width; ++x) {
310 if ((c = i_io_getc(ig)) == EOF || (c != '0' && c != '1')) {
312 if (allow_incomplete) {
313 i_tags_setn(&im->tags, "i_incomplete", 1);
314 i_tags_setn(&im->tags, "i_lines_read", y);
319 i_push_error(0, "invalid data for ascii pnm");
321 i_push_error(0, "short read - file truncated?");
326 *linep++ = c == '0' ? 0 : 1;
328 i_ppal(im, 0, width, y, line);
337 read_pgm_ppm_ascii(io_glue *ig, i_img *im, int width, int height, int channels,
338 int maxval, int allow_incomplete) {
339 i_color *line, *linep;
341 int rounder = maxval / 2;
343 line = mymalloc(width * sizeof(i_color));
344 for(y=0;y<height;y++) {
346 for(x=0; x<width; x++) {
347 for(ch=0; ch<channels; ch++) {
350 if (!gnum(ig, &sample)) {
352 if (allow_incomplete) {
353 i_tags_setn(&im->tags, "i_incomplete", 1);
354 i_tags_setn(&im->tags, "i_lines_read", 1);
358 if (i_io_peekc(ig) != EOF)
359 i_push_error(0, "invalid data for ascii pnm");
361 i_push_error(0, "short read - file truncated?");
368 linep->channel[ch] = (sample * 255 + rounder) / maxval;
372 i_plin(im, 0, width, y, line);
381 read_pgm_ppm_ascii_16(io_glue *ig, i_img *im, int width, int height,
382 int channels, int maxval, int allow_incomplete) {
383 i_fcolor *line, *linep;
385 double maxvalf = maxval;
387 line = mymalloc(width * sizeof(i_fcolor));
388 for(y=0;y<height;y++) {
390 for(x=0; x<width; x++) {
391 for(ch=0; ch<channels; ch++) {
394 if (!gnum(ig, &sample)) {
396 if (allow_incomplete) {
397 i_tags_setn(&im->tags, "i_incomplete", 1);
398 i_tags_setn(&im->tags, "i_lines_read", y);
402 if (i_io_peekc(ig) != EOF)
403 i_push_error(0, "invalid data for ascii pnm");
405 i_push_error(0, "short read - file truncated?");
412 linep->channel[ch] = sample / maxvalf;
416 i_plinf(im, 0, width, y, line);
424 =item i_readpnm_wiol(ig, allow_incomplete)
426 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
429 allow_incomplete - allows a partial file to be read successfully
435 i_readpnm_wiol( io_glue *ig, int allow_incomplete) {
438 int width, height, maxval, channels;
443 mm_log((1,"i_readpnm(ig %p, allow_incomplete %d)\n", ig, allow_incomplete));
448 i_push_error(0, "bad header magic, not a PNM file");
449 mm_log((1, "i_readpnm: Could not read header of file\n"));
453 if ((c = i_io_getc(ig)) == EOF ) {
454 mm_log((1, "i_readpnm: Could not read header of file\n"));
460 if (type < 1 || type > 6) {
461 i_push_error(0, "unknown PNM file type, not a PNM file");
462 mm_log((1, "i_readpnm: Not a pnm file\n"));
466 if ( (c = i_io_getc(ig)) == EOF ) {
467 mm_log((1, "i_readpnm: Could not read header of file\n"));
471 if ( !misspace(c) ) {
472 i_push_error(0, "unexpected character, not a PNM file");
473 mm_log((1, "i_readpnm: Not a pnm file\n"));
477 mm_log((1, "i_readpnm: image is a %s\n", typenames[type-1] ));
480 /* Read sizes and such */
482 if (!skip_comment(ig)) {
483 i_push_error(0, "while skipping to width");
484 mm_log((1, "i_readpnm: error reading before width\n"));
488 if (!gnum(ig, &width)) {
489 i_push_error(0, "could not read image width");
490 mm_log((1, "i_readpnm: error reading width\n"));
494 if (!skip_comment(ig)) {
495 i_push_error(0, "while skipping to height");
496 mm_log((1, "i_readpnm: error reading before height\n"));
500 if (!gnum(ig, &height)) {
501 i_push_error(0, "could not read image height");
502 mm_log((1, "i_readpnm: error reading height\n"));
506 if (!(type == 1 || type == 4)) {
507 if (!skip_comment(ig)) {
508 i_push_error(0, "while skipping to maxval");
509 mm_log((1, "i_readpnm: error reading before maxval\n"));
513 if (!gnum(ig, &maxval)) {
514 i_push_error(0, "could not read maxval");
515 mm_log((1, "i_readpnm: error reading maxval\n"));
520 i_push_error(0, "maxval is zero - invalid pnm file");
521 mm_log((1, "i_readpnm: maxval is zero, invalid pnm file\n"));
524 else if (maxval > 65535) {
525 i_push_errorf(0, "maxval of %d is over 65535 - invalid pnm file",
527 mm_log((1, "i_readpnm: maxval of %d is over 65535 - invalid pnm file\n", maxval));
531 rounder = maxval / 2;
533 if ((c = i_io_getc(ig)) == EOF || !misspace(c)) {
534 i_push_error(0, "garbage in header, invalid PNM file");
535 mm_log((1, "i_readpnm: garbage in header\n"));
539 channels = (type == 3 || type == 6) ? 3:1;
541 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
542 mm_log((1, "i_readpnm: image size exceeds limits\n"));
546 mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval));
548 if (type == 1 || type == 4) {
550 pbm_pal[0].channel[0] = 255;
551 pbm_pal[1].channel[0] = 0;
553 im = i_img_pal_new(width, height, 1, 256);
554 i_addcolors(im, pbm_pal, 2);
558 im = i_img_16_new(width, height, channels);
560 im = i_img_8_new(width, height, channels);
564 case 1: /* Ascii types */
565 im = read_pbm_ascii(ig, im, width, height, allow_incomplete);
571 im = read_pgm_ppm_ascii_16(ig, im, width, height, channels, maxval, allow_incomplete);
573 im = read_pgm_ppm_ascii(ig, im, width, height, channels, maxval, allow_incomplete);
576 case 4: /* binary pbm */
577 im = read_pbm_bin(ig, im, width, height, allow_incomplete);
580 case 5: /* binary pgm */
581 case 6: /* binary ppm */
583 im = read_pgm_ppm_bin16(ig, im, width, height, channels, maxval, allow_incomplete);
585 im = read_pgm_ppm_bin8(ig, im, width, height, channels, maxval, allow_incomplete);
589 mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type));
596 i_tags_add(&im->tags, "i_format", 0, "pnm", -1, 0);
597 i_tags_setn(&im->tags, "pnm_maxval", maxval);
598 i_tags_setn(&im->tags, "pnm_type", type);
603 static void free_images(i_img **imgs, int count) {
607 for (i = 0; i < count; ++i)
608 i_img_destroy(imgs[i]);
613 i_img **i_readpnm_multi_wiol(io_glue *ig, int *count, int allow_incomplete) {
614 i_img **results = NULL;
617 int result_alloc = 0,
623 mm_log((1, "read image %i\n", 1+*count));
624 img = i_readpnm_wiol( ig, allow_incomplete );
626 free_images( results, *count );
630 if (*count > result_alloc) {
631 if (result_alloc == 0) {
633 results = mymalloc(result_alloc * sizeof(i_img *));
636 /* myrealloc never fails (it just dies if it can't allocate) */
638 results = myrealloc(results, result_alloc * sizeof(i_img *));
641 results[*count-1] = img;
644 if( i_tags_get_int(&img->tags, "i_incomplete", 0, &value ) && value) {
647 else if( skip_spaces( ig ) && ( c=i_io_peekc( ig ) ) != EOF && c == 'P' ) {
661 write_pbm(i_img *im, io_glue *ig, int zero_is_white) {
664 i_img_dim write_size;
665 unsigned char *write_buf;
666 unsigned char *writep;
670 sprintf(header, "P4\012# CREATOR: Imager\012%" i_DF " %" i_DF "\012",
671 i_DFc(im->xsize), i_DFc(im->ysize));
672 if (i_io_write(ig, header, strlen(header)) < 0) {
673 i_push_error(0, "could not write pbm header");
676 write_size = (im->xsize + 7) / 8;
677 line = mymalloc(sizeof(i_palidx) * im->xsize);
678 write_buf = mymalloc(write_size);
679 for (y = 0; y < im->ysize; ++y) {
680 i_gpal(im, 0, im->xsize, y, line);
683 memset(write_buf, 0, write_size);
684 for (x = 0; x < im->xsize; ++x) {
685 if (zero_is_white ? line[x] : !line[x])
693 if (i_io_write(ig, write_buf, write_size) != write_size) {
694 i_push_error(0, "write failure");
708 write_ppm_data_8(i_img *im, io_glue *ig, int want_channels) {
709 size_t write_size = im->xsize * want_channels;
710 size_t buf_size = im->xsize * im->channels;
711 unsigned char *data = mymalloc(buf_size);
716 i_get_file_background(im, &bg);
717 while (y < im->ysize && rc >= 0) {
718 i_gsamp_bg(im, 0, im->xsize, y, data, want_channels, &bg);
719 if (i_io_write(ig, data, write_size) != write_size) {
720 i_push_error(errno, "could not write ppm data");
733 write_ppm_data_16(i_img *im, io_glue *ig, int want_channels) {
734 size_t line_size = im->channels * im->xsize * sizeof(i_fsample_t);
735 size_t sample_count = want_channels * im->xsize;
736 size_t write_size = sample_count * 2;
737 i_fsample_t *line_buf = mymalloc(line_size);
738 i_fsample_t *samplep;
739 unsigned char *write_buf = mymalloc(write_size);
740 unsigned char *writep;
746 i_get_file_backgroundf(im, &bg);
748 while (y < im->ysize) {
749 i_gsampf_bg(im, 0, im->xsize, y, line_buf, want_channels, &bg);
752 for (sample_num = 0; sample_num < sample_count; ++sample_num) {
753 unsigned sample16 = SampleFTo16(*samplep++);
754 *writep++ = sample16 >> 8;
755 *writep++ = sample16 & 0xFF;
757 if (i_io_write(ig, write_buf, write_size) != write_size) {
758 i_push_error(errno, "could not write ppm data");
771 i_writeppm_wiol(i_img *im, io_glue *ig) {
776 mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig));
779 /* Add code to get the filename info from the iolayer */
780 /* Also add code to check for mmapped code */
782 if (i_img_is_monochrome(im, &zero_is_white)) {
783 if (!write_pbm(im, ig, zero_is_white))
789 int want_channels = im->channels;
791 if (want_channels == 2 || want_channels == 4)
794 if (!i_tags_get_int(&im->tags, "pnm_write_wide_data", 0, &wide_data))
797 if (want_channels == 3) {
800 else if (want_channels == 1) {
804 i_push_error(0, "can only save 1 or 3 channel images to pnm");
805 mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
808 if (im->bits <= 8 || !wide_data)
813 sprintf(header,"P%d\n#CREATOR: Imager\n%" i_DF " %" i_DF"\n%d\n",
814 type, i_DFc(im->xsize), i_DFc(im->ysize), maxval);
816 if (i_io_write(ig,header,strlen(header)) != strlen(header)) {
817 i_push_error(errno, "could not write ppm header");
818 mm_log((1,"i_writeppm: unable to write ppm header.\n"));
822 if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type
823 && im->channels == want_channels) {
824 if (i_io_write(ig,im->idata,im->bytes) != im->bytes) {
825 i_push_error(errno, "could not write ppm data");
829 else if (maxval == 255) {
830 if (!write_ppm_data_8(im, ig, want_channels))
834 if (!write_ppm_data_16(im, ig, want_channels))
838 if (i_io_close(ig)) {
839 i_push_errorf(i_io_error(ig), "Error closing stream: %d", i_io_error(ig));
851 Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook <tonyc@cpan.org>,
852 Philip Gwyn <gwyn@cpan.org>.