diff options
Diffstat (limited to 'generator/pamstereogram.c')
-rw-r--r-- | generator/pamstereogram.c | 398 |
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[]) { } - |