|
Packit |
78deda |
/* ppmchange.c - change a given color to another
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** Copyright (C) 1991 by Wilson H. Bent, Jr.
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** Permission to use, copy, modify, and distribute this software and its
|
|
Packit |
78deda |
** documentation for any purpose and without fee is hereby granted, provided
|
|
Packit |
78deda |
** that the above copyright notice appear in all copies and that both that
|
|
Packit |
78deda |
** copyright notice and this permission notice appear in supporting
|
|
Packit |
78deda |
** documentation. This software is provided "as is" without express or
|
|
Packit |
78deda |
** implied warranty.
|
|
Packit |
78deda |
**
|
|
Packit |
78deda |
** Modified by Alberto Accomazzi (alberto@cfa.harvard.edu).
|
|
Packit |
78deda |
** 28 Jan 94 - Added multiple color substitution function.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#include "pm_c_util.h"
|
|
Packit |
78deda |
#include "ppm.h"
|
|
Packit |
78deda |
#include "shhopt.h"
|
|
Packit |
78deda |
#include "mallocvar.h"
|
|
Packit |
78deda |
|
|
Packit |
78deda |
#define TCOLS 256
|
|
Packit |
78deda |
static double const sqrt3 = 1.73205080756887729352;
|
|
Packit |
78deda |
/* The square root of 3 */
|
|
Packit |
78deda |
static double const EPSILON = 1.0e-5;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct CmdlineInfo {
|
|
Packit |
78deda |
/* All the information the user supplied in the command line,
|
|
Packit |
78deda |
in a form easy for the program to use.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
const char *input_filespec; /* Filespecs of input files */
|
|
Packit |
78deda |
int ncolors; /* Number of valid entries in color0[], color1[] */
|
|
Packit |
78deda |
const char * oldcolorname[TCOLS];
|
|
Packit |
78deda |
/* colors user wants replaced */
|
|
Packit |
78deda |
const char * newcolorname[TCOLS];
|
|
Packit |
78deda |
/* colors with which he wants them replaced */
|
|
Packit |
78deda |
float closeness;
|
|
Packit |
78deda |
const char * remainder_colorname;
|
|
Packit |
78deda |
/* Color user specified for -remainder. Null pointer if he didn't
|
|
Packit |
78deda |
specify -remainder.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
unsigned int closeok;
|
|
Packit |
78deda |
};
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
parseCommandLine(int argc, const char ** const argv,
|
|
Packit |
78deda |
struct CmdlineInfo * const cmdlineP) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Note that the file spec array we return is stored in the storage that
|
|
Packit |
78deda |
was passed to us as the argv array.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
optEntry *option_def;
|
|
Packit |
78deda |
/* Instructions to OptParseOptions3 on how to parse our options.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
optStruct3 opt;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
unsigned int option_def_index;
|
|
Packit |
78deda |
unsigned int closenessSpec, remainderSpec;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
MALLOCARRAY_NOFAIL(option_def, 100);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
option_def_index = 0; /* incremented by OPTENTRY */
|
|
Packit |
78deda |
OPTENT3(0, "closeness", OPT_FLOAT,
|
|
Packit |
78deda |
&cmdlineP->closeness, &closenessSpec, 0);
|
|
Packit |
78deda |
OPTENT3(0, "remainder", OPT_STRING,
|
|
Packit |
78deda |
&cmdlineP->remainder_colorname, &remainderSpec, 0);
|
|
Packit |
78deda |
OPTENT3(0, "closeok", OPT_FLAG,
|
|
Packit |
78deda |
NULL, &cmdlineP->closeok, 0);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
opt.opt_table = option_def;
|
|
Packit |
78deda |
opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
|
|
Packit |
78deda |
opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
|
|
Packit |
78deda |
/* Uses and sets argc, argv, and some of *cmdlineP and others. */
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (!remainderSpec)
|
|
Packit |
78deda |
cmdlineP->remainder_colorname = NULL;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (!closenessSpec)
|
|
Packit |
78deda |
cmdlineP->closeness = 0.0;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (cmdlineP->closeness < 0.0)
|
|
Packit |
78deda |
pm_error("-closeness value %f is negative", cmdlineP->closeness);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (cmdlineP->closeness > 100)
|
|
Packit |
78deda |
pm_error("-closeness value %f is more than 100%%",
|
|
Packit |
78deda |
cmdlineP->closeness);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if ((argc-1) % 2 == 0)
|
|
Packit |
78deda |
cmdlineP->input_filespec = "-";
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
cmdlineP->input_filespec = argv[argc-1];
|
|
Packit |
78deda |
|
|
Packit |
78deda |
{
|
|
Packit |
78deda |
int argn;
|
|
Packit |
78deda |
cmdlineP->ncolors = 0; /* initial value */
|
|
Packit |
78deda |
for (argn = 1;
|
|
Packit |
78deda |
argn+1 < argc && cmdlineP->ncolors < TCOLS;
|
|
Packit |
78deda |
argn += 2) {
|
|
Packit |
78deda |
cmdlineP->oldcolorname[cmdlineP->ncolors] = argv[argn];
|
|
Packit |
78deda |
cmdlineP->newcolorname[cmdlineP->ncolors] = argv[argn+1];
|
|
Packit |
78deda |
cmdlineP->ncolors++;
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static bool
|
|
Packit |
78deda |
colorMatches(pixel const comparand,
|
|
Packit |
78deda |
pixel const comparator,
|
|
Packit |
78deda |
unsigned int const allowableDiff) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
The colors 'comparand' and 'comparator' are within 'allowableDiff'
|
|
Packit |
78deda |
color levels of each other, in cartesian distance.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
/* Fast path for usual case */
|
|
Packit |
78deda |
if (allowableDiff < EPSILON)
|
|
Packit |
78deda |
return PPM_EQUAL(comparand, comparator);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return PPM_DISTANCE(comparand, comparator) <= SQR(allowableDiff);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
static void
|
|
Packit |
78deda |
changeRow(const pixel * const inrow,
|
|
Packit |
78deda |
pixel * const outrow,
|
|
Packit |
78deda |
int const cols,
|
|
Packit |
78deda |
int const ncolors,
|
|
Packit |
78deda |
const pixel colorfrom[],
|
|
Packit |
78deda |
const pixel colorto[],
|
|
Packit |
78deda |
bool const remainder_specified,
|
|
Packit |
78deda |
pixel const remainder_color,
|
|
Packit |
78deda |
unsigned int const allowableDiff) {
|
|
Packit |
78deda |
/*----------------------------------------------------------------------------
|
|
Packit |
78deda |
Replace the colors in a single row. There are 'ncolors' colors to
|
|
Packit |
78deda |
replace. The to-replace colors are in the array colorfrom[], and the
|
|
Packit |
78deda |
replace-with colors are in corresponding elements of colorto[].
|
|
Packit |
78deda |
Iff 'remainder_specified' is true, replace all colors not mentioned
|
|
Packit |
78deda |
in colorfrom[] with 'remainder_color'.
|
|
Packit |
78deda |
|
|
Packit |
78deda |
Consider the color in inrow[] to match a color in colorfrom[] if it is
|
|
Packit |
78deda |
within 'allowableDiff' color levels of it, in cartesian distance (e.g.
|
|
Packit |
78deda |
color (1,1,1) is sqrt(12) = 3.5 color levels distant from (3,3,3),
|
|
Packit |
78deda |
so if 'allowableDiff' is 4, they match).
|
|
Packit |
78deda |
|
|
Packit |
78deda |
The input row is 'inrow'. The output is returned as 'outrow', in
|
|
Packit |
78deda |
storage which must be already allocated. Both are 'cols' columns wide.
|
|
Packit |
78deda |
-----------------------------------------------------------------------------*/
|
|
Packit |
78deda |
unsigned int col;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
for (col = 0; col < cols; ++col) {
|
|
Packit |
78deda |
unsigned int i;
|
|
Packit |
78deda |
bool haveMatch; /* logical: It's a color user said to change */
|
|
Packit |
78deda |
pixel newcolor;
|
|
Packit |
78deda |
/* Color to which we must change current pixel. Undefined unless
|
|
Packit |
78deda |
'haveMatch' is true.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
haveMatch = FALSE; /* haven't found a match yet */
|
|
Packit |
78deda |
for (i = 0; i < ncolors && !haveMatch; ++i) {
|
|
Packit |
78deda |
haveMatch = colorMatches(inrow[col], colorfrom[i], allowableDiff);
|
|
Packit |
78deda |
newcolor = colorto[i];
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
if (haveMatch)
|
|
Packit |
78deda |
outrow[col] = newcolor;
|
|
Packit |
78deda |
else if (remainder_specified)
|
|
Packit |
78deda |
outrow[col] = remainder_color;
|
|
Packit |
78deda |
else
|
|
Packit |
78deda |
outrow[col] = inrow[col];
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
|
|
Packit |
78deda |
int
|
|
Packit |
78deda |
main(int argc, const char ** const argv) {
|
|
Packit |
78deda |
|
|
Packit |
78deda |
struct CmdlineInfo cmdline;
|
|
Packit |
78deda |
FILE * ifP;
|
|
Packit |
78deda |
int format;
|
|
Packit |
78deda |
int rows, cols;
|
|
Packit |
78deda |
pixval maxval;
|
|
Packit |
78deda |
unsigned int allowableDiff;
|
|
Packit |
78deda |
/* The amount of difference between two colors we allow and still
|
|
Packit |
78deda |
consider those colors to be the same, for the purposes of
|
|
Packit |
78deda |
determining which pixels in the image to change. This is a
|
|
Packit |
78deda |
cartesian distance between the color triples, on a maxval scale
|
|
Packit |
78deda |
(which means it can be as high as sqrt(3) * maxval)
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
int row;
|
|
Packit |
78deda |
pixel * inrow;
|
|
Packit |
78deda |
pixel * outrow;
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pixel oldcolor[TCOLS]; /* colors user wants replaced */
|
|
Packit |
78deda |
pixel newcolor[TCOLS]; /* colors with which he wants them replaced */
|
|
Packit |
78deda |
pixel remainder_color;
|
|
Packit |
78deda |
/* Color user specified for -remainder. Undefined if he didn't
|
|
Packit |
78deda |
specify -remainder.
|
|
Packit |
78deda |
*/
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_proginit(&argc, argv);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
parseCommandLine(argc, argv, &cmdline);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ifP = pm_openr(cmdline.input_filespec);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
if (cmdline.remainder_colorname)
|
|
Packit |
78deda |
remainder_color = ppm_parsecolor2(cmdline.remainder_colorname, maxval,
|
|
Packit |
78deda |
cmdline.closeok);
|
|
Packit |
78deda |
{
|
|
Packit |
78deda |
int i;
|
|
Packit |
78deda |
for (i = 0; i < cmdline.ncolors; ++i) {
|
|
Packit |
78deda |
oldcolor[i] = ppm_parsecolor2(cmdline.oldcolorname[i], maxval,
|
|
Packit |
78deda |
cmdline.closeok);
|
|
Packit |
78deda |
newcolor[i] = ppm_parsecolor2(cmdline.newcolorname[i], maxval,
|
|
Packit |
78deda |
cmdline.closeok);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
allowableDiff = ROUNDU(sqrt3 * maxval * cmdline.closeness/100);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ppm_writeppminit( stdout, cols, rows, maxval, 0 );
|
|
Packit |
78deda |
inrow = ppm_allocrow(cols);
|
|
Packit |
78deda |
outrow = ppm_allocrow(cols);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
/* Scan for the desired color */
|
|
Packit |
78deda |
for (row = 0; row < rows; row++) {
|
|
Packit |
78deda |
ppm_readppmrow(ifP, inrow, cols, maxval, format);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
changeRow(inrow, outrow, cols, cmdline.ncolors, oldcolor, newcolor,
|
|
Packit |
78deda |
cmdline.remainder_colorname != NULL,
|
|
Packit |
78deda |
remainder_color, allowableDiff);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
ppm_writeppmrow(stdout, outrow, cols, maxval, 0);
|
|
Packit |
78deda |
}
|
|
Packit |
78deda |
|
|
Packit |
78deda |
pm_close(ifP);
|
|
Packit |
78deda |
|
|
Packit |
78deda |
return 0;
|
|
Packit |
78deda |
}
|