X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/d87dc9a439267f2f014ca9a1b00a518706105998..c9eba720d9c56aa28e027ee320001489cfe04ce9:/pnm.c diff --git a/pnm.c b/pnm.c index 3c96a56f..5dfd0c88 100644 --- a/pnm.c +++ b/pnm.c @@ -36,169 +36,66 @@ Some of these functions are internal. */ -#define BSIZ 1024 #define misspace(x) (x==' ' || x=='\n' || x=='\r' || x=='\t' || x=='\f' || x=='\v') #define misnumber(x) (x <= '9' && x>='0') static char *typenames[]={"ascii pbm", "ascii pgm", "ascii ppm", "binary pbm", "binary pgm", "binary ppm"}; /* - * Type to encapsulate the local buffer - * management skipping over in a file - */ - -typedef struct { - io_glue *ig; - int len; - int cp; - char buf[BSIZ]; -} mbuf; - - -static -void init_buf(mbuf *mb, io_glue *ig) { - mb->len = 0; - mb->cp = 0; - mb->ig = ig; -} - - - -/* -=item gnext(mbuf *mb) - -Fetches a character and advances in stream by one character. -Returns a pointer to the byte or NULL on failure (internal). - - mb - buffer object - -=cut -*/ - -#define gnext(mb) (((mb)->cp == (mb)->len) ? gnextf(mb) : (mb)->buf + (mb)->cp++) - -static -char * -gnextf(mbuf *mb) { - io_glue *ig = mb->ig; - if (mb->cp == mb->len) { - mb->cp = 0; - mb->len = ig->readcb(ig, mb->buf, BSIZ); - if (mb->len == -1) { - i_push_error(errno, "file read error"); - mm_log((1, "i_readpnm: read error\n")); - return NULL; - } - if (mb->len == 0) { - mm_log((1, "i_readpnm: end of file\n")); - return NULL; - } - } - return &mb->buf[mb->cp++]; -} - - -/* -=item gpeek(mbuf *mb) - -Fetches a character but does NOT advance. Returns a pointer to -the byte or NULL on failure (internal). - - mb - buffer object - -=cut -*/ - -#define gpeek(mb) ((mb)->cp == (mb)->len ? gpeekf(mb) : (mb)->buf + (mb)->cp) - -static -char * -gpeekf(mbuf *mb) { - io_glue *ig = mb->ig; - if (mb->cp == mb->len) { - mb->cp = 0; - mb->len = ig->readcb(ig, mb->buf, BSIZ); - if (mb->len == -1) { - i_push_error(errno, "read error"); - mm_log((1, "i_readpnm: read error\n")); - return NULL; - } - if (mb->len == 0) { - mm_log((1, "i_readpnm: end of file\n")); - return NULL; - } - } - return &mb->buf[mb->cp]; -} - -int -gread(mbuf *mb, unsigned char *buf, size_t read_size) { - int total_read = 0; - if (mb->cp != mb->len) { - int avail_size = mb->len - mb->cp; - int use_size = read_size > avail_size ? avail_size : read_size; - memcpy(buf, mb->buf+mb->cp, use_size); - mb->cp += use_size; - total_read += use_size; - read_size -= use_size; - buf += use_size; - } - if (read_size) { - io_glue *ig = mb->ig; - int read_res = i_io_read(ig, buf, read_size); - if (read_res >= 0) { - total_read += read_res; - } - } - return total_read; -} - - -/* -=item skip_spaces(mb) +=item skip_spaces(ig) Advances in stream until it is positioned at a non white space character. (internal) - mb - buffer object + ig - io_glue =cut */ static int -skip_spaces(mbuf *mb) { - char *cp; - while( (cp = gpeek(mb)) && misspace(*cp) ) if ( !gnext(mb) ) break; - if (!cp) return 0; +skip_spaces(io_glue *ig) { + int c; + while( (c = i_io_peekc(ig)) != EOF && misspace(c) ) { + if ( i_io_getc(ig) == EOF ) + break; + } + if (c == EOF) + return 0; + return 1; } /* -=item skip_comment(mb) +=item skip_comment(ig) Advances in stream over whitespace and a comment if one is found. (internal) - mb - buffer object + ig - io_glue object =cut */ static int -skip_comment(mbuf *mb) { - char *cp; +skip_comment(io_glue *ig) { + int c; + + if (!skip_spaces(ig)) + return 0; - if (!skip_spaces(mb)) return 0; + if ((c = i_io_peekc(ig)) == EOF) + return 0; - if (!(cp = gpeek(mb))) return 0; - if (*cp == '#') { - while( (cp = gpeek(mb)) && (*cp != '\n' && *cp != '\r') ) { - if ( !gnext(mb) ) break; + if (c == '#') { + while( (c = i_io_peekc(ig)) != EOF && (c != '\n' && c != '\r') ) { + if ( i_io_getc(ig) == EOF ) + break; } } - if (!cp) return 0; + if (c == EOF) + return 0; return 1; } @@ -218,26 +115,33 @@ on success else false. static int -gnum(mbuf *mb, int *i) { - char *cp; +gnum(io_glue *ig, int *i) { + int c; *i = 0; - if (!skip_spaces(mb)) return 0; + if (!skip_spaces(ig)) return 0; - if (!(cp = gpeek(mb))) + if ((c = i_io_peekc(ig)) == EOF) return 0; - if (!misnumber(*cp)) + if (!misnumber(c)) return 0; - while( (cp = gpeek(mb)) && misnumber(*cp) ) { - *i = *i*10+(*cp-'0'); - cp = gnext(mb); + while( (c = i_io_peekc(ig)) != EOF && misnumber(c) ) { + int work = *i * 10 + (c - '0'); + if (work < *i) { + /* overflow */ + i_push_error(0, "integer overflow"); + return 0; + } + *i = work; + i_io_getc(ig); } + return 1; } static i_img * -read_pgm_ppm_bin8(mbuf *mb, i_img *im, int width, int height, +read_pgm_ppm_bin8(io_glue *ig, i_img *im, int width, int height, int channels, int maxval, int allow_incomplete) { i_color *line, *linep; int read_size; @@ -251,7 +155,7 @@ read_pgm_ppm_bin8(mbuf *mb, i_img *im, int width, int height, for(y=0;ytags, "i_incomplete", 1); @@ -411,7 +315,7 @@ read_pbm_ascii(mbuf *mb, i_img *im, int width, int height, int allow_incomplete) return im; } else { - if (cp) + if (c != EOF) i_push_error(0, "invalid data for ascii pnm"); else i_push_error(0, "short read - file truncated?"); @@ -419,7 +323,7 @@ read_pbm_ascii(mbuf *mb, i_img *im, int width, int height, int allow_incomplete) return NULL; } } - *linep++ = *cp == '0' ? 0 : 1; + *linep++ = c == '0' ? 0 : 1; } i_ppal(im, 0, width, y, line); } @@ -430,7 +334,7 @@ read_pbm_ascii(mbuf *mb, i_img *im, int width, int height, int allow_incomplete) static i_img * -read_pgm_ppm_ascii(mbuf *mb, i_img *im, int width, int height, int channels, +read_pgm_ppm_ascii(io_glue *ig, i_img *im, int width, int height, int channels, int maxval, int allow_incomplete) { i_color *line, *linep; int x, y, ch; @@ -443,7 +347,7 @@ read_pgm_ppm_ascii(mbuf *mb, i_img *im, int width, int height, int channels, for(ch=0; chtags, "i_incomplete", 1); @@ -451,7 +355,7 @@ read_pgm_ppm_ascii(mbuf *mb, i_img *im, int width, int height, int channels, return im; } else { - if (gpeek(mb)) + if (i_io_peekc(ig) != EOF) i_push_error(0, "invalid data for ascii pnm"); else i_push_error(0, "short read - file truncated?"); @@ -474,7 +378,7 @@ read_pgm_ppm_ascii(mbuf *mb, i_img *im, int width, int height, int channels, static i_img * -read_pgm_ppm_ascii_16(mbuf *mb, i_img *im, int width, int height, +read_pgm_ppm_ascii_16(io_glue *ig, i_img *im, int width, int height, int channels, int maxval, int allow_incomplete) { i_fcolor *line, *linep; int x, y, ch; @@ -487,7 +391,7 @@ read_pgm_ppm_ascii_16(mbuf *mb, i_img *im, int width, int height, for(ch=0; chtags, "i_incomplete", 1); @@ -495,7 +399,7 @@ read_pgm_ppm_ascii_16(mbuf *mb, i_img *im, int width, int height, return im; } else { - if (gpeek(mb)) + if (i_io_peekc(ig) != EOF) i_push_error(0, "invalid data for ascii pnm"); else i_push_error(0, "short read - file truncated?"); @@ -527,36 +431,31 @@ Retrieve an image and stores in the iolayer object. Returns NULL on fatal error. =cut */ - i_img * -i_readpnm_wiol(io_glue *ig, int allow_incomplete) { +i_readpnm_wiol( io_glue *ig, int allow_incomplete) { i_img* im; int type; - int width, height, maxval, channels, pcount; + int width, height, maxval, channels; int rounder; - char *cp; - mbuf buf; + int c; i_clear_error(); mm_log((1,"i_readpnm(ig %p, allow_incomplete %d)\n", ig, allow_incomplete)); - io_glue_commit_types(ig); - init_buf(&buf, ig); + c = i_io_getc(ig); - cp = gnext(&buf); - - if (!cp || *cp != 'P') { + if (c != 'P') { i_push_error(0, "bad header magic, not a PNM file"); mm_log((1, "i_readpnm: Could not read header of file\n")); return NULL; } - if ( !(cp = gnext(&buf)) ) { + if ((c = i_io_getc(ig)) == EOF ) { mm_log((1, "i_readpnm: Could not read header of file\n")); return NULL; } - type = *cp-'0'; + type = c - '0'; if (type < 1 || type > 6) { i_push_error(0, "unknown PNM file type, not a PNM file"); @@ -564,12 +463,12 @@ i_readpnm_wiol(io_glue *ig, int allow_incomplete) { return NULL; } - if ( !(cp = gnext(&buf)) ) { + if ( (c = i_io_getc(ig)) == EOF ) { mm_log((1, "i_readpnm: Could not read header of file\n")); return NULL; } - if ( !misspace(*cp) ) { + if ( !misspace(c) ) { i_push_error(0, "unexpected character, not a PNM file"); mm_log((1, "i_readpnm: Not a pnm file\n")); return NULL; @@ -580,38 +479,38 @@ i_readpnm_wiol(io_glue *ig, int allow_incomplete) { /* Read sizes and such */ - if (!skip_comment(&buf)) { + if (!skip_comment(ig)) { i_push_error(0, "while skipping to width"); mm_log((1, "i_readpnm: error reading before width\n")); return NULL; } - if (!gnum(&buf, &width)) { + if (!gnum(ig, &width)) { i_push_error(0, "could not read image width"); mm_log((1, "i_readpnm: error reading width\n")); return NULL; } - if (!skip_comment(&buf)) { + if (!skip_comment(ig)) { i_push_error(0, "while skipping to height"); mm_log((1, "i_readpnm: error reading before height\n")); return NULL; } - if (!gnum(&buf, &height)) { + if (!gnum(ig, &height)) { i_push_error(0, "could not read image height"); mm_log((1, "i_readpnm: error reading height\n")); return NULL; } if (!(type == 1 || type == 4)) { - if (!skip_comment(&buf)) { + if (!skip_comment(ig)) { i_push_error(0, "while skipping to maxval"); mm_log((1, "i_readpnm: error reading before maxval\n")); return NULL; } - if (!gnum(&buf, &maxval)) { + if (!gnum(ig, &maxval)) { i_push_error(0, "could not read maxval"); mm_log((1, "i_readpnm: error reading maxval\n")); return NULL; @@ -625,20 +524,19 @@ i_readpnm_wiol(io_glue *ig, int allow_incomplete) { else if (maxval > 65535) { i_push_errorf(0, "maxval of %d is over 65535 - invalid pnm file", maxval); - mm_log((1, "i_readpnm: maxval of %d is over 65535 - invalid pnm file\n")); + mm_log((1, "i_readpnm: maxval of %d is over 65535 - invalid pnm file\n", maxval)); return NULL; } } else maxval=1; rounder = maxval / 2; - if (!(cp = gnext(&buf)) || !misspace(*cp)) { + if ((c = i_io_getc(ig)) == EOF || !misspace(c)) { i_push_error(0, "garbage in header, invalid PNM file"); mm_log((1, "i_readpnm: garbage in header\n")); return NULL; } channels = (type == 3 || type == 6) ? 3:1; - pcount = width*height*channels; if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) { mm_log((1, "i_readpnm: image size exceeds limits\n")); @@ -664,27 +562,27 @@ i_readpnm_wiol(io_glue *ig, int allow_incomplete) { switch (type) { case 1: /* Ascii types */ - im = read_pbm_ascii(&buf, im, width, height, allow_incomplete); + im = read_pbm_ascii(ig, im, width, height, allow_incomplete); break; case 2: case 3: if (maxval > 255) - im = read_pgm_ppm_ascii_16(&buf, im, width, height, channels, maxval, allow_incomplete); + im = read_pgm_ppm_ascii_16(ig, im, width, height, channels, maxval, allow_incomplete); else - im = read_pgm_ppm_ascii(&buf, im, width, height, channels, maxval, allow_incomplete); + im = read_pgm_ppm_ascii(ig, im, width, height, channels, maxval, allow_incomplete); break; case 4: /* binary pbm */ - im = read_pbm_bin(&buf, im, width, height, allow_incomplete); + im = read_pbm_bin(ig, im, width, height, allow_incomplete); break; case 5: /* binary pgm */ case 6: /* binary ppm */ if (maxval > 255) - im = read_pgm_ppm_bin16(&buf, im, width, height, channels, maxval, allow_incomplete); + im = read_pgm_ppm_bin16(ig, im, width, height, channels, maxval, allow_incomplete); else - im = read_pgm_ppm_bin8(&buf, im, width, height, channels, maxval, allow_incomplete); + im = read_pgm_ppm_bin8(ig, im, width, height, channels, maxval, allow_incomplete); break; default: @@ -702,19 +600,75 @@ i_readpnm_wiol(io_glue *ig, int allow_incomplete) { return im; } +static void free_images(i_img **imgs, int count) { + int i; + + if (count) { + for (i = 0; i < count; ++i) + i_img_destroy(imgs[i]); + myfree(imgs); + } +} + +i_img **i_readpnm_multi_wiol(io_glue *ig, int *count, int allow_incomplete) { + i_img **results = NULL; + i_img *img = NULL; + char c = EOF; + int result_alloc = 0, + value = 0, + eof = 0; + *count=0; + + do { + mm_log((1, "read image %i\n", 1+*count)); + img = i_readpnm_wiol( ig, allow_incomplete ); + if( !img ) { + free_images( results, *count ); + return NULL; + } + ++*count; + if (*count > result_alloc) { + if (result_alloc == 0) { + result_alloc = 5; + results = mymalloc(result_alloc * sizeof(i_img *)); + } + else { + /* myrealloc never fails (it just dies if it can't allocate) */ + result_alloc *= 2; + results = myrealloc(results, result_alloc * sizeof(i_img *)); + } + } + results[*count-1] = img; + + + if( i_tags_get_int(&img->tags, "i_incomplete", 0, &value ) && value) { + eof = 1; + } + else if( skip_spaces( ig ) && ( c=i_io_peekc( ig ) ) != EOF && c == 'P' ) { + eof = 0; + } + else { + eof = 1; + } + } while(!eof); + return results; +} + + + static int write_pbm(i_img *im, io_glue *ig, int zero_is_white) { int x, y; i_palidx *line; - int write_size; + i_img_dim write_size; unsigned char *write_buf; unsigned char *writep; char header[255]; unsigned mask; - sprintf(header, "P4\012# CREATOR: Imager\012%d %d\012", - im->xsize, im->ysize); + sprintf(header, "P4\012# CREATOR: Imager\012%" i_DF " %" i_DF "\012", + i_DFc(im->xsize), i_DFc(im->ysize)); if (i_io_write(ig, header, strlen(header)) < 0) { i_push_error(0, "could not write pbm header"); return 0; @@ -751,14 +705,17 @@ write_pbm(i_img *im, io_glue *ig, int zero_is_white) { static int -write_ppm_data_8(i_img *im, io_glue *ig) { - int write_size = im->xsize * im->channels; - unsigned char *data = mymalloc(write_size); - int y = 0; +write_ppm_data_8(i_img *im, io_glue *ig, int want_channels) { + size_t write_size = im->xsize * want_channels; + size_t buf_size = im->xsize * im->channels; + unsigned char *data = mymalloc(buf_size); + i_img_dim y = 0; int rc = 1; + i_color bg; + i_get_file_background(im, &bg); while (y < im->ysize && rc >= 0) { - i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels); + i_gsamp_bg(im, 0, im->xsize, y, data, want_channels, &bg); if (i_io_write(ig, data, write_size) != write_size) { i_push_error(errno, "could not write ppm data"); rc = 0; @@ -773,20 +730,23 @@ write_ppm_data_8(i_img *im, io_glue *ig) { static int -write_ppm_data_16(i_img *im, io_glue *ig) { - int sample_count = im->channels * im->xsize; - int write_size = sample_count * 2; - int line_size = sample_count * sizeof(i_fsample_t); +write_ppm_data_16(i_img *im, io_glue *ig, int want_channels) { + size_t line_size = im->channels * im->xsize * sizeof(i_fsample_t); + size_t sample_count = want_channels * im->xsize; + size_t write_size = sample_count * 2; i_fsample_t *line_buf = mymalloc(line_size); i_fsample_t *samplep; unsigned char *write_buf = mymalloc(write_size); unsigned char *writep; - int sample_num; - int y = 0; + size_t sample_num; + i_img_dim y = 0; int rc = 1; + i_fcolor bg; + + i_get_file_backgroundf(im, &bg); while (y < im->ysize) { - i_gsampf(im, 0, im->xsize, y, line_buf, NULL, im->channels); + i_gsampf_bg(im, 0, im->xsize, y, line_buf, want_channels, &bg); samplep = line_buf; writep = write_buf; for (sample_num = 0; sample_num < sample_count; ++sample_num) { @@ -819,22 +779,25 @@ i_writeppm_wiol(i_img *im, io_glue *ig) { /* Add code to get the filename info from the iolayer */ /* Also add code to check for mmapped code */ - io_glue_commit_types(ig); - if (i_img_is_monochrome(im, &zero_is_white)) { - return write_pbm(im, ig, zero_is_white); + if (!write_pbm(im, ig, zero_is_white)) + return 0; } else { int type; int maxval; + int want_channels = im->channels; + + if (want_channels == 2 || want_channels == 4) + --want_channels; if (!i_tags_get_int(&im->tags, "pnm_write_wide_data", 0, &wide_data)) wide_data = 0; - if (im->channels == 3) { + if (want_channels == 3) { type = 6; } - else if (im->channels == 1) { + else if (want_channels == 1) { type = 5; } else { @@ -847,31 +810,35 @@ i_writeppm_wiol(i_img *im, io_glue *ig) { else maxval = 65535; - sprintf(header,"P%d\n#CREATOR: Imager\n%d %d\n%d\n", - type, im->xsize, im->ysize, maxval); + sprintf(header,"P%d\n#CREATOR: Imager\n%" i_DF " %" i_DF"\n%d\n", + type, i_DFc(im->xsize), i_DFc(im->ysize), maxval); - if (ig->writecb(ig,header,strlen(header)) != strlen(header)) { + if (i_io_write(ig,header,strlen(header)) != strlen(header)) { i_push_error(errno, "could not write ppm header"); mm_log((1,"i_writeppm: unable to write ppm header.\n")); return(0); } - if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) { - if (ig->writecb(ig,im->idata,im->bytes) != im->bytes) { + if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type + && im->channels == want_channels) { + if (i_io_write(ig,im->idata,im->bytes) != im->bytes) { i_push_error(errno, "could not write ppm data"); return 0; } } else if (maxval == 255) { - if (!write_ppm_data_8(im, ig)) + if (!write_ppm_data_8(im, ig, want_channels)) return 0; } else { - if (!write_ppm_data_16(im, ig)) + if (!write_ppm_data_16(im, ig, want_channels)) return 0; } } - ig->closecb(ig); + if (i_io_close(ig)) { + i_push_errorf(i_io_error(ig), "Error closing stream: %d", i_io_error(ig)); + return 0; + } return(1); } @@ -881,7 +848,8 @@ i_writeppm_wiol(i_img *im, io_glue *ig) { =head1 AUTHOR -Arnar M. Hrafnkelsson , Tony Cook +Arnar M. Hrafnkelsson , Tony Cook , +Philip Gwyn . =head1 SEE ALSO