Blame editor/ppmchange.c

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
}