X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/43c5dacbc0c703d1eee26a0eb93adf6db6a1e247..c5e09af4359bc56ec00feeda9c6cfa686f59f0d2:/pnm.c diff --git a/pnm.c b/pnm.c index 5073a8c1..15fa4bb6 100644 --- a/pnm.c +++ b/pnm.c @@ -1,7 +1,7 @@ -#include "image.h" -#include "io.h" +#include "imager.h" #include "log.h" #include "iolayer.h" +#include "imageri.h" #include #include @@ -15,7 +15,7 @@ pnm.c - implements reading and writing ppm/pnm/pbm files, uses io layer. =head1 SYNOPSIS io_glue *ig = io_new_fd( fd ); - i_img *im = i_readpnm_wiol(ig, -1); // no limit on how much is read + i_img *im = i_readpnm_wiol(ig, 0); // no limit on how much is read // or io_glue *ig = io_new_fd( fd ); return_code = i_writepnm_wiol(im, ig); @@ -36,147 +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 -*/ - -static -char * -gnext(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) { - i_push_error(errno, "unexpected end of file"); - mm_log((1, "i_readpnm: end of file\n")); - return NULL; - } - } - return &mb->buf[mb->cp++]; -} - - -/* -=item gnext(mbuf *mb) - -Fetches a character but does NOT advance. Returns a pointer to -the byte or NULL on failure (internal). - - mb - buffer object - -=cut -*/ - -static -char * -gpeek(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) { - i_push_error(0, "unexpected end of file"); - mm_log((1, "i_readpnm: end of file\n")); - return NULL; - } - } - return &mb->buf[mb->cp]; -} - - - - -/* -=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_spaces(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(mb)) return 0; + if (!skip_spaces(ig)) + return 0; - if (!(cp = gpeek(mb))) return 0; - if (*cp == '#') { - while( (cp = gpeek(mb)) && (*cp != '\n' && *cp != '\r') ) { - if ( !gnext(mb) ) break; + if ((c = i_io_peekc(ig)) == EOF) + return 0; + + 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; } @@ -196,82 +115,346 @@ 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; - - while( (cp = gpeek(mb)) && misnumber(*cp) ) { - *i = *i*10+(*cp-'0'); - cp = gnext(mb); + if (!skip_spaces(ig)) return 0; + + if ((c = i_io_peekc(ig)) == EOF) + return 0; + if (!misnumber(c)) + return 0; + 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(io_glue *ig, i_img *im, int width, int height, + int channels, int maxval, int allow_incomplete) { + i_color *line, *linep; + int read_size; + unsigned char *read_buf, *readp; + int x, y, ch; + int rounder = maxval / 2; + + line = mymalloc(width * sizeof(i_color)); + read_size = channels * width; + read_buf = mymalloc(read_size); + for(y=0;ytags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", y); + return im; + } + else { + i_push_error(0, "short read - file truncated?"); + i_img_destroy(im); + return NULL; + } + } + if (maxval == 255) { + for(x=0; xchannel[ch] = *readp++; + } + ++linep; + } + } + else { + for(x=0; x maxval) + sample = maxval; + linep->channel[ch] = (sample * 255 + rounder) / maxval; + } + ++linep; + } + } + i_plin(im, 0, width, y, line); + } + myfree(read_buf); + myfree(line); + + return im; +} + +static +i_img * +read_pgm_ppm_bin16(io_glue *ig, i_img *im, int width, int height, + int channels, int maxval, int allow_incomplete) { + i_fcolor *line, *linep; + int read_size; + unsigned char *read_buf, *readp; + int x, y, ch; + double maxvalf = maxval; + + line = mymalloc(width * sizeof(i_fcolor)); + read_size = channels * width * 2; + read_buf = mymalloc(read_size); + for(y=0;ytags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", y); + return im; + } + else { + i_push_error(0, "short read - file truncated?"); + i_img_destroy(im); + return NULL; + } + } + for(x=0; x maxval) + sample = maxval; + readp += 2; + linep->channel[ch] = sample / maxvalf; + } + ++linep; + } + i_plinf(im, 0, width, y, line); + } + myfree(read_buf); + myfree(line); -/* -=item i_readpnm_wiol(ig, length) + return im; +} -Retrieve an image and stores in the iolayer object. Returns NULL on fatal error. +static +i_img * +read_pbm_bin(io_glue *ig, i_img *im, int width, int height, int allow_incomplete) { + i_palidx *line, *linep; + int read_size; + unsigned char *read_buf, *readp; + int x, y; + unsigned mask; + + line = mymalloc(width * sizeof(i_palidx)); + read_size = (width + 7) / 8; + read_buf = mymalloc(read_size); + for(y = 0; y < height; y++) { + if (i_io_read(ig, read_buf, read_size) != read_size) { + myfree(line); + myfree(read_buf); + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", y); + return im; + } + else { + i_push_error(0, "short read - file truncated?"); + i_img_destroy(im); + return NULL; + } + } + linep = line; + readp = read_buf; + mask = 0x80; + for(x = 0; x < width; ++x) { + *linep++ = *readp & mask ? 1 : 0; + mask >>= 1; + if (mask == 0) { + ++readp; + mask = 0x80; + } + } + i_ppal(im, 0, width, y, line); + } + myfree(read_buf); + myfree(line); - ig - io_glue object - length - maximum length to read from data source, before closing it -1 - signifies no limit. + return im; +} -=cut +/* unlike pgm/ppm pbm: + - doesn't require spaces between samples (bits) + - 1 (maxval) is black instead of white */ +static +i_img * +read_pbm_ascii(io_glue *ig, i_img *im, int width, int height, int allow_incomplete) { + i_palidx *line, *linep; + int x, y; + + line = mymalloc(width * sizeof(i_palidx)); + for(y = 0; y < height; y++) { + linep = line; + for(x = 0; x < width; ++x) { + int c; + skip_spaces(ig); + if ((c = i_io_getc(ig)) == EOF || (c != '0' && c != '1')) { + myfree(line); + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", y); + return im; + } + else { + if (c != EOF) + i_push_error(0, "invalid data for ascii pnm"); + else + i_push_error(0, "short read - file truncated?"); + i_img_destroy(im); + return NULL; + } + } + *linep++ = c == '0' ? 0 : 1; + } + i_ppal(im, 0, width, y, line); + } + myfree(line); + return im; +} +static i_img * -i_readpnm_wiol(io_glue *ig, int length) { - i_img* im; - int type; +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; - int width, height, maxval, channels, pcount; - char *cp; - unsigned char *uc; - mbuf buf; - i_color val; - int mult; + int rounder = maxval / 2; - i_clear_error(); + line = mymalloc(width * sizeof(i_color)); + for(y=0;ytags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", 1); + return im; + } + else { + if (i_io_peekc(ig) != EOF) + i_push_error(0, "invalid data for ascii pnm"); + else + i_push_error(0, "short read - file truncated?"); + i_img_destroy(im); + return NULL; + } + } + if (sample > maxval) + sample = maxval; + linep->channel[ch] = (sample * 255 + rounder) / maxval; + } + ++linep; + } + i_plin(im, 0, width, y, line); + } + myfree(line); - /* char *pp; */ + return im; +} - mm_log((1,"i_readpnm(ig %p, length %d)\n", ig, length)); +static +i_img * +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; + double maxvalf = maxval; - /* - pp = mymalloc(20); - - pp[-1]= 'c'; - pp[-2]= 'c'; - - bndcheck_all(); + line = mymalloc(width * sizeof(i_fcolor)); + for(y=0;ytags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", y); + return im; + } + else { + if (i_io_peekc(ig) != EOF) + i_push_error(0, "invalid data for ascii pnm"); + else + i_push_error(0, "short read - file truncated?"); + i_img_destroy(im); + return NULL; + } + } + if (sample > maxval) + sample = maxval; + linep->channel[ch] = sample / maxvalf; + } + ++linep; + } + i_plinf(im, 0, width, y, line); + } + myfree(line); + + return im; +} + +/* +=item i_readpnm_wiol(ig, allow_incomplete) - myfree(pp); +Retrieve an image and stores in the iolayer object. Returns NULL on fatal error. - mm_log((1, "Hack is exiting\n")); + ig - io_glue object + allow_incomplete - allows a partial file to be read successfully +=cut */ - - io_glue_commit_types(ig); - init_buf(&buf, ig); - cp = gnext(&buf); +i_img * +i_readpnm_wiol( io_glue *ig, int allow_incomplete) { + i_img* im; + int type; + int width, height, maxval, channels; + int c; - if (!cp || *cp != 'P') { + i_clear_error(); + mm_log((1,"i_readpnm(ig %p, allow_incomplete %d)\n", ig, allow_incomplete)); + + c = i_io_getc(ig); + + 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"); @@ -279,12 +462,12 @@ i_readpnm_wiol(io_glue *ig, int length) { 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; @@ -295,118 +478,298 @@ i_readpnm_wiol(io_glue *ig, int length) { /* 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; } + + if (maxval == 0) { + i_push_error(0, "maxval is zero - invalid pnm file"); + mm_log((1, "i_readpnm: maxval is zero, invalid pnm file\n")); + return NULL; + } + 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", maxval)); + return NULL; + } } else maxval=1; - 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")); + return NULL; + } mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval)); - - im = i_img_empty_ch(NULL, width, height, channels); + + if (type == 1 || type == 4) { + i_color pbm_pal[2]; + pbm_pal[0].channel[0] = 255; + pbm_pal[1].channel[0] = 0; + + im = i_img_pal_new(width, height, 1, 256); + i_addcolors(im, pbm_pal, 2); + } + else { + if (maxval > 255) + im = i_img_16_new(width, height, channels); + else + im = i_img_8_new(width, height, channels); + } switch (type) { case 1: /* Ascii types */ + im = read_pbm_ascii(ig, im, width, height, allow_incomplete); + break; + case 2: case 3: - mult = type == 1 ? 255 : 1; - for(y=0;y 255) + im = read_pgm_ppm_ascii_16(ig, im, width, height, channels, maxval, allow_incomplete); + else + im = read_pgm_ppm_ascii(ig, im, width, height, channels, maxval, allow_incomplete); break; case 4: /* binary pbm */ - for(y=0;y>xt)) ? 0 : 255; - i_ppix(im, x+xt, y, &val); - } - } else { - mm_log((1,"i_readpnm: gnext() returned false in data\n")); - return im; - } - } + im = read_pbm_bin(ig, im, width, height, allow_incomplete); break; case 5: /* binary pgm */ case 6: /* binary ppm */ - for(y=0;y 255) + im = read_pgm_ppm_bin16(ig, im, width, height, channels, maxval, allow_incomplete); + else + im = read_pgm_ppm_bin8(ig, im, width, height, channels, maxval, allow_incomplete); break; + default: mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type)); return NULL; } + + if (!im) + return NULL; + + i_tags_add(&im->tags, "i_format", 0, "pnm", -1, 0); + i_tags_setn(&im->tags, "pnm_maxval", maxval); + i_tags_setn(&im->tags, "pnm_type", type); + 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; + i_img_dim write_size; + unsigned char *write_buf; + unsigned char *writep; + char header[255]; + unsigned mask; + + 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; + } + write_size = (im->xsize + 7) / 8; + line = mymalloc(sizeof(i_palidx) * im->xsize); + write_buf = mymalloc(write_size); + for (y = 0; y < im->ysize; ++y) { + i_gpal(im, 0, im->xsize, y, line); + mask = 0x80; + writep = write_buf; + memset(write_buf, 0, write_size); + for (x = 0; x < im->xsize; ++x) { + if (zero_is_white ? line[x] : !line[x]) + *writep |= mask; + mask >>= 1; + if (!mask) { + ++writep; + mask = 0x80; + } + } + if (i_io_write(ig, write_buf, write_size) != write_size) { + i_push_error(0, "write failure"); + myfree(write_buf); + myfree(line); + return 0; + } + } + myfree(write_buf); + myfree(line); + + return 1; +} + +static +int +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_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; + break; + } + ++y; + } + myfree(data); + + return rc; +} + +static +int +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; + 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_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) { + unsigned sample16 = SampleFTo16(*samplep++); + *writep++ = sample16 >> 8; + *writep++ = sample16 & 0xFF; + } + if (i_io_write(ig, write_buf, write_size) != write_size) { + i_push_error(errno, "could not write ppm data"); + rc = 0; + break; + } + ++y; + } + myfree(line_buf); + myfree(write_buf); + + return rc; +} undef_int i_writeppm_wiol(i_img *im, io_glue *ig) { char header[255]; - int rc; - writep write_func; + int zero_is_white; + int wide_data; mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig)); i_clear_error(); @@ -414,89 +777,65 @@ 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)) { + if (!write_pbm(im, ig, zero_is_white)) + return 0; + } + else { + int type; + int maxval; + int want_channels = im->channels; - if (im->channels == 3) { - sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize); - if (ig->writecb(ig,header,strlen(header))<0) { - i_push_error(errno, "could not write ppm header"); - mm_log((1,"i_writeppm: unable to write ppm header.\n")); - return(0); - } + 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->virtual && im->bits == i_8_bits && im->type == i_direct_type) { - rc = ig->writecb(ig,im->idata,im->bytes); + if (want_channels == 3) { + type = 6; } - else { - unsigned char *data = mymalloc(3 * im->xsize); - if (data != NULL) { - int y = 0; - int x, ch; - unsigned char *p; - static int rgb_chan[3] = { 0, 1, 2 }; - - rc = 0; - while (y < im->ysize && rc >= 0) { - i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3); - rc = ig->writecb(ig, data, im->xsize * 3); - ++y; - } - myfree(data); - } - else { - i_push_error(0, "Out of memory"); - return 0; - } + else if (want_channels == 1) { + type = 5; } - if (rc<0) { - i_push_error(errno, "could not write ppm data"); - mm_log((1,"i_writeppm: unable to write ppm data.\n")); + else { + i_push_error(0, "can only save 1 or 3 channel images to pnm"); + mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels)); return(0); } - } - else if (im->channels == 1) { - sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n", - im->xsize, im->ysize); - if (ig->writecb(ig,header, strlen(header)) < 0) { - i_push_error(errno, "could not write pgm header"); - mm_log((1,"i_writeppm: unable to write pgm header.\n")); + if (im->bits <= 8 || !wide_data) + maxval = 255; + else + maxval = 65535; + + 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 (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) { - rc=ig->writecb(ig,im->idata,im->bytes); - } - else { - unsigned char *data = mymalloc(im->xsize); - if (data != NULL) { - int y = 0; - int x, ch; - int chan = 0; - unsigned char *p; - - rc = 0; - while (y < im->ysize && rc >= 0) { - i_gsamp(im, 0, im->xsize, y, data, &chan, 1); - rc = ig->writecb(ig, data, im->xsize); - ++y; - } - myfree(data); - } - else { - i_push_error(0, "Out of memory"); + 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; } } - if (rc<0) { - i_push_error(errno, "could not write pgm data"); - mm_log((1,"i_writeppm: unable to write pgm data.\n")); - return(0); + else if (maxval == 255) { + if (!write_ppm_data_8(im, ig, want_channels)) + return 0; + } + else { + if (!write_ppm_data_16(im, ig, want_channels)) + return 0; } } - else { - i_push_error(0, "can only save 1 or 3 channel images to pnm"); - mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels)); - return(0); + if (i_io_close(ig)) { + i_push_errorf(i_io_error(ig), "Error closing stream: %d", i_io_error(ig)); + return 0; } return(1); @@ -507,7 +846,8 @@ i_writeppm_wiol(i_img *im, io_glue *ig) { =head1 AUTHOR -Arnar M. Hrafnkelsson +Arnar M. Hrafnkelsson , Tony Cook , +Philip Gwyn . =head1 SEE ALSO