/* * pnmmpaste - do multiple "pnmpaste -replace" operations at once. * * pnmmpaste file x y [file x y [file x y...]] * * Reads a pnm file from stdin, outputs what would be obtained by * pnmpasteing each of the files into it in turn. (In particular, if * a given pixel gets pnmpasted over multiple times, the final output * pixel will be the one from the last-named file.) * * If any part of any of the argument files falls outside the input * file, an error results. */ #include #include #include #include #include #include extern const char *__progname; typedef struct fdesc FDESC; struct fdesc { char class; #define T_PBM 'b' #define T_PGM 'g' #define T_PPM 'p' #define T_PAM 'a' #define T_RPBM 'B' #define T_RPGM 'G' #define T_RPPM 'P' #define T_RPAM 'A' const char *fn; FILE *f; unsigned int maxval; unsigned int maxval2; unsigned int w; unsigned int h; unsigned char rpbmbuf; unsigned char rpbmbit; } ; typedef struct ifile IFILE; struct ifile { FDESC desc; int atx; int aty; } ; typedef struct pixval PIXVAL; struct pixval { unsigned int r; unsigned int g; unsigned int b; unsigned int a; } ; static FDESC odesc; static FDESC idesc; static IFILE *ifiles; static int nifiles; static char class_rawform(char class) { switch (class) { case T_PBM: case T_RPBM: return(T_RPBM); break; case T_PGM: case T_RPGM: return(T_RPGM); break; case T_PPM: case T_RPPM: return(T_RPPM); break; case T_PAM: case T_RPAM: return(T_RPAM); break; } abort(); } static char class_is_raw(char class) { switch (class) { case T_PBM: case T_PGM: case T_PPM: case T_PAM: return(0); break; case T_RPBM: case T_RPGM: case T_RPPM: case T_RPAM: return(1); break; } abort(); } static void promote_merge(const FDESC *i, FDESC *o) { switch (class_rawform(i->class)) { case T_RPAM: o->class = T_RPAM; if (i->maxval > o->maxval) o->maxval = i->maxval; if (i->maxval2 > o->maxval2) o->maxval2 = i->maxval2; break; case T_RPPM: switch (class_rawform(o->class)) { case T_RPAM: case T_RPPM: break; default: o->class = T_RPPM; if (i->maxval > o->maxval) o->maxval = i->maxval; break; } break; case T_RPGM: if (class_rawform(o->class) == T_RPBM) { o->class = T_RPGM; if (i->maxval > o->maxval) o->maxval = i->maxval; } break; case T_RPBM: break; default: abort(); break; } } static int number(const char *s) { char *e; int v; v = strtol(s,&e,0); if (*e || (e == s)) { fprintf(stderr,"%s: invalid number `%s'\n",__progname,s); exit(1); } return(v); } static void badpnm(FDESC *, const char *, ...) __attribute__((__noreturn__)); static void badpnm(FDESC *d, const char *fmt, ...) { va_list ap; va_start(ap,fmt); fprintf(stderr,"%s: %s: bad pnm file: ",__progname,d->fn); vfprintf(stderr,fmt,ap); fprintf(stderr,"\n"); va_end(ap); exit(1); } static void dataeof(FDESC *) __attribute__((__noreturn__)); static void dataeof(FDESC *d) { fprintf(stderr,"%s: %s: EOF while reading pixels\n",__progname,d->fn); exit(1); } #define digitval(c) (((int)(c))-'0') static int pnmskipcomments(FDESC *d) { int c; while (1) { c = getc(d->f); if (isspace(c)) continue; if (c == '#') { do { c = getc(d->f); } while ((c != '\n') && (c != EOF)); continue; } return(c); } } static unsigned int pnmgetnum(FDESC *d, const char *what) { int c; unsigned int v; v = 0; c = pnmskipcomments(d); if (c == EOF) badpnm(d,"EOF while reading %s",what); if (! isdigit(c)) badpnm(d,"non-digit when reading %s",what); v = digitval(c); while (1) { c = getc(d->f); if (! isdigit(c)) break; v = (v * 10) + digitval(c); } ungetc(c,d->f); return(v); } static int pnmgetbit(FDESC *d) { int c; unsigned int v; v = 0; c = pnmskipcomments(d); if (c == EOF) badpnm(d,"EOF while reading pixel value"); switch (c) { case '0': return(0); break; case '1': return(1); break; } badpnm(d,"invalid pixel character"); } static unsigned int getrawval(FDESC *d) { int c; c = getc(d->f); if (c == EOF) dataeof(d); return(c); } static void skip1white(FDESC *d) { int c; c = getc(d->f); if (! isspace(c)) ungetc(c,d->f); } static void init_input(FILE *f, const char *name, FDESC *d) { int c; d->fn = name; d->f = f; c = getc(f); if (c != 'P') badpnm(d,"bad magic number"); c = getc(f); switch (c) { case '1': d->class = T_PBM; break; case '2': d->class = T_PGM; break; case '3': d->class = T_PPM; break; case '7': d->class = T_PAM; break; case '4': d->class = T_RPBM; break; case '5': d->class = T_RPGM; break; case '6': d->class = T_RPPM; break; case '8': d->class = T_RPAM; break; default: badpnm(d,"bad magic number"); break; } d->w = pnmgetnum(d,"width"); d->h = pnmgetnum(d,"height"); d->maxval = 0; d->maxval2 = 0; d->rpbmbuf = 0; d->rpbmbit = 0; switch (d->class) { case T_PGM: case T_PPM: case T_PAM: case T_RPGM: case T_RPPM: case T_RPAM: d->maxval = pnmgetnum(d,"maxval"); break; } switch (d->class) { case T_PAM: case T_RPAM: d->maxval2 = pnmgetnum(d,"maxval2"); break; } if (class_is_raw(d->class)) skip1white(d); } static void setup(int ac, char **av) { int i; if ((ac < 4) || ((ac%3) != 1)) { fprintf(stderr,"Usage: %s file x y [file x y ...]\n",__progname); exit(1); } nifiles = ac / 3; ifiles = malloc(nifiles*sizeof(IFILE)); init_input(stdin,"standard input",&idesc); odesc.class = class_rawform(idesc.class); odesc.fn = "standard output"; odesc.f = stdout; odesc.maxval = idesc.maxval; odesc.maxval2 = idesc.maxval2; odesc.w = idesc.w; odesc.h = idesc.h; for (i=0;iatx < 0) || (f->atx+f->desc.w > idesc.w) || (f->aty < 0) || (f->aty+f->desc.h > idesc.h) ) { fprintf(stderr,"%s: %s falls outside input image - %ux%u at (%d,%d) falls outside %ux%u\n",__progname,f->desc.fn,f->desc.w,f->desc.h,f->atx,f->aty,idesc.w,idesc.h); exit(1); } } } static void init_output(void) { switch (odesc.class) { case T_RPBM: printf("P4\n%u %u\n",odesc.w,odesc.h); odesc.rpbmbuf = 0; odesc.rpbmbit = 0x80; break; case T_RPGM: if (odesc.maxval > 255) { odesc.class = T_PGM; printf("P2\n%u %u\n%u\n",odesc.w,odesc.h,odesc.maxval); } else { printf("P5\n%u %u\n%u\n",odesc.w,odesc.h,odesc.maxval); } break; case T_RPPM: if (odesc.maxval > 255) { odesc.class = T_PPM; printf("P3\n%u %u\n%u\n",odesc.w,odesc.h,odesc.maxval); } else { printf("P6\n%u %u\n%u\n",odesc.w,odesc.h,odesc.maxval); } break; case T_RPAM: if ((odesc.maxval > 255) || (odesc.maxval2 > 255)) { odesc.class = T_PAM; printf("P7\n%u %u\n%u %u\n",odesc.w,odesc.h,odesc.maxval,odesc.maxval2); } else { printf("P8\n%u %u\n%u %u\n",odesc.w,odesc.h,odesc.maxval,odesc.maxval2); } break; default: abort(); break; } } static PIXVAL pnmgetpixel(FDESC *d) { PIXVAL rv; switch (d->class) { default: abort(); break; case T_PBM: { int v; v = pnmgetbit(d); if (0) { case T_RPBM: if (d->rpbmbit < 2) { v = getc(d->f); if (v == EOF) dataeof(d); d->rpbmbuf = v; d->rpbmbit = 0x80; v &= 0x80; } else { v = d->rpbmbuf & (d->rpbmbit >>= 1); } } switch (odesc.class) { default: abort(); break; case T_PBM: case T_RPBM: rv.r = !v; break; case T_PGM: case T_RPGM: rv.r = v ? 0 : odesc.maxval; break; case T_PPM: case T_RPPM: rv.r = v ? 0 : odesc.maxval; rv.g = rv.r; rv.b = rv.r; break; case T_PAM: case T_RPAM: rv.r = v ? 0 : odesc.maxval; rv.g = rv.r; rv.b = rv.r; rv.a = 0; break; } } break; case T_PGM: { unsigned int v; v = pnmgetnum(d,"pixel value"); if (0) { case T_RPGM: v = getrawval(d); } if (d->maxval != odesc.maxval) v = (v * (odesc.maxval+1)) / (d->maxval+1); switch (odesc.class) { default: abort(); break; case T_PGM: case T_RPGM: rv.r = v; break; case T_PPM: case T_RPPM: rv.r = v; rv.g = v; rv.b = v; break; case T_PAM: case T_RPAM: rv.r = v; rv.g = v; rv.b = v; rv.a = 0; break; } } break; case T_PPM: rv.r = pnmgetnum(d,"pixel value"); rv.g = pnmgetnum(d,"pixel value"); rv.b = pnmgetnum(d,"pixel value"); if (0) { case T_RPPM: rv.r = getrawval(d); rv.g = getrawval(d); rv.b = getrawval(d); } if (d->maxval != odesc.maxval) { rv.r = (rv.r * (odesc.maxval+1)) / (d->maxval+1); rv.g = (rv.g * (odesc.maxval+1)) / (d->maxval+1); rv.b = (rv.b * (odesc.maxval+1)) / (d->maxval+1); } switch (odesc.class) { default: abort(); break; case T_PPM: case T_RPPM: break; case T_PAM: case T_RPAM: rv.a = 0; break; } break; case T_PAM: rv.r = pnmgetnum(d,"pixel value"); rv.g = pnmgetnum(d,"pixel value"); rv.b = pnmgetnum(d,"pixel value"); rv.a = pnmgetnum(d,"pixel value"); if (0) { case T_RPAM: rv.r = getrawval(d); rv.g = getrawval(d); rv.b = getrawval(d); rv.a = getrawval(d); } if (d->maxval != odesc.maxval) { rv.r = (rv.r * (odesc.maxval+1)) / (d->maxval+1); rv.g = (rv.g * (odesc.maxval+1)) / (d->maxval+1); rv.b = (rv.b * (odesc.maxval+1)) / (d->maxval+1); } if (d->maxval2 != odesc.maxval2) rv.a = (rv.a * (odesc.maxval2+1)) / (d->maxval2+1); break; } return(rv); } static void pnmputpixel(FDESC *d, PIXVAL v) { switch (d->class) { default: abort(); break; case T_RPBM: if (d->rpbmbit == 0) { putc(d->rpbmbuf,d->f); d->rpbmbuf = 0; d->rpbmbit = 0x80; } if (! v.r) d->rpbmbuf |= d->rpbmbit; d->rpbmbit >>= 1; break; case T_PGM: fprintf(d->f,"%u\n",v.r); break; case T_RPGM: putc(v.r,d->f); break; case T_PPM: fprintf(d->f,"%u %u %u\n",v.r,v.g,v.b); break; case T_RPPM: putc(v.r,d->f); putc(v.g,d->f); putc(v.b,d->f); break; case T_PAM: fprintf(d->f,"%u %u %u %u\n",v.r,v.g,v.b,v.a); break; case T_RPAM: putc(v.r,d->f); putc(v.g,d->f); putc(v.b,d->f); putc(v.a,d->f); break; } } static void pnmget_endrow(FDESC *d) { switch (d->class) { case T_RPBM: d->rpbmbit = 0; break; } } static void pnmput_endrow(FDESC *d) { switch (d->class) { case T_RPBM: putc(d->rpbmbuf,d->f); d->rpbmbuf = 0; d->rpbmbit = 0x80; break; } } static void crank(void) { unsigned int x; unsigned int y; int i; IFILE *f; PIXVAL cur; x = 0; y = 0; while (1) { cur = pnmgetpixel(&idesc); for (i=0;i= f->atx) && (y >= f->aty) && (x-f->atx < f->desc.w) && (y-f->aty < f->desc.h) ) { cur = pnmgetpixel(&f->desc); } } pnmputpixel(&odesc,cur); x ++; if (x >= odesc.w) { pnmget_endrow(&idesc); pnmput_endrow(&odesc); for (i=0;i= odesc.h) break; } } } int main(int, char **); int main(int ac, char **av) { setup(ac,av); check_placement(); init_output(); crank(); exit(0); }