/* ** ppmr4.c - redact portions of a PPM picture ** Written by Harry Sintonen ** ** Compile: ** % gcc -Wall -O2 ppmr4.c -o ppmr4 ** ** Usage: ** Redact two rectangles: ** % echo -e '5 8 90 24\n5 32 90 48' | ./ppmr4 secret.ppm > out.ppm ** Redact the whole picture: ** % echo '0 0 99999 99999' | ./ppmr4 secret.ppm > out.ppm */ #include #include #include 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, c; while ((c = fgetc(fh)) != EOF) if (state) state = c != '\n'; else if (c == '#') state = 1; else if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { ungetc(c, fh); break; } return fscanf(fh, "%u ", &val) == 1 ? val : -1; } struct rect { int minx, miny, maxx, maxy; }; struct region { int size, numrects; struct rect *rects; }; struct data { FILE *fh; struct region region; int bpp, bpr, ypos; }; static void addregionrect(struct region *r, const struct rect *rect) { if (++r->numrects > r->size) { r->size += 16; if (!(r->rects = realloc(r->rects, r->size * sizeof(struct rect)))) fail("out of memory\n"); } r->rects[r->numrects - 1] = *rect; } static void redact(struct data *d) { unsigned char in[d->bpr], out[d->bpr]; while (fread(in, 1, d->bpr, d->fh) == d->bpr) { struct rect *r = d->region.rects; int x, y = d->ypos++; for (x = 0; x < d->bpr; x++) out[x] = in[x]; for (; r < &d->region.rects[d->region.numrects]; r++) if (y >= r->miny && y < r->maxy) for (x = r->minx * d->bpp; x < r->maxx * d->bpp; x++) out[x] = 0; if (fwrite(out, 1, d->bpr, stdout) != d->bpr) fail("error writing output\n"); else redact(d); fwrite(in, 1, d->bpr, stdout); break; } } int main(int argc, char **argv) { int cols = 0, rows = 0, maxval = 0; unsigned char id[2]; struct data data = {0}; struct rect r; if (argc != 2) fail("usage: %s in.ppm < redactions.txt > out.ppm\n", argv[0]); if (!(data.fh = fopen(argv[1], "r"))) fail("could not open file \"%s\"\n", argv[1]); if (fread(id, 1, 2, data.fh) != 2 || id[0] != 'P' || id[1] != '6' || (cols = readval(data.fh)) < 0 || (rows = readval(data.fh)) < 0 || (maxval = readval(data.fh)) < 0 || maxval > 65535) fail("invalid or unsupported PPM file \"%s\"\n", argv[1]); while (scanf("%d %d %d %d", &r.minx, &r.miny, &r.maxx, &r.maxy) == 4) { if (r.maxx > cols) r.maxx = cols; /* clip to image width */ addregionrect(&data.region, &r); } printf("P6\n%d %d\n%d\n", cols, rows, maxval); data.bpp = 3 * (maxval < 256 ? 1 : 2); data.bpr = cols * data.bpp; redact(&data); return EXIT_SUCCESS; }