about summary refs log tree commit diff
path: root/other
diff options
context:
space:
mode:
Diffstat (limited to 'other')
-rw-r--r--other/pamstack.c189
-rw-r--r--other/pnmcolormap.c173
2 files changed, 253 insertions, 109 deletions
diff --git a/other/pamstack.c b/other/pamstack.c
index 308852c8..75b66cb7 100644
--- a/other/pamstack.c
+++ b/other/pamstack.c
@@ -23,22 +23,30 @@
 #define MAX_INPUTS 16
     /* The most input PAMs we allow user to specify */
 
-struct cmdlineInfo {
+enum MaxvalScaling {
+    /* How to scale maxvals if the inputs don't all have the same maxval */
+    MAXVALSCALE_NONE,  /* Don't scale -- fail program */
+    MAXVALSCALE_FIRST, /* Scale everything to maxval of first input */
+    MAXVALSCALE_LCM    /* Scale everything to least common multiple */
+};
+
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *tupletype;       /* Tuple type for output PAM */
     unsigned int nInput;
         /* The number of input PAMs.  At least 1, at most 16. */
     const char * inputFileName[MAX_INPUTS];
         /* The PAM files to combine, in order. */
+    const char * tupletype;
+    enum MaxvalScaling maxvalScaling;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec strings we return are stored in the storage that
    was passed to us as the argv array.
@@ -50,19 +58,21 @@ parseCommandLine(int argc, char ** argv,
     extern struct pam pam;  /* Just so we can look at field sizes */
 
     unsigned int option_def_index;
-    unsigned int tupletypeSpec;
+    unsigned int tupletypeSpec, firstmaxvalSpec, lcmmaxvalSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
-    
+
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "tupletype",  OPT_STRING, &cmdlineP->tupletype, 
+    OPTENT3(0, "tupletype",   OPT_STRING, &cmdlineP->tupletype,
             &tupletypeSpec, 0);
+    OPTENT3(0, "firstmaxval", OPT_FLAG, NULL, &firstmaxvalSpec, 0);
+    OPTENT3(0, "lcmmaxval",   OPT_FLAG, NULL, &lcmmaxvalSpec,   0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have 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 *cmdlineP and others. */
 
     if (!tupletypeSpec)
@@ -73,12 +83,25 @@ parseCommandLine(int argc, char ** argv,
                      "%u characters allowed.",
                      (unsigned)sizeof(pam.tuple_type));
 
+    if (firstmaxvalSpec) {
+        if (lcmmaxvalSpec)
+            pm_error("Cannot specify both -lcmmaxval and -firstmaxval");
+        else
+            cmdlineP->maxvalScaling = MAXVALSCALE_FIRST;
+    } else if (lcmmaxvalSpec) {
+        if (firstmaxvalSpec)
+            pm_error("Cannot specify both -lcmmaxval and -firstmaxval");
+        else
+            cmdlineP->maxvalScaling = MAXVALSCALE_LCM;
+    } else
+        cmdlineP->maxvalScaling = MAXVALSCALE_NONE;
+
     cmdlineP->nInput = 0;  /* initial value */
-    { 
+    {
         unsigned int argn;
         bool stdinUsed;
         for (argn = 1, stdinUsed = false; argn < argc; ++argn) {
-            if (cmdlineP->nInput >= MAX_INPUTS) 
+            if (cmdlineP->nInput >= MAX_INPUTS)
                 pm_error("You may not specify more than %u input images.",
                          MAX_INPUTS);
             cmdlineP->inputFileName[cmdlineP->nInput++] = argv[argn];
@@ -110,51 +133,71 @@ openAllStreams(unsigned int  const nInput,
 
 
 static void
-outputRaster(const struct pam       inpam[], 
-             unsigned int     const nInput,
-             struct pam             outpam) {
+outputRaster(const struct pam * const inpam,  /* array */
+             unsigned int       const nInput,
+             struct pam         const outpam) {
+/*----------------------------------------------------------------------------
+   Write the raster of the output image according to 'outpam'.  Compose it
+   from the 'nInput' input images described by 'inpam'.
+
+   'outpam' may indicate a different maxval from some or all of the input
+   images.
+-----------------------------------------------------------------------------*/
+    tuple * inrow;
+    tuple * outrow;
 
-    tuple *inrow;
-    tuple *outrow;
-        
     outrow = pnm_allocpamrow(&outpam);
-    inrow = pnm_allocpamrow(&outpam);      
+    inrow  = pnm_allocpamrow(&outpam);
 
-    { 
-        int row;
-        
-        for (row = 0; row < outpam.height; row++) {
+    {
+        unsigned int row;
+
+        for (row = 0; row < outpam.height; ++row) {
             unsigned int inputSeq;
-            int outplane;
-            outplane = 0;  /* initial value */
-            for (inputSeq = 0; inputSeq < nInput; ++inputSeq) {
+            unsigned int outPlane;
+
+            for (inputSeq = 0, outPlane = 0; inputSeq < nInput; ++inputSeq) {
                 struct pam thisInpam = inpam[inputSeq];
-                int col;
+                unsigned int col;
 
                 pnm_readpamrow(&thisInpam, inrow);
 
-                for (col = 0; col < outpam.width; col ++) {
-                    int inplane;
-                    for (inplane = 0; inplane < thisInpam.depth; ++inplane) 
-                        outrow[col][outplane+inplane] = inrow[col][inplane];
+                pnm_scaletuplerow(&thisInpam, inrow, inrow, outpam.maxval);
+
+                for (col = 0; col < outpam.width; ++col) {
+                    unsigned int inPlane;
+                    for (inPlane = 0; inPlane < thisInpam.depth; ++inPlane) {
+                        outrow[col][outPlane+inPlane] = inrow[col][inPlane];
+                    }
                 }
-                outplane += thisInpam.depth;
+                outPlane += thisInpam.depth;
             }
             pnm_writepamrow(&outpam, outrow);
         }
     }
     pnm_freepamrow(outrow);
-    pnm_freepamrow(inrow);        
+    pnm_freepamrow(inrow);
 }
 
 
 
 static void
-processOneImageInAllStreams(unsigned int const nInput,
-                            FILE *       const ifP[],
-                            FILE *       const ofP,
-                            const char * const tupletype) {
+processOneImageInAllStreams(unsigned int       const nInput,
+                            FILE *             const ifP[],
+                            FILE *             const ofP,
+                            const char *       const tupletype,
+                            enum MaxvalScaling const maxvalScaling) {
+/*----------------------------------------------------------------------------
+   Take one image from each of the 'nInput' open input streams ifP[]
+   and stack them into one output image on *ofP.
 
+   Take the images from the current positions of those streams and leave
+   the streams positioned after them.
+
+   Make the output image have tuple type 'tupletype'.
+
+   Scale input samples for output according to 'maxvalScaling'.
+-----------------------------------------------------------------------------*/
     struct pam inpam[MAX_INPUTS];   /* Input PAM images */
     struct pam outpam;  /* Output PAM image */
 
@@ -166,30 +209,61 @@ processOneImageInAllStreams(unsigned int const nInput,
 
     unsigned int outputDepth;
     outputDepth = 0;  /* initial value */
-    
-    for (inputSeq = 0; inputSeq < nInput; ++inputSeq) {
+    sample maxvalLcm;
+        /* Least common multiple of all maxvals or PNM_OVERALLMAXVAL if the
+           LCM is greater than that.
+        */
+    bool allImagesSameMaxval;
+        /* The images all have the same maxval */
 
-        pnm_readpaminit(ifP[inputSeq], &inpam[inputSeq], 
+    for (inputSeq = 0, allImagesSameMaxval = true, maxvalLcm = 1;
+         inputSeq < nInput;
+         ++inputSeq) {
+
+        pnm_readpaminit(ifP[inputSeq], &inpam[inputSeq],
                         PAM_STRUCT_SIZE(tuple_type));
 
-        if (inputSeq > 0) {
-            /* All images, including this one, must be compatible with the 
-               first image.
-            */
-            if (inpam[inputSeq].width != inpam[0].width)
-                pm_error("Image no. %u does not have the same width as "
-                         "Image 0.", inputSeq);
-            if (inpam[inputSeq].height != inpam[0].height)
-                pm_error("Image no. %u does not have the same height as "
-                         "Image 0.", inputSeq);
-            if (inpam[inputSeq].maxval != inpam[0].maxval)
-                pm_error("Image no. %u does not have the same maxval as "
-                         "Image 0.", inputSeq);
-        }
+        /* All images, including this one, must have same dimensions as
+           the first image.
+        */
+        if (inpam[inputSeq].width != inpam[0].width)
+            pm_error("Image no. %u does not have the same width as "
+                     "Image 0.", inputSeq);
+        if (inpam[inputSeq].height != inpam[0].height)
+            pm_error("Image no. %u does not have the same height as "
+                     "Image 0.", inputSeq);
+
+        if (inpam[inputSeq].maxval != inpam[0].maxval)
+            allImagesSameMaxval = false;
+
+        maxvalLcm = pm_lcm(maxvalLcm, inpam[inputSeq].maxval, 1,
+                           PAM_OVERALL_MAXVAL);
+
         outputDepth += inpam[inputSeq].depth;
     }
 
     outpam        = inpam[0];     /* Initial value */
+
+    switch (maxvalScaling) {
+    case MAXVALSCALE_NONE:
+        if (!allImagesSameMaxval)
+            pm_message("Inputs do not all have same maxval.  "
+                       "Consider -firstmaxval or -lcmmaxval");
+        outpam.maxval = inpam[0].maxval;
+        break;
+    case MAXVALSCALE_FIRST:
+        outpam.maxval = inpam[0].maxval;
+        if (!allImagesSameMaxval)
+            pm_message("Input maxvals vary; making output maxval %lu "
+                       "per -firstmaxval", outpam.maxval);
+        break;
+    case MAXVALSCALE_LCM:
+        outpam.maxval = maxvalLcm;
+        if (!allImagesSameMaxval)
+            pm_message("Input maxvals vary; making output maxval %lu "
+                       "per -lcmmaxval", outpam.maxval);
+        break;
+    }
     outpam.depth  = outputDepth;
     outpam.file   = ofP;
     outpam.format = PAM_FORMAT;
@@ -226,13 +300,13 @@ nextImageAllStreams(unsigned int const nInput,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP[MAX_INPUTS];
     bool eof;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -241,10 +315,13 @@ main(int argc, char *argv[]) {
     eof = FALSE;
     while (!eof) {
         processOneImageInAllStreams(cmdline.nInput, ifP, stdout,
-                                    cmdline.tupletype);
+                                    cmdline.tupletype, cmdline.maxvalScaling);
 
         nextImageAllStreams(cmdline.nInput, ifP, &eof);
     }
 
     return 0;
 }
+
+
+
diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c
index fbe85d4e..15d664d7 100644
--- a/other/pnmcolormap.c
+++ b/other/pnmcolormap.c
@@ -42,6 +42,8 @@ struct Box {
    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;
@@ -138,14 +140,14 @@ 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, "debug",     OPT_FLAG,   NULL,
-            &cmdlineP->debug,      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 */
@@ -220,28 +222,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;
 }
 
 
@@ -259,7 +296,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;
 }
 
@@ -279,6 +318,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;
 }
 
@@ -298,6 +339,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;
 }
 
@@ -673,6 +716,44 @@ 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,
          enum MethodForLargest const methodForLargest,
@@ -693,19 +774,14 @@ splitBox(struct BoxVector *    const boxVectorP,
     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;
+    compareColorParm.comparePlane    = boxVectorP->box[boxIdx].maxdim;
+    compareColorParm.colorDepth      = boxVectorP->colorDepth;
     qsort((char*) &boxVectorP->colorFreqTable.table[boxStart], boxSize,
           sizeof(boxVectorP->colorFreqTable.table[boxStart]),
-          compareplane);
+          compareColor);
 
     {
         /* Find the median based on the counts, so that about half the pixels
@@ -720,27 +796,18 @@ splitBox(struct BoxVector *    const boxVectorP,
         }
         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, boxVectorP->colorFreqTable,
-                         boxVectorP->colorDepth, methodForLargest,
-                         &oldBoxP->maxdim, &oldBoxP->spread);
-    }
-    {
-        struct Box * const newBoxP = &boxVectorP->box[boxVectorP->boxCt];
-
-        newBoxP->startIndex = boxStart + medianIndex;
-        newBoxP->colorCt    = boxSize - medianIndex;
-        newBoxP->sum        = sum - lowerSum;
-        computeBoxSpread(newBoxP, boxVectorP->colorFreqTable,
-                         boxVectorP->colorDepth, methodForLargest,
-                         &newBoxP->maxdim, &newBoxP->spread);
-        ++boxVectorP->boxCt;
-    }
+    makeNewBox(boxVectorP,
+               boxStart + medianIndex, boxSize - medianIndex,
+               sum - lowerSum,
+               methodForLargest);
 
     sortBoxes(boxVectorP, methodForSplit);
 }
@@ -1183,7 +1250,7 @@ main(int argc, const char * argv[] ) {
                              cmdline.methodForLargest,
                              cmdline.methodForRep,
                              cmdline.methodForSplit,
-                             cmdline.debug,
+                             !!cmdline.debug,
                              &format, &colormapPam, &colormap);
 
     pm_close(ifP);