/* This code is ripped from Autotrace-0.29. Small modifications by pts. */ /* input-pnm.ci: * The pnm reading and writing code was written from scratch by Erik Nygren * (nygren@mit.edu) based on the specifications in the man pages and * does not contain any code from the netpbm or pbmplus distributions. */ #ifdef __GNUC__ #ifndef __clang__ #pragma implementation #endif #endif /* #include "types.h" */ #include "at_bitmap.h" /* #include "input-pnm.h" */ /* #include "message.h" */ /* #include "xstd.h" */ /* #include -- ceil(); emulated */ #include /* atoi(...) */ /**** pts ****/ /* #include */ #define isspace(c) ((c)=='\0' || (c)==' ' || ((unsigned char)((c)-011)<=(unsigned char)(015-011))) /* ^^^ not strictly POSIX C locale */ #define isdigit(c) ((unsigned char)(c-'0')<=(unsigned char)('9'-'0')) #if 0 # define PNMFILE FILE # define fread_PNMFILE(s,slen,f) fread(s, 1, slen, f) #else # define PNMFILE /*Filter::UngetFILED*/GenBuffer::Readable # define fread_PNMFILE(s,slen,f) f->vi_read(s, slen) #endif /* Declare local data types */ typedef struct _PNMScanner { PNMFILE *fd; /* The file descriptor of the file being read */ char cur; /* The current character in the input stream */ int eof; /* Have we reached end of file? */ } PNMScanner; typedef struct _PNMInfo { unsigned int xres, yres; /* The size of the image */ int maxval; /* For ascii image files, the max value * which we need to normalize to */ int np; /* Number of image planes (0 for pbm) */ int asciibody; /* 1 if ascii body, 0 if raw body */ /* Routine to use to load the pnm body */ void (* loader) (PNMScanner *, struct _PNMInfo *, unsigned char *); } PNMInfo; /* Contains the information needed to write out PNM rows */ typedef struct _PNMRowInfo { PNMFILE *fd; /* File descriptor */ char *rowbuf; /* Buffer for writing out rows */ int xres; /* X resolution */ int np; /* Number of planes */ unsigned char *red; /* Colormap red */ unsigned char *grn; /* Colormap green */ unsigned char *blu; /* Colormap blue */ } PNMRowInfo; /* Save info */ typedef struct { int raw; /* raw or ascii */ } PNMSaveVals; typedef struct { int run; /* run */ } PNMSaveInterface; #define PNM_BUFLEN 512 /* The input buffer size for data returned * from the scanner. Note that lines * aren't allowed to be over 256 characters * by the spec anyways so this shouldn't * be an issue. */ #define SAVE_COMMENT_STRING "# CREATOR: The GIMP's PNM Filter Version 1.0\n" /* Declare some local functions. */ static void pnm_load_ascii (PNMScanner *scan, PNMInfo *info, unsigned char *pixel_rgn); static void pnm_load_raw (PNMScanner *scan, PNMInfo *info, unsigned char *pixel_rgn); static void pnm_load_rawpbm (PNMScanner *scan, PNMInfo *info, unsigned char *pixel_rgn); static void pnmscanner_destroy (PNMScanner *s); #if 0 static void pnmscanner_createbuffer (PNMScanner *s, unsigned int bufsize); static void pnmscanner_getchar (PNMScanner *s); static void pnmscanner_getsmalltoken (PNMScanner *s, unsigned char *buf); #endif static void pnmscanner_eatwhitespace (PNMScanner *s); static void pnmscanner_gettoken (PNMScanner *s, unsigned char *buf, unsigned int bufsize); static unsigned pnmscanner_getint(PNMScanner *s); static PNMScanner * pnmscanner_create (PNMFILE *fd); #define pnmscanner_eof(s) ((s)->eof) #define pnmscanner_fd(s) ((s)->fd) #if 0 /* pnmscanner_getchar --- * Reads a character from the input stream */ static void pnmscanner_getchar (PNMScanner *s) { if (s->inbuf) { s->cur = s->inbuf[s->inbufpos++]; if (s->inbufpos >= s->inbufvalidsize) { if (s->inbufsize > s->inbufvalidsize) s->eof = 1; else s->inbufvalidsize = fread(s->inbuf, 1, s->inbufsize, s->fd); s->inbufpos = 0; } } else s->eof = !fread(&(s->cur), 1, 1, s->fd); } #endif #define pnmscanner_getchar(s) do { s->eof = !fread_PNMFILE(&(s->cur), 1, s->fd); } while(0) /* pnmscanner_eatwhitespace --- * Eats up whitespace from the input and returns when done or eof. * Also deals with comments. */ static inline void pnmscanner_eatwhitespace(PNMScanner *s) { /**** pts ****/ while (1) { if (s->cur=='#') { do pnmscanner_getchar(s); while (s->cur!='\n'); } else if (!isspace(s->cur)) { break; } pnmscanner_getchar(s); } } static struct struct_pnm_types { char name; int np; int asciibody; int maxval; void (* loader) (PNMScanner *, struct _PNMInfo *, unsigned char *pixel_rgn); } pnm_types[] = { { '1', 0, 1, 1, pnm_load_ascii }, /* ASCII PBM */ { '2', 1, 1, 255, pnm_load_ascii }, /* ASCII PGM */ { '3', 3, 1, 255, pnm_load_ascii }, /* ASCII PPM */ { '4', 0, 0, 1, pnm_load_rawpbm }, /* RAW PBM */ { '5', 1, 0, 255, pnm_load_raw }, /* RAW PGM */ { '6', 3, 0, 255, pnm_load_raw }, /* RAW PPM */ { 0 , 0, 0, 0, NULL} }; #if PTS_SAM2P bitmap_type pnm_load_image (PNMFILE* filename) #else bitmap_type pnm_load_image (at_string filename) #endif { char buf[PNM_BUFLEN]; /* buffer for random things like scanning */ PNMInfo *pnminfo; PNMScanner * volatile scan; int ctr; PNMFILE* fd; bitmap_type bitmap; #if PTS_SAM2P /**** pts ****/ fd=filename; #else /* open the file */ fd = xfopen (filename, "rb"); if (fd == NULL) { FATAL("PNM: can't open file\n"); BITMAP_BITS (bitmap) = NULL; BITMAP_WIDTH (bitmap) = 0; BITMAP_HEIGHT (bitmap) = 0; BITMAP_PLANES (bitmap) = 0; return (bitmap); } #endif /* allocate the necessary structures */ /* pnminfo = (PNMInfo *) malloc (sizeof (PNMInfo)); */ XMALLOCT(pnminfo, PNMInfo*, sizeof(PNMInfo)); scan = NULL; /* set error handling */ scan = pnmscanner_create(fd); /* Get magic number */ pnmscanner_gettoken (scan, (unsigned char *)buf, PNM_BUFLEN); if (pnmscanner_eof(scan)) FATALP ("PNM: premature end of file"); if (buf[0] != 'P' || buf[2]) FATALP ("PNM: is not a valid file"); /* Look up magic number to see what type of PNM this is */ for (ctr=0; pnm_types[ctr].name; ctr++) if (buf[1] == pnm_types[ctr].name) { pnminfo->np = pnm_types[ctr].np; pnminfo->asciibody = pnm_types[ctr].asciibody; pnminfo->maxval = pnm_types[ctr].maxval; pnminfo->loader = pnm_types[ctr].loader; } if (!pnminfo->loader) FATALP ("PNM: file not in a supported format"); pnmscanner_gettoken(scan, (unsigned char *)buf, PNM_BUFLEN); if (pnmscanner_eof(scan)) FATALP ("PNM: premature end of file"); pnminfo->xres = isdigit(*buf)?atoi(buf):0; if (pnminfo->xres<=0) FATALP ("PNM: invalid xres while loading"); pnmscanner_gettoken(scan, (unsigned char *)buf, PNM_BUFLEN); if (pnmscanner_eof(scan)) FATALP ("PNM: premature end of file"); pnminfo->yres = isdigit(*buf)?atoi(buf):0; if (pnminfo->yres<=0) FATALP ("PNM: invalid yres while loading"); if (pnminfo->np != 0) /* pbm's don't have a maxval field */ { pnmscanner_gettoken(scan, (unsigned char *)buf, PNM_BUFLEN); if (pnmscanner_eof(scan)) FATALP ("PNM: premature end of file"); pnminfo->maxval = isdigit(*buf)?atoi(buf):0; if ((pnminfo->maxval<=0) || (pnminfo->maxval>255 && !pnminfo->asciibody)) FATALP ("PNM: invalid maxval while loading"); } BITMAP_WIDTH (bitmap) = (at_dimen_t) pnminfo->xres; BITMAP_HEIGHT (bitmap) = (at_dimen_t) pnminfo->yres; BITMAP_PLANES (bitmap) = (pnminfo->np)?(pnminfo->np):1; /* BITMAP_BITS (bitmap) = (unsigned char *) malloc (pnminfo->yres * pnminfo->xres * BITMAP_PLANES (bitmap)); */ XMALLOCT(BITMAP_BITS (bitmap), unsigned char *, pnminfo->yres * pnminfo->xres * BITMAP_PLANES (bitmap)); pnminfo->loader (scan, pnminfo, BITMAP_BITS (bitmap)); /* vvv Dat: We detect truncation late truncated files will just have garbage :-( */ if (pnmscanner_eof(scan)) FATALP ("PNM: truncated image data"); /* Destroy the scanner */ pnmscanner_destroy (scan); /* free the structures */ /* free (pnminfo); */ XFREE(pnminfo); /* close the file */ /* xfclose (fd, filename); */ return (bitmap); } static void pnm_load_ascii (PNMScanner *scan, PNMInfo *info, unsigned char *data) { register unsigned char *d, *dend; unsigned u, s; #if 0 /**** pts ****/ /* Buffer reads to increase performance */ /* !! convert(1) is faster -- maybe buffering helps? */ pnmscanner_createbuffer(scan, 4096); #endif d = data; if (info->np==0) { /* PBM */ dend=d+info->xres*info->yres; while (d!=dend) { /* pnmscanner_getsmalltoken(scan, (unsigned char *)buf); */ pnmscanner_eatwhitespace(scan); *d++=-(scan->cur=='0'); pnmscanner_getchar(scan); } } else { /* PGM or PPM */ /**** pts ****/ dend=d+info->xres*info->yres*info->np; switch (s=info->maxval) { case 255: while (d!=dend) { *d++=pnmscanner_getint(scan); /* Dat: removed isdigit() */ } break; case 15: while (d!=dend) { *d++ = pnmscanner_getint(scan)*17; } break; case 3: while (d!=dend) { *d++ = pnmscanner_getint(scan)*85; } break; case 0: /* avoid division by 0 */ case 1: while (d!=dend) { *d++ = -(0==pnmscanner_getint(scan)); /* (*buf=='0')?0xff:0x00; */ } default: while (d!=dend) { u=pnmscanner_getint(scan); *d++ = (0UL+u*255UL+(s>>1))/s; /* always <= 255 */ } } } } static void pnm_load_raw (PNMScanner *scan, PNMInfo *info, unsigned char *data) { unsigned char *d, *dend; unsigned s=info->maxval; slen_t delta, scanlines; PNMFILE *fd=pnmscanner_fd(scan); scanlines = info->yres; d = data; delta=info->xres * info->np; dend=d+delta*scanlines; while (d!=dend) { if (info->xres*info->np != fread_PNMFILE((char*)d, delta, fd)) return; d+=delta; } d=data; switch (s=info->maxval) { /**** pts ****/ case 1: case 0: for (; d!=dend; d++) if (*d!=0) *d=255; break; case 3: while (d!=dend) *d++*=85; break; case 15: while (d!=dend) *d++*=17; break; case 255: break; default: for (; d!=dend; d++) *d=(0UL+*d*255UL+(s>>1))/s; /* always <= 255 */ break; } } static void pnm_load_rawpbm (PNMScanner *scan, PNMInfo *info, unsigned char *data) { unsigned char *buf; unsigned char curbyte; unsigned char *d; unsigned int x, i; unsigned int start, end, scanlines; PNMFILE *fd; unsigned int rowlen, bufpos; fd = pnmscanner_fd(scan); /****pts****/ /* rowlen = (unsigned int)ceil((double)(info->xres)/8.0);*/ rowlen=(info->xres+7)>>3; /* buf = (unsigned char *)malloc(rowlen*sizeof(unsigned char)); */ XMALLOCT(buf, unsigned char*, rowlen*sizeof(unsigned char)); start = 0; end = info->yres; scanlines = end - start; d = data; for (i = 0; i < scanlines; i++) { if (rowlen != fread_PNMFILE((char*)buf, rowlen, fd)) FATALP ("PNM: error reading file"); bufpos = 0; curbyte = buf[0]; for (x = 0; x < info->xres; x++) { if ((x % 8) == 0) { curbyte = buf[bufpos++]; /* // if (curbyte!=0) printf("%d <%u>\n", x, curbyte); */ } /* // if (curbyte!=0) printf("[%u]\n", curbyte); */ d[x] = (curbyte&0x80) ? 0x00 : 0xff; curbyte <<= 1; } d += info->xres; } XFREE(buf); } /**************** FILE SCANNER UTILITIES **************/ /* pnmscanner_create --- * Creates a new scanner based on a file descriptor. The * look ahead buffer is one character initially. */ static PNMScanner * pnmscanner_create (PNMFILE *fd) { PNMScanner *s; XMALLOCT (s, PNMScanner*, sizeof(PNMScanner)); s->fd = fd; s->eof = !fread_PNMFILE(&(s->cur), 1, s->fd); return s; } /* pnmscanner_destroy --- * Destroys a scanner and its resources. Doesn't close the fd. */ static void pnmscanner_destroy (PNMScanner *s) { XFREE(s); } #if 0 /**** pts ****/ /* pnmscanner_createbuffer --- * Creates a buffer so we can do buffered reads. */ static void pnmscanner_createbuffer (PNMScanner *s, unsigned int bufsize) { /* s->inbuf = (char *)malloc(sizeof(char)*bufsize); */ XMALLOCT(s->inbuf, char*, sizeof(char)*bufsize); s->inbufsize = bufsize; s->inbufpos = 0; s->inbufvalidsize = fread(s->inbuf, 1, bufsize, s->fd); } #endif /* pnmscanner_gettoken --- * Gets the next token, eating any leading whitespace. */ static void pnmscanner_gettoken (PNMScanner *s, unsigned char *buf, unsigned int bufsize) { unsigned char *bufend=buf+bufsize-1; pnmscanner_eatwhitespace(s); while (!pnmscanner_eof(s) && !isspace(s->cur) && s->cur!='#') { if (buf!=bufend) *buf++=s->cur; pnmscanner_getchar(s); } *buf='\0'; } static unsigned pnmscanner_getint(PNMScanner *s) { unsigned ret=0; pnmscanner_eatwhitespace(s); while (!pnmscanner_eof(s)) { if (isdigit(s->cur)) ret=10*ret+s->cur-'0'; else if (isspace(s->cur) || s->cur=='#') break; pnmscanner_getchar(s); } return ret; }