about summary refs log tree commit diff
path: root/editor/pamfunc.c
diff options
context:
space:
mode:
Diffstat (limited to 'editor/pamfunc.c')
-rw-r--r--editor/pamfunc.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/editor/pamfunc.c b/editor/pamfunc.c
new file mode 100644
index 00000000..dbb1ca70
--- /dev/null
+++ b/editor/pamfunc.c
@@ -0,0 +1,221 @@
+/******************************************************************************
+                               pamfunc
+*******************************************************************************
+  Apply one of various functions to each sample in a PAM image
+
+  By Bryan Henderson, San Jose CA 2002.06.16.
+
+  Contributed to the public domain
+
+  ENHANCEMENT IDEAS:
+
+  1) speed up by doing integer arithmetic instead of floating point for
+  multiply/divide where possible.  Especially when multiplying by an 
+  integer.
+
+  2) For multiply/divide, give option of simply changing the maxval and
+  leaving the raster alone.
+
+******************************************************************************/
+
+#include "pam.h"
+#include "shhopt.h"
+
+enum function {FN_MULTIPLY, FN_DIVIDE, FN_ADD, FN_SUBTRACT, FN_MIN, FN_MAX};
+
+/* Note that when the user specifies a minimum, that means he's requesting
+   a "max" function.
+*/
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* Filespec of input file */
+    enum function function;
+    union {
+        float multiplier;
+        float divisor;
+        int adder;
+        int subtractor;
+        unsigned int max;
+        unsigned int min;
+    } u;
+    unsigned int verbose;
+};
+
+
+static void
+parseCommandLine(int argc, char ** const 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 = malloc(100*sizeof(optEntry));
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int multiplierSpec, divisorSpec, adderSpec, subtractorSpec;
+    unsigned int maxSpec, minSpec;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "multiplier", OPT_FLOAT,  &cmdlineP->u.multiplier, 
+            &multiplierSpec, 0);
+    OPTENT3(0,   "divisor",    OPT_FLOAT,  &cmdlineP->u.divisor,
+            &divisorSpec, 0);
+    OPTENT3(0,   "adder",      OPT_INT,    &cmdlineP->u.adder,
+            &adderSpec, 0);
+    OPTENT3(0,   "subtractor", OPT_INT,    &cmdlineP->u.subtractor,
+            &subtractorSpec, 0);
+    OPTENT3(0,   "min",        OPT_UINT,   &cmdlineP->u.min,
+            &minSpec, 0);
+    OPTENT3(0,   "max",        OPT_UINT,   &cmdlineP->u.max,
+            &maxSpec, 0);
+    OPTENT3(0,   "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (multiplierSpec + divisorSpec + adderSpec + subtractorSpec +
+        minSpec + maxSpec > 1)
+        pm_error("You may specify at most one of -multiplier, -divisor,"
+                 "-adder, -subtractor, -min, and -max");
+
+    if (multiplierSpec) {
+        cmdlineP->function = FN_MULTIPLY;
+        if (cmdlineP->u.multiplier < 0)
+            pm_error("Multiplier must be nonnegative.  You specified %f", 
+                     cmdlineP->u.multiplier);
+    } else if (divisorSpec) {
+        cmdlineP->function = FN_DIVIDE;
+        if (cmdlineP->u.divisor < 0)
+            pm_error("Divisor must be nonnegative.  You specified %f", 
+                     cmdlineP->u.divisor);
+    } else if (adderSpec) {
+        cmdlineP->function = FN_ADD;
+    } else if (subtractorSpec) {
+        cmdlineP->function = FN_SUBTRACT;
+    } else if (minSpec) {
+        cmdlineP->function = FN_MAX;
+    } else if (maxSpec) {
+        cmdlineP->function = FN_MIN;
+    } else 
+        pm_error("You must specify one of -multiplier, -divisor, "
+                 "-adder, -subtractor, -min, or -max");
+        
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%d).  File spec is the only argument.",
+                 argc-1);
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else 
+        cmdlineP->inputFilespec = argv[1];
+    
+}
+
+
+
+static void
+applyFunction(struct cmdlineInfo const cmdline,
+              struct pam         const inpam,
+              struct pam         const outpam,
+              tuple *            const inputRow,
+              tuple *            const outputRow) {
+
+    float const oneOverDivisor = 1/cmdline.u.divisor;
+        /* In my experiments, the compiler couldn't figure out that
+           1/cmdline.u.divisor is a constant and instead recomputed it
+           for each and every pixel.  division is slower than
+           multiplication, so we want to multiply by
+           1/cmdline.u.divisor instead of divide by cmdline.u.divisor,
+           so we compute that here.  Note that if the function isn't
+           divide, both cmdline.u.divisor and oneOverDivisor are
+           meaningless.  
+        */
+    int col;
+
+    for (col = 0; col < inpam.width; ++col) {
+        int plane;
+        for (plane = 0; plane < inpam.depth; ++plane) {
+            sample const inSample = inputRow[col][plane];
+            sample outSample;  /* Could be > maxval  */
+
+            switch (cmdline.function) {
+            case FN_MULTIPLY:
+                outSample = ROUNDU(inSample * cmdline.u.multiplier);
+                break;
+            case FN_DIVIDE:
+                outSample = ROUNDU(inSample * oneOverDivisor);
+                break;
+            case FN_ADD:
+                outSample = MAX(0, (long)inSample + cmdline.u.adder);
+                break;
+            case FN_SUBTRACT:
+                outSample = MAX(0, (long)inSample - cmdline.u.subtractor);
+                break;
+            case FN_MAX:
+                outSample = MAX(inSample, cmdline.u.min);
+                break;
+            case FN_MIN:
+                outSample = MIN(inSample, cmdline.u.max);
+                break;
+            }
+            outputRow[col][plane] = MIN(outpam.maxval, outSample);
+        }
+    }
+}                
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE* ifP;
+    tuple* inputRow;   /* Row from input image */
+    tuple* outputRow;  /* Row of output image */
+    int row;
+    struct cmdlineInfo cmdline;
+    struct pam inpam;   /* Input PAM image */
+    struct pam outpam;  /* Output PAM image */
+
+    pnm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    inputRow = pnm_allocpamrow(&inpam);
+
+    outpam = inpam;    /* Initial value -- most fields should be same */
+    outpam.file = stdout;
+
+    pnm_writepaminit(&outpam);
+
+    outputRow = pnm_allocpamrow(&outpam);
+
+    for (row = 0; row < inpam.height; row++) {
+        pnm_readpamrow(&inpam, inputRow);
+
+        applyFunction(cmdline, inpam, outpam, inputRow, outputRow);
+
+        pnm_writepamrow(&outpam, outputRow);
+    }
+    pnm_freepamrow(outputRow);
+    pnm_freepamrow(inputRow);
+    pm_close(inpam.file);
+    pm_close(outpam.file);
+    
+    exit(0);
+}
+