about summary refs log tree commit diff
path: root/generator/pamstereogram.c
diff options
context:
space:
mode:
Diffstat (limited to 'generator/pamstereogram.c')
-rw-r--r--generator/pamstereogram.c398
1 files changed, 296 insertions, 102 deletions
diff --git a/generator/pamstereogram.c b/generator/pamstereogram.c
index 6e5f5ce0..4bc1ea92 100644
--- a/generator/pamstereogram.c
+++ b/generator/pamstereogram.c
@@ -16,7 +16,7 @@
  *
  * ----------------------------------------------------------------------
  *
- * Copyright (C) 2006-2015 Scott Pakin <scott+pbm@pakin.org>
+ * Copyright (C) 2006-2021 Scott Pakin <scott+pbm@pakin.org>
  *
  * All rights reserved.
  *
@@ -58,11 +58,12 @@
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "rand.h"
 #include "shhopt.h"
 #include "pam.h"
 
 
-enum outputType {OUTPUT_BW, OUTPUT_GRAYSCALE, OUTPUT_COLOR};
+enum OutputType {OUTPUT_BW, OUTPUT_GRAYSCALE, OUTPUT_COLOR};
 
 /* ---------------------------------------------------------------------- */
 
@@ -85,15 +86,17 @@ struct cmdlineInfo {
     unsigned int magnifypat;     /* -magnifypat option */
     int xshift;                  /* -xshift option */
     int yshift;                  /* -yshift option */
+    int yfillshift;              /* -yfillshift option */
     const char * patfile;        /* -patfile option.  Null if none */
     const char * texfile;        /* -texfile option.  Null if none */
     const char * bgcolor;        /* -bgcolor option */
     unsigned int smoothing;      /* -smoothing option */
     unsigned int randomseed;     /* -randomseed option */
     unsigned int randomseedSpec; /* -randomseed option count */
-    enum outputType outputType;  /* Type of output file */
+    enum OutputType outputType;  /* Type of output file */
     unsigned int xbegin;         /* -xbegin option */
     unsigned int xbeginSpec;     /* -xbegin option count */
+    unsigned int tileable;       /* -tileable option */
 };
 
 
@@ -152,8 +155,8 @@ parseCommandLine(int                  argc,
     unsigned int option_def_index;
 
     unsigned int patfileSpec, texfileSpec, dpiSpec, eyesepSpec, depthSpec,
-        guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec,
-      bgcolorSpec, smoothingSpec, planesSpec;
+        guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec, yfillshiftSpec,
+        bgcolorSpec, smoothingSpec, planesSpec;
 
     unsigned int blackandwhite, grayscale, color;
     const char ** planes;
@@ -193,6 +196,8 @@ parseCommandLine(int                  argc,
             &xshiftSpec,              0);
     OPTENT3(0, "yshift",          OPT_INT,    &cmdlineP->yshift,
             &yshiftSpec,              0);
+    OPTENT3(0, "yfillshift",      OPT_INT,    &cmdlineP->yfillshift,
+            &yfillshiftSpec,          0);
     OPTENT3(0, "patfile",         OPT_STRING, &cmdlineP->patfile,
             &patfileSpec,             0);
     OPTENT3(0, "texfile",         OPT_STRING, &cmdlineP->texfile,
@@ -207,6 +212,8 @@ parseCommandLine(int                  argc,
             &planesSpec,              0);
     OPTENT3(0, "xbegin",          OPT_UINT,   &cmdlineP->xbegin,
             &cmdlineP->xbeginSpec,    0);
+    OPTENT3(0, "tileable",        OPT_FLAG,   NULL,
+            (unsigned int *)&cmdlineP->tileable,  0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -279,9 +286,10 @@ parseCommandLine(int                  argc,
 
     if (!xshiftSpec)
         cmdlineP->xshift = 0;
-
     if (!yshiftSpec)
         cmdlineP->yshift = 0;
+    if (!yfillshiftSpec)
+        cmdlineP->yfillshift = 0;
 
     if (xshiftSpec && !cmdlineP->patfile)
         pm_error("-xshift is valid only with -patfile");
@@ -291,6 +299,11 @@ parseCommandLine(int                  argc,
     if (cmdlineP->makemask && cmdlineP->patfile)
         pm_error("You may not specify both -makemask and -patfile");
 
+    if (cmdlineP->tileable && cmdlineP->xbeginSpec)
+        pm_error("You may not specify both -tileable and -xbegin");
+    if (cmdlineP->tileable && !cmdlineP->patfile)
+        pm_error("-tileable is valid only with -patfile");
+
     if (cmdlineP->patfile && blackandwhite)
         pm_error("-blackandwhite is not valid with -patfile");
     if (cmdlineP->patfile && grayscale)
@@ -394,11 +407,12 @@ typedef struct outGenerator {
 
 
 
-struct randomState {
+struct RandomState {
     /* The state of a randomColor generator. */
     unsigned int magnifypat;
     tuple *      currentRow;
     unsigned int prevy;
+    struct pm_randSt * randStP;
 };
 
 
@@ -413,7 +427,7 @@ randomColor(outGenerator * const outGenP,
 /*----------------------------------------------------------------------------
    Return a random RGB value.
 -----------------------------------------------------------------------------*/
-    struct randomState * const stateP = outGenP->stateP;
+    struct RandomState * const stateP = outGenP->stateP;
 
     /* Every time we start a new row, we select a new sequence of random
        colors.
@@ -428,7 +442,7 @@ randomColor(outGenerator * const outGenP,
             unsigned int plane;
 
             for (plane = 0; plane < outGenP->pam.depth; ++plane) {
-                unsigned int const randval = rand();
+                unsigned int const randval = pm_rand(stateP->randStP);
                 thisTuple[plane] = randval % modulus;
             }
         }
@@ -448,9 +462,13 @@ static outGenStateTerm termRandomColor;
 static void
 termRandomColor(outGenerator * const outGenP) {
 
-    struct randomState * const stateP = outGenP->stateP;
+    struct RandomState * const stateP = outGenP->stateP;
 
     pnm_freepamrow(stateP->currentRow);
+
+    pm_randterm(stateP->randStP);
+
+    free(stateP->randStP);
 }
 
 
@@ -460,7 +478,7 @@ initRandomColor(outGenerator *     const outGenP,
                 const struct pam * const inPamP,
                 struct cmdlineInfo const cmdline) {
 
-    struct randomState * stateP;
+    struct RandomState * stateP;
 
     outGenP->pam.format      = PAM_FORMAT;
     outGenP->pam.plainformat = 0;
@@ -491,6 +509,10 @@ initRandomColor(outGenerator *     const outGenP,
     stateP->magnifypat = cmdline.magnifypat;
     stateP->prevy      = (unsigned int)(-cmdline.magnifypat);
 
+    MALLOCVAR_NOFAIL(stateP->randStP);
+    pm_randinit(stateP->randStP);
+    pm_srand2(stateP->randStP, cmdline.randomseedSpec, cmdline.randomseed);
+
     outGenP->stateP         = stateP;
     outGenP->getTuple       = &randomColor;
     outGenP->terminateState = &termRandomColor;
@@ -844,11 +866,12 @@ makeStereoRow(const struct pam * const inPamP,
               unsigned int       const dpi,
               unsigned int       const optWidth,
               unsigned int       const smoothing) {
+/*----------------------------------------------------------------------------
+  Given a row of the depth map inRow[], compute the sameL and sameR arrays,
+  which indicate for each pixel which pixel to its left and right it should be
+  colored the same as.
+-----------------------------------------------------------------------------*/
 
-/* Given a row of the depth map, compute the sameL and sameR arrays,
- * which indicate for each pixel which pixel to its left and right it
- * should be colored the same as.
- */
 #define Z(X) (inRow[X][0]/(double)inPamP->maxval)
 
     unsigned int col;
@@ -865,7 +888,7 @@ makeStereoRow(const struct pam * const inPamP,
 
         if (left >= 0 && right < inPamP->width) {
             bool isVisible;
-        
+
             if (sameL[right] != right) {
                 /* Right point already linked */
                 if (sameL[right] < left) {
@@ -1114,12 +1137,15 @@ static void
 makeImageRowMts(outGenerator *       const outGenP,
                 unsigned int         const row,
                 const unsigned int * const same,
-                unsigned int *       const sameFp,
+                unsigned int *       const colNumBuffer,
                 tuple *              const rowBuffer,
                 const tuple *        const outRow) {
 /*----------------------------------------------------------------------------
   Make a row of a mapped-texture stereogram.
 -----------------------------------------------------------------------------*/
+    unsigned int * const sameFp = colNumBuffer;
+        /* Fixed point of same[] */
+
     unsigned int * tuplesInCol;
         /* tuplesInCol[C] is the number of tuples averaged together to make
            Column C.
@@ -1165,8 +1191,9 @@ makeImageRowMts(outGenerator *       const outGenP,
 static void
 makeImageRow(outGenerator *       const outGenP,
              unsigned int         const row,
-             unsigned int         const optWidth,
              unsigned int         const xbegin,
+             unsigned int         const farWidth,
+             int                  const yfillshift,
              const unsigned int * const sameL,
              const unsigned int * const sameR,
              const tuple *        const outRow) {
@@ -1189,42 +1216,59 @@ makeImageRow(outGenerator *       const outGenP,
 
   sameL[N] > N is not allowed.
 -----------------------------------------------------------------------------*/
-    int col;
-    int lastLinked;
+    unsigned int const width  = outGenP->pam.width;
+    unsigned int const height = outGenP->pam.height;
 
-    for (col = (int)xbegin, lastLinked = INT_MIN;
-         col < outGenP->pam.width;
-         ++col) {
+    unsigned int col;
+    bool colHasBeenLinked;
+    unsigned int lastLinkedCol;
+        /* Last column to have been linked; meaningless if 'colHasBeenLinked'
+           is false.
+        */
+
+    for (col = xbegin, colHasBeenLinked = false; col < width; ++col) {
 
         tuple newtuple;
 
-        if (sameL[col] == col || sameL[col] < (int)xbegin) {
-            if (lastLinked == col - 1)
-                newtuple = outRow[col - 1];
-            else
-                newtuple = outGenP->getTuple(outGenP, col, row);
+        if (sameL[col] == col || sameL[col] < xbegin) {
+            if (colHasBeenLinked && lastLinkedCol == col - 1)
+                newtuple = outRow[lastLinkedCol];
+            else {
+                if (col < xbegin + farWidth)
+                    newtuple = outGenP->getTuple(outGenP, col, row);
+                else
+                    newtuple = outGenP->getTuple(
+                        outGenP, col, (row + height - yfillshift) % height);
+            }
         } else {
-          newtuple = outRow[sameL[col]];
-          lastLinked = col;
-              /* Keep track of the last pixel to be constrained. */
+            newtuple = outRow[sameL[col]];
+            colHasBeenLinked = true;
+            lastLinkedCol = col;
+                /* Keep track of the last pixel to be constrained. */
         }
         pnm_assigntuple(&outGenP->pam, outRow[col], newtuple);
     }
 
-    for (col = (int)xbegin - 1, lastLinked = INT_MIN; col >= 0; --col) {
+    for (col = xbegin, colHasBeenLinked = false; col > 0; --col) {
         tuple newtuple;
 
-        if (sameR[col] == col) {
-            if (lastLinked == col + 1)
-                newtuple = outRow[col + 1];
-            else
-                newtuple = outGenP->getTuple(outGenP, col, row);
+        if (sameR[col-1] == col-1) {
+            if (colHasBeenLinked && lastLinkedCol == col)
+                newtuple = outRow[lastLinkedCol];
+            else {
+                if (col > xbegin - farWidth)
+                    newtuple = outGenP->getTuple(outGenP, col-1, row);
+                else
+                    newtuple = outGenP->getTuple(
+                        outGenP, col-1, (row + height - yfillshift) % height);
+            }
         } else {
-            newtuple = outRow[sameR[col]];
-            lastLinked = col;
+            newtuple = outRow[sameR[col-1]];
+            colHasBeenLinked = true;
+            lastLinkedCol = col - 1;
                 /* Keep track of the last pixel to be constrained. */
         }
-        pnm_assigntuple(&outGenP->pam, outRow[col], newtuple);
+        pnm_assigntuple(&outGenP->pam, outRow[col-1], newtuple);
     }
 }
 
@@ -1243,6 +1287,174 @@ invertHeightRow(const struct pam * const heightPamP,
 
 
 static void
+makeOneImageRow(unsigned int         const row,
+                outGenerator *       const outputGeneratorP,
+                bool                 const makeMask,
+                const unsigned int * const sameL,
+                const unsigned int * const sameR,
+                unsigned int *       const colNumBuffer,
+                unsigned int         const xbegin,
+                unsigned int         const farWidth,
+                int                  const yfillshift,
+                tuple *              const rowBuffer,
+                tuple *              const outRow) {
+
+    if (makeMask)
+        makeMaskRow(&outputGeneratorP->pam, xbegin, sameL, sameR, outRow);
+    else {
+        if (outputGeneratorP->textureP)
+            makeImageRowMts(outputGeneratorP, row, sameR, colNumBuffer,
+                            rowBuffer, outRow);
+        else
+            makeImageRow(outputGeneratorP, row,
+                         xbegin, farWidth, yfillshift, sameL, sameR, outRow);
+    }
+}
+
+
+
+static void
+constructRowTileable(const struct pam *   const inPamP,
+                     outGenerator *       const outputGeneratorP,
+                     bool                 const makeMask,
+                     const unsigned int * const sameL,
+                     const unsigned int * const sameR,
+                     unsigned int         const farWidth,
+                     int                  const yfillshift,
+                     unsigned int         const row,
+                     tuple *              const outRow,
+                     tuple *              const outRowBuf1,
+                     tuple *              const outRowBuf2,
+                     unsigned int *       const colNumBuf2) {
+
+    tuple * const outRowMax = outRowBuf1;
+
+    unsigned int col;
+
+    /* Create two rows with extreme xbegin values and blend the second
+       into the first.  outRow[] serves as both the buffer for the xbegin=0
+       version and the merged output.  outRowMax[] is the buffer for the
+       xbegin=maximum case.
+    */
+    makeOneImageRow(row, outputGeneratorP, makeMask, sameL, sameR, colNumBuf2,
+                    farWidth, yfillshift, 0, outRowBuf2, outRow);
+
+    makeOneImageRow(row, outputGeneratorP, makeMask, sameL, sameR, colNumBuf2,
+                    farWidth, yfillshift, inPamP->width - 1, outRowBuf2, outRowMax);
+
+    for (col = 0; col < inPamP->width; ++col) {
+        unsigned int plane;
+        unsigned int oplane;
+
+        if (outputGeneratorP->pam.have_opacity)
+            oplane = outputGeneratorP->pam.opacity_plane;
+
+        for (plane = 0; plane < outputGeneratorP->pam.color_depth; ++plane) {
+
+            sample samp, sampMax;
+
+            if (outputGeneratorP->pam.have_opacity) {
+                /* If one sample is fully transparent, use the
+                   other sample for both purposes
+                */
+                if (outRow[col][oplane] == 0)
+                    samp = sampMax = outRowMax[col][plane];
+                else if (outRowMax[col][oplane] == 0)
+                    samp = sampMax = outRow[col][plane];
+                else {
+                    samp    = outRow[col][plane];
+                    sampMax = outRowMax[col][plane];
+                }
+            } else {
+                samp    = outRow[col][plane];
+                sampMax = outRowMax[col][plane];
+            }
+
+            outRow[col][plane] =
+                (col*sampMax + (inPamP->width - col - 1)*samp) /
+                (inPamP->width - 1);
+        }
+        if (outputGeneratorP->pam.have_opacity) {
+            sample samp, sampMax;
+
+            /* Take the maximum alpha for partially transparent samples. */
+            samp    = outRow[col][oplane];
+            sampMax = outRowMax[col][oplane];
+            outRow[col][oplane] = MAX(samp, sampMax);
+        }
+    }
+}
+
+
+
+static void
+doRow(const struct pam * const inPamP,
+      outGenerator *     const outputGeneratorP,
+      double             const depthOfField,
+      double             const eyesep,
+      unsigned int       const dpi,
+      bool               const crossEyed,
+      bool               const makeMask,
+      bool               const tileable,
+      unsigned int       const magnifypat,
+      unsigned int       const smoothing,
+      unsigned int       const xbegin,
+      unsigned int       const farWidth,
+      int                const yfillshift,
+      unsigned int       const row,
+      tuple *            const inRow,
+      tuple *            const outRowBuf0,
+      tuple *            const outRowBuf1,
+      tuple *            const outRowBuf2,
+      unsigned int *     const colNumBuf0,
+      unsigned int *     const colNumBuf1,
+      unsigned int *     const colNumBuf2) {
+
+    tuple *        const outRow = outRowBuf0;
+    unsigned int * const sameL  = colNumBuf0;
+        /* sameL[N] is the column number of a pixel to the
+           left forced to have the same color as the one in column N
+        */
+    unsigned int * const sameR = colNumBuf1;
+        /* sameR[N] is the column number of a pixel to the
+           right forced to have the same color as the one in column N
+        */
+
+    pnm_readpamrow(inPamP, inRow);
+
+    if (crossEyed)
+        /* Invert heights for cross-eyed (as opposed to wall-eyed)
+           people.
+        */
+        invertHeightRow(inPamP, inRow);
+
+    /* Determine color constraints. */
+    makeStereoRow(inPamP, inRow, sameL, sameR, depthOfField, eyesep, dpi,
+                  ROUNDU(eyesep * dpi)/(magnifypat * 2),
+                  smoothing);
+
+    /* Construct a single row. */
+    if (tileable) {
+        constructRowTileable(inPamP, outputGeneratorP, makeMask,
+                             sameL, sameR,
+                             farWidth, yfillshift,
+                             row, outRow,
+                             outRowBuf1, outRowBuf2, colNumBuf2);
+
+    } else {
+        makeOneImageRow(row, outputGeneratorP, makeMask,
+                        sameL, sameR, colNumBuf2,
+                        xbegin, farWidth, yfillshift,
+                        outRowBuf1, outRow);
+    }
+
+    /* Write the resulting row. */
+    pnm_writepamrow(&outputGeneratorP->pam, outRow);
+}
+
+
+
+static void
 makeImageRows(const struct pam * const inPamP,
               outGenerator *     const outputGeneratorP,
               double             const depthOfField,
@@ -1250,72 +1462,56 @@ makeImageRows(const struct pam * const inPamP,
               unsigned int       const dpi,
               bool               const crossEyed,
               bool               const makeMask,
+              bool               const tileable,
               unsigned int       const magnifypat,
               unsigned int       const smoothing,
-              unsigned int       const xbegin) {
-
-    tuple * inRow;     /* One row of pixels read from the height-map file */
-    tuple * outRow;    /* One row of pixels to write to the height-map file */
-    unsigned int * sameR;
-        /* Malloced array: sameR[N] is the column number of a pixel to the
-           right forced to have the same color as the one in column N
-        */
-    unsigned int * sameL;
-        /* Malloced array: sameL[N] is the column number of a pixel to the
-           left forced to have the same color as the one in column N
-        */
-    unsigned int * sameRfp;
-        /* Malloced array: Fixed point of sameR[] */
-    tuple * rowBuffer;     /* Scratch row needed for texture manipulation */
+              unsigned int       const xbegin,
+              int                const yfillshift) {
+
+    tuple * inRow;    /* Buffer for use in reading from the height-map image */
+    tuple * outRowBuf0; /* Buffer for use in generating output rows */
+    tuple * outRowBuf1; /* Buffer for use in generating output rows */
+    tuple * outRowBuf2; /* Buffer for use in generating output rows */
+    unsigned int * colNumBuf0;
+    unsigned int * colNumBuf1;
+    unsigned int * colNumBuf2;
     unsigned int row;      /* Current row in the input and output files */
+    unsigned int pixelEyesep;
+    unsigned int farWidth;
 
     inRow = pnm_allocpamrow(inPamP);
-    outRow = pnm_allocpamrow(&outputGeneratorP->pam);
-    MALLOCARRAY(sameR, inPamP->width);
-    if (sameR == NULL)
-        pm_error("Unable to allocate space for \"sameR\" array.");
-    MALLOCARRAY(sameL, inPamP->width);
-    if (sameL == NULL)
-        pm_error("Unable to allocate space for \"sameL\" array.");
-
-    MALLOCARRAY(sameRfp, inPamP->width);
-    if (sameRfp == NULL)
-        pm_error("Unable to allocate space for \"sameRfp\" array.");
-    rowBuffer = pnm_allocpamrow(&outputGeneratorP->pam);
+    outRowBuf0 = pnm_allocpamrow(&outputGeneratorP->pam);
+    outRowBuf1 = pnm_allocpamrow(&outputGeneratorP->pam);
+    outRowBuf2 = pnm_allocpamrow(&outputGeneratorP->pam);
+    MALLOCARRAY(colNumBuf0, inPamP->width);
+    if (colNumBuf0 == NULL)
+        pm_error("Unable to allocate space for %u column buffer",
+                 inPamP->width);
+    MALLOCARRAY(colNumBuf1, inPamP->width);
+    if (colNumBuf1 == NULL)
+        pm_error("Unable to allocate space for %u column buffer",
+                 inPamP->width);
+    MALLOCARRAY(colNumBuf2, inPamP->width);
+    if (colNumBuf2 == NULL)
+        pm_error("Unable to allocate space for %u column buffer",
+                 inPamP->width);
+
+    pixelEyesep = ROUNDU(eyesep * dpi);
+    farWidth = pixelEyesep/(magnifypat * 2);
 
     for (row = 0; row < inPamP->height; ++row) {
-        pnm_readpamrow(inPamP, inRow);
-        if (crossEyed)
-            /* Invert heights for cross-eyed (as opposed to wall-eyed)
-               people.
-            */
-            invertHeightRow(inPamP, inRow);
-
-        /* Determine color constraints. */
-        makeStereoRow(inPamP, inRow, sameL, sameR, depthOfField, eyesep, dpi,
-                      ROUNDU(eyesep * dpi)/(magnifypat * 2),
-                      smoothing);
-
-        if (makeMask)
-            makeMaskRow(&outputGeneratorP->pam, xbegin, sameL, sameR, outRow);
-        else {
-            if (outputGeneratorP->textureP)
-                makeImageRowMts(outputGeneratorP, row, sameR, sameRfp,
-                                rowBuffer, outRow);
-            else
-                makeImageRow(outputGeneratorP, row,
-                             ROUNDU(eyesep * dpi)/(magnifypat * 2),
-                             xbegin, sameL, sameR, outRow);
-        }
-        /* Write the resulting row. */
-        pnm_writepamrow(&outputGeneratorP->pam, outRow);
+        doRow(inPamP, outputGeneratorP,  depthOfField, eyesep, dpi,
+              crossEyed, makeMask, tileable, magnifypat, smoothing, xbegin,
+              farWidth, yfillshift, row,
+              inRow, outRowBuf0, outRowBuf1, outRowBuf2,
+              colNumBuf0, colNumBuf1, colNumBuf2);
     }
 
-    pnm_freepamrow(rowBuffer);
-    free(sameRfp);
-    free(sameL);
-    free(sameR);
-    pnm_freepamrow(outRow);
+    free(colNumBuf2);
+    free(colNumBuf1);
+    free(colNumBuf0);
+    pnm_freepamrow(outRowBuf1);
+    pnm_freepamrow(outRowBuf0);
     pnm_freepamrow(inRow);
 }
 
@@ -1361,8 +1557,9 @@ produceStereogram(FILE *             const ifP,
 
     makeImageRows(&inPam, outputGeneratorP,
                   cmdline.depth, cmdline.eyesep, cmdline.dpi,
-                  cmdline.crosseyed, cmdline.makemask, cmdline.magnifypat,
-                  cmdline.smoothing, xbegin);
+                  cmdline.crosseyed, cmdline.makemask, cmdline.tileable,
+                  cmdline.magnifypat, cmdline.smoothing, xbegin,
+                  cmdline.yfillshift);
 
     if (cmdline.guidebottom)
         drawguides(cmdline.guidesize, &outputGeneratorP->pam,
@@ -1419,8 +1616,6 @@ main(int argc, const char *argv[]) {
     if (cmdline.verbose)
         reportParameters(cmdline);
 
-    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
-
     ifP = pm_openr(cmdline.inputFilespec);
 
     /* Produce a stereogram. */
@@ -1432,4 +1627,3 @@ main(int argc, const char *argv[]) {
 }
 
 
-