about summary refs log tree commit diff
path: root/other
diff options
context:
space:
mode:
Diffstat (limited to 'other')
-rw-r--r--other/pamstack.c154
1 files changed, 114 insertions, 40 deletions
diff --git a/other/pamstack.c b/other/pamstack.c
index e5340129..75b66cb7 100644
--- a/other/pamstack.c
+++ b/other/pamstack.c
@@ -23,15 +23,23 @@
 #define MAX_INPUTS 16
     /* The most input PAMs we allow user to specify */
 
+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;
 };
 
 
@@ -50,13 +58,15 @@ parseCommandLine(int argc, const 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 */
@@ -73,6 +83,19 @@ parseCommandLine(int argc, const 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;
@@ -110,35 +133,44 @@ 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'.
 
-    tuple *inrow;
-    tuple *outrow;
+   'outpam' may indicate a different maxval from some or all of the input
+   images.
+-----------------------------------------------------------------------------*/
+    tuple * inrow;
+    tuple * outrow;
 
     outrow = pnm_allocpamrow(&outpam);
-    inrow = pnm_allocpamrow(&outpam);
+    inrow  = pnm_allocpamrow(&outpam);
 
     {
-        int row;
+        unsigned int row;
 
-        for (row = 0; row < outpam.height; 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);
         }
@@ -150,11 +182,22 @@ outputRaster(const struct pam       inpam[],
 
 
 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 */
+    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 */
 
-    for (inputSeq = 0; inputSeq < nInput; ++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;
@@ -241,7 +315,7 @@ main(int argc, const char *argv[]) {
     eof = FALSE;
     while (!eof) {
         processOneImageInAllStreams(cmdline.nInput, ifP, stdout,
-                                    cmdline.tupletype);
+                                    cmdline.tupletype, cmdline.maxvalScaling);
 
         nextImageAllStreams(cmdline.nInput, ifP, &eof);
     }