/* ** ppmr3.c - redact portions of a PPM picture ** Written by Harry Sintonen ** ** Compile: ** % gcc -Wall -O2 ppmr3.c -o ppmr3 ** ** Usage: ** Redact two rectangles: ** % echo -e '5 8 90 24\n5 32 90 48' | ./ppmr3 secret.ppm > out.ppm ** Redact the whole picture: ** % echo '0 0 99999 99999' | ./ppmr3 secret.ppm > out.ppm */ #include #include #include #include enum { D_RECT, D_VERBOSE, NUMDEBUGFLAGS }; static int debug[NUMDEBUGFLAGS]; int ftime(time_t *); #ifdef __GNUC__ static void fail(const char *fmt, ...) __attribute__ ((noreturn)); #endif static void fail(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); exit(EXIT_FAILURE); } /* Read number from a file, PPM header style, return -1 for an EOF */ static int readval(FILE *fh) { int state = 0, val = 0, c; while ((c = fgetc(fh)) != EOF) { if (debug[D_VERBOSE]) printf("# state=%d c=%d\n", state, c); switch (state) { case 0: /* skipping whitespaces */ if (c == ' ' || c == '\t' || c == '\r' || c == '\n') break; state = 1; case 1: /* checking for possible comment start */ if (c == '#') { state = 3; break; } state = 2; case 2: /* parsing digits */ if (c < '0' || c > '9') return val; val = val * 10 + c - '0'; break; case 3: /* skipping comment */ if (c == '\n') state = 0; } } return state == 2 ? val : -1; } static __inline void clip(int *valptr, int maxval) { if (*valptr < 0) *valptr = 0; else if (*valptr > maxval) *valptr = maxval; } int main(int argc, char **argv) { static time_t seed; int optind = 1, cols, rows, maxval, bpp, bpr; int minx, miny, maxx, maxy, x, y; unsigned char id[2], *pixels; FILE *fh; if (ftime(&seed)) srand(seed); if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'd') { debug[D_RECT] = 1; debug[D_VERBOSE] = 1; argc--; optind++; } if (argc < 2) fail("usage: %s [-d] in.ppm < redactions.txt > out.ppm\n", argv[0]); if (!(fh = fopen(argv[optind], "r"))) fail("could not open file \"%s\"\n", argv[optind]); if (fread(id, 1, 2, fh) != 2 || id[0] != 'P' || id[1] != '6' || (cols = readval(fh)) < 0 || (rows = readval(fh)) < 0 || (maxval = readval(fh)) < 0 || maxval > 65535) fail("invalid or unsupported PPM file \"%s\"\n", argv[optind]); bpp = maxval < 256 ? 1 : 2; bpr = cols * bpp * 3; if (debug[D_VERBOSE]) printf("# cols=%d rows=%d maxval=%d bpp=%d bpr=%d\n", cols, rows, maxval, bpp, bpr); if (!(pixels = malloc(bpr * rows))) fail("out of memory\n"); if (fread(pixels, bpr, rows, fh) != rows) fail("could not read file \"%s\"\n", argv[optind]); fclose(fh); if (printf("P6\n%d %d\n", cols, rows) < 0) fail("could not write PPM file header\n"); while ((minx = readval(stdin)) >= 0 && (miny = readval(stdin)) >= 0 && (maxx = readval(stdin)) >= 0 && (maxy = readval(stdin)) >= 0) { clip(&minx, cols); clip(&maxx, cols); clip(&miny, rows); clip(&maxy, rows); if (debug[D_RECT]) printf("# %d,%d to %d,%d\n", minx, miny, maxx, maxy); minx *= 3; maxx *= 3 * bpp; for (y = miny; y < maxy; y++) { unsigned char *ptr = &pixels[y * bpr + minx * bpp]; for (x = minx; x < maxx; x++) *ptr++ ^= rand(); } } if (printf("%d\n", maxval) < 0 || fwrite(pixels, bpr, rows, stdout) != rows) fail("could not write PPM file pixel data\n"); return EXIT_SUCCESS; }