Blame editor/pnmnorm.c

Packit 78deda
/******************************************************************************
Packit 78deda
                                  Pnmnorm
Packit 78deda
*******************************************************************************
Packit 78deda
Packit 78deda
  This program normalizes the contrast in a Netpbm image.
Packit 78deda
Packit 78deda
  by Bryan Henderson bryanh@giraffe-data.com San Jose CA March 2002.
Packit 78deda
  Adapted from Ppmnorm.
Packit 78deda
Packit 78deda
  Ppmnorm is by Wilson H. Bent, Jr. (whb@usc.edu)
Packit 78deda
  Extensively hacked from pgmnorm.c, which carries the following note:
Packit 78deda
  
Packit 78deda
  Copyright (C) 1989, 1991 by Jef Poskanzer.
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
  (End of note from pgmnorm.c)
Packit 78deda
Packit 78deda
  Pgmnorm's man page also said:
Packit 78deda
  
Packit 78deda
  Partially based on the fbnorm filter in Michael Mauldin's "Fuzzy Pixmap"
Packit 78deda
  package.
Packit 78deda
*****************************************************************************/
Packit 78deda
Packit 78deda
#include <assert.h>
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "mallocvar.h"
Packit 78deda
#include "nstring.h"
Packit 78deda
#include "shhopt.h"
Packit 78deda
#include "matrix.h"
Packit 78deda
#include "pnm.h"
Packit 78deda
Packit 78deda
enum brightMethod {BRIGHT_LUMINOSITY, BRIGHT_COLORVALUE, BRIGHT_SATURATION};
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 * inputFileName;  /* Name of input file */
Packit 78deda
    unsigned int bvalueSpec;
Packit 78deda
    xelval bvalue;
Packit 78deda
    unsigned int bpercentSpec;
Packit 78deda
    float bpercent;
Packit 78deda
    unsigned int wvalueSpec;
Packit 78deda
    xelval wvalue;
Packit 78deda
    unsigned int wpercentSpec;
Packit 78deda
    float wpercent;
Packit 78deda
    unsigned int bsingle;
Packit 78deda
    unsigned int wsingle;
Packit 78deda
    float middle;
Packit 78deda
    unsigned int midvalueSpec;
Packit 78deda
    xelval midvalue;
Packit 78deda
    enum brightMethod brightMethod;
Packit 78deda
    unsigned int keephues;
Packit 78deda
    float maxExpansion;
Packit 78deda
        /* The maximum allowed expansion factor for expansion specified
Packit 78deda
           by per centile.  This is a factor, not a per cent increase.
Packit 78deda
           E.g. 50% increase means a factor of 1.50.
Packit 78deda
        */
Packit 78deda
    unsigned int verbose;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommandLine(int argc, const char ** argv,
Packit 78deda
                 struct cmdlineInfo * const cmdlineP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   parse program command line described in Unix standard form by argc
Packit 78deda
   and argv.  Return the information in the options as *cmdlineP.  
Packit 78deda
Packit 78deda
   If command line is internally inconsistent (invalid options, etc.),
Packit 78deda
   issue error message to stderr and abort program.
Packit 78deda
Packit 78deda
   Note that the strings we return are stored in the storage that
Packit 78deda
   was passed to us as the argv array.  We also trash *argv.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    optEntry *option_def;
Packit 78deda
        /* Instructions to pm_optParseOptions3 on how to parse our options.
Packit 78deda
         */
Packit 78deda
    optStruct3 opt;
Packit 78deda
Packit 78deda
    unsigned int luminosity, colorvalue, saturation;
Packit 78deda
    unsigned int middleSpec, maxexpandSpec;
Packit 78deda
    float maxexpand;
Packit 78deda
    
Packit 78deda
    unsigned int option_def_index;
Packit 78deda
Packit 78deda
    MALLOCARRAY_NOFAIL(option_def, 100);
Packit 78deda
Packit 78deda
    option_def_index = 0;   /* incremented by OPTENT3 */
Packit 78deda
    OPTENT3(0,   "bpercent",      OPT_FLOAT,   
Packit 78deda
            &cmdlineP->bpercent,   &cmdlineP->bpercentSpec, 0);
Packit 78deda
    OPTENT3(0,   "wpercent",      OPT_FLOAT,   
Packit 78deda
            &cmdlineP->wpercent,   &cmdlineP->wpercentSpec, 0);
Packit 78deda
    OPTENT3(0,   "bvalue",        OPT_UINT,   
Packit 78deda
            &cmdlineP->bvalue,     &cmdlineP->bvalueSpec, 0);
Packit 78deda
    OPTENT3(0,   "wvalue",        OPT_UINT,   
Packit 78deda
            &cmdlineP->wvalue,     &cmdlineP->wvalueSpec, 0);
Packit 78deda
    OPTENT3(0,   "bsingle",       OPT_FLAG,   
Packit 78deda
            NULL,                 &cmdlineP->bsingle, 0);
Packit 78deda
    OPTENT3(0,   "wsingle",       OPT_FLAG,   
Packit 78deda
            NULL,                 &cmdlineP->wsingle, 0);
Packit 78deda
    OPTENT3(0,   "middle",        OPT_FLOAT,   
Packit 78deda
            &cmdlineP->middle,     &middleSpec, 0);
Packit 78deda
    OPTENT3(0,   "midvalue",      OPT_UINT,   
Packit 78deda
            &cmdlineP->midvalue,   &cmdlineP->midvalueSpec, 0);
Packit 78deda
    OPTENT3(0,   "maxexpand",     OPT_FLOAT,   
Packit 78deda
            &maxexpand,            &maxexpandSpec, 0);
Packit 78deda
    OPTENT3(0,   "keephues",      OPT_FLAG,   
Packit 78deda
            NULL,                  &cmdlineP->keephues, 0);
Packit 78deda
    OPTENT3(0,   "luminosity",    OPT_FLAG,   
Packit 78deda
            NULL,                  &luminosity, 0);
Packit 78deda
    OPTENT3(0,   "colorvalue",    OPT_FLAG,   
Packit 78deda
            NULL,                  &colorvalue, 0);
Packit 78deda
    OPTENT3(0,   "saturation",    OPT_FLAG,   
Packit 78deda
            NULL,                  &saturation, 0);
Packit 78deda
    OPTENT3(0,   "brightmax",     OPT_FLAG,   
Packit 78deda
            NULL,                  &colorvalue, 0);
Packit 78deda
    OPTENT3(0,   "verbose",       OPT_FLAG,   
Packit 78deda
            NULL,                  &cmdlineP->verbose, 0);
Packit 78deda
Packit 78deda
    /* Note: -brightmax was documented and accepted long before it was
Packit 78deda
       actually implemented.  By the time we implemented it, we
Packit 78deda
       decided -colorvalue was a better name for it.
Packit 78deda
    */
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 have no 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 *cmdline_p and others. */
Packit 78deda
Packit 78deda
    if (!cmdlineP->wpercentSpec)
Packit 78deda
        cmdlineP->wpercent = 1.0;
Packit 78deda
    if (!cmdlineP->bpercentSpec)
Packit 78deda
        cmdlineP->bpercent = 2.0;
Packit 78deda
Packit 78deda
    if (cmdlineP->wpercent < 0.0)
Packit 78deda
        pm_error("You specified a negative value for wpercent: %f",
Packit 78deda
                 cmdlineP->wpercent);
Packit 78deda
    if (cmdlineP->bpercent < 0.0)
Packit 78deda
        pm_error("You specified a negative value for bpercent: %f",
Packit 78deda
                 cmdlineP->bpercent);
Packit 78deda
    if (cmdlineP->wpercent > 100.0)
Packit 78deda
        pm_error("You specified a per centage > 100 for wpercent: %f",
Packit 78deda
                 cmdlineP->wpercent);
Packit 78deda
    if (cmdlineP->bpercent > 100.0)
Packit 78deda
        pm_error("You specified a per centage > 100 for bpercent: %f",
Packit 78deda
                 cmdlineP->bpercent);
Packit 78deda
Packit 78deda
    if (cmdlineP->bsingle && (cmdlineP->bpercentSpec || cmdlineP->bvalueSpec))
Packit 78deda
        pm_error("You cannot specify both -bsingle and -bpercent or -bvalue");
Packit 78deda
Packit 78deda
    if (cmdlineP->wsingle && (cmdlineP->wpercentSpec || cmdlineP->wvalueSpec))
Packit 78deda
        pm_error("You cannot specify both -wsingle and -wpercent or -wvalue");
Packit 78deda
Packit 78deda
    if (middleSpec) {
Packit 78deda
        if (cmdlineP->middle < 0.0 || cmdlineP->middle > 1.0)
Packit 78deda
            pm_error("-middle is a normalized brightness value; it "
Packit 78deda
                     "must be in the range 0..1.  You specified %f",
Packit 78deda
                     cmdlineP->middle);
Packit 78deda
    } else
Packit 78deda
        cmdlineP->middle = 0.5;
Packit 78deda
Packit 78deda
    if (luminosity + colorvalue + saturation > 1)
Packit 78deda
        pm_error("You can specify only one of "
Packit 78deda
                 "-luminosity, -colorvalue, and -saturation");
Packit 78deda
    else {
Packit 78deda
        if (colorvalue)
Packit 78deda
            cmdlineP->brightMethod = BRIGHT_COLORVALUE;
Packit 78deda
        else if (saturation)
Packit 78deda
            cmdlineP->brightMethod = BRIGHT_SATURATION;
Packit 78deda
        else
Packit 78deda
            cmdlineP->brightMethod = BRIGHT_LUMINOSITY;
Packit 78deda
    }
Packit 78deda
    if (maxexpandSpec) {
Packit 78deda
        if (maxexpand < 0)
Packit 78deda
            pm_error("-maxexpand must be positive.  You specified %f",
Packit 78deda
                     maxexpand);
Packit 78deda
        else
Packit 78deda
            cmdlineP->maxExpansion = 1 + (float)maxexpand/100;
Packit 78deda
    } else
Packit 78deda
        cmdlineP->maxExpansion = 1e6;  /* essentially infinite */
Packit 78deda
Packit 78deda
    if (argc-1 > 1)
Packit 78deda
        pm_error("Program takes at most one argument: the input file "
Packit 78deda
                 "specification.  "
Packit 78deda
                 "You specified %d arguments.", argc-1);
Packit 78deda
    if (argc-1 < 1)
Packit 78deda
        cmdlineP->inputFileName = "-";
Packit 78deda
    else
Packit 78deda
        cmdlineP->inputFileName = argv[1];
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
buildHistogram(FILE *   const ifp, 
Packit 78deda
               int      const cols,
Packit 78deda
               int      const rows,
Packit 78deda
               xelval   const maxval,
Packit 78deda
               int      const format,
Packit 78deda
               unsigned int   hist[],
Packit 78deda
               enum brightMethod const brightMethod) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Build the histogram of brightness values for the image that is in file
Packit 78deda
   'ifp', which is positioned just after the header (at the raster).
Packit 78deda
Packit 78deda
   The histogram is the array hist[] such that hist[x] is the number
Packit 78deda
   of xels in the image that have brightness x.  That brightness is
Packit 78deda
   either the color value (intensity of most intense component) of the
Packit 78deda
   xel or it is the luminosity of the xel, depending on
Packit 78deda
   'brightMethod'.  In either case, it is based on the same maxval as
Packit 78deda
   the image, which is 'maxval'.  The image is 'cols' columns wide by
Packit 78deda
   'rows' rows high.
Packit 78deda
Packit 78deda
   Leave the file positioned arbitrarily.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    int row;
Packit 78deda
    xel * xelrow;
Packit 78deda
    
Packit 78deda
    xelrow = pnm_allocrow(cols);
Packit 78deda
Packit 78deda
    {
Packit 78deda
        unsigned int i;
Packit 78deda
        for (i = 0; i <= maxval; ++i)
Packit 78deda
            hist[i] = 0;
Packit 78deda
    }
Packit 78deda
    for (row = 0; row < rows; ++row) {
Packit 78deda
        int col;
Packit 78deda
        pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
Packit 78deda
        for (col = 0; col < cols; ++col) {
Packit 78deda
            xelval brightness;
Packit 78deda
            xel const p = xelrow[col];
Packit 78deda
            if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
Packit 78deda
                switch(brightMethod) {
Packit 78deda
                case BRIGHT_LUMINOSITY:
Packit 78deda
                    brightness = ppm_luminosity(p);
Packit 78deda
                    break;
Packit 78deda
                case BRIGHT_COLORVALUE:
Packit 78deda
                    brightness = ppm_colorvalue(p);
Packit 78deda
                    break;
Packit 78deda
                case BRIGHT_SATURATION:
Packit 78deda
                    brightness = ppm_saturation(p, maxval);
Packit 78deda
                    break;
Packit 78deda
                }
Packit 78deda
            } else
Packit 78deda
                brightness = PNM_GET1(p);
Packit 78deda
            ++hist[brightness];
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    pnm_freerow(xelrow);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static xelval
Packit 78deda
minimumValue(const unsigned int * const hist,
Packit 78deda
             unsigned int         const highest) {
Packit 78deda
Packit 78deda
    xelval i;
Packit 78deda
    bool foundOne;
Packit 78deda
Packit 78deda
    for (i = 0, foundOne = false; !foundOne; ) {
Packit 78deda
        if (hist[i] > 0)
Packit 78deda
            foundOne = true;
Packit 78deda
        else {
Packit 78deda
            if (i == highest)
Packit 78deda
                pm_error("INTERNAL ERROR in '%s'.  No pixels", __FUNCTION__);
Packit 78deda
            else
Packit 78deda
                ++i;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    return i;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static xelval
Packit 78deda
maximumValue(const unsigned int * const hist,
Packit 78deda
             unsigned int         const highest) {
Packit 78deda
Packit 78deda
    xelval i;
Packit 78deda
    bool foundOne;
Packit 78deda
Packit 78deda
    for (i = highest, foundOne = false; !foundOne; ) {
Packit 78deda
        if (hist[i] > 0)
Packit 78deda
            foundOne = true;
Packit 78deda
        else {
Packit 78deda
            if (i == 0)
Packit 78deda
                pm_error("INTERNAL ERROR in '%s'.  No pixels", __FUNCTION__);
Packit 78deda
            else
Packit 78deda
                --i;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    return i;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computeBottomPercentile(unsigned int         hist[], 
Packit 78deda
                        unsigned int   const highest,
Packit 78deda
                        unsigned int   const total,
Packit 78deda
                        float          const percent, 
Packit 78deda
                        unsigned int * const percentileP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Compute the lowest index of hist[] such that the sum of the hist[]
Packit 78deda
   values with that index and lower represent at least 'percent' per cent of
Packit 78deda
   'total' (which is assumed to be the sum of all the values in hist[],
Packit 78deda
   given to us to save us the time of computing it).
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int cutoff = total * percent / 100.0;
Packit 78deda
    unsigned int count;
Packit 78deda
    unsigned int percentile;
Packit 78deda
Packit 78deda
    percentile = 0; /* initial value */
Packit 78deda
    count = hist[0];  /* initial value */
Packit 78deda
Packit 78deda
    while (count < cutoff) {
Packit 78deda
        if (percentile == highest)
Packit 78deda
            pm_error("Internal error: computeBottomPercentile() received"
Packit 78deda
                     "a 'total' value greater than the sum of the hist[]"
Packit 78deda
                     "values");
Packit 78deda
        ++percentile;
Packit 78deda
        count += hist[percentile];
Packit 78deda
    }        
Packit 78deda
    *percentileP = percentile;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computeTopPercentile(unsigned int         hist[], 
Packit 78deda
                     unsigned int   const highest, 
Packit 78deda
                     unsigned int   const total,
Packit 78deda
                     float          const percent, 
Packit 78deda
                     unsigned int * const percentileP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Compute the highest index of hist[] such that the sum of the hist[]
Packit 78deda
   values with that index and higher represent 'percent' per cent of
Packit 78deda
   'total' (which is assumed to be the sum of all the values in hist[],
Packit 78deda
   given to us to save us the time of computing it).
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int cutoff = total * percent / 100.0;
Packit 78deda
    unsigned int count;
Packit 78deda
    unsigned int percentile;
Packit 78deda
Packit 78deda
    percentile = highest; /* initial value */
Packit 78deda
    count = hist[highest];
Packit 78deda
Packit 78deda
    while (count < cutoff) {
Packit 78deda
        --percentile;
Packit 78deda
        count += hist[percentile];
Packit 78deda
    }
Packit 78deda
    *percentileP = percentile;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computeAdjustmentForExpansionLimit(xelval   const maxval,
Packit 78deda
                                   xelval   const unlBvalue,
Packit 78deda
                                   xelval   const unlWvalue,
Packit 78deda
                                   float    const maxExpansion,
Packit 78deda
                                   xelval * const bLowerP,
Packit 78deda
                                   xelval * const wRaiseP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Assuming 'unlBvalue' and 'unlWvalue' are the appropriate bvalue and
Packit 78deda
   wvalue to normalize the image to 0 .. maxval, compute the amount
Packit 78deda
   by which the bvalue must be raised and the wvalue lowered from that
Packit 78deda
   in order to cap the expansion factor at 'maxExpansion'.
Packit 78deda
Packit 78deda
   E.g. if 'maxval' is 100, 'unlBvalue' is 20 and 'unlWvalue' is 70, that
Packit 78deda
   implies an expansion factor of 100/50 (because the range goes from
Packit 78deda
   70-20, which is 50, to 100 - 0, which is 100).  If 'maxEpansion' is
Packit 78deda
   1.333, these values are unacceptable.  To get down to the desired 1.333
Packit 78deda
   factor, we need the span of bvalue to wvalue to be 75, not 50.  So
Packit 78deda
   we need to raise the bvalue and lower the wvalue by a total of 25.
Packit 78deda
   We apportion that adjustment to bvalue and wvalue in proportion to
Packit 78deda
   how close each is already to it's end (which we call the margin).
Packit 78deda
   'unlBvalue' is 20 from its end, while 'unlWvalue' is 30 from its end,
Packit 78deda
   so we want to lower the bvalue by 10 and raise the wvalue by 15.
Packit 78deda
   Ergo we return *bLowerP = 10 and *wRaise = 15.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int const newRange = maxval - 0;
Packit 78deda
        /* The range of sample values after normalization, if we used
Packit 78deda
           the unlimited bvalue and wvalue
Packit 78deda
        */
Packit 78deda
    unsigned int const oldRange = unlWvalue - unlBvalue;
Packit 78deda
        /* The range of sample values in the original image that normalize
Packit 78deda
           to 0 .. maxval, if we used the unlimited bvalue and wvalue
Packit 78deda
        */
Packit 78deda
    float const unlExpansion = (float)newRange/oldRange;
Packit 78deda
    
Packit 78deda
    if (unlExpansion <= maxExpansion) {
Packit 78deda
        /* No capping is necessary.  Unlimited values are already within
Packit 78deda
           range.
Packit 78deda
           */
Packit 78deda
        *bLowerP = 0;
Packit 78deda
        *wRaiseP = 0;
Packit 78deda
    } else {
Packit 78deda
        unsigned int const totalWidening = newRange/maxExpansion - oldRange;
Packit 78deda
            /* Amount by which the (bvalue, wvalue) range must be widened
Packit 78deda
               to limit expansion to 'maxExpansion'
Packit 78deda
            */
Packit 78deda
        unsigned int const bMargin = unlBvalue - 0;
Packit 78deda
        unsigned int const wMargin = maxval - unlWvalue;
Packit 78deda
Packit 78deda
        /* Apportion 'totalWidening' between the black and and the
Packit 78deda
           white end
Packit 78deda
        */
Packit 78deda
        *bLowerP =
Packit 78deda
            ROUNDU((float)bMargin / (bMargin + wMargin) * totalWidening);
Packit 78deda
        *wRaiseP =
Packit 78deda
            ROUNDU((float)wMargin / (bMargin + wMargin) * totalWidening);
Packit 78deda
Packit 78deda
        pm_message("limiting expansion of %.1f%% to %.1f%%",
Packit 78deda
                   (unlExpansion - 1) * 100, (maxExpansion -1) * 100);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
disOverlap(xelval   const reqBvalue,
Packit 78deda
           xelval   const reqWvalue,
Packit 78deda
           bool     const bIsFixed,
Packit 78deda
           bool     const wIsFixed,
Packit 78deda
           xelval   const maxval,
Packit 78deda
           xelval * const nonOlapBvalueP,
Packit 78deda
           xelval * const nonOlapWvalueP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Compute black and white end values that don't overlap, i.e. the
Packit 78deda
   black value is darker than the white, from an initial attempt that
Packit 78deda
   might overlap.
Packit 78deda
Packit 78deda
   'req{B|W}value' is that initial attempt.  We return the
Packit 78deda
   nonoverlapping version as *nonOlap{B|W}valueP.
Packit 78deda
Packit 78deda
   '{b|w}IsFixed' means we cannot change that endpoint.
Packit 78deda
Packit 78deda
   If both ends are fixed 'reqBvalue' and 'reqWvalue' overlap, we just
Packit 78deda
   fail the program -- the user asked for the impossible.
Packit 78deda
Packit 78deda
   Where one end is fixed and the other is not, we move the unfixed end
Packit 78deda
   to be one unit above or below the fixed end, as appropriate.
Packit 78deda
Packit 78deda
   Where both ends are free, we move them to the point halfway between them,
Packit 78deda
   the white end being one more than the black end.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    assert(maxval > 0);
Packit 78deda
Packit 78deda
    if (reqBvalue < reqWvalue) {
Packit 78deda
        /* No overlap; initial attempt is fine. */
Packit 78deda
        *nonOlapBvalueP = reqBvalue;
Packit 78deda
        *nonOlapWvalueP = reqWvalue;
Packit 78deda
    } else {
Packit 78deda
        if (bIsFixed && wIsFixed)
Packit 78deda
            pm_error("The colors which become black (value <= %u) "
Packit 78deda
                     "would overlap the "
Packit 78deda
                     "colors which become white (value >= %u).",
Packit 78deda
                     reqBvalue, reqWvalue);
Packit 78deda
        else if (bIsFixed) {
Packit 78deda
            if (reqBvalue >= maxval)
Packit 78deda
                pm_error("The black value must be less than the maxval");
Packit 78deda
            else {
Packit 78deda
                *nonOlapBvalueP = reqBvalue;
Packit 78deda
                *nonOlapWvalueP = reqBvalue + 1;
Packit 78deda
            }
Packit 78deda
        } else if (wIsFixed) {
Packit 78deda
            if (reqWvalue == 0)
Packit 78deda
                pm_error("The white value must be greater than 0");
Packit 78deda
            else {
Packit 78deda
                *nonOlapBvalueP = reqWvalue - 1;
Packit 78deda
                *nonOlapWvalueP = reqWvalue;
Packit 78deda
            }
Packit 78deda
        } else {
Packit 78deda
            /* Both ends are free; use the point halfway between them. */
Packit 78deda
            xelval const midPoint = (reqWvalue + reqBvalue + maxval/2)/2;
Packit 78deda
            *nonOlapBvalueP = MIN(midPoint, maxval-1);
Packit 78deda
            *nonOlapWvalueP = *nonOlapBvalueP + 1;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
resolvePercentParams(FILE *             const ifP,
Packit 78deda
                     unsigned int       const cols,
Packit 78deda
                     unsigned int       const rows,
Packit 78deda
                     xelval             const maxval,
Packit 78deda
                     int                const format,
Packit 78deda
                     struct cmdlineInfo const cmdline,
Packit 78deda
                     xelval *           const bvalueP,
Packit 78deda
                     xelval *           const wvalueP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Figure out the endpoint of the stretch (the value that is to be stretched
Packit 78deda
   to black and the one that is to be stretched to white) as requested
Packit 78deda
   by the -{b,w}{value,percent,single} options.
Packit 78deda
Packit 78deda
   These values may be invalid because of overlapping, and they may exceed
Packit 78deda
   the maximum allowed stretch; Caller must deal with that.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int * hist;  /* malloc'ed */
Packit 78deda
Packit 78deda
    MALLOCARRAY(hist, PNM_OVERALLMAXVAL+1);
Packit 78deda
Packit 78deda
    if (hist == NULL)
Packit 78deda
        pm_error("Unable to allocate storage for intensity histogram.");
Packit 78deda
    else {
Packit 78deda
        buildHistogram(ifP, cols, rows, maxval, format, hist,
Packit 78deda
                       cmdline.brightMethod);
Packit 78deda
Packit 78deda
        if (cmdline.bsingle)
Packit 78deda
            *bvalueP = minimumValue(hist, maxval);
Packit 78deda
        else if (cmdline.bvalueSpec && !cmdline.bpercentSpec) {
Packit 78deda
            *bvalueP = cmdline.bvalue;
Packit 78deda
        } else {
Packit 78deda
            xelval percentBvalue;
Packit 78deda
            computeBottomPercentile(hist, maxval, cols*rows, cmdline.bpercent, 
Packit 78deda
                                    &percentBvalue);
Packit 78deda
            if (cmdline.bvalueSpec)
Packit 78deda
                *bvalueP = MIN(percentBvalue, cmdline.bvalue);
Packit 78deda
            else
Packit 78deda
                *bvalueP = percentBvalue;
Packit 78deda
        }
Packit 78deda
Packit 78deda
        if (cmdline.wsingle)
Packit 78deda
            *wvalueP = maximumValue(hist, maxval);
Packit 78deda
        else if (cmdline.wvalueSpec && !cmdline.wpercentSpec) {
Packit 78deda
            *wvalueP = cmdline.wvalue;
Packit 78deda
        } else {
Packit 78deda
            xelval percentWvalue;
Packit 78deda
            computeTopPercentile(hist, maxval, cols*rows, cmdline.wpercent, 
Packit 78deda
                                 &percentWvalue);
Packit 78deda
            if (cmdline.wvalueSpec)
Packit 78deda
                *wvalueP = MAX(percentWvalue, cmdline.wvalue);
Packit 78deda
            else
Packit 78deda
                *wvalueP = percentWvalue;
Packit 78deda
        }
Packit 78deda
        free(hist);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computeEndValues(FILE *             const ifP,
Packit 78deda
                 int                const cols,
Packit 78deda
                 int                const rows,
Packit 78deda
                 xelval             const maxval,
Packit 78deda
                 int                const format,
Packit 78deda
                 struct cmdlineInfo const cmdline,
Packit 78deda
                 xelval *           const bvalueP,
Packit 78deda
                 xelval *           const wvalueP,
Packit 78deda
                 bool *             const quadraticP,
Packit 78deda
                 xelval *           const midvalueP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Figure out what original values will be translated to full bright and full
Packit 78deda
   dark and, if user requested, a middle brightness -- thus defining to what
Packit 78deda
   all the other values get translated.
Packit 78deda
Packit 78deda
   This may involve looking at the image.  The image is in the file 'ifP',
Packit 78deda
   which is positioned just past the header (at the raster).  Leave it
Packit 78deda
   positioned arbitrarily.
Packit 78deda
Packit 78deda
   We return *quadraticP == true iff the normalization is to be via a
Packit 78deda
   quadratic transfer function fixed at 3 points - full bright, full dark, and
Packit 78deda
   something in between.  In that case, the original brightnesses of those
Packit 78deda
   three points are *bvalueP, *midvalueP, and *wvalueP.  We return *quadraticP
Packit 78deda
   == false iff the normalization is to be via a linear function fixed at 2
Packit 78deda
   points - full bright and full dark.  In that case, *midvalueP is
Packit 78deda
   meaningless.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    xelval reqBvalue, reqWvalue, nonOlapBvalue, nonOlapWvalue;
Packit 78deda
    unsigned int bLower, wRaise;
Packit 78deda
Packit 78deda
    resolvePercentParams(ifP, cols, rows, maxval, format, cmdline,
Packit 78deda
                         &reqBvalue, &reqWvalue);
Packit 78deda
Packit 78deda
    disOverlap(reqBvalue, reqWvalue,
Packit 78deda
               cmdline.bvalueSpec, cmdline.wvalueSpec, maxval,
Packit 78deda
               &nonOlapBvalue, &nonOlapWvalue);
Packit 78deda
Packit 78deda
    computeAdjustmentForExpansionLimit(
Packit 78deda
        maxval, nonOlapBvalue, nonOlapWvalue, cmdline.maxExpansion,
Packit 78deda
        &bLower, &wRaise);
Packit 78deda
Packit 78deda
    *bvalueP = nonOlapBvalue - bLower;
Packit 78deda
    *wvalueP = nonOlapWvalue + wRaise;
Packit 78deda
Packit 78deda
    if (cmdline.midvalueSpec) {
Packit 78deda
        if (cmdline.midvalue > *bvalueP && cmdline.midvalue < *wvalueP) {
Packit 78deda
            *quadraticP = true;
Packit 78deda
            *midvalueP = cmdline.midvalue;
Packit 78deda
        } else
Packit 78deda
            *quadraticP = false;
Packit 78deda
    } else
Packit 78deda
        *quadraticP = false;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computeLinearTransfer(xelval   const bvalue,
Packit 78deda
                      xelval   const wvalue,
Packit 78deda
                      xelval   const maxval,
Packit 78deda
                      xelval * const newBrightness) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Map the middle brightnesses (the ones that don't get clipped to full dark
Packit 78deda
   or full bright, i.e. from 'bvalue' to 'wvalue') linearly onto 0..maxval.
Packit 78deda
   Set this mapping in newBrightness[].
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int const range = wvalue - bvalue;
Packit 78deda
Packit 78deda
    xelval i;
Packit 78deda
    unsigned int val;
Packit 78deda
    /* The following for structure is a hand optimization of this one:
Packit 78deda
       for (i = bvalue; i <= wvalue; ++i)
Packit 78deda
       newBrightness[i] = (i-bvalue)*maxval/range);
Packit 78deda
       (with proper rounding)
Packit 78deda
    */
Packit 78deda
    for (i = bvalue, val = range/2; 
Packit 78deda
         i <= wvalue; 
Packit 78deda
         ++i, val += maxval)
Packit 78deda
        newBrightness[i] = MIN(val / range, maxval);
Packit 78deda
Packit 78deda
    assert(newBrightness[bvalue] == 0);
Packit 78deda
    assert(newBrightness[wvalue] == maxval);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
typedef struct {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   A quadratic polynomial function
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    double a;  /* x^2 coefficient */
Packit 78deda
    double b;  /* x^1 coefficient */
Packit 78deda
    double c;  /* x^0 coefficient */
Packit 78deda
} Quadfn;
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computeQuadraticFunction(xelval   const bvalue,
Packit 78deda
                         xelval   const midvalue,
Packit 78deda
                         xelval   const wvalue,
Packit 78deda
                         xelval   const middle,
Packit 78deda
                         xelval   const maxval,
Packit 78deda
                         Quadfn * const functionP) {
Packit 78deda
Packit 78deda
    /* The matrix equation we solve (for varMatrix) is
Packit 78deda
Packit 78deda
       a * x = c
Packit 78deda
    */
Packit 78deda
    double ** a;
Packit 78deda
    double c[3];
Packit 78deda
    double x[3];
Packit 78deda
    const char * error;
Packit 78deda
Packit 78deda
    MALLOCARRAY2_NOFAIL(a, 3, 3);
Packit 78deda
Packit 78deda
    a[0][0] = SQR(bvalue);   a[0][1] = bvalue;   a[0][2] = 1.0;
Packit 78deda
    a[1][0] = SQR(midvalue); a[1][1] = midvalue; a[1][2] = 1.0;
Packit 78deda
    a[2][0] = SQR(wvalue);   a[2][1] = wvalue;   a[2][2] = 1.0;
Packit 78deda
        
Packit 78deda
    c[0] = 0.0;
Packit 78deda
    c[1] = middle;
Packit 78deda
    c[2] = maxval;
Packit 78deda
    
Packit 78deda
    pm_solvelineareq(a, x, c, 3, &error);
Packit 78deda
Packit 78deda
    if (error) {
Packit 78deda
        pm_error("Cannot fit a quadratic function to the points "
Packit 78deda
                 "(%u, %u), (%u, %u), and (%u, %u).  %s",
Packit 78deda
                 bvalue, 0, midvalue, middle, wvalue, maxval, error);
Packit 78deda
        pm_strfree(error);
Packit 78deda
    } else {
Packit 78deda
        functionP->a = x[0];
Packit 78deda
        functionP->b = x[1];
Packit 78deda
        functionP->c = x[2];
Packit 78deda
    }
Packit 78deda
Packit 78deda
    pm_freearray2((void **)a);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computeQuadraticTransfer(xelval   const bvalue,
Packit 78deda
                         xelval   const midvalue,
Packit 78deda
                         xelval   const wvalue,
Packit 78deda
                         float    const middleNorm,
Packit 78deda
                         xelval   const maxval,
Packit 78deda
                         bool     const verbose,
Packit 78deda
                         xelval * const newBrightness) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Map the middle brightnesses (the ones that don't get clipped to full dark
Packit 78deda
   or full bright, i.e. from 'bvalue' to 'wvalue') quadratically onto
Packit 78deda
   0..maxval, such that 'bvalue' maps to 0, 'wvalue' maps to 'maxval, and
Packit 78deda
   'midvalue' maps to the normalized value 'middleNorm' (i.e. the actual
Packit 78deda
   xelval middleNorm * maxval).
Packit 78deda
Packit 78deda
   Set this mapping in newBrightness[].
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    xelval const middle = ROUNDU(middleNorm * maxval);
Packit 78deda
Packit 78deda
    /* Computing this function is just the task of finding a parabola that
Packit 78deda
       passes through 3 given points:
Packit 78deda
        
Packit 78deda
           (bvalue, 0)
Packit 78deda
           (midvalue, middle)
Packit 78deda
           (wvalue, maxval)
Packit 78deda
Packit 78deda
       We do that by solving the system of three linear equations in
Packit 78deda
       in 3 variables.  The 3 variables are the coefficients of the
Packit 78deda
       quadratic function we're looking for -- A, B, and C in this:
Packit 78deda
Packit 78deda
           NEWVAL = A * OLDVAL^2 + B * OLDVAL + C
Packit 78deda
Packit 78deda
       The three equations of the system are:
Packit 78deda
Packit 78deda
           0      = A * bvalue^2   + B * bvalue   + C
Packit 78deda
           middle = A * midvalue^2 + B * midvalue + C
Packit 78deda
           maxval = A * wvalue^2   + B * wvalue   + C
Packit 78deda
Packit 78deda
       Expressed in matrix form:
Packit 78deda
Packit 78deda
          [ bvalue^2   bvalue   1 ]   [ A ]   [ 0      ]
Packit 78deda
          [ midvalue^2 midvalue 1 ] * [ B ] = [ middle ]
Packit 78deda
          [ wvalue^2   wvalue   1 ]   [ C ]   [ maxval ]
Packit 78deda
Packit 78deda
       So we solve that for A, B, and C.
Packit 78deda
Packit 78deda
       With those coefficients, we have the quadratic function, and we
Packit 78deda
       simple apply it to every old sample value I in the range to get the
Packit 78deda
       new:
Packit 78deda
Packit 78deda
           newBrightness[I] = A * I^2 + B * I + C
Packit 78deda
    */
Packit 78deda
Packit 78deda
    Quadfn xfer;
Packit 78deda
Packit 78deda
    computeQuadraticFunction(bvalue, midvalue, wvalue, middle, maxval,
Packit 78deda
                             &xfer);
Packit 78deda
Packit 78deda
Packit 78deda
    if (verbose)
Packit 78deda
        pm_message("Transfer function is %f * s^2 + %f * s + %f",
Packit 78deda
                   xfer.a, xfer.b, xfer.c);
Packit 78deda
Packit 78deda
    {
Packit 78deda
        xelval i;
Packit 78deda
        for (i = bvalue; i <= wvalue; ++i)
Packit 78deda
            newBrightness[i] =
Packit 78deda
                MIN(ROUNDU(xfer.a * SQR(i) + xfer.b * i + xfer.c), maxval);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computeTransferFunction(bool      const quadratic,
Packit 78deda
                        xelval    const bvalue, 
Packit 78deda
                        xelval    const midvalue, 
Packit 78deda
                        xelval    const wvalue,
Packit 78deda
                        float     const middle,
Packit 78deda
                        xelval    const maxval,
Packit 78deda
                        bool      const verbose,
Packit 78deda
                        xelval ** const newBrightnessP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Compute the transfer function, i.e. the array *newBrightnessP such that
Packit 78deda
   (*newBrightnessP)[x] is the brightness of the xel that should replace a
Packit 78deda
   xel with brightness x.  Brightness in this case means either luminosity
Packit 78deda
   or color value (and it doesn't matter to us which).
Packit 78deda
Packit 78deda
   'bvalue' is the highest brightness that should map to zero brightness;
Packit 78deda
   'wvalue' is the lowest brightness that should map to full brightness.
Packit 78deda
Packit 78deda
   If 'quadratic' is false, brightnesses in between should be stretched
Packit 78deda
   linearly.  Otherwise, brightness 'midvalue' should map to brightness
Packit 78deda
   'middle' (which is expressed on a 0..1 normalized scale) and brightnesses
Packit 78deda
   should be stretched according to a quadratic polynomial that includes those
Packit 78deda
   3 points.
Packit 78deda
Packit 78deda
   This stretching could conceivably result in more brightnesses mapping to
Packit 78deda
   zero and full brightness that 'bvalue' and 'wvalue' demand, because of
Packit 78deda
   rounding.
Packit 78deda
Packit 78deda
   Define function only for values 0..maxval.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    xelval * newBrightness;
Packit 78deda
    xelval i;
Packit 78deda
Packit 78deda
    MALLOCARRAY(newBrightness, maxval+1);
Packit 78deda
Packit 78deda
    if (newBrightness == NULL)
Packit 78deda
        pm_error("Unable to allocate memory for transfer function.");
Packit 78deda
Packit 78deda
    /* Clip the lowest brightnesses to zero */
Packit 78deda
    if (bvalue > 0) 
Packit 78deda
        for (i = 0; i < bvalue; ++i)
Packit 78deda
            newBrightness[i] = 0;
Packit 78deda
Packit 78deda
    /* Map the middle brightnesses onto 0..maxval */
Packit 78deda
    
Packit 78deda
    if (quadratic)
Packit 78deda
        computeQuadraticTransfer(bvalue, midvalue, wvalue, middle, maxval,
Packit 78deda
                                 verbose, newBrightness);
Packit 78deda
    else
Packit 78deda
        computeLinearTransfer(bvalue, wvalue, maxval, newBrightness);
Packit 78deda
Packit 78deda
    /* Clip the highest brightnesses to maxval */
Packit 78deda
    for (i = wvalue+1; i <= maxval; ++i)
Packit 78deda
        newBrightness[i] = maxval;
Packit 78deda
Packit 78deda
    *newBrightnessP = newBrightness;
Packit 78deda
}
Packit 78deda
            
Packit 78deda
Packit 78deda
Packit 78deda
static float
Packit 78deda
brightScaler(xel               const p,
Packit 78deda
             pixval            const maxval,
Packit 78deda
             xelval            const newBrightness[],
Packit 78deda
             enum brightMethod const brightMethod) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Return the multiple by which the brightness pixel of color 'p' (based
Packit 78deda
  on maxval 'maxval') should be changed according to the transfer
Packit 78deda
  function newBrightness[], using the 'brightMethod' measure of
Packit 78deda
  brightness.
Packit 78deda
Packit 78deda
  For example, if 'brightMethod' is BRIGHT_LUMINOSITY, p is has
Packit 78deda
  luminosity 50, and newBrightness[50] is 75, we would return 1.5.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    xelval oldBrightness;
Packit 78deda
    float scaler;
Packit 78deda
             
Packit 78deda
    switch (brightMethod) {
Packit 78deda
    case BRIGHT_LUMINOSITY:
Packit 78deda
        oldBrightness = ppm_luminosity(p);
Packit 78deda
        break;
Packit 78deda
    case BRIGHT_COLORVALUE:
Packit 78deda
        oldBrightness = ppm_colorvalue(p);
Packit 78deda
        break;
Packit 78deda
    case BRIGHT_SATURATION:
Packit 78deda
        oldBrightness = ppm_saturation(p, maxval);
Packit 78deda
        break;
Packit 78deda
    }
Packit 78deda
    if (oldBrightness == 0) {
Packit 78deda
        assert(newBrightness[oldBrightness] == 0);
Packit 78deda
        /* Doesn't matter what we scale by.  zero times anything is zero. */
Packit 78deda
        scaler = 1.0;
Packit 78deda
    } else
Packit 78deda
        scaler = (float)newBrightness[oldBrightness]/oldBrightness;
Packit 78deda
Packit 78deda
    return scaler;
Packit 78deda
}
Packit 78deda
            
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeRowNormalized(xel *             const xelrow,
Packit 78deda
                   int               const cols,
Packit 78deda
                   xelval            const maxval,
Packit 78deda
                   int               const format,
Packit 78deda
                   enum brightMethod const brightMethod,
Packit 78deda
                   bool              const keephues,
Packit 78deda
                   xelval            const newBrightness[],
Packit 78deda
                   xel *             const rowbuf) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Write to Standard Output a normalized version of the xel row 
Packit 78deda
   'xelrow'.  Normalize it via the transfer function newBrightness[].
Packit 78deda
Packit 78deda
   Use 'rowbuf' as a work buffer.  It is at least 'cols' columns wide.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    xel * const outrow = rowbuf;
Packit 78deda
                
Packit 78deda
    unsigned int col;
Packit 78deda
    for (col = 0; col < cols; ++col) {
Packit 78deda
        xel const p = xelrow[col];
Packit 78deda
Packit 78deda
        if (PPM_FORMAT_TYPE(format) == PPM_TYPE) {
Packit 78deda
            if (keephues) {
Packit 78deda
                float const scaler =
Packit 78deda
                    brightScaler(p, maxval, newBrightness, brightMethod);
Packit 78deda
Packit 78deda
                xelval const r = MIN(ROUNDU(PPM_GETR(p)*scaler), maxval);
Packit 78deda
                xelval const g = MIN(ROUNDU(PPM_GETG(p)*scaler), maxval);
Packit 78deda
                xelval const b = MIN(ROUNDU(PPM_GETB(p)*scaler), maxval);
Packit 78deda
                PNM_ASSIGN(outrow[col], r, g, b);
Packit 78deda
            } else 
Packit 78deda
                PNM_ASSIGN(outrow[col], 
Packit 78deda
                           newBrightness[PPM_GETR(p)], 
Packit 78deda
                           newBrightness[PPM_GETG(p)], 
Packit 78deda
                           newBrightness[PPM_GETB(p)]);
Packit 78deda
        } else 
Packit 78deda
            PNM_ASSIGN1(outrow[col], newBrightness[PNM_GET1(p)]);
Packit 78deda
    }
Packit 78deda
    pnm_writepnmrow(stdout, outrow, cols, maxval, format, 0);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
reportTransferParm(bool   const quadratic,
Packit 78deda
                   xelval const bvalue,
Packit 78deda
                   xelval const midvalue,
Packit 78deda
                   xelval const wvalue,
Packit 78deda
                   xelval const maxval,
Packit 78deda
                   float  const middle) {
Packit 78deda
Packit 78deda
    if (quadratic)
Packit 78deda
        pm_message("remapping %u..%u..%u to %u..%u..%u",
Packit 78deda
                   bvalue, midvalue, wvalue,
Packit 78deda
                   0, ROUNDU(maxval*middle), maxval);
Packit 78deda
    else
Packit 78deda
        pm_message("remapping %u..%u to %u..%u",
Packit 78deda
                   bvalue, wvalue, 0, maxval);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int
Packit 78deda
main(int argc, const char *argv[]) {
Packit 78deda
Packit 78deda
    struct cmdlineInfo cmdline;
Packit 78deda
    FILE *ifP;
Packit 78deda
    pm_filepos imagePos;
Packit 78deda
    xelval maxval;
Packit 78deda
    int rows, cols, format;
Packit 78deda
    bool quadratic;
Packit 78deda
    xelval bvalue, midvalue, wvalue;
Packit 78deda
    
Packit 78deda
    pm_proginit(&argc, argv);
Packit 78deda
Packit 78deda
    parseCommandLine(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    ifP = pm_openr_seekable(cmdline.inputFileName);
Packit 78deda
Packit 78deda
    /* Rescale so that bvalue maps to 0, wvalue maps to maxval. */
Packit 78deda
    pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
Packit 78deda
    pm_tell2(ifP, &imagePos, sizeof(imagePos));
Packit 78deda
Packit 78deda
    computeEndValues(ifP, cols, rows, maxval, format, cmdline, 
Packit 78deda
                     &bvalue, &wvalue, &quadratic, &midvalue);
Packit 78deda
    {
Packit 78deda
        xelval * newBrightness;
Packit 78deda
        int row;
Packit 78deda
        xel * xelrow;
Packit 78deda
        xel * rowbuf;
Packit 78deda
        
Packit 78deda
        assert(wvalue > bvalue);
Packit 78deda
Packit 78deda
        xelrow = pnm_allocrow(cols);
Packit 78deda
Packit 78deda
        reportTransferParm(quadratic, bvalue, midvalue, wvalue, maxval,
Packit 78deda
                           cmdline.middle);
Packit 78deda
Packit 78deda
        
Packit 78deda
        computeTransferFunction(quadratic, bvalue, midvalue, wvalue,
Packit 78deda
                                cmdline.middle, maxval, cmdline.verbose,
Packit 78deda
                                &newBrightness);
Packit 78deda
Packit 78deda
        pm_seek2(ifP, &imagePos, sizeof(imagePos));
Packit 78deda
        pnm_writepnminit(stdout, cols, rows, maxval, format, 0);
Packit 78deda
Packit 78deda
        rowbuf = pnm_allocrow(cols);
Packit 78deda
Packit 78deda
        for (row = 0; row < rows; ++row) {
Packit 78deda
            pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
Packit 78deda
            writeRowNormalized(xelrow, cols, maxval, format,
Packit 78deda
                               cmdline.brightMethod, cmdline.keephues,
Packit 78deda
                               newBrightness, rowbuf);
Packit 78deda
        }
Packit 78deda
        free(newBrightness);
Packit 78deda
        pnm_freerow(rowbuf);
Packit 78deda
        pnm_freerow(xelrow);
Packit 78deda
    } 
Packit 78deda
    pm_close(ifP);
Packit 78deda
    return 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda