/* ppmchange.c - change a given color to another ** ** Copyright (C) 1991 by Wilson H. Bent, Jr. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. ** ** Modified by Alberto Accomazzi (alberto@cfa.harvard.edu). ** 28 Jan 94 - Added multiple color substitution function. */ #include "pm_c_util.h" #include "ppm.h" #include "shhopt.h" #include "mallocvar.h" #define TCOLS 256 #define SQRT3 1.73205080756887729352 /* The square root of 3 */ struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char *input_filespec; /* Filespecs of input files */ int ncolors; /* Number of valid entries in color0[], color1[] */ char * oldcolorname[TCOLS]; /* colors user wants replaced */ char * newcolorname[TCOLS]; /* colors with which he wants them replaced */ int closeness; /* -closeness option value. Zero if no -closeness option */ char * remainder_colorname; /* Color user specified for -remainder. Null pointer if he didn't specify -remainder. */ unsigned int closeok; }; static void parseCommandLine(int argc, 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. -----------------------------------------------------------------------------*/ optEntry *option_def; /* Instructions to OptParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; unsigned int closenessSpec, remainderSpec; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "closeness", OPT_UINT, &cmdlineP->closeness, &closenessSpec, 0); OPTENT3(0, "remainder", OPT_STRING, &cmdlineP->remainder_colorname, &remainderSpec, 0); OPTENT3(0, "closeok", OPT_FLAG, NULL, &cmdlineP->closeok, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!remainderSpec) cmdlineP->remainder_colorname = NULL; if (!closenessSpec) cmdlineP->closeness = 0; if ((argc-1) % 2 == 0) cmdlineP->input_filespec = "-"; else cmdlineP->input_filespec = argv[argc-1]; { int argn; cmdlineP->ncolors = 0; /* initial value */ for (argn = 1; argn+1 < argc && cmdlineP->ncolors < TCOLS; argn += 2) { cmdlineP->oldcolorname[cmdlineP->ncolors] = argv[argn]; cmdlineP->newcolorname[cmdlineP->ncolors] = argv[argn+1]; cmdlineP->ncolors++; } } } static double sqrf(float const F) { return F*F; } static int colormatch(pixel const comparand, pixel const comparator, float const closeness) { /*---------------------------------------------------------------------------- Return true iff 'comparand' matches 'comparator' in color within the fuzz factor 'closeness'. -----------------------------------------------------------------------------*/ /* Fast path for usual case */ if (closeness == 0) return PPM_EQUAL(comparand, comparator); return PPM_DISTANCE(comparand, comparator) <= sqrf(closeness); } static void changeRow(const pixel * const inrow, pixel * const outrow, int const cols, int const ncolors, const pixel colorfrom[], const pixel colorto[], bool const remainder_specified, pixel const remainder_color, float const closeness) { /*---------------------------------------------------------------------------- Replace the colors in a single row. There are 'ncolors' colors to replace. The to-replace colors are in the array colorfrom[], and the replace-with colors are in corresponding elements of colorto[]. Iff 'remainder_specified' is true, replace all colors not mentioned in colorfrom[] with 'remainder_color'. Use the closeness factor 'closeness' in determining if something in the input row matches a color in colorfrom[]. The input row is 'inrow'. The output is returned as 'outrow', in storage which must be already allocated. Both are 'cols' columns wide. -----------------------------------------------------------------------------*/ unsigned int col; for (col = 0; col < cols; ++col) { unsigned int i; bool haveMatch; /* logical: It's a color user said to change */ pixel newcolor; /* Color to which we must change current pixel. Undefined unless 'haveMatch' is true. */ haveMatch = FALSE; /* haven't found a match yet */ for (i = 0; i < ncolors && !haveMatch; ++i) { haveMatch = colormatch(inrow[col], colorfrom[i], closeness); newcolor = colorto[i]; } if (haveMatch) outrow[col] = newcolor; else if (remainder_specified) outrow[col] = remainder_color; else outrow[col] = inrow[col]; } } int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; FILE * ifP; int format; int rows, cols; pixval maxval; float closeness; int row; pixel* inrow; pixel* outrow; pixel oldcolor[TCOLS]; /* colors user wants replaced */ pixel newcolor[TCOLS]; /* colors with which he wants them replaced */ pixel remainder_color; /* Color user specified for -remainder. Undefined if he didn't specify -remainder. */ ppm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.input_filespec); ppm_readppminit(ifP, &cols, &rows, &maxval, &format); if (cmdline.remainder_colorname) remainder_color = ppm_parsecolor2(cmdline.remainder_colorname, maxval, cmdline.closeok); { int i; for (i = 0; i < cmdline.ncolors; ++i) { oldcolor[i] = ppm_parsecolor2(cmdline.oldcolorname[i], maxval, cmdline.closeok); newcolor[i] = ppm_parsecolor2(cmdline.newcolorname[i], maxval, cmdline.closeok); } } closeness = SQRT3 * maxval * cmdline.closeness/100; ppm_writeppminit( stdout, cols, rows, maxval, 0 ); inrow = ppm_allocrow(cols); outrow = ppm_allocrow(cols); /* Scan for the desired color */ for (row = 0; row < rows; row++) { ppm_readppmrow(ifP, inrow, cols, maxval, format); changeRow(inrow, outrow, cols, cmdline.ncolors, oldcolor, newcolor, cmdline.remainder_colorname != NULL, remainder_color, closeness); ppm_writeppmrow(stdout, outrow, cols, maxval, 0); } pm_close(ifP); return 0; }