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.c457
1 files changed, 294 insertions, 163 deletions
diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c
index 7da3122b..f59a1a0d 100644
--- a/other/pnmcolormap.c
+++ b/other/pnmcolormap.c
@@ -1,7 +1,6 @@
-/******************************************************************************
-                               pnmcolormap.c
-*******************************************************************************
-
+/*=============================================================================
+                               pnmcolormap
+===============================================================================
   Create a colormap file (a PPM image containing one pixel of each of a set
   of colors).  Base the set of colors on an input image.
 
@@ -20,9 +19,8 @@
   copyright notice and this permission notice appear in supporting
   documentation.  This software is provided "as is" without express or
   implied warranty.
-
-******************************************************************************/
-
+=============================================================================*/
+#include <stdbool.h>
 #include <assert.h>
 #include <math.h>
 
@@ -41,19 +39,41 @@ enum MethodForRep {REP_CENTER_BOX, REP_AVERAGE_COLORS, REP_AVERAGE_PIXELS};
 enum MethodForSplit {SPLIT_MAX_PIXELS, SPLIT_MAX_COLORS, SPLIT_MAX_SPREAD};
 
 struct Box {
-    unsigned int index;
+/*----------------------------------------------------------------------------
+   A box contains an extent of a color frequency table, i.e. the colors
+   with some consecutive index values in the color frequency table.
+-----------------------------------------------------------------------------*/
+    unsigned int serialNum;
+        /* Unique identifier of this box; sequence number of creation. */
+    unsigned int startIndex;
+        /* First index in the extent */
     unsigned int colorCt;
+        /* Size of the extent (Number of colors in it -- at least 1) */
     unsigned int sum;
+        /* Number of pixels of all colors in the extent */
     unsigned int maxdim;
-        /* which dimension has the largest spread.  RGB plane number. */
+        /* Which dimension has the largest spread.  RGB plane number. */
+        /* Meaningless if box contains only 1 color */
     sample       spread;
         /* spread in dimension 'maxdim' */
+        /* Meaningless if box contains only 1 color */
 };
 
 struct BoxVector {
+    tupletable2 colorFreqTable;
+        /* The colors and their frequencies (number of pixels in the image of
+           that color), ordered into consecutive boxes, as defined by 'box'.
+        */
+    unsigned int colorDepth;
+        /* Number of planes in the tuples of 'colorFreqTable' */
     struct Box * box;  /* malloc'ed array */
+        /* An array of boxes that contain consecutive extents of
+           'colorFreqTable'.  The list covers the entire table.
+        */
     unsigned int boxCt;
+        /* Number of boxes in the above list */
     unsigned int capacity;
+        /* Number of boxes the array is capable of containing */
 };
 
 struct CmdlineInfo {
@@ -62,7 +82,7 @@ struct CmdlineInfo {
     */
     const char * inputFileNm;  /* Name of input file */
     unsigned int allcolors;  /* boolean: select all colors from the input */
-    unsigned int newcolors;
+    unsigned int newColorCt;
         /* Number of colors argument; meaningless if allcolors true */
     enum MethodForLargest methodForLargest;
         /* -spreadintensity/-spreadluminosity options */
@@ -73,13 +93,14 @@ struct CmdlineInfo {
     unsigned int sort;
     unsigned int square;
     unsigned int verbose;
+    unsigned int debug;
 };
 
 
 
 static void
 parseCommandLine (int argc, const char ** argv,
-                  struct CmdlineInfo *cmdlineP) {
+                  struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.
@@ -90,9 +111,7 @@ parseCommandLine (int argc, const char ** argv,
    Note that the strings we return are stored in the storage that
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def;
-        /* Instructions to pm_optParseOptions3 on how to parse our options.
-         */
+    optEntry * option_def;
     optStruct3 opt;
 
     unsigned int option_def_index;
@@ -120,18 +139,20 @@ parseCommandLine (int argc, const char ** argv,
             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,
-            &cmdlineP->square,       0 );
-    OPTENT3(0, "verbose",   OPT_FLAG,   NULL,
-            &cmdlineP->verbose,       0 );
+    OPTENT3(0, "sort",               OPT_FLAG,   NULL,
+            &cmdlineP->sort,                               0);
+    OPTENT3(0, "square",             OPT_FLAG,   NULL,
+            &cmdlineP->square,                             0);
+    OPTENT3(0, "verbose",            OPT_FLAG,   NULL,
+            &cmdlineP->verbose,                            0);
+    OPTENT3(0, "debug",              OPT_FLAG,   NULL,
+            &cmdlineP->debug,                              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 */
+    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, (char **)argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions4( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
 
@@ -177,21 +198,18 @@ parseCommandLine (int argc, const char ** argv,
                      "output as an argument.");
         else {
             if (strcmp(argv[1], "all") == 0)
-                cmdlineP->allcolors = TRUE;
+                cmdlineP->allcolors = true;
             else {
-                char * tail;
-                long int const newcolors = strtol(argv[1], &tail, 10);
-                if (*tail != '\0')
+                const char * error;
+                cmdlineP->allcolors = false;
+                pm_string_to_uint(argv[1], &cmdlineP->newColorCt, &error);
+
+                if (error) {
                     pm_error("The number of colors argument '%s' is not "
-                             "a number or 'all'", argv[1]);
-                else if (newcolors < 1)
+                             "an unsigned number or 'all'.  %s",
+                             argv[1], error);
+                } else if (cmdlineP->newColorCt == 0)
                     pm_error("The number of colors must be positive");
-                else if (newcolors == 1)
-                    pm_error("The number of colors must be greater than 1.");
-                else {
-                    cmdlineP->newcolors = newcolors;
-                    cmdlineP->allcolors = FALSE;
-                }
             }
         }
     }
@@ -200,28 +218,63 @@ parseCommandLine (int argc, const char ** argv,
 
 
 #ifndef LITERAL_FN_DEF_MATCH
-static qsort_comparison_fn compareplane;
+static qsort_comparison_fn compareColor;
 #endif
 
-static unsigned int compareplanePlane;
-    /* This is a parameter to compareplane().  We use this global variable
-       so that compareplane() can be called by qsort(), to compare two
-       tuples.  qsort() doesn't pass any arguments except the two tuples.
-    */
+static struct {
+/*----------------------------------------------------------------------------
+  This is a parameter to compareColor().  We use this global variable
+  so that compareColor() can be called by qsort(), to compare two
+  tuples.  qsort() doesn't pass any arguments except the two tuples.
+-----------------------------------------------------------------------------*/
+    unsigned int comparePlane;
+        /* The number of the plane to compare between the tuples */
+    unsigned int colorDepth;
+        /* Depth (number of planes) of the tuples */
+} compareColorParm;
+
 static int
-compareplane(const void * const arg1,
+compareColor(const void * const arg1,
              const void * const arg2) {
 
     const struct tupleint * const * const comparandPP  = arg1;
     const struct tupleint * const * const comparatorPP = arg2;
 
-    sample const comparandSample  = (*comparandPP) ->tuple[compareplanePlane];
-    sample const comparatorSample = (*comparatorPP)->tuple[compareplanePlane];
+    sample const comparandSample  =
+        (*comparandPP) ->tuple[compareColorParm.comparePlane];
+    sample const comparatorSample =
+        (*comparatorPP)->tuple[compareColorParm.comparePlane];
 
-    return
-        comparandSample < comparatorSample ? -1 :
-        comparandSample > comparatorSample ? 1 :
-        0;
+    int retval;
+
+    if (comparandSample < comparatorSample)
+        retval = -1;
+    else if (comparandSample > comparatorSample)
+        retval = +1;
+    else {
+        /* In the plane that matters, samples are equal, but we're going to
+           try to differentiate the colors anyway so as to make qsort put the
+           colors in a deterministic order so the boxes are deterministic.
+        */
+        unsigned int plane;
+        int bestDiffSoFar;  /* -1, 0, or 1, like our return value */
+        for (plane = 0, bestDiffSoFar = 0;
+             plane < compareColorParm.colorDepth && bestDiffSoFar == 0;
+             ++plane) {
+
+            sample const comparandSample  =
+                (*comparandPP) ->tuple[compareColorParm.comparePlane];
+            sample const comparatorSample =
+                (*comparatorPP)->tuple[compareColorParm.comparePlane];
+
+            if (comparandSample < comparatorSample)
+                bestDiffSoFar = -1;
+            else if (comparandSample > comparatorSample)
+                bestDiffSoFar = +1;
+        }
+        retval = bestDiffSoFar;
+    }
+    return retval;
 }
 
 
@@ -239,7 +292,9 @@ sumcompare(const void * const arg1,
 
     return
         comparatorP->sum < comparandP->sum ? -1 :
-        comparatorP->sum > comparandP->sum ? 1 :
+        comparatorP->sum > comparandP->sum ? +1 :
+        comparatorP->serialNum < comparandP->serialNum ? -1 :
+        comparatorP->serialNum > comparandP->serialNum ? +1 :
         0;
 }
 
@@ -259,6 +314,8 @@ colcompare(const void * const arg1,
     return
         comparatorP->colorCt < comparandP->colorCt ? -1 :
         comparatorP->colorCt > comparandP->colorCt ? 1 :
+        comparatorP->serialNum < comparandP->serialNum ? -1 :
+        comparatorP->serialNum > comparandP->serialNum ? +1 :
         0;
 }
 
@@ -278,6 +335,8 @@ spreadcompare(const void * const arg1,
     return
         comparatorP->spread < comparandP->spread ? -1 :
         comparatorP->spread > comparandP->spread ? 1 :
+        comparatorP->serialNum < comparandP->serialNum ? -1 :
+        comparatorP->serialNum > comparandP->serialNum ? +1 :
         0;
 }
 
@@ -308,7 +367,7 @@ sortBoxes(struct BoxVector *  const boxVectorP,
 */
 
 static void
-findBoxBoundaries(tupletable2  const colorfreqtable,
+findBoxBoundaries(tupletable2  const colorFreqTable,
                   unsigned int const depth,
                   unsigned int const boxStart,
                   unsigned int const boxSize,
@@ -322,14 +381,14 @@ findBoxBoundaries(tupletable2  const colorfreqtable,
     unsigned int i;
 
     for (plane = 0; plane < depth; ++plane) {
-        minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
+        minval[plane] = colorFreqTable.table[boxStart]->tuple[plane];
         maxval[plane] = minval[plane];
     }
 
     for (i = 1; i < boxSize; ++i) {
         unsigned int plane;
         for (plane = 0; plane < depth; ++plane) {
-            sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
+            sample const v = colorFreqTable.table[boxStart + i]->tuple[plane];
             if (v < minval[plane]) minval[plane] = v;
             if (v > maxval[plane]) maxval[plane] = v;
         }
@@ -402,7 +461,7 @@ findPlaneWithLargestSpreadByLuminosity(sample         const minval[],
 
 static void
 computeBoxSpread(const struct Box *    const boxP,
-                 tupletable2           const colorfreqtable,
+                 tupletable2           const colorFreqTable,
                  unsigned int          const depth,
                  enum MethodForLargest const methodForLargest,
                  unsigned int *        const planeWithLargestP,
@@ -420,7 +479,7 @@ computeBoxSpread(const struct Box *    const boxP,
     MALLOCARRAY_NOFAIL(minval, depth);
     MALLOCARRAY_NOFAIL(maxval, depth);
 
-    findBoxBoundaries(colorfreqtable, depth, boxP->index, boxP->colorCt,
+    findBoxBoundaries(colorFreqTable, depth, boxP->startIndex, boxP->colorCt,
                       minval, maxval);
 
     switch (methodForLargest) {
@@ -439,13 +498,13 @@ computeBoxSpread(const struct Box *    const boxP,
 
 
 static unsigned int
-freqTotal(tupletable2 const freqtable) {
+freqTotal(tupletable2 const freqTable) {
 
     unsigned int i;
     unsigned int sum;
 
-    for (i = 0, sum = 0; i < freqtable.size; ++i)
-        sum += freqtable.table[i]->value;
+    for (i = 0, sum = 0; i < freqTable.size; ++i)
+        sum += freqTable.table[i]->value;
 
     return sum;
 }
@@ -453,27 +512,30 @@ freqTotal(tupletable2 const freqtable) {
 
 
 static struct BoxVector
-newBoxVector(tupletable2           const colorfreqtable,
+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);
+    unsigned int const colorCt = colorFreqTable.size;
+    unsigned int const sum     = freqTotal(colorFreqTable);
 
     struct BoxVector boxVector;
 
+    boxVector.colorFreqTable = colorFreqTable;
+    boxVector.colorDepth     = depth;
+
     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;
+    boxVector.box[0].startIndex = 0;
+    boxVector.box[0].colorCt    = colorCt;
+    boxVector.box[0].sum        = sum;
 
-    computeBoxSpread(&boxVector.box[0], colorfreqtable, depth,
+    computeBoxSpread(&boxVector.box[0], colorFreqTable, depth,
                      methodForLargest,
                      &boxVector.box[0].maxdim,
                      &boxVector.box[0].spread);
@@ -497,7 +559,7 @@ destroyBoxVector(struct BoxVector const boxVector) {
 static void
 centerBox(int          const boxStart,
           int          const boxSize,
-          tupletable2  const colorfreqtable,
+          tupletable2  const colorFreqTable,
           unsigned int const depth,
           tuple        const newTuple) {
 
@@ -507,10 +569,10 @@ centerBox(int          const boxStart,
         int minval, maxval;
         unsigned int i;
 
-        minval = maxval = colorfreqtable.table[boxStart]->tuple[plane];
+        minval = maxval = colorFreqTable.table[boxStart]->tuple[plane];
 
         for (i = 1; i < boxSize; ++i) {
-            int const v = colorfreqtable.table[boxStart + i]->tuple[plane];
+            int const v = colorFreqTable.table[boxStart + i]->tuple[plane];
             minval = MIN( minval, v);
             maxval = MAX( maxval, v);
         }
@@ -547,7 +609,7 @@ newColorMap(unsigned int const colorCt,
 static void
 averageColors(int          const boxStart,
               int          const boxSize,
-              tupletable2  const colorfreqtable,
+              tupletable2  const colorFreqTable,
               unsigned int const depth,
               tuple        const newTuple) {
 
@@ -560,7 +622,7 @@ averageColors(int          const boxStart,
         sum = 0;
 
         for (i = 0; i < boxSize; ++i)
-            sum += colorfreqtable.table[boxStart+i]->tuple[plane];
+            sum += colorFreqTable.table[boxStart+i]->tuple[plane];
 
         newTuple[plane] = ROUNDDIV(sum, boxSize);
     }
@@ -571,7 +633,7 @@ averageColors(int          const boxStart,
 static void
 averagePixels(int          const boxStart,
               int          const boxSize,
-              tupletable2  const colorfreqtable,
+              tupletable2  const colorFreqTable,
               unsigned int const depth,
               tuple        const newTuple) {
 
@@ -583,7 +645,7 @@ averagePixels(int          const boxStart,
     /* Count the tuples in question */
     n = 0;  /* initial value */
     for (i = 0; i < boxSize; ++i)
-        n += colorfreqtable.table[boxStart + i]->value;
+        n += colorFreqTable.table[boxStart + i]->value;
 
 
     for (plane = 0; plane < depth; ++plane) {
@@ -593,8 +655,8 @@ averagePixels(int          const boxStart,
         sum = 0;
 
         for (i = 0; i < boxSize; ++i)
-            sum += colorfreqtable.table[boxStart+i]->tuple[plane]
-                * colorfreqtable.table[boxStart+i]->value;
+            sum += colorFreqTable.table[boxStart+i]->tuple[plane]
+                * colorFreqTable.table[boxStart+i]->value;
 
         newTuple[plane] = ROUNDDIV(sum, n);
     }
@@ -605,8 +667,6 @@ averagePixels(int          const boxStart,
 static tupletable2
 colormapFromBv(unsigned int      const colorCt,
                struct BoxVector  const boxVector,
-               tupletable2       const colorfreqtable,
-               unsigned int      const depth,
                enum MethodForRep const methodForRep) {
     /*
     ** Ok, we've got enough boxes.  Now choose a representative color for
@@ -619,26 +679,26 @@ colormapFromBv(unsigned int      const colorCt,
     tupletable2 colormap;
     unsigned int boxIdx;
 
-    colormap = newColorMap(colorCt, depth);
+    colormap = newColorMap(colorCt, boxVector.colorDepth);
 
     for (boxIdx = 0; boxIdx < boxVector.boxCt; ++boxIdx) {
         switch (methodForRep) {
         case REP_CENTER_BOX:
-            centerBox(boxVector.box[boxIdx].index,
+            centerBox(boxVector.box[boxIdx].startIndex,
                       boxVector.box[boxIdx].colorCt,
-                      colorfreqtable, depth,
+                      boxVector.colorFreqTable, boxVector.colorDepth,
                       colormap.table[boxIdx]->tuple);
             break;
         case REP_AVERAGE_COLORS:
-            averageColors(boxVector.box[boxIdx].index,
+            averageColors(boxVector.box[boxIdx].startIndex,
                           boxVector.box[boxIdx].colorCt,
-                          colorfreqtable, depth,
+                          boxVector.colorFreqTable, boxVector.colorDepth,
                           colormap.table[boxIdx]->tuple);
             break;
         case REP_AVERAGE_PIXELS:
-            averagePixels(boxVector.box[boxIdx].index,
+            averagePixels(boxVector.box[boxIdx].startIndex,
                           boxVector.box[boxIdx].colorCt,
-                          colorfreqtable, depth,
+                          boxVector.colorFreqTable, boxVector.colorDepth,
                           colormap.table[boxIdx]->tuple);
             break;
         default:
@@ -652,42 +712,72 @@ colormapFromBv(unsigned int      const colorCt,
 
 
 static void
+setBox(struct Box *          const boxP,
+       unsigned int          const startIndex,
+       unsigned int          const colorCt,
+       unsigned int          const sum,
+       struct BoxVector *    const boxVectorP,
+       enum MethodForLargest const methodForLargest
+    ) {
+
+    boxP->startIndex = startIndex;
+    boxP->colorCt    = colorCt;
+    boxP->sum        = sum;
+
+    computeBoxSpread(boxP, boxVectorP->colorFreqTable,
+                     boxVectorP->colorDepth, methodForLargest,
+                     &boxP->maxdim, &boxP->spread);
+}
+
+
+
+static void
+makeNewBox(struct BoxVector *    const boxVectorP,
+           unsigned int          const startIndex,
+           unsigned int          const colorCt,
+           unsigned int          const sum,
+           enum MethodForLargest const methodForLargest) {
+
+    struct Box * const boxP = &boxVectorP->box[boxVectorP->boxCt++];
+
+    assert(boxVectorP->boxCt <= boxVectorP->capacity);
+
+    boxP->serialNum = boxVectorP->boxCt;
+
+    setBox(boxP, startIndex, colorCt, sum, boxVectorP, methodForLargest);
+}
+
+
+
+static void
 splitBox(struct BoxVector *    const boxVectorP,
          unsigned int          const boxIdx,
-         tupletable2           const colorfreqtable,
-         unsigned int          const depth,
          enum MethodForLargest const methodForLargest,
          enum MethodForSplit   const methodForSplit) {
 /*----------------------------------------------------------------------------
-   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.
+   Split Box 'boxIdx' in the box vector 'boxVector' (so that 'boxVector'
+   contains one more box than it did as input).  Split it so that each new box
+   represents about half of the pixels in the image 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 = boxVectorP->box[boxIdx].index;
+    unsigned int const boxStart = boxVectorP->box[boxIdx].startIndex;
     unsigned int const boxSize  = boxVectorP->box[boxIdx].colorCt;
     unsigned int const sum      = boxVectorP->box[boxIdx].sum;
 
     unsigned int medianIndex;
-    int lowersum;
+    unsigned int lowerSum;
         /* Number of pixels whose value is "less than" the median */
 
-
-    /* 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().
+    /* Set the gross global variable 'compareColorParm' as a
+       parameter to compareColor(), which is called by qsort().
     */
-    compareplanePlane = boxVectorP->box[boxIdx].maxdim;
-    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
-          sizeof(colorfreqtable.table[boxStart]),
-          compareplane);
+    compareColorParm.comparePlane    = boxVectorP->box[boxIdx].maxdim;
+    compareColorParm.colorDepth      = boxVectorP->colorDepth;
+    qsort((char*) &boxVectorP->colorFreqTable.table[boxStart], boxSize,
+          sizeof(boxVectorP->colorFreqTable.table[boxStart]),
+          compareColor);
 
     {
         /* Find the median based on the counts, so that about half the pixels
@@ -695,31 +785,25 @@ splitBox(struct BoxVector *    const boxVectorP,
         */
         unsigned int i;
 
-        lowersum = colorfreqtable.table[boxStart]->value; /* initial value */
-        for (i = 1; i < boxSize - 1 && lowersum < sum/2; ++i) {
-            lowersum += colorfreqtable.table[boxStart + i]->value;
+        lowerSum = boxVectorP->colorFreqTable.table[boxStart]->value;
+            /* initial value */
+        for (i = 1; i < boxSize - 1 && lowerSum < sum/2; ++i) {
+            lowerSum += boxVectorP->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];
+    /* Split the box, and sort to bring the biggest boxes to the top.  The old
+       box becomes the lower half; we make a new box for the upper half.
+    */
+    setBox(&boxVectorP->box[boxIdx],
+           boxStart, medianIndex,
+           lowerSum,
+           boxVectorP, methodForLargest);
 
-        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;
-    }
+    makeNewBox(boxVectorP,
+               boxStart + medianIndex, boxSize - medianIndex,
+               sum - lowerSum,
+               methodForLargest);
 
     sortBoxes(boxVectorP, methodForSplit);
 }
@@ -727,21 +811,58 @@ splitBox(struct BoxVector *    const boxVectorP,
 
 
 static void
-mediancut(tupletable2           const colorfreqtable,
+reportBoxVector(struct BoxVector const boxVector) {
+
+    unsigned int i;
+
+    pm_message("All colors of image, sorted into %u boxes:", boxVector.boxCt);
+
+    for (i = 0; i < boxVector.boxCt; ++i) {
+        const struct Box * const boxP = &boxVector.box[i];
+
+        unsigned int j;
+
+        pm_message("Box %u, %u colors starting with index %u (%u pixels):",
+                   i, boxP->colorCt, boxP->startIndex, boxP->sum);
+        if (boxP->colorCt > 1)
+            pm_message("Largest spread is %lu, in plane %u",
+                       boxP->spread, boxP->maxdim);
+
+        for (j = 0; j < boxP->colorCt; ++j) {
+            unsigned int colorIdx = boxP->startIndex + j;
+
+            assert(colorIdx < boxVector.colorFreqTable.size);
+
+            tuple const color =
+                boxVector.colorFreqTable.table[colorIdx]->tuple;
+
+            pm_message("(%lu, %lu, %lu)",
+                       color[PAM_RED_PLANE],
+                       color[PAM_GRN_PLANE],
+                       color[PAM_BLU_PLANE]);
+        }
+    }
+}
+
+
+
+static void
+mediancut(tupletable2           const colorFreqTable,
           unsigned int          const depth,
-          int                   const newcolorCt,
+          unsigned int          const newColorCt,
           enum MethodForLargest const methodForLargest,
           enum MethodForRep     const methodForRep,
           enum MethodForSplit   const methodForSplit,
+          bool                  const wantBvReport,
           tupletable2 *         const colormapP) {
 /*----------------------------------------------------------------------------
-   Compute a set of only 'newcolorCt' 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
-   have a particular color.
+   'colorFreqTable'.  Each tuple in that table has depth 'depth'.
+   colorFreqTable.table[i] tells the number of pixels in the subject image
+   that have a particular color.
 
-   As a side effect, sort 'colorfreqtable'.
+   As a side effect, sort 'colorFreqTable'.
 -----------------------------------------------------------------------------*/
     struct BoxVector boxVector;
     bool multicolorBoxesExist;
@@ -749,13 +870,13 @@ mediancut(tupletable2           const colorfreqtable,
            there is more splitting we can do.
         */
 
-    boxVector = newBoxVector(colorfreqtable, newcolorCt, depth,
+    boxVector = newBoxVector(colorFreqTable, newColorCt, depth,
                              methodForLargest);
 
-    multicolorBoxesExist = (colorfreqtable.size > 1);
+    multicolorBoxesExist = (colorFreqTable.size > 1);
 
     /* Split boxes until we have enough. */
-    while (boxVector.boxCt < newcolorCt && multicolorBoxesExist) {
+    while (boxVector.boxCt < newColorCt && multicolorBoxesExist) {
         unsigned int boxIdx;
 
         for (boxIdx = 0;
@@ -764,13 +885,18 @@ mediancut(tupletable2           const colorfreqtable,
             /* Find the first splittable box. */
 
         if (boxIdx >= boxVector.boxCt)
-            multicolorBoxesExist = FALSE;
+            multicolorBoxesExist = false;
         else
-            splitBox(&boxVector, boxIdx, colorfreqtable, depth,
-                     methodForLargest, methodForSplit);
+            splitBox(&boxVector, boxIdx, methodForLargest, methodForSplit);
+                /* Side effect: sorts the extent of 'colorfreqTable' that is
+                   in the box
+                */
     }
-    *colormapP = colormapFromBv(newcolorCt, boxVector, colorfreqtable,
-                                depth, methodForRep);
+
+    if (wantBvReport)
+        reportBoxVector(boxVector);
+
+    *colormapP = colormapFromBv(newColorCt, boxVector, methodForRep);
 
     destroyBoxVector(boxVector);
 }
@@ -834,7 +960,7 @@ static void
 computeHistogram(FILE *         const ifP,
                  int *          const formatP,
                  struct pam *   const freqPamP,
-                 tupletable2 *  const colorfreqtableP) {
+                 tupletable2 *  const colorFreqTableP) {
 /*----------------------------------------------------------------------------
   Make a histogram of the colors in the image stream in the file '*ifP'.
 
@@ -856,7 +982,7 @@ computeHistogram(FILE *         const ifP,
     tuplehash = pnm_createtuplehash();
     colorCount = 0;
 
-    eof = FALSE;
+    eof = false;
 
     for (imageSeq = 0; !eof; ++imageSeq) {
         struct pam inpam;
@@ -876,13 +1002,13 @@ computeHistogram(FILE *         const ifP,
 
         pnm_nextimage(ifP, &eof);
     }
-    colorfreqtableP->table =
+    colorFreqTableP->table =
         pnm_tuplehashtotable(&firstPam, tuplehash, colorCount);
-    colorfreqtableP->size = colorCount;
+    colorFreqTableP->size = colorCount;
 
     pnm_destroytuplehash(tuplehash);
 
-    pm_message("%u colors found", colorfreqtableP->size);
+    pm_message("%u colors found", colorFreqTableP->size);
 
     freqPamP->size   = sizeof(*freqPamP);
     freqPamP->len    = PAM_STRUCT_SIZE(tuple_type);
@@ -899,10 +1025,11 @@ computeHistogram(FILE *         const ifP,
 static void
 computeColorMapFromInput(FILE *                const ifP,
                          bool                  const allColors,
-                         int                   const reqColors,
+                         unsigned int          const reqColorCt,
                          enum MethodForLargest const methodForLargest,
                          enum MethodForRep     const methodForRep,
                          enum MethodForSplit   const methodForSplit,
+                         bool                  const wantBvReport,
                          int *                 const formatP,
                          struct pam *          const freqPamP,
                          tupletable2 *         const colormapP) {
@@ -911,7 +1038,7 @@ computeColorMapFromInput(FILE *                const ifP,
    image stream in file 'ifP'.  Figure it out using the median cut
    technique.
 
-   The colormap will have 'reqcolors' or fewer colors in it, unless
+   The colormap will have 'reqcolorCt' or fewer colors in it, unless
    'allcolors' is true, in which case it will have all the colors that
    are in the input.
 
@@ -925,23 +1052,26 @@ computeColorMapFromInput(FILE *                const ifP,
    *formatP and *freqPamP.  (This information is not really
    relevant to our colormap mission; just a fringe benefit).
 -----------------------------------------------------------------------------*/
-    tupletable2 colorfreqtable;
+    tupletable2 colorFreqTable;
+        /* Table of all colors in the image, with the number of pixels of
+           each color.
+        */
 
-    computeHistogram(ifP, formatP, freqPamP, &colorfreqtable);
+    computeHistogram(ifP, formatP, freqPamP, &colorFreqTable);
 
     if (allColors) {
-        *colormapP = colorfreqtable;
+        *colormapP = colorFreqTable;
     } else {
-        if (colorfreqtable.size <= reqColors) {
-            pm_message("Image already has few enough colors (<=%d).  "
-                       "Keeping same colors.", reqColors);
-            *colormapP = colorfreqtable;
+        if (colorFreqTable.size <= reqColorCt) {
+            pm_message("Image already has few enough colors (<=%u).  "
+                       "Keeping same colors.", reqColorCt);
+            *colormapP = colorFreqTable;
         } else {
-            pm_message("choosing %d colors...", reqColors);
-            mediancut(colorfreqtable, freqPamP->depth,
-                      reqColors, methodForLargest, methodForRep,
-                      methodForSplit, colormapP);
-            pnm_freetupletable2(freqPamP, colorfreqtable);
+            pm_message("choosing %u colors...", reqColorCt);
+            mediancut(colorFreqTable, freqPamP->depth,
+                      reqColorCt, methodForLargest, methodForRep,
+                      methodForSplit, wantBvReport, colormapP);
+            pnm_freetupletable2(freqPamP, colorFreqTable);
         }
     }
 }
@@ -967,16 +1097,16 @@ sortColormap(tupletable2  const colormap,
             unsigned int plane;
             bool iIsGreater, iIsLess;
 
-            iIsGreater = FALSE; iIsLess = FALSE;
+            iIsGreater = false; iIsLess = false;
             for (plane = 0;
                  plane < depth && !iIsGreater && !iIsLess;
                  ++plane) {
                 if (colormap.table[i]->tuple[plane] >
                     colormap.table[j]->tuple[plane])
-                    iIsGreater = TRUE;
+                    iIsGreater = true;
                 else if (colormap.table[i]->tuple[plane] <
                          colormap.table[j]->tuple[plane])
-                    iIsLess = TRUE;
+                    iIsLess = true;
             }
             if (iIsGreater) {
                 for (plane = 0; plane < depth; ++plane) {
@@ -1077,7 +1207,7 @@ colormapToImage(int                const format,
     outpamP->size             = sizeof(*outpamP);
     outpamP->len              = PAM_STRUCT_SIZE(tuple_type);
     outpamP->format           = format,
-    outpamP->plainformat      = FALSE;
+    outpamP->plainformat      = false;
     outpamP->depth            = colormapPamP->depth;
     outpamP->maxval           = colormapPamP->maxval;
     outpamP->bytes_per_sample = pnm_bytespersample(outpamP->maxval);
@@ -1112,10 +1242,11 @@ main(int argc, const char * argv[] ) {
     ifP = pm_openr(cmdline.inputFileNm);
 
     computeColorMapFromInput(ifP,
-                             cmdline.allcolors, cmdline.newcolors,
+                             cmdline.allcolors, cmdline.newColorCt,
                              cmdline.methodForLargest,
                              cmdline.methodForRep,
                              cmdline.methodForSplit,
+                             !!cmdline.debug,
                              &format, &colormapPam, &colormap);
 
     pm_close(ifP);