/* * pnmselect - pick between two PNM files based on a PBM file * * pnmselect flag file0 file1 * * All three files must be the same size, and the first one must be a * PBM file. The output (whose type is the more general of the types * of file0 and file1) takes its pixels from file0 where the flag file * has 0 pixels and from file1 where the flag file has 1 pixels. */ #include #include #include #include #include #include extern const char *__progname; typedef enum { T_PBM = 'b', T_PGM = 'g', T_PPM = 'p', T_PAM = 'a', T_RPBM = 'B', T_RPGM = 'G', T_RPPM = 'P', T_RPAM = 'A' } FILECLASS; typedef struct fdesc FDESC; struct fdesc { FILECLASS class; 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 pixval PIXVAL; struct pixval { unsigned int r; unsigned int g; unsigned int b; unsigned int a; } ; static FDESC iflag; static FDESC ifile0; static FDESC ifile1; static FDESC ofile; 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); } #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 class_is_raw(FILECLASS 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 skip1white(FDESC *d) { int c; c = getc(d->f); if (! isspace(c)) ungetc(c,d->f); } static void init_input(const char *name, FDESC *d) { FILE *f; int c; f = fopen(name,"r"); if (f == 0) { fprintf(stderr,"%s: %s: %s\n",__progname,name,strerror(errno)); exit(1); } 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; default: break; } switch (d->class) { case T_PAM: case T_RPAM: d->maxval2 = pnmgetnum(d,"maxval2"); break; default: break; } if (class_is_raw(d->class)) skip1white(d); } static FILECLASS class_rawform(FILECLASS 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 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; break; } if (i->maxval > o->maxval) o->maxval = i->maxval; 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 void setup(int ac, char **av) { if (ac != 4) { fprintf(stderr,"Usage: %s flagfile file0 file1\n",__progname); exit(1); } init_input(av[1],&iflag); init_input(av[2],&ifile0); init_input(av[3],&ifile1); switch (iflag.class) { case T_PBM: case T_RPBM: break; default: fprintf(stderr,"%s: %s: not a PBM file\n",__progname,iflag.fn); exit(1); break; } if ( ( ((iflag.w != ifile0.w) || (iflag.h != ifile0.h)) && ((ifile0.w != 1) || (ifile0.h != 1)) ) || ( ((iflag.w != ifile1.w) || (iflag.h != ifile1.h)) && ((ifile1.w != 1) || (ifile1.h != 1)) ) ) { fprintf(stderr,"%s: all three files must be the same size (or 1x1 for data files)\n",__progname); exit(1); } ofile.class = class_rawform(ifile0.class); ofile.fn = "standard output"; ofile.f = stdout; ofile.maxval = ifile0.maxval; ofile.maxval2 = ifile0.maxval2; ofile.w = iflag.w; ofile.h = iflag.h; promote_merge(&ifile1,&ofile); } static void init_output(void) { switch (ofile.class) { case T_RPBM: printf("P4\n%u %u\n",ofile.w,ofile.h); ofile.rpbmbuf = 0; ofile.rpbmbit = 0x80; break; case T_RPGM: if (ofile.maxval > 255) { ofile.class = T_PGM; printf("P2\n%u %u\n%u\n",ofile.w,ofile.h,ofile.maxval); } else { printf("P5\n%u %u\n%u\n",ofile.w,ofile.h,ofile.maxval); } break; case T_RPPM: if (ofile.maxval > 255) { ofile.class = T_PPM; printf("P3\n%u %u\n%u\n",ofile.w,ofile.h,ofile.maxval); } else { printf("P6\n%u %u\n%u\n",ofile.w,ofile.h,ofile.maxval); } break; case T_RPAM: if ((ofile.maxval > 255) || (ofile.maxval2 > 255)) { ofile.class = T_PAM; printf("P7\n%u %u\n%u %u\n",ofile.w,ofile.h,ofile.maxval,ofile.maxval2); } else { printf("P8\n%u %u\n%u %u\n",ofile.w,ofile.h,ofile.maxval,ofile.maxval2); } break; default: abort(); break; } } 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 void dataeof(FDESC *) __attribute__((__noreturn__)); static void dataeof(FDESC *d) { fprintf(stderr,"%s: %s: EOF while reading pixels\n",__progname,d->fn); exit(1); } static unsigned int getrawval(FDESC *d) { int c; c = getc(d->f); if (c == EOF) dataeof(d); return(c); } 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); } } rv.r = v ? 1 : 0; } break; case T_PGM: rv.r = pnmgetnum(d,"pixel value"); break; case T_RPGM: rv.r = getrawval(d); break; case T_PPM: rv.r = pnmgetnum(d,"pixel value"); rv.g = pnmgetnum(d,"pixel value"); rv.b = pnmgetnum(d,"pixel value"); break; case T_RPPM: rv.r = getrawval(d); rv.g = getrawval(d); rv.b = getrawval(d); 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"); break; case T_RPAM: rv.r = getrawval(d); rv.g = getrawval(d); rv.b = getrawval(d); rv.a = getrawval(d); break; } return(rv); } static PIXVAL promotepixel(PIXVAL v, FDESC *i, FDESC *o) { switch (i->class) { default: abort(); break; case T_PBM: case T_RPBM: switch (o->class) { default: abort(); break; case T_PBM: case T_RPBM: break; case T_PAM: case T_RPAM: v.a = 0; /* fall through */ case T_PPM: case T_RPPM: v.r = v.r ? 0 : o->maxval; v.g = v.r; v.b = v.r; break; case T_PGM: case T_RPGM: v.r = v.r ? 0 : o->maxval; break; } break; case T_PGM: case T_RPGM: switch (o->class) { default: abort(); break; case T_PGM: case T_RPGM: break; case T_PAM: case T_RPAM: v.a = 0; /* fall through */ case T_PPM: case T_RPPM: if (i->maxval != o->maxval) v.r = (v.r * (o->maxval+1)) / (i->maxval+1); v.g = v.r; v.b = v.r; break; } break; case T_PPM: case T_RPPM: switch (o->class) { default: abort(); break; case T_PAM: case T_RPAM: v.a = 0; /* fall through */ case T_PPM: case T_RPPM: if (i->maxval != o->maxval) { v.r = (v.r * (o->maxval+1)) / (i->maxval+1); v.g = (v.g * (o->maxval+1)) / (i->maxval+1); v.b = (v.b * (o->maxval+1)) / (i->maxval+1); } break; } break; case T_PAM: case T_RPAM: switch (o->class) { default: abort(); break; case T_PAM: case T_RPAM: if (i->maxval != o->maxval) { v.r = (v.r * (o->maxval+1)) / (i->maxval+1); v.g = (v.g * (o->maxval+1)) / (i->maxval+1); v.b = (v.b * (o->maxval+1)) / (i->maxval+1); } if (i->maxval2 != o->maxval2) v.a = (v.a * (o->maxval2+1)) / (i->maxval2+1); break; } break; } return(v); } 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) { if (d->class == T_RPBM) d->rpbmbit = 0; } static void pnmput_endrow(FDESC *d) { if (d->class == T_RPBM) { putc(d->rpbmbuf,d->f); d->rpbmbuf = 0; d->rpbmbit = 0x80; } } static void crank_both(void) { unsigned int x; unsigned int y; PIXVAL fpix; PIXVAL pix0; PIXVAL pix1; pix0 = promotepixel(pnmgetpixel(&ifile0),&ifile0,&ofile); pix1 = promotepixel(pnmgetpixel(&ifile1),&ifile1,&ofile); x = 0; y = 0; while (1) { fpix = pnmgetpixel(&iflag); pnmputpixel(&ofile,fpix.r?pix1:pix0); x ++; if (x >= iflag.w) { pnmget_endrow(&iflag); pnmput_endrow(&ofile); x = 0; y ++; if (y >= iflag.h) break; } } } static void crank_file0(void) { unsigned int x; unsigned int y; PIXVAL fpix; PIXVAL pix0; PIXVAL pix1; pix0 = promotepixel(pnmgetpixel(&ifile0),&ifile0,&ofile); x = 0; y = 0; while (1) { fpix = pnmgetpixel(&iflag); pix1 = promotepixel(pnmgetpixel(&ifile1),&ifile1,&ofile); pnmputpixel(&ofile,fpix.r?pix1:pix0); x ++; if (x >= iflag.w) { pnmget_endrow(&iflag); pnmget_endrow(&ifile1); pnmput_endrow(&ofile); x = 0; y ++; if (y >= iflag.h) break; } } } static void crank_file1(void) { unsigned int x; unsigned int y; PIXVAL fpix; PIXVAL pix0; PIXVAL pix1; pix1 = promotepixel(pnmgetpixel(&ifile1),&ifile1,&ofile); x = 0; y = 0; while (1) { fpix = pnmgetpixel(&iflag); pix0 = promotepixel(pnmgetpixel(&ifile0),&ifile0,&ofile); pnmputpixel(&ofile,fpix.r?pix1:pix0); x ++; if (x >= iflag.w) { pnmget_endrow(&iflag); pnmget_endrow(&ifile0); pnmput_endrow(&ofile); x = 0; y ++; if (y >= iflag.h) break; } } } static void crank_neither(void) { unsigned int x; unsigned int y; PIXVAL fpix; PIXVAL pix0; PIXVAL pix1; x = 0; y = 0; while (1) { fpix = pnmgetpixel(&iflag); pix0 = promotepixel(pnmgetpixel(&ifile0),&ifile0,&ofile); pix1 = promotepixel(pnmgetpixel(&ifile1),&ifile1,&ofile); pnmputpixel(&ofile,fpix.r?pix1:pix0); x ++; if (x >= iflag.w) { pnmget_endrow(&iflag); pnmget_endrow(&ifile0); pnmget_endrow(&ifile1); pnmput_endrow(&ofile); x = 0; y ++; if (y >= iflag.h) break; } } } static void crank(void) { if ((ifile0.w == 1) && (ifile0.h == 1)) { if ((ifile1.w == 1) && (ifile1.h == 1)) { crank_both(); } else { crank_file0(); } } else { if ((ifile1.w == 1) && (ifile1.h == 1)) { crank_file1(); } else { crank_neither(); } } } int main(int, char **); int main(int ac, char **av) { setup(ac,av); init_output(); crank(); exit(0); }