about summary refs log tree commit diff
path: root/other/pnmcolormap.c
diff options
context:
space:
mode:
Diffstat (limited to 'other/pnmcolormap.c')
-rw-r--r--other/pnmcolormap.c531
1 files changed, 347 insertions, 184 deletions
diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c
index f687f037..7da3122b 100644
--- a/other/pnmcolormap.c
+++ b/other/pnmcolormap.c
@@ -23,6 +23,7 @@
 
 ******************************************************************************/
 
+#include <assert.h>
 #include <math.h>
 
 #include "pm_config.h"
@@ -33,29 +34,42 @@
 #include "pam.h"
 #include "pammap.h"
 
-enum methodForLargest {LARGE_NORM, LARGE_LUM};
+enum MethodForLargest {LARGE_NORM, LARGE_LUM};
 
-enum methodForRep {REP_CENTER_BOX, REP_AVERAGE_COLORS, REP_AVERAGE_PIXELS};
+enum MethodForRep {REP_CENTER_BOX, REP_AVERAGE_COLORS, REP_AVERAGE_PIXELS};
 
-typedef struct box* boxVector;
-struct box {
-    int ind;
-    int colors;
-    int sum;
+enum MethodForSplit {SPLIT_MAX_PIXELS, SPLIT_MAX_COLORS, SPLIT_MAX_SPREAD};
+
+struct Box {
+    unsigned int index;
+    unsigned int colorCt;
+    unsigned int sum;
+    unsigned int maxdim;
+        /* which dimension has the largest spread.  RGB plane number. */
+    sample       spread;
+        /* spread in dimension 'maxdim' */
+};
+
+struct BoxVector {
+    struct Box * box;  /* malloc'ed array */
+    unsigned int boxCt;
+    unsigned int capacity;
 };
 
-struct cmdlineInfo {
+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 */
+    const char * inputFileNm;  /* Name of input file */
     unsigned int allcolors;  /* boolean: select all colors from the input */
     unsigned int newcolors;
         /* Number of colors argument; meaningless if allcolors true */
-    enum methodForLargest methodForLargest;
+    enum MethodForLargest methodForLargest;
         /* -spreadintensity/-spreadluminosity options */
-    enum methodForRep methodForRep;
+    enum MethodForRep methodForRep;
         /* -center/-meancolor/-meanpixel options */
+    enum MethodForSplit methodForSplit;
+        /* -splitpixelct/-splitcolorct/-splitspread options */
     unsigned int sort;
     unsigned int square;
     unsigned int verbose;
@@ -64,8 +78,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine (int argc, char ** argv,
-                  struct cmdlineInfo *cmdlineP) {
+parseCommandLine (int argc, const char ** argv,
+                  struct CmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.
@@ -85,6 +99,7 @@ parseCommandLine (int argc, char ** argv,
 
     unsigned int spreadbrightness, spreadluminosity;
     unsigned int center, meancolor, meanpixel;
+    unsigned int splitpixelct, splitcolorct, splitspread;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -99,6 +114,12 @@ parseCommandLine (int argc, char ** argv,
             NULL,                       &meancolor,        0);
     OPTENT3(0,   "meanpixel",        OPT_FLAG,
             NULL,                       &meanpixel,        0);
+    OPTENT3(0,   "splitpixelct",     OPT_FLAG,
+            NULL,                       &splitpixelct,     0);
+    OPTENT3(0,   "splitcolorct",     OPT_FLAG,
+            NULL,                       &splitcolorct,     0);
+    OPTENT3(0,   "splitspread",      OPT_FLAG,
+            NULL,                       &splitspread,      0);
     OPTENT3(0, "sort",     OPT_FLAG,   NULL,
             &cmdlineP->sort,       0 );
     OPTENT3(0, "square",   OPT_FLAG,   NULL,
@@ -110,7 +131,7 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
 
@@ -132,15 +153,24 @@ parseCommandLine (int argc, char ** argv,
     else
         cmdlineP->methodForRep = REP_CENTER_BOX;
 
+    if (splitpixelct)
+        cmdlineP->methodForSplit = SPLIT_MAX_PIXELS;
+    else if (splitcolorct)
+        cmdlineP->methodForSplit = SPLIT_MAX_COLORS;
+    else if (splitspread)
+        cmdlineP->methodForSplit = SPLIT_MAX_SPREAD;
+    else
+        cmdlineP->methodForSplit = SPLIT_MAX_PIXELS;
+
     if (argc-1 > 2)
         pm_error("Program takes at most two arguments: number of colors "
                  "and input file specification.  "
                  "You specified %d arguments.", argc-1);
     else {
         if (argc-1 < 2)
-            cmdlineP->inputFilespec = "-";
+            cmdlineP->inputFileNm = "-";
         else
-            cmdlineP->inputFilespec = argv[2];
+            cmdlineP->inputFileNm = argv[2];
 
         if (argc-1 < 1)
             pm_error("You must specify the number of colors in the "
@@ -185,8 +215,13 @@ compareplane(const void * const arg1,
     const struct tupleint * const * const comparandPP  = arg1;
     const struct tupleint * const * const comparatorPP = arg2;
 
-    return (*comparandPP)->tuple[compareplanePlane] -
-        (*comparatorPP)->tuple[compareplanePlane];
+    sample const comparandSample  = (*comparandPP) ->tuple[compareplanePlane];
+    sample const comparatorSample = (*comparatorPP)->tuple[compareplanePlane];
+
+    return
+        comparandSample < comparatorSample ? -1 :
+        comparandSample > comparatorSample ? 1 :
+        0;
 }
 
 
@@ -196,61 +231,82 @@ static qsort_comparison_fn sumcompare;
 #endif
 
 static int
-sumcompare(const void * const b1, const void * const b2) {
-    return(((boxVector)b2)->sum - ((boxVector)b1)->sum);
+sumcompare(const void * const arg1,
+           const void * const arg2) {
+
+    struct Box * const comparandP  = (struct Box *)arg1;
+    struct Box * const comparatorP = (struct Box *)arg2;
+
+    return
+        comparatorP->sum < comparandP->sum ? -1 :
+        comparatorP->sum > comparandP->sum ? 1 :
+        0;
 }
 
 
 
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn colcompare;
+#endif
 
-/*
-** Here is the fun part, the median-cut colormap generator.  This is based
-** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
-** Display", SIGGRAPH '82 Proceedings, page 297.
-*/
+static int
+colcompare(const void * const arg1,
+           const void * const arg2) {
 
-static tupletable2
-newColorMap(unsigned int const newcolors,
-            unsigned int const depth) {
+    struct Box * const comparandP  = (struct Box *)arg1;
+    struct Box * const comparatorP = (struct Box *)arg2;
 
-    tupletable2 colormap;
-    unsigned int i;
-    struct pam pam;
+    return
+        comparatorP->colorCt < comparandP->colorCt ? -1 :
+        comparatorP->colorCt > comparandP->colorCt ? 1 :
+        0;
+}
 
-    pam.depth = depth;
 
-    colormap.table = pnm_alloctupletable(&pam, newcolors);
 
-    for (i = 0; i < newcolors; ++i) {
-        unsigned int plane;
-        for (plane = 0; plane < depth; ++plane)
-            colormap.table[i]->tuple[plane] = 0;
-    }
-    colormap.size = newcolors;
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn spreadcompare;
+#endif
 
-    return colormap;
+static int
+spreadcompare(const void * const arg1,
+              const void * const arg2) {
+
+    struct Box * const comparandP  = (struct Box *)arg1;
+    struct Box * const comparatorP = (struct Box *)arg2;
+
+    return
+        comparatorP->spread < comparandP->spread ? -1 :
+        comparatorP->spread > comparandP->spread ? 1 :
+        0;
 }
 
 
 
-static boxVector
-newBoxVector(int const colors, int const sum, int const newcolors) {
+static void
+sortBoxes(struct BoxVector *  const boxVectorP,
+          enum MethodForSplit const methodForSplit) {
 
-    boxVector bv;
-    MALLOCARRAY(bv, newcolors);
-    if (bv == NULL)
-        pm_error("out of memory allocating box vector table");
+    qsort_comparison_fn * comparisonFn;
 
-    /* Set up the initial box. */
-    bv[0].ind = 0;
-    bv[0].colors = colors;
-    bv[0].sum = sum;
+    switch (methodForSplit){
+    case SPLIT_MAX_PIXELS: comparisonFn = &sumcompare;    break;
+    case SPLIT_MAX_COLORS: comparisonFn = &colcompare;    break;
+    case SPLIT_MAX_SPREAD: comparisonFn = &spreadcompare; break;
+    }
 
-    return bv;
+    qsort((char*) &boxVectorP->box[0], boxVectorP->boxCt, sizeof(struct Box),
+          comparisonFn);
 }
 
 
 
+/*
+** Here is the fun part, the median-cut colormap generator.  This is based
+** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
+** Display", SIGGRAPH '82 Proceedings, page 297.
+*/
+
 static void
 findBoxBoundaries(tupletable2  const colorfreqtable,
                   unsigned int const depth,
@@ -259,8 +315,8 @@ findBoxBoundaries(tupletable2  const colorfreqtable,
                   sample             minval[],
                   sample             maxval[]) {
 /*----------------------------------------------------------------------------
-  Go through the box finding the minimum and maximum of each
-  component - the boundaries of the box.
+  Go through the box finding the minimum and maximum of each component - the
+  boundaries of the box.
 -----------------------------------------------------------------------------*/
     unsigned int plane;
     unsigned int i;
@@ -282,57 +338,158 @@ findBoxBoundaries(tupletable2  const colorfreqtable,
 
 
 
-static unsigned int
-largestByNorm(sample minval[], sample maxval[], unsigned int const depth) {
-
-    unsigned int largestDimension;
+static void
+findPlaneWithLargestSpreadByNorm(sample         const minval[],
+                                 sample         const maxval[],
+                                 unsigned int   const depth,
+                                 unsigned int * const planeP,
+                                 sample *       const spreadP) {
+
+    unsigned int planeWithLargest;
+    sample       largestSpreadSoFar;
     unsigned int plane;
 
-    sample largestSpreadSoFar = 0;
-    largestDimension = 0;
-    for (plane = 0; plane < depth; ++plane) {
+    for (plane = 0, largestSpreadSoFar = 0; plane < depth; ++plane) {
+
         sample const spread = maxval[plane]-minval[plane];
         if (spread > largestSpreadSoFar) {
-            largestDimension = plane;
             largestSpreadSoFar = spread;
+            planeWithLargest   = plane;
         }
     }
-    return largestDimension;
+    *planeP  = planeWithLargest;
+    *spreadP = largestSpreadSoFar;
 }
 
 
 
-static unsigned int
-largestByLuminosity(sample minval[], sample maxval[],
-                    unsigned int const depth) {
+static void
+findPlaneWithLargestSpreadByLuminosity(sample         const minval[],
+                                       sample         const maxval[],
+                                       unsigned int   const depth,
+                                       unsigned int * const planeP,
+                                       sample *       const spreadP) {
 /*----------------------------------------------------------------------------
    This subroutine presumes that the tuple type is either
    BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
    To save time, we don't actually check it.
 -----------------------------------------------------------------------------*/
-    unsigned int retval;
-
-    if (depth == 1)
-        retval = 0;
-    else {
+    if (depth == 1){
+        *planeP  = 0;
+        *spreadP = 0;
+    } else {
         /* An RGB tuple */
-        unsigned int largestDimension;
+        unsigned int planeWithLargest;
+        sample       largestSpreadSoFar;
         unsigned int plane;
-        double largestSpreadSoFar;
 
-        largestSpreadSoFar = 0.0;
+        assert(depth >= 3);
 
-        for (plane = 0; plane < 3; ++plane) {
+        for (plane = 0, largestSpreadSoFar = 0; plane < 3; ++plane) {
             double const spread =
                 pnm_lumin_factor[plane] * (maxval[plane]-minval[plane]);
             if (spread > largestSpreadSoFar) {
-                largestDimension = plane;
                 largestSpreadSoFar = spread;
+                planeWithLargest   = plane;
             }
         }
-        retval = largestDimension;
+        *planeP  = planeWithLargest;
+        *spreadP = largestSpreadSoFar;
+    }
+}
+
+
+
+static void
+computeBoxSpread(const struct Box *    const boxP,
+                 tupletable2           const colorfreqtable,
+                 unsigned int          const depth,
+                 enum MethodForLargest const methodForLargest,
+                 unsigned int *        const planeWithLargestP,
+                 sample *              const spreadP
+                 ) {
+/*----------------------------------------------------------------------------
+  Find the spread in the dimension in which it is greatest.
+
+  Return as *planeWithLargestP the number of that plane and as *spreadP the
+  spread in that plane.
+-----------------------------------------------------------------------------*/
+    sample * minval;  /* malloc'ed array */
+    sample * maxval;  /* malloc'ed array */
+
+    MALLOCARRAY_NOFAIL(minval, depth);
+    MALLOCARRAY_NOFAIL(maxval, depth);
+
+    findBoxBoundaries(colorfreqtable, depth, boxP->index, boxP->colorCt,
+                      minval, maxval);
+
+    switch (methodForLargest) {
+    case LARGE_NORM:
+        findPlaneWithLargestSpreadByNorm(minval, maxval, depth,
+                                         planeWithLargestP, spreadP);
+        break;
+    case LARGE_LUM:
+        findPlaneWithLargestSpreadByLuminosity(minval, maxval, depth,
+                                               planeWithLargestP, spreadP);
+        break;
     }
-    return retval;
+    free(minval); free(maxval);
+}
+
+
+
+static unsigned int
+freqTotal(tupletable2 const freqtable) {
+
+    unsigned int i;
+    unsigned int sum;
+
+    for (i = 0, sum = 0; i < freqtable.size; ++i)
+        sum += freqtable.table[i]->value;
+
+    return sum;
+}
+
+
+
+static struct BoxVector
+newBoxVector(tupletable2           const colorfreqtable,
+             unsigned int          const capacity,
+             unsigned int          const depth,
+             enum MethodForLargest const methodForLargest) {
+
+    unsigned int const colorCt = colorfreqtable.size;
+    unsigned int const sum     = freqTotal(colorfreqtable);
+
+    struct BoxVector boxVector;
+
+    MALLOCARRAY(boxVector.box, capacity);
+
+    if (!boxVector.box)
+        pm_error("out of memory allocating box vector table");
+
+    /* Set up the initial box. */
+    boxVector.box[0].index   = 0;
+    boxVector.box[0].colorCt = colorCt;
+    boxVector.box[0].sum     = sum;
+
+    computeBoxSpread(&boxVector.box[0], colorfreqtable, depth,
+                     methodForLargest,
+                     &boxVector.box[0].maxdim,
+                     &boxVector.box[0].spread);
+
+    boxVector.boxCt    = 1;
+    boxVector.capacity = capacity;
+
+    return boxVector;
+}
+
+
+
+static void
+destroyBoxVector(struct BoxVector const boxVector) {
+
+    free(boxVector.box);
 }
 
 
@@ -363,6 +520,30 @@ centerBox(int          const boxStart,
 
 
 
+static tupletable2
+newColorMap(unsigned int const colorCt,
+            unsigned int const depth) {
+
+    tupletable2 colormap;
+    unsigned int i;
+    struct pam pam;
+
+    pam.depth = depth;
+
+    colormap.table = pnm_alloctupletable(&pam, colorCt);
+
+    for (i = 0; i < colorCt; ++i) {
+        unsigned int plane;
+        for (plane = 0; plane < depth; ++plane)
+            colormap.table[i]->tuple[plane] = 0;
+    }
+    colormap.size = colorCt;
+
+    return colormap;
+}
+
+
+
 static void
 averageColors(int          const boxStart,
               int          const boxSize,
@@ -422,12 +603,11 @@ averagePixels(int          const boxStart,
 
 
 static tupletable2
-colormapFromBv(unsigned int      const newcolors,
-               boxVector         const bv,
-               unsigned int      const boxes,
+colormapFromBv(unsigned int      const colorCt,
+               struct BoxVector  const boxVector,
                tupletable2       const colorfreqtable,
                unsigned int      const depth,
-               enum methodForRep const methodForRep) {
+               enum MethodForRep const methodForRep) {
     /*
     ** Ok, we've got enough boxes.  Now choose a representative color for
     ** each box.  There are a number of possible ways to make this choice.
@@ -437,23 +617,29 @@ colormapFromBv(unsigned int      const newcolors,
     ** method is to average all the pixels in the box.
     */
     tupletable2 colormap;
-    unsigned int bi;
+    unsigned int boxIdx;
 
-    colormap = newColorMap(newcolors, depth);
+    colormap = newColorMap(colorCt, depth);
 
-    for (bi = 0; bi < boxes; ++bi) {
+    for (boxIdx = 0; boxIdx < boxVector.boxCt; ++boxIdx) {
         switch (methodForRep) {
         case REP_CENTER_BOX:
-            centerBox(bv[bi].ind, bv[bi].colors, colorfreqtable, depth,
-                      colormap.table[bi]->tuple);
+            centerBox(boxVector.box[boxIdx].index,
+                      boxVector.box[boxIdx].colorCt,
+                      colorfreqtable, depth,
+                      colormap.table[boxIdx]->tuple);
             break;
         case REP_AVERAGE_COLORS:
-            averageColors(bv[bi].ind, bv[bi].colors, colorfreqtable, depth,
-                          colormap.table[bi]->tuple);
+            averageColors(boxVector.box[boxIdx].index,
+                          boxVector.box[boxIdx].colorCt,
+                          colorfreqtable, depth,
+                          colormap.table[boxIdx]->tuple);
             break;
         case REP_AVERAGE_PIXELS:
-            averagePixels(bv[bi].ind, bv[bi].colors, colorfreqtable, depth,
-                          colormap.table[bi]->tuple);
+            averagePixels(boxVector.box[boxIdx].index,
+                          boxVector.box[boxIdx].colorCt,
+                          colorfreqtable, depth,
+                          colormap.table[boxIdx]->tuple);
             break;
         default:
             pm_error("Internal error: invalid value of methodForRep: %d",
@@ -465,107 +651,77 @@ colormapFromBv(unsigned int      const newcolors,
 
 
 
-static unsigned int
-freqTotal(tupletable2 const freqtable) {
-
-    unsigned int i;
-    unsigned int sum;
-
-    sum = 0;
-
-    for (i = 0; i < freqtable.size; ++i)
-        sum += freqtable.table[i]->value;
-
-    return sum;
-}
-
-
 static void
-splitBox(boxVector             const bv,
-         unsigned int *        const boxesP,
-         unsigned int          const bi,
+splitBox(struct BoxVector *    const boxVectorP,
+         unsigned int          const boxIdx,
          tupletable2           const colorfreqtable,
          unsigned int          const depth,
-         enum methodForLargest const methodForLargest) {
+         enum MethodForLargest const methodForLargest,
+         enum MethodForSplit   const methodForSplit) {
 /*----------------------------------------------------------------------------
-   Split Box 'bi' in the box vector bv (so that bv contains one more box
-   than it did as input).  Split it so that each new box represents about
-   half of the pixels in the distribution given by 'colorfreqtable' for
-   the colors in the original box, but with distinct colors in each of the
-   two new boxes.
+   Split Box 'boxIdx' in the box vector 'boxVector' (so that bv contains one
+   more box than it did as input).  Split it so that each new box represents
+   about half of the pixels in the distribution given by 'colorfreqtable' for
+   the colors in the original box, but with distinct colors in each of the two
+   new boxes.
 
    Assume the box contains at least two colors.
 -----------------------------------------------------------------------------*/
-    unsigned int const boxStart = bv[bi].ind;
-    unsigned int const boxSize  = bv[bi].colors;
-    unsigned int const sm       = bv[bi].sum;
+    unsigned int const boxStart = boxVectorP->box[boxIdx].index;
+    unsigned int const boxSize  = boxVectorP->box[boxIdx].colorCt;
+    unsigned int const sum      = boxVectorP->box[boxIdx].sum;
 
-    sample * minval;  /* malloc'ed array */
-    sample * maxval;  /* malloc'ed array */
-
-    unsigned int largestDimension;
-        /* number of the plane with the largest spread */
     unsigned int medianIndex;
     int lowersum;
         /* Number of pixels whose value is "less than" the median */
 
-    MALLOCARRAY_NOFAIL(minval, depth);
-    MALLOCARRAY_NOFAIL(maxval, depth);
-
-    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
-                      minval, maxval);
-
-    /* Find the largest dimension, and sort by that component.  I have
-       included two methods for determining the "largest" dimension;
-       first by simply comparing the range in RGB space, and second by
-       transforming into luminosities before the comparison.
-    */
-    switch (methodForLargest) {
-    case LARGE_NORM:
-        largestDimension = largestByNorm(minval, maxval, depth);
-        break;
-    case LARGE_LUM:
-        largestDimension = largestByLuminosity(minval, maxval, depth);
-        break;
-    }
 
-    /* TODO: I think this sort should go after creating a box,
-       not before splitting.  Because you need the sort to use
-       the REP_CENTER_BOX method of choosing a color to
-       represent the final boxes
+    /* Perhaps this sort should go after creating a box, not before splitting.
+       Because you need the sort to use the REP_CENTER_BOX method of choosing
+       a color to represent the final boxes
     */
 
     /* Set the gross global variable 'compareplanePlane' as a
        parameter to compareplane(), which is called by qsort().
     */
-    compareplanePlane = largestDimension;
+    compareplanePlane = boxVectorP->box[boxIdx].maxdim;
     qsort((char*) &colorfreqtable.table[boxStart], boxSize,
           sizeof(colorfreqtable.table[boxStart]),
           compareplane);
 
     {
-        /* Now find the median based on the counts, so that about half
-           the pixels (not colors, pixels) are in each subdivision.  */
-
+        /* Find the median based on the counts, so that about half the pixels
+           (not colors, pixels) are in each subdivision.
+        */
         unsigned int i;
 
         lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
-        for (i = 1; i < boxSize - 1 && lowersum < sm/2; ++i) {
+        for (i = 1; i < boxSize - 1 && lowersum < sum/2; ++i) {
             lowersum += colorfreqtable.table[boxStart + i]->value;
         }
         medianIndex = i;
     }
     /* Split the box, and sort to bring the biggest boxes to the top.  */
+    {
+        struct Box * const oldBoxP = &boxVectorP->box[boxIdx];
 
-    bv[bi].colors = medianIndex;
-    bv[bi].sum = lowersum;
-    bv[*boxesP].ind = boxStart + medianIndex;
-    bv[*boxesP].colors = boxSize - medianIndex;
-    bv[*boxesP].sum = sm - lowersum;
-    ++(*boxesP);
-    qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare);
+        oldBoxP->colorCt = medianIndex;
+        oldBoxP->sum     = lowersum;
+        computeBoxSpread(oldBoxP, colorfreqtable, depth, methodForLargest,
+                         &oldBoxP->maxdim, &oldBoxP->spread);
+    }
+    {
+        struct Box * const newBoxP = &boxVectorP->box[boxVectorP->boxCt];
+
+        newBoxP->index   = boxStart + medianIndex;
+        newBoxP->colorCt = boxSize - medianIndex;
+        newBoxP->sum     = sum - lowersum;
+        computeBoxSpread(newBoxP, colorfreqtable, depth, methodForLargest,
+                         &newBoxP->maxdim, &newBoxP->spread);
+        ++boxVectorP->boxCt;
+    }
 
-    free(minval); free(maxval);
+    sortBoxes(boxVectorP, methodForSplit);
 }
 
 
@@ -573,12 +729,13 @@ splitBox(boxVector             const bv,
 static void
 mediancut(tupletable2           const colorfreqtable,
           unsigned int          const depth,
-          int                   const newcolors,
-          enum methodForLargest const methodForLargest,
-          enum methodForRep     const methodForRep,
+          int                   const newcolorCt,
+          enum MethodForLargest const methodForLargest,
+          enum MethodForRep     const methodForRep,
+          enum MethodForSplit   const methodForSplit,
           tupletable2 *         const colormapP) {
 /*----------------------------------------------------------------------------
-   Compute a set of only 'newcolors' colors that best represent an
+   Compute a set of only 'newcolorCt' colors that best represent an
    image whose pixels are summarized by the histogram
    'colorfreqtable'.  Each tuple in that table has depth 'depth'.
    colorfreqtable.table[i] tells the number of pixels in the subject image
@@ -586,32 +743,36 @@ mediancut(tupletable2           const colorfreqtable,
 
    As a side effect, sort 'colorfreqtable'.
 -----------------------------------------------------------------------------*/
-    boxVector bv;
-    unsigned int bi;
-    unsigned int boxes;
+    struct BoxVector boxVector;
     bool multicolorBoxesExist;
         /* There is at least one box that contains at least 2 colors; ergo,
            there is more splitting we can do.
         */
 
-    bv = newBoxVector(colorfreqtable.size, freqTotal(colorfreqtable),
-                      newcolors);
-    boxes = 1;
+    boxVector = newBoxVector(colorfreqtable, newcolorCt, depth,
+                             methodForLargest);
+
     multicolorBoxesExist = (colorfreqtable.size > 1);
 
-    /* Main loop: split boxes until we have enough. */
-    while (boxes < newcolors && multicolorBoxesExist) {
-        /* Find the first splittable box. */
-        for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi);
-        if (bi >= boxes)
+    /* Split boxes until we have enough. */
+    while (boxVector.boxCt < newcolorCt && multicolorBoxesExist) {
+        unsigned int boxIdx;
+
+        for (boxIdx = 0;
+             boxIdx < boxVector.boxCt && boxVector.box[boxIdx].colorCt < 2;
+             ++boxIdx);
+            /* Find the first splittable box. */
+
+        if (boxIdx >= boxVector.boxCt)
             multicolorBoxesExist = FALSE;
         else
-            splitBox(bv, &boxes, bi, colorfreqtable, depth, methodForLargest);
+            splitBox(&boxVector, boxIdx, colorfreqtable, depth,
+                     methodForLargest, methodForSplit);
     }
-    *colormapP = colormapFromBv(newcolors, bv, boxes, colorfreqtable, depth,
-                                methodForRep);
+    *colormapP = colormapFromBv(newcolorCt, boxVector, colorfreqtable,
+                                depth, methodForRep);
 
-    free(bv);
+    destroyBoxVector(boxVector);
 }
 
 
@@ -739,8 +900,9 @@ static void
 computeColorMapFromInput(FILE *                const ifP,
                          bool                  const allColors,
                          int                   const reqColors,
-                         enum methodForLargest const methodForLargest,
-                         enum methodForRep     const methodForRep,
+                         enum MethodForLargest const methodForLargest,
+                         enum MethodForRep     const methodForRep,
+                         enum MethodForSplit   const methodForSplit,
                          int *                 const formatP,
                          struct pam *          const freqPamP,
                          tupletable2 *         const colormapP) {
@@ -778,7 +940,7 @@ computeColorMapFromInput(FILE *                const ifP,
             pm_message("choosing %d colors...", reqColors);
             mediancut(colorfreqtable, freqPamP->depth,
                       reqColors, methodForLargest, methodForRep,
-                      colormapP);
+                      methodForSplit, colormapP);
             pnm_freetupletable2(freqPamP, colorfreqtable);
         }
     }
@@ -933,9 +1095,9 @@ colormapToImage(int                const format,
 
 
 int
-main(int argc, char * argv[] ) {
+main(int argc, const char * argv[] ) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     int format;
     struct pam colormapPam;
@@ -943,16 +1105,17 @@ main(int argc, char * argv[] ) {
     tuple ** colormapRaster;
     tupletable2 colormap;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileNm);
 
     computeColorMapFromInput(ifP,
                              cmdline.allcolors, cmdline.newcolors,
                              cmdline.methodForLargest,
                              cmdline.methodForRep,
+                             cmdline.methodForSplit,
                              &format, &colormapPam, &colormap);
 
     pm_close(ifP);