diff options
-rw-r--r-- | editor/pbmmask.c | 333 |
1 files changed, 221 insertions, 112 deletions
diff --git a/editor/pbmmask.c b/editor/pbmmask.c index 0def35cc..7fc3f446 100644 --- a/editor/pbmmask.c +++ b/editor/pbmmask.c @@ -10,13 +10,56 @@ ** implied warranty. */ +#include <stdbool.h> + #include "pbm.h" +#include "shhopt.h" #include "mallocvar.h" -static bit ** bits; -static bit ** mask; -static bit backcolor; -static int rows, cols; +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* File name of input file */ + unsigned int expand; +}; + + + +static void +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optStruct3 opt; /* set by OPTENT3 */ + optEntry * option_def; + unsigned int option_def_index; + + MALLOCARRAY(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "expand", OPT_FLAG, NULL, &cmdlineP->expand, 0); + + opt.opt_table = option_def; + opt.short_allowed = false; /* We have no short (old-fashioned) options */ + opt.allowNegNum = true; /* We sort of allow negative numbers as parms */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + free(option_def); + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("You specified too many arguments (%u). The only " + "possible argument is the optional input file specification.", + argc-1); +} @@ -28,8 +71,59 @@ static int fstackp = 0; static void -addflood(int const col, - int const row) { +clearMask(bit ** const mask, + unsigned int const cols, + unsigned int const rows) { + + /* Clear out the mask. */ + unsigned int row; + + for (row = 0; row < rows; ++row) { + unsigned int col; + + for (col = 0; col < cols; ++col) + mask[row][col] = PBM_BLACK; + } +} + + + +static bit +backcolorFmImage(bit ** const bits, + unsigned int const cols, + unsigned int const rows) { + + /* Figure out the background color, by counting along the edge. */ + + unsigned int row; + unsigned int col; + unsigned int wcount; + + wcount = 0; + for (row = 0; row < rows; ++row) { + if (bits[row][0] == PBM_WHITE) + ++wcount; + if (bits[row][cols - 1] == PBM_WHITE) + ++wcount; + } + for (col = 1; col < cols - 1; ++col) { + if (bits[0][col] == PBM_WHITE) + ++wcount; + if (bits[rows - 1][col] == PBM_WHITE) + ++wcount; + } + + return (wcount >= rows + cols - 2) ? PBM_WHITE : PBM_BLACK; +} + + + +static void +addflood(bit ** const bits, + bit ** const mask, + unsigned int const col, + unsigned int const row, + bit const backcolor) { if (bits[row][col] == backcolor && mask[row][col] == PBM_BLACK) { if (fstackp >= fstacksize) { @@ -38,15 +132,15 @@ addflood(int const col, MALLOCARRAY(fcols, fstacksize); MALLOCARRAY(frows, fstacksize); if (fcols == NULL || frows == NULL) - pm_error( "out of memory" ); + pm_error("out of memory"); } else { fstacksize *= 2; fcols = (short*) realloc( - (char*) fcols, fstacksize * sizeof(short) ); + (char*) fcols, fstacksize * sizeof(short)); frows = (short*) realloc( - (char*) frows, fstacksize * sizeof(short) ); - if ( fcols == (short*) 0 || frows == (short*) 0 ) - pm_error( "out of memory" ); + (char*) frows, fstacksize * sizeof(short)); + if (fcols == (short*) 0 || frows == (short*)0) + pm_error("out of memory"); } } fcols[fstackp] = col; @@ -58,7 +152,38 @@ addflood(int const col, static void -flood(void) { +floodEdge(bit ** const bits, + unsigned int const cols, + unsigned int const rows, + bit const backcolor, + bit ** const mask) { + + int col; + int row; + + /* Flood the entire edge. Probably the first call will be enough, but + might as well be sure. + */ + for (col = cols - 3; col >= 2; col -= 2) { + addflood(bits, mask, col, rows - 1, backcolor); + addflood(bits, mask, col, 0, backcolor); + } + for (row = rows - 1; row >= 0; row -= 2) { + addflood(bits, mask, cols - 1, row, backcolor); + addflood(bits, mask, 0, row, backcolor); + } +} + + + +static void +flood(bit ** const bits, + unsigned int const cols, + unsigned int const rows, + bit const backcolor, + bit ** const mask) { + + floodEdge(bits, cols, rows, backcolor, mask); while (fstackp > 0) { int col, row; @@ -69,35 +194,35 @@ flood(void) { int c; mask[row][col] = PBM_WHITE; if (row - 1 >= 0) - addflood(col, row - 1); + addflood(bits, mask, col, row - 1, backcolor); if (row + 1 < rows) - addflood(col, row + 1); + addflood(bits, mask, col, row + 1, backcolor); for (c = col + 1; c < cols; ++c) { if (bits[row][c] == backcolor && mask[row][c] == PBM_BLACK) { mask[row][c] = PBM_WHITE; if (row - 1 >= 0 && (bits[row - 1][c - 1] != backcolor || mask[row - 1][c - 1] != PBM_BLACK)) - addflood(c, row - 1); + addflood(bits, mask, c, row - 1, backcolor); if (row + 1 < rows && (bits[row + 1][c - 1] != backcolor || mask[row + 1][c - 1] != PBM_BLACK)) - addflood(c, row + 1); + addflood(bits, mask, c, row + 1, backcolor); } else break; } for (c = col - 1; c >= 0; --c) { - if ( bits[row][c] == backcolor && mask[row][c] == PBM_BLACK ) { + if (bits[row][c] == backcolor && mask[row][c] == PBM_BLACK) { mask[row][c] = PBM_WHITE; if (row - 1 >= 0 && (bits[row - 1][c + 1] != backcolor || mask[row - 1][c + 1] != PBM_BLACK)) - addflood(c, row - 1); + addflood(bits, mask, c, row - 1, backcolor); if (row + 1 < rows && (bits[row + 1][c + 1] != backcolor || mask[row + 1][c + 1] != PBM_BLACK)) - addflood(c, row + 1); + addflood(bits, mask, c, row + 1, backcolor); } else break; } @@ -107,115 +232,99 @@ flood(void) { -int -main(int argc, char * argv[]) { - - FILE* ifp; - int argn, expand, wcount; - register int row, col; - const char* const usage = "[-expand] [pbmfile]"; - - pbm_init(&argc, argv); - - argn = 1; - expand = 0; - - if (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') - { - if (pm_keymatch( argv[argn], "-expand", 2)) - expand = 1; - else if (pm_keymatch( argv[argn], "-noexpand", 2)) - expand = 0; - else - pm_usage(usage); - ++argn; - } +static bit ** +expandedByOnePixel(bit ** const mask, + unsigned int const cols, + unsigned int const rows) { - if (argn == argc) - ifp = stdin; - else { - ifp = pm_openr(argv[argn]); - ++argn; - } + /* Expand by one pixel. */ - if (argn != argc) - pm_usage(usage); + bit ** const emask = pbm_allocarray(cols, rows); - bits = pbm_readpbm(ifp, &cols, &rows); - pm_close(ifp); - mask = pbm_allocarray(cols, rows); + unsigned int row; - /* Clear out the mask. */ - for (row = 0; row < rows; ++row) + for (row = 0; row < rows; ++row) { + unsigned int col; for (col = 0; col < cols; ++col) - mask[row][col] = PBM_BLACK; + if (mask[row][col] == PBM_BLACK) + emask[row][col] = PBM_BLACK; + else { + unsigned int srow; - /* Figure out the background color, by counting along the edge. */ - wcount = 0; - for (row = 0; row < rows; ++row) { - if (bits[row][0] == PBM_WHITE) - ++wcount; - if (bits[row][cols - 1] == PBM_WHITE) - ++wcount; - } - for (col = 1; col < cols - 1; ++col) { - if (bits[0][col] == PBM_WHITE) - ++wcount; - if (bits[rows - 1][col] == PBM_WHITE) - ++wcount; - } - if (wcount >= rows + cols - 2) - backcolor = PBM_WHITE; - else - backcolor = PBM_BLACK; + emask[row][col] = PBM_WHITE; - /* Flood the entire edge. Probably the first call will be enough, but - might as well be sure. - */ - for (col = cols - 3; col >= 2; col -= 2) { - addflood(col, rows - 1); - addflood(col, 0); - } - for (row = rows - 1; row >= 0; row -= 2) { - addflood(cols - 1, row); - addflood(0, row); + for (srow = row - 1; srow <= row + 1; ++srow) { + unsigned int scol; + + for (scol = col - 1; scol <= col + 1; ++scol) { + if (srow >= 0 && srow < rows && + scol >= 0 && scol < cols && + mask[srow][scol] == PBM_BLACK) { + + emask[row][col] = PBM_BLACK; + break; + } + } + } + } } - flood(); + return emask; +} + + + +static void +pbmmask(FILE * const ifP, + FILE * const ofP, + struct CmdlineInfo const cmdline) { + + int cols, rows; + bit ** mask; + bit ** bits; + bit backcolor; - if (!expand) + bits = pbm_readpbm(ifP, &cols, &rows); + + mask = pbm_allocarray(cols, rows); + + clearMask(mask, cols, rows); + + backcolor = backcolorFmImage(bits, cols, rows); + + flood(bits, cols, rows, backcolor, mask); + + if (!cmdline.expand) { /* Done. */ pbm_writepbm(stdout, mask, cols, rows, 0); - else { - /* Expand by one pixel. */ - int srow, scol; - unsigned int row; - bit ** emask; - - emask = pbm_allocarray(cols, rows); - - for (row = 0; row < rows; ++row) { - unsigned int col; - for (col = 0; col < cols; ++col) - if (mask[row][col] == PBM_BLACK) - emask[row][col] = PBM_BLACK; - else { - emask[row][col] = PBM_WHITE; - for (srow = row - 1; srow <= row + 1; ++srow) - for (scol = col - 1; scol <= col + 1; ++scol) - if (srow >= 0 && srow < rows && - scol >= 0 && scol < cols && - mask[srow][scol] == PBM_BLACK) { - - emask[row][col] = PBM_BLACK; - break; - } - } - } + } else { + bit ** const emask = expandedByOnePixel(mask, cols, rows); + pbm_writepbm(stdout, emask, cols, rows, 0); + + pbm_freearray(emask, rows); } +} + + + +int +main(int argc, const char ** argv) { + + struct CmdlineInfo cmdline; + FILE * ifP; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + ifP = pm_openr(cmdline.inputFileName); + + pbmmask(ifP, stdout, cmdline); + + pm_close(ifP); pm_close(stdout); return 0; } + |