diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2019-06-28 23:07:55 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2019-06-28 23:07:55 +0000 |
commit | 11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32 (patch) | |
tree | 7c40f096dd973943ef563ec87b2a68d8205db4fb /generator | |
parent | 89c6ec14eb7514630aea5abc4b90b51d1473d33a (diff) | |
download | netpbm-mirror-11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32.tar.gz netpbm-mirror-11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32.tar.xz netpbm-mirror-11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32.zip |
Promote Stable to Super_stable
git-svn-id: http://svn.code.sf.net/p/netpbm/code/super_stable@3640 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'generator')
-rw-r--r-- | generator/Makefile | 7 | ||||
-rw-r--r-- | generator/pamcrater.c | 514 | ||||
-rw-r--r-- | generator/pamgauss.c | 20 | ||||
-rw-r--r-- | generator/pamgradient.c | 8 | ||||
-rw-r--r-- | generator/pamseq.c | 7 | ||||
-rw-r--r-- | generator/pamshadedrelief.c | 250 | ||||
-rw-r--r-- | generator/pamstereogram.c | 947 | ||||
-rw-r--r-- | generator/pbmmake.c | 25 | ||||
-rw-r--r-- | generator/pbmmake.test | 9 | ||||
-rw-r--r-- | generator/pbmpage.c | 251 | ||||
-rw-r--r-- | generator/pbmtext.c | 9 | ||||
-rw-r--r-- | generator/pbmtextps.c | 43 | ||||
-rwxr-xr-x | generator/pgmcrater | 94 | ||||
-rw-r--r-- | generator/pgmcrater.c | 382 | ||||
-rw-r--r-- | generator/pgmkernel.c | 288 | ||||
-rw-r--r-- | generator/pgmmake.c | 20 | ||||
-rw-r--r-- | generator/pgmnoise.c | 105 | ||||
-rw-r--r-- | generator/pgmramp.c | 62 | ||||
-rw-r--r-- | generator/ppmcie.c | 351 | ||||
-rw-r--r-- | generator/ppmcolors.c | 9 | ||||
-rw-r--r-- | generator/ppmforge.c | 417 | ||||
-rw-r--r-- | generator/ppmmake.c | 20 | ||||
-rw-r--r-- | generator/ppmpat.c | 42 | ||||
-rwxr-xr-x | generator/ppmrainbow | 26 | ||||
-rw-r--r-- | generator/ppmrough.c | 530 |
25 files changed, 2931 insertions, 1505 deletions
diff --git a/generator/Makefile b/generator/Makefile index 3c30cdd0..d0ea6b60 100644 --- a/generator/Makefile +++ b/generator/Makefile @@ -14,9 +14,10 @@ include $(BUILDDIR)/config.mk # This package is so big, it's useful even when some parts won't # build. -PORTBINARIES = pamgauss pamgradient pamseq pamstereogram \ +PORTBINARIES = pamcrater pamgauss pamgradient \ + pamseq pamshadedrelief pamstereogram \ pbmpage pbmmake pbmtext pbmtextps pbmupc \ - pgmcrater pgmkernel pgmmake pgmnoise pgmramp \ + pgmkernel pgmmake pgmnoise pgmramp \ ppmcie ppmcolors ppmforge ppmmake ppmpat ppmrough ppmwheel \ # We don't include programs that have special library dependencies in the @@ -28,7 +29,7 @@ MERGEBINARIES = $(PORTBINARIES) BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES) -SCRIPTS = ppmrainbow +SCRIPTS = pgmcrater ppmrainbow OBJECTS = $(BINARIES:%=%.o) diff --git a/generator/pamcrater.c b/generator/pamcrater.c new file mode 100644 index 00000000..8c1fda40 --- /dev/null +++ b/generator/pamcrater.c @@ -0,0 +1,514 @@ +/*============================================================================= + pamcrater +=============================================================================== + Fractal cratering + + This is derived from John Walker's 'pgmcrater' which not only creates + the terrain map as this program does, but then does a relief filter to + convert it to a shaded visual image. + + The algorithm used to determine crater size is as described on + pages 31 and 32 of: + + Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal + Images, New York: Springer Verlag, 1988. + + The mathematical technique used to calculate crater radii that + obey the proper area law distribution from a uniformly distributed + pseudorandom sequence was developed by Rudy Rucker. + + The original program carried this attribution and license: + + Designed and implemented in November of 1989 by: + + John Walker + Autodesk SA + Avenue des Champs-Montants 14b + CH-2074 MARIN + Switzerland + Usenet: kelvin@Autodesk.com + Fax: 038/33 88 15 + Voice: 038/33 76 33 + + Permission to use, copy, modify, and distribute this software and + its documentation for any purpose and without fee is hereby + granted, without any conditions or restrictions. This software is + provided "as is" without express or implied warranty. + +=============================================================================*/ + +/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at + right edge. Make craters wrap around the image (enables tiling of image). + */ + +#define _XOPEN_SOURCE /* get M_PI in math.h */ + +#include <assert.h> +#include <math.h> + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "shhopt.h" +#include "nstring.h" +#include "pam.h" + + +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + unsigned int number; + unsigned int height; + unsigned int width; + unsigned int randomseedSpec; + unsigned int randomseed; + unsigned int verbose; + unsigned int test; + unsigned int radius; + int offset; +}; + + + +static void +parseCommandLine(int argc, const char ** const argv, + struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + unsigned int option_def_index; + + unsigned int numberSpec, heightSpec, widthSpec, radiusSpec, offsetSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "number", OPT_UINT, &cmdlineP->number, + &numberSpec, 0); + OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, + &heightSpec, 0); + OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, + &widthSpec, 0); + OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, + &cmdlineP->randomseedSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + OPTENT3(0, "test", OPT_FLAG, NULL, + &cmdlineP->test, 0); + OPTENT3(0, "radius", OPT_UINT, &cmdlineP->radius, + &radiusSpec, 0); + OPTENT3(0, "offset", OPT_INT, &cmdlineP->offset, + &offsetSpec, 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, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 > 0) + pm_error("There are no non-option arguments. You specified %u", + argc-1); + + if (!heightSpec) + cmdlineP->height = 256; + + if (cmdlineP->height == 0) + pm_error("-height must be positive"); + + if (!widthSpec) + cmdlineP->width = 256; + + if (cmdlineP->width == 0) + pm_error("-width must be positive"); + + if (!offsetSpec) + cmdlineP->offset=0; + + if (cmdlineP->test) { + if (!radiusSpec) + pm_error("With -test, you must specify -radius"); + else { + if(MAX(cmdlineP->height, cmdlineP->width) * 2 < cmdlineP->radius) + pm_error("Radius (%u) too large", cmdlineP->radius); + + if (numberSpec) + pm_error("-number is meaningless with -test"); + + if (cmdlineP->randomseedSpec) + pm_error("-randomseed is meaningless with -test"); + } + } else { + if (radiusSpec) + pm_error("-radius is meaningful only with -test"); + + if (offsetSpec) + pm_error("-offset is meaningful only with -test"); + + if (!numberSpec) + cmdlineP->number = 50000; + + if (cmdlineP->number == 0) + pm_error("-number must be positive"); + } + free(option_def); +} + + + +static double const arand = 32767.0; /* Random number parameters */ +static double const CdepthPower = 1.5; /* Crater depth power factor */ +static double const DepthBias2 = 0.5; /* Square of depth bias */ + + + +static double const +cast(double const high) { +/*---------------------------------------------------------------------------- + A random number in the range [0, 'high']. +-----------------------------------------------------------------------------*/ + return high * ((rand() & 0x7FFF) / arand); +} + + + +static unsigned int +mod(int const t, + unsigned int const n) { + + /* This is used to transform coordinates beyond bounds into ones + within: craters "wrap around" the edges. This enables tiling + of the image. + + Produces strange effects when crater radius is very large compared + to image size. + */ + + int m; + + m = t % (int)n; + + if (m < 0) + m += n; + + return m; +} + + + +static sample * +terrainModP(struct pam * const pamP, + tuple ** const terrain, + int const x, + int const y) { +/*---------------------------------------------------------------------------- + A pointer to the sample in 'terrain' of an image described by *pamP that is + at Column 'x' of Row 'y', but modulus the image size. + + So e.g. if the image is 10 x 10 and 'x' and 'y' are both 12, our value + would be a pointer to the sample at Column 2 or Row 2. If they are both + -1, we would point to Column 9, Row 9. +-----------------------------------------------------------------------------*/ + return &terrain[mod(y, pamP->height)][mod(x, pamP->width)][0]; +} + + + + +static sample +terrainMod(struct pam * const pamP, + tuple ** const terrain, + int const x, + int const y) { +/*---------------------------------------------------------------------------- + The value of the sample in 'terrain' of an image described by *pamP that is + at Column 'x' of Row 'y', but modulus the image size. + + So e.g. if the image is 10 x 10 and 'x' and 'y' are both 12, our value + would be the value of the sample at Column 2 or Row 2. If they are both + -1, we would return Column 9, Row 9. +-----------------------------------------------------------------------------*/ + return *terrainModP(pamP, terrain, x, y); +} + + + +static void +setElev(struct pam * const pamP, + tuple ** const terrain, + int const cx, + int const cy, + unsigned int const elevation) { + + *terrainModP(pamP, terrain, cx, cy) = MIN(pamP->maxval, elevation); +} + + + +static void +smallCrater(struct pam * const pamP, + tuple ** const terrain, + int const cx, + int const cy, + double const radius) { +/*---------------------------------------------------------------------------- + Generate a crater with a special method for tiny craters. + + Center the crater at Column 'cx', Row 'cy'; wrap as necessary to get them + on the canvas. These might even be negative. +-----------------------------------------------------------------------------*/ + int y; + unsigned int amptot; + unsigned int npatch; + + assert(radius < 3); + + /* Set pixel to the average of its Moore neighborhood. */ + + for (y = cy - 1, amptot = 0, npatch = 0; y <= cy + 1; ++y) { + int x; + for (x = cx - 1; x <= cx + 1; ++x) { + amptot += terrainMod(pamP, terrain, x, y); + ++npatch; + } + } + { + unsigned int const axelev = amptot / npatch; + /* The mean elevation of the Moore neighborhood (9 pixels + centered on the crater location). + */ + + /* Perturb the mean elevation by a small random factor. */ + + int const x = radius >= 1 ? ((rand() >> 8) & 0x3) - 1 : 0; + + assert(axelev > 0); + + setElev(pamP, terrain, cx, cy, axelev + x); + } +} + + + +static unsigned int +meanElev(struct pam * const pamP, + tuple ** const terrain, + int const cx, + int const cy, + double const radius) { +/*---------------------------------------------------------------------------- + The mean elevation in 'terrain', which is described by *pamP, within + 'radius' pixels vertically and horizontally of (cx, cy). + + We assume the area is a fraction the whole 'terrain'. +-----------------------------------------------------------------------------*/ + unsigned int amptot; + unsigned int npatch; + int y; + + for (y = cy - radius, amptot = 0, npatch = 0; y <= cy + radius; ++y) { + int x; + for (x = cx - radius; x <= cx + radius; ++x) { + amptot += terrainMod(pamP, terrain, x, y); + ++npatch; + } + } + assert(npatch > 0); + + return amptot / npatch; +} + + + +static void +normalCrater(struct pam * const pamP, + tuple ** const terrain, + int const cx, + int const cy, + double const radius) { +/*---------------------------------------------------------------------------- + Generate a regular (not tiny) crater. + + Generate an impact feature of the correct size and shape. +----------------------------------------------------------------------------*/ + int const impactRadius = (int) MAX(2, (radius / 3)); + int const craterRadius = (int) radius; + double const rollmin = 0.9; + + int y; + + unsigned int const axelev = meanElev(pamP, terrain, cx, cy, impactRadius); + /* The mean elevation of the impact area, before impact */ + + for (y = cy - craterRadius; y <= cy + craterRadius; ++y) { + int const dysq = SQR(cy - y); + + int x; + + for (x = cx - craterRadius; x <= cx + craterRadius; ++x) { + int const dxsq = SQR(cx - x); + double const cd = (dxsq + dysq) / (double) SQR(craterRadius); + double const cd2 = cd * 2.25; + double const tcz = sqrt(DepthBias2) - sqrt(fabs(1 - cd2)); + double cz; + double roll; + + cz = MAX((cd2 > 1) ? 0.0 : -10, tcz); /* Initial value */ + + cz *= pow(craterRadius, CdepthPower); + if (dysq == 0 && dxsq == 0 && ((int) cz) == 0) { + cz = cz < 0 ? -1 : 1; + } + + roll = (((1 / (1 - MIN(rollmin, cd))) / + (1 / (1 - rollmin))) - (1 - rollmin)) / rollmin; + + { + unsigned int av; + av = (axelev + cz) * (1 - roll) + + (terrainMod(pamP, terrain, x, y) + cz) * roll; + av = MAX(1000, MIN(64000, av)); + + setElev(pamP, terrain, x, y, av); + } + } + } +} + + + +/* We should also have largeCrater() */ + + + +static void +plopCrater(struct pam * const pamP, + tuple ** const terrain, + int const cx, + int const cy, + double const radius, + bool const verbose) { + + if (verbose && pm_have_float_format()) + pm_message("Plopping crater at (%4d, %4d) with radius %g", + cx, cy, radius); + + if (radius < 3) + smallCrater (pamP, terrain, cx, cy, radius); + else + normalCrater(pamP, terrain, cx, cy, radius); +} + + + +static void +initCanvas(unsigned int const width, + unsigned int const height, + struct pam * const pamP, + tuple *** const terrainP) { +/*---------------------------------------------------------------------------- + Initialize the output image to a flat area of middle elevation. +-----------------------------------------------------------------------------*/ + tuple ** terrain; /* elevation array */ + unsigned int row; + + pamP->size = sizeof(*pamP); + pamP->len = PAM_STRUCT_SIZE(tuple_type); + pamP->file = stdout; + pamP->format = PAM_FORMAT; + pamP->height = height; + pamP->width = width; + pamP->depth = 1; + pamP->maxval = 65535; + pamP->bytes_per_sample = 2; + STRSCPY(pamP->tuple_type, "elevation"); + + terrain = pnm_allocpamarray(pamP); + + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) + terrain[row][col][0] = pamP->maxval / 2; + } + *terrainP = terrain; +} + + + +static void +genCraters(struct CmdlineInfo const cmdline) { +/*---------------------------------------------------------------------------- + Generate cratered terrain +-----------------------------------------------------------------------------*/ + tuple ** terrain; /* elevation array */ + struct pam pam; + + /* Allocate the elevation array and initialize it to mean surface + elevation. + */ + + initCanvas(cmdline.width, cmdline.height, &pam, &terrain); + + if (cmdline.test) + plopCrater(&pam, terrain, + pam.width/2 + cmdline.offset, + pam.height/2 + cmdline.offset, + (double) cmdline.radius, cmdline.verbose); + else { + unsigned int const ncraters = cmdline.number; /* num of craters */ + unsigned int l; + + for (l = 0; l < ncraters; ++l) { + int const cx = cast((double) pam.width - 1); + int const cy = cast((double) pam.height - 1); + + /* Thanks, Rudy, for this equation that maps the uniformly + distributed numbers from cast() into an area-law distribution + as observed on cratered bodies. + + Produces values within the interval: + 0.56419 <= radius <= 56.419 + */ + double const radius = sqrt(1 / (M_PI * (1 - cast(0.9999)))); + + plopCrater(&pam, terrain, cx, cy, radius, cmdline.verbose); + + if (((l + 1) % 100000) == 0) + pm_message("%u craters generated of %u (%u%% done)", + l + 1, ncraters, ((l + 1) * 100) / ncraters); + } + } + + pnm_writepam(&pam, terrain); + + pnm_freepamarray(terrain, &pam); + + pm_close(stdout); +} + + + +int +main(int argc, const char ** argv) { + + struct CmdlineInfo cmdline; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); + + genCraters(cmdline); + + return 0; +} + + + diff --git a/generator/pamgauss.c b/generator/pamgauss.c index 1fb47af6..2dd6a726 100644 --- a/generator/pamgauss.c +++ b/generator/pamgauss.c @@ -9,11 +9,9 @@ #include "mallocvar.h" #include "pam.h" -#define true (1) -#define false (0) -struct cmdlineInfo { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ @@ -27,8 +25,8 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** argv, - struct cmdlineInfo * const cmdlineP) { +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the program can use easily, struct cmdlineInfo. Validate arguments along @@ -59,7 +57,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - 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) @@ -68,7 +66,8 @@ parseCommandLine(int argc, char ** argv, struct pam pam; if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type)) pm_error("The tuple type you specified is too long. " - "Maximum %d characters.", sizeof(pam.tuple_type)-1); + "Maximum %u characters.", + (unsigned)sizeof(pam.tuple_type)-1); } if (!sigmaSpec) @@ -102,6 +101,7 @@ parseCommandLine(int argc, char ** argv, pm_error("height argument must be a positive number. You " "specified '%s'", argv[2]); } + free(option_def); } @@ -161,15 +161,15 @@ imageNormalizer(struct pam * const pamP, int -main(int argc, char **argv) { +main(int argc, const char **argv) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; struct pam pam; int row; double normalizer; tuplen * tuplerown; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/generator/pamgradient.c b/generator/pamgradient.c index aa559d27..57e78288 100644 --- a/generator/pamgradient.c +++ b/generator/pamgradient.c @@ -18,7 +18,7 @@ struct cmdlineInfo { }; static void -parseCommandLine(int argc, char **argv, +parseCommandLine(int argc, const char **argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the @@ -45,7 +45,7 @@ parseCommandLine(int argc, char **argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - 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 (!maxvalSpec) @@ -153,7 +153,7 @@ createEdge(const struct pam * const pamP, int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; struct pam pam; @@ -162,7 +162,7 @@ main(int argc, char *argv[]) { tuple * rightEdge; unsigned int row; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/generator/pamseq.c b/generator/pamseq.c index 98eac1cc..1af5252a 100644 --- a/generator/pamseq.c +++ b/generator/pamseq.c @@ -7,8 +7,6 @@ #include "pam.h" #include "shhopt.h" -#define true (1) -#define false (0) struct cmdlineInfo { @@ -50,7 +48,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!tupletypeSpec) @@ -59,7 +57,8 @@ parseCommandLine(int argc, char ** argv, struct pam pam; if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type)) pm_error("The tuple type you specified is too long. " - "Maximum %d characters.", sizeof(pam.tuple_type)-1); + "Maximum %u characters.", + (unsigned)sizeof(pam.tuple_type)-1); } if (argc-1 < 2) diff --git a/generator/pamshadedrelief.c b/generator/pamshadedrelief.c new file mode 100644 index 00000000..89996c83 --- /dev/null +++ b/generator/pamshadedrelief.c @@ -0,0 +1,250 @@ +/*============================================================================= + pamshaderelief +=============================================================================== + Generate a shaded relief image of terrain, given a terrain map - a two + dimensional map of elevations. A shaded relief image is an image of + what terrain with the given elevations would look like illuminated by + oblique light. + + The input array is a one-channel PAM image. The sample values are + elevations of terrain. + + This is derived from John Walker's 'pgmcrater' which not only does this + shading, but first generates a terrain map of fractal craters on which to + run it. + + + The original program carried this attribution and license: + + Designed and implemented in November of 1989 by: + + John Walker + Autodesk SA + Avenue des Champs-Montants 14b + CH-2074 MARIN + Switzerland + Usenet: kelvin@Autodesk.com + Fax: 038/33 88 15 + Voice: 038/33 76 33 + + Permission to use, copy, modify, and distribute this software and + its documentation for any purpose and without fee is hereby + granted, without any conditions or restrictions. This software is + provided "as is" without express or implied warranty. + +=============================================================================*/ + +/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at right + edge. +*/ + +#define _XOPEN_SOURCE /* get M_PI in math.h */ + +#include <assert.h> +#include <math.h> + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" +#include "pam.h" + + + +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; + float gamma; +}; + + + +static void +parseCommandLine(int argc, const char ** const argv, + struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + unsigned int option_def_index; + + unsigned int gammaSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "gamma", OPT_FLOAT, &cmdlineP->gamma, + &gammaSpec, 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, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!gammaSpec) + cmdlineP->gamma = 1.0; + + if (cmdlineP->gamma <= 0.0) + pm_error("gamma correction must be greater than 0"); + + if (argc-1 == 0) + cmdlineP->inputFileName = "-"; + else if (argc-1 != 1) + pm_error("Program takes zero or one argument (filename). You " + "specified %u", argc-1); + else + cmdlineP->inputFileName = argv[1]; + + free(option_def); +} + + + +/* Definitions for obtaining random numbers. */ + +/* Display parameters */ + +static double const ImageGamma = 0.5; /* Inherent gamma of mapped image */ +static int const slopemin = -52; +static int const slopemax = 52; + + + +static void +generateSlopeGrayMap(sample * const slopeGrayMap, + double const dgamma) { +/*---------------------------------------------------------------------------- + Map each possible slope to the brightness that terrain with that + left-to-right slope should have in the shaded relief. + + The brightness is what would result from light incident from the left + falling on the terrain. +-----------------------------------------------------------------------------*/ + double const gamma = dgamma * ImageGamma; + + int i; + + for (i = slopemin; i <= 0; ++i) { /* Negative, downhill, dark */ + slopeGrayMap[i - slopemin] = + 128 - 127.0 * pow(sin((M_PI / 2) * i / slopemin), gamma); + } + for (i = 0; i <= slopemax; ++i) { /* Positive, uphill, bright */ + slopeGrayMap[i - slopemin] = + 128 + 127.0 * pow(sin((M_PI / 2) * i / slopemax), gamma); + } + + /* Confused? OK, we're using the left-to-right slope to + calculate a shade based on the sine of the angle with + respect to the vertical (light incident from the left). + Then, with one exponentiation, we account for both the + inherent gamma of the image (ad-hoc), and the + user-specified display gamma, using the identity: + (x^y)^z = (x^(y*z)) + */ +} + + + +static gray +brightnessOfSlope(int const slope, + sample * const slopeGrayMap) { + + return slopeGrayMap[MIN(MAX(slopemin, slope), slopemax) - slopemin]; +} + + + +static void +writeShadedRelief(struct pam * const terrainPamP, + tuple ** const terrain, + double const dgamma, + FILE * const ofP) { + + unsigned int row; + tuple * outrow; + sample * slopeGrayMap; /* Slope to gray value map */ + struct pam outpam; + + outpam.size = sizeof(outpam); + outpam.len = PAM_STRUCT_SIZE(tuple_type); + outpam.file = ofP; + outpam.format = PAM_FORMAT; + outpam.height = terrainPamP->height; + outpam.width = terrainPamP->width; + outpam.depth = 1; + outpam.maxval = 255; + outpam.bytes_per_sample = 1; + STRSCPY(outpam.tuple_type, "GRAYSCALE"); + + outrow = pnm_allocpamrow(&outpam); + + pnm_writepaminit(&outpam); + + MALLOCARRAY(slopeGrayMap, slopemax - slopemin + 1); + + generateSlopeGrayMap(slopeGrayMap, dgamma); + + for (row = 0; row < terrainPamP->height; ++row) { + unsigned int col; + + for (col = 0; col < terrainPamP->width - 1; ++col) { + int const slope = terrain[row][col+1][0] - terrain[row][col][0]; + outrow[col][0] = brightnessOfSlope(slope, slopeGrayMap); + } + { + /* Wrap around to determine shade of pixel on right edge */ + int const slope = + terrain[row][0][0] - terrain[row][outpam.width-1][0]; + outrow[outpam.width - 1][0] = + brightnessOfSlope(slope, slopeGrayMap); + } + pnm_writepamrow(&outpam, outrow); + } + + free(slopeGrayMap); + pnm_freepamrow(outrow); +} + + + +static void +readTerrain(FILE * const ifP, + struct pam * const pamP, + tuple *** const tuplesP) { + + *tuplesP = pnm_readpam(ifP, pamP, PAM_STRUCT_SIZE(tuple_type)); +} + + + +int +main(int argc, const char ** argv) { + + struct CmdlineInfo cmdline; + FILE * ifP; + struct pam terrainPam; + tuple ** terrain; + /* Array of elevations */ + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + readTerrain(ifP, &terrainPam, &terrain); + + writeShadedRelief(&terrainPam, terrain, cmdline.gamma, stdout); + + return 0; +} + + diff --git a/generator/pamstereogram.c b/generator/pamstereogram.c index 0ce63853..6e5f5ce0 100644 --- a/generator/pamstereogram.c +++ b/generator/pamstereogram.c @@ -8,12 +8,15 @@ * The core of this program is a simple adaptation of the code in * "Displaying 3D Images: Algorithms for Single Image Random Dot * Stereograms" by Harold W. Thimbleby, Stuart Inglis, and Ian - * H. Witten in IEEE Computer, 27(10):38-48, October 1994. See that - * paper for a thorough explanation of what's going on here. + * H. Witten in IEEE Computer, 27(10):38-48, October 1994 plus some + * enhancements presented in "Stereograms: Technical Details" by + * W. A. Steer at http://www.techmind.org/stereo/stech.html. See + * those references for a thorough explanation of what's going on + * here. * * ---------------------------------------------------------------------- * - * Copyright (C) 2006 Scott Pakin <scott+pbm@pakin.org> + * Copyright (C) 2006-2015 Scott Pakin <scott+pbm@pakin.org> * * All rights reserved. * @@ -43,20 +46,21 @@ * ---------------------------------------------------------------------- */ +#define _ISOC99_SOURCE /* Make sure strtof() is in <stdlib.h> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> +#include <limits.h> #include <assert.h> #include "pm_config.h" #include "pm_c_util.h" -#include "pam.h" -#include "shhopt.h" #include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" +#include "pam.h" -/* Define a few helper macros. */ -#define round2int(X) ((int)((X)+0.5)) /* Nonnegative numbers only */ enum outputType {OUTPUT_BW, OUTPUT_GRAYSCALE, OUTPUT_COLOR}; @@ -71,25 +75,65 @@ struct cmdlineInfo { unsigned int crosseyed; /* -crosseyed option */ unsigned int makemask; /* -makemask option */ unsigned int dpi; /* -dpi option */ - float eyesep; /* -eyesep option */ - float depth; /* -depth option */ + float eyesep; /* -eyesep option */ + float depth; /* -depth option */ unsigned int maxvalSpec; /* -maxval option count */ - unsigned int maxval; /* -maxval option value x*/ - int guidesize; /* -guidesize option */ + unsigned int maxval; /* -maxval option value */ + unsigned int guidetop; /* -guidetop option count */ + unsigned int guidebottom; /* -guidebottom option count */ + unsigned int guidesize; /* -guidesize option value */ unsigned int magnifypat; /* -magnifypat option */ - unsigned int xshift; /* -xshift option */ - unsigned int yshift; /* -yshift option */ - const char * patFilespec; /* -patfile option. Null if none */ + int xshift; /* -xshift option */ + int yshift; /* -yshift 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 */ + unsigned int xbegin; /* -xbegin option */ + unsigned int xbeginSpec; /* -xbegin option count */ }; static void -parseCommandLine(int argc, - char ** argv, - struct cmdlineInfo *cmdlineP ) { +parseNearFarPlanes(const char ** const nearFarPlanes, + float * const nearPlaneP, + float * const farPlaneP) { +/*---------------------------------------------------------------------------- + Parse nearFarPlanes option value into exactly two positive numbers +-----------------------------------------------------------------------------*/ + float nearPlane, farPlane; + + if (nearFarPlanes == NULL || nearFarPlanes[0] == NULL || + nearFarPlanes[1] == NULL || nearFarPlanes[2] != NULL) + pm_error("-planes requires exactly two positive numbers"); + + errno = 0; + nearPlane = strtof(nearFarPlanes[0], NULL); + if (errno != 0 || nearPlane <= 0.0) + pm_error("-planes requires exactly two positive numbers"); + + farPlane = strtof(nearFarPlanes[1], NULL); + if (errno != 0 || farPlane <= 0.0) + pm_error("-planes requires exactly two positive numbers"); + + if (nearPlane >= farPlane) + pm_error("-planes requires the near-plane value " + "to be less than the far-plane value"); + + *nearPlaneP = nearPlane; + *farPlaneP = farPlane; +} + + + +static void +parseCommandLine(int argc, + const char ** argv, + struct cmdlineInfo * cmdlineP ) { /*---------------------------------------------------------------------------- Parse program command line described in Unix standard form by argc and argv. Return the information in the options as *cmdlineP. @@ -101,17 +145,18 @@ parseCommandLine(int argc, was passed to us as the argv array. We also trash *argv. -----------------------------------------------------------------------------*/ optEntry *option_def; - /* Instructions to optParseOptions3 on how to parse our options. + /* Instructions to pm_optParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; - unsigned int patfileSpec, dpiSpec, eyesepSpec, depthSpec, - guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec, randomseedSpec; + unsigned int patfileSpec, texfileSpec, dpiSpec, eyesepSpec, depthSpec, + guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec, + bgcolorSpec, smoothingSpec, planesSpec; unsigned int blackandwhite, grayscale, color; - + const char ** planes; MALLOCARRAY_NOFAIL(option_def, 100); @@ -136,27 +181,40 @@ parseCommandLine(int argc, &depthSpec, 0); OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &cmdlineP->maxvalSpec, 0); - OPTENT3(0, "guidesize", OPT_INT, &cmdlineP->guidesize, + OPTENT3(0, "guidetop", OPT_FLAG, NULL, + &cmdlineP->guidetop, 0); + OPTENT3(0, "guidebottom", OPT_FLAG, NULL, + &cmdlineP->guidebottom, 0); + OPTENT3(0, "guidesize", OPT_UINT, &cmdlineP->guidesize, &guidesizeSpec, 0); OPTENT3(0, "magnifypat", OPT_UINT, &cmdlineP->magnifypat, &magnifypatSpec, 0); - OPTENT3(0, "xshift", OPT_UINT, &cmdlineP->xshift, + OPTENT3(0, "xshift", OPT_INT, &cmdlineP->xshift, &xshiftSpec, 0); - OPTENT3(0, "yshift", OPT_UINT, &cmdlineP->yshift, + OPTENT3(0, "yshift", OPT_INT, &cmdlineP->yshift, &yshiftSpec, 0); - OPTENT3(0, "patfile", OPT_STRING, &cmdlineP->patFilespec, + OPTENT3(0, "patfile", OPT_STRING, &cmdlineP->patfile, &patfileSpec, 0); + OPTENT3(0, "texfile", OPT_STRING, &cmdlineP->texfile, + &texfileSpec, 0); + OPTENT3(0, "bgcolor", OPT_STRING, &cmdlineP->bgcolor, + &bgcolorSpec, 0); OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, - &randomseedSpec, 0); + &cmdlineP->randomseedSpec, 0); + OPTENT3(0, "smoothing", OPT_UINT, &cmdlineP->smoothing, + &smoothingSpec, 0); + OPTENT3(0, "planes", OPT_STRINGLIST, &planes, + &planesSpec, 0); + OPTENT3(0, "xbegin", OPT_UINT, &cmdlineP->xbegin, + &cmdlineP->xbeginSpec, 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 */ - 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 (blackandwhite + grayscale + color == 0) cmdlineP->outputType = OUTPUT_BW; else if (blackandwhite + grayscale + color > 1) @@ -173,15 +231,21 @@ parseCommandLine(int argc, } } if (!patfileSpec) - cmdlineP->patFilespec = NULL; + cmdlineP->patfile = NULL; + if (!texfileSpec) + cmdlineP->texfile = NULL; + if (!bgcolorSpec) + cmdlineP->bgcolor = NULL; + if (!smoothingSpec) + cmdlineP->smoothing = 0; if (!dpiSpec) - cmdlineP->dpi = 96; + cmdlineP->dpi = 100; else if (cmdlineP->dpi < 1) pm_error("The argument to -dpi must be a positive integer"); if (!eyesepSpec) - cmdlineP->eyesep = 2.5; + cmdlineP->eyesep = 2.56; else if (cmdlineP->eyesep <= 0.0) pm_error("The argument to -eyesep must be a positive number"); @@ -197,9 +261,16 @@ parseCommandLine(int argc, pm_error("-maxval must be at most %u. You specified %u", PNM_OVERALLMAXVAL, cmdlineP->maxval); } + if (bgcolorSpec && !texfileSpec) + pm_message("warning: -bgcolor has no effect " + "except in conjunction with -texfile"); + + if (guidesizeSpec && !(cmdlineP->guidetop || cmdlineP->guidebottom)) + pm_error("-guidesize has no meaning " + "without -guidetop or -guidebottom"); if (!guidesizeSpec) - cmdlineP->guidesize = 0; + cmdlineP->guidesize = 20; if (!magnifypatSpec) cmdlineP->magnifypat = 1; @@ -212,26 +283,42 @@ parseCommandLine(int argc, if (!yshiftSpec) cmdlineP->yshift = 0; - if (!randomseedSpec) - cmdlineP->randomseed = time(NULL); - - if (xshiftSpec && !cmdlineP->patFilespec) + if (xshiftSpec && !cmdlineP->patfile) pm_error("-xshift is valid only with -patfile"); - if (yshiftSpec && !cmdlineP->patFilespec) + if (yshiftSpec && !cmdlineP->patfile) pm_error("-yshift is valid only with -patfile"); - if (cmdlineP->makemask && cmdlineP->patFilespec) + if (cmdlineP->makemask && cmdlineP->patfile) pm_error("You may not specify both -makemask and -patfile"); - if (cmdlineP->patFilespec && blackandwhite) + if (cmdlineP->patfile && blackandwhite) pm_error("-blackandwhite is not valid with -patfile"); - if (cmdlineP->patFilespec && grayscale) + if (cmdlineP->patfile && grayscale) pm_error("-grayscale is not valid with -patfile"); - if (cmdlineP->patFilespec && color) + if (cmdlineP->patfile && color) pm_error("-color is not valid with -patfile"); - if (cmdlineP->patFilespec && cmdlineP->maxvalSpec) + if (cmdlineP->patfile && cmdlineP->maxvalSpec) pm_error("-maxval is not valid with -patfile"); + if (cmdlineP->texfile && blackandwhite) + pm_error("-blackandwhite is not valid with -texfile"); + if (cmdlineP->texfile && grayscale) + pm_error("-grayscale is not valid with -texfile"); + if (cmdlineP->texfile && color) + pm_error("-color is not valid with -texfile"); + if (cmdlineP->texfile && cmdlineP->maxvalSpec) + pm_error("-maxval is not valid with -texfile"); + if (planesSpec && eyesepSpec) + pm_error("-planes is not valid with -eyesep"); + if (planesSpec && depthSpec) + pm_error("-planes is not valid with -depth"); + + if (planesSpec) { + float nearPlane, farPlane; + parseNearFarPlanes(planes, &nearPlane, &farPlane); + cmdlineP->eyesep = 2.0*farPlane/cmdlineP->dpi; + cmdlineP->depth = 2.0*(farPlane-nearPlane) / (2.0*farPlane-nearPlane); + } if (argc-1 < 1) cmdlineP->inputFilespec = "-"; @@ -244,7 +331,7 @@ parseCommandLine(int argc, -static int +static unsigned int separation(double const dist, double const eyesep, unsigned int const dpi, @@ -254,9 +341,9 @@ separation(double const dist, Return a separation in pixels which corresponds to a 3-D distance between the viewer's eyes and a point on an object. -----------------------------------------------------------------------------*/ - int const pixelEyesep = round2int(eyesep * dpi); + unsigned int const pixelEyesep = ROUNDU(eyesep * dpi); - return round2int((1.0 - dof * dist) * pixelEyesep / (2.0 - dof * dist)); + return ROUNDU((1.0 - dof * dist) * pixelEyesep / (2.0 - dof * dist)); } @@ -282,13 +369,27 @@ typedef tuple coord2Color(struct outGenerator *, int, int); /* A type to use for functions that map a 2-D coordinate to a color. */ typedef void outGenStateTerm(struct outGenerator *); +typedef struct { + struct pam pam; + tuple ** imageData; + tuple bgColor; + bool replaceBgColor; + /* replace background color with pattern color */ + unsigned int smoothing; + /* Number of background-smoothing iterations to perform */ +} texState; typedef struct outGenerator { - struct pam pam; - coord2Color * getTuple; + struct pam pam; + coord2Color * getTuple; /* Map from a height-map (x,y) coordinate to a tuple */ outGenStateTerm * terminateState; - void * stateP; + void * stateP; + texState * textureP; + /* Mapped-texture part of the state of operation. Null + means we are doing an ordinary stereogram instead of a + mapped-texture one. + */ } outGenerator; @@ -401,8 +502,8 @@ struct patternPixelState { /* This is the state of a patternPixel generator.*/ struct pam patPam; /* Descriptor of pattern image */ tuple ** patTuples; /* Entire image read from the pattern file */ - unsigned int xshift; - unsigned int yshift; + int xshift; + int yshift; unsigned int magnifypat; }; @@ -465,9 +566,9 @@ initPatternPixel(outGenerator * const outGenP, MALLOCVAR_NOFAIL(stateP); - assert(cmdline.patFilespec); - - patternFileP = pm_openr(cmdline.patFilespec); + assert(cmdline.patfile); + + patternFileP = pm_openr(cmdline.patfile); stateP->patTuples = pnm_readpam(patternFileP, @@ -495,22 +596,92 @@ initPatternPixel(outGenerator * const outGenP, static void +readTextureImage(struct cmdlineInfo const cmdline, + const struct pam * const inpamP, + const struct pam * const outpamP, + texState ** const texturePP) { + + FILE * textureFileP; + texState * textureP; + struct pam * texPamP; + + MALLOCVAR_NOFAIL(textureP); + texPamP = &textureP->pam; + + textureFileP = pm_openr(cmdline.texfile); + textureP->imageData = + pnm_readpam(textureFileP, texPamP, PAM_STRUCT_SIZE(tuple_type)); + pm_close(textureFileP); + + if (cmdline.bgcolor) + textureP->bgColor = + pnm_parsecolor(cmdline.bgcolor, texPamP->maxval); + else + textureP->bgColor = + pnm_backgroundtuple(texPamP, textureP->imageData); + textureP->replaceBgColor = (cmdline.patfile != NULL); + textureP->smoothing = cmdline.smoothing; + + if (cmdline.verbose) { + const char * const colorname = + pnm_colorname(texPamP, textureP->bgColor, 1); + + reportImageParameters("Texture file", texPamP); + if (cmdline.bgcolor && strcmp(colorname, cmdline.bgcolor)) + pm_message("Texture background color: %s (%s)", + cmdline.bgcolor, colorname); + else + pm_message("Texture background color: %s", colorname); + pm_strfree(colorname); + } + + if (texPamP->width != inpamP->width || texPamP->height != inpamP->height) + pm_error("The texture image must have the same width and height " + "as the input image"); + if (cmdline.patfile && + (!streq(texPamP->tuple_type, outpamP->tuple_type) || + texPamP->maxval != outpamP->maxval)) + pm_error("The texture image must be of the same tuple type " + "and maxval as the pattern image"); + + textureP->pam.file = outpamP->file; + + *texturePP = textureP; +} + + + +static unsigned int +totalGuideHeight(struct cmdlineInfo const cmdline) { + + /* Each pair of guides is cmdline.guidesize high, and we add that much + white above and below as well, so the total vertical space is three + times cmdline.giudesize. + */ + + return + (cmdline.guidetop ? 3 * cmdline.guidesize : 0) + + (cmdline.guidebottom ? 3 * cmdline.guidesize : 0); +} + + + +static void createoutputGenerator(struct cmdlineInfo const cmdline, const struct pam * const inPamP, outGenerator ** const outputGeneratorPP) { outGenerator * outGenP; - + MALLOCVAR_NOFAIL(outGenP); outGenP->pam.size = sizeof(struct pam); outGenP->pam.len = PAM_STRUCT_SIZE(tuple_type); outGenP->pam.file = stdout; - outGenP->pam.height = inPamP->height + 3 * abs(cmdline.guidesize); - /* Allow room for guides. */ + outGenP->pam.height = inPamP->height + totalGuideHeight(cmdline); outGenP->pam.width = inPamP->width; - if (cmdline.patFilespec) { + if (cmdline.patfile) { /* Background pixels should come from the pattern file. */ initPatternPixel(outGenP, cmdline); @@ -522,6 +693,12 @@ createoutputGenerator(struct cmdlineInfo const cmdline, outGenP->pam.bytes_per_sample = pnm_bytespersample(outGenP->pam.maxval); + if (cmdline.texfile) { + readTextureImage(cmdline, inPamP, &outGenP->pam, &outGenP->textureP); + outGenP->pam = outGenP->textureP->pam; + } else + outGenP->textureP = NULL; + *outputGeneratorPP = outGenP; } @@ -544,7 +721,7 @@ static void makeWhiteRow(const struct pam * const pamP, tuple * const tuplerow) { - int col; + unsigned int col; for (col = 0; col < pamP->width; ++col) { unsigned int plane; @@ -561,144 +738,205 @@ writeRowCopies(const struct pam * const outPamP, unsigned int const copyCount) { unsigned int i; + for (i = 0; i < copyCount; ++i) pnm_writepamrow(outPamP, outrow); } -/* Draw a pair of guide boxes. */ static void -drawguides(int const guidesize, +writeWhiteRows(const struct pam * const outPamP, + unsigned int const count) { + + tuple * outrow; /* One row of output data */ + + outrow = pnm_allocpamrow(outPamP); + + makeWhiteRow(outPamP, outrow); + + writeRowCopies(outPamP, outrow, count); + + pnm_freerow(outrow); +} + + + +static void +drawguides(unsigned int const guidesize, const struct pam * const outPamP, double const eyesep, unsigned int const dpi, double const depthOfField) { - - int const far = separation(0, eyesep, dpi, depthOfField); +/*---------------------------------------------------------------------------- + Draw a pair of guide boxes, left and right. +-----------------------------------------------------------------------------*/ + unsigned int const far = separation(0, eyesep, dpi, depthOfField); /* Space between the two guide boxes. */ - int const width = outPamP->width; /* Width of the output image */ + unsigned int const width = outPamP->width; /* Width of the output image */ - tuple *outrow; /* One row of output data */ + tuple * outrow; /* One row of output data */ tuple blackTuple; - int col; + unsigned int col; pnm_createBlackTuple(outPamP, &blackTuple); outrow = pnm_allocpamrow(outPamP); - /* Leave some blank rows before the guides. */ + /* Put some white rows before the guides */ + writeWhiteRows(outPamP, guidesize); + + /* Initialize the row buffer to white */ makeWhiteRow(outPamP, outrow); - writeRowCopies(outPamP, outrow, guidesize); - /* Draw the guides. */ - if ((width - far + guidesize)/2 < 0 || - (width + far - guidesize)/2 >= width) + if (far > width + guidesize) pm_message("warning: the guide boxes are completely out of bounds " - "at %d DPI", dpi); - else if ((width - far - guidesize)/2 < 0 || - (width + far + guidesize)/2 >= width) - pm_message("warning: the guide boxes are partially out of bounds " - "at %d DPI", dpi); - - for (col = (width - far - guidesize)/2; - col < (width - far + guidesize)/2; - ++col) - if (col >= 0 && col < width) - pnm_assigntuple(outPamP, outrow[col], blackTuple); + "at %u DPI", dpi); + else { + unsigned int leftBeg, leftEnd, rightBeg, rightEnd; + + assert(far <= width + guidesize); + leftEnd = (width - far + guidesize)/2; + assert(guidesize <= width + far); + rightBeg = (width + far - guidesize)/2; + + if (far + guidesize > width) { + pm_message("warning: the guide boxes are partially out of bounds " + "at %u DPI", dpi); + + leftBeg = 0; + rightEnd = width; + } else { + assert(far + guidesize <= width); + leftBeg = (width - far - guidesize)/2; + rightEnd = (width + far + guidesize)/2; + } - for (col = (width + far - guidesize)/2; - col < (width + far + guidesize)/2; - ++col) - if (col >= 0 && col < width) + /* Draw the left guide black in the buffer */ + assert(leftEnd < outPamP->width); + for (col = leftBeg; col < leftEnd; ++col) pnm_assigntuple(outPamP, outrow[col], blackTuple); - writeRowCopies(outPamP,outrow, guidesize); + /* Draw the right guide black in the buffer */ + assert(rightEnd <= outPamP->width); + for (col = rightBeg; col < rightEnd; ++col) + pnm_assigntuple(outPamP, outrow[col], blackTuple); + } + /* Write out the guide rows */ - /* Leave some blank rows after the guides. */ - makeWhiteRow(outPamP, outrow); writeRowCopies(outPamP, outrow, guidesize); + /* Put some white rows after the guides */ + writeWhiteRows(outPamP, guidesize); + pnm_freerow(outrow); } -/* Do the bulk of the work. See the paper cited above for code - * comments. All I (Scott) did was transcribe the code and make - * minimal changes for Netpbm. And some style changes by Bryan to - * match Netpbm style. - */ static void makeStereoRow(const struct pam * const inPamP, tuple * const inRow, - int * const same, + unsigned int * const sameL, + unsigned int * const sameR, double const depthOfField, double const eyesep, - unsigned int const dpi) { + unsigned int const dpi, + unsigned int const optWidth, + unsigned int const smoothing) { -#define Z(X) (1.0-inRow[X][0]/(double)inPamP->maxval) +/* 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) - int const width = inPamP->width; - int const pixelEyesep = round2int(eyesep * dpi); - /* Separation in pixels between the viewer's eyes */ + unsigned int col; - int col; + for (col = 0; col < inPamP->width; ++col) { + sameL[col] = col; + sameR[col] = col; + } - for (col = 0; col < width; ++col) - same[col] = col; - - for (col = 0; col < width; ++col) { - int const s = separation(Z(col), eyesep, dpi, depthOfField); - int left, right; - - left = col - s/2; /* initial value */ - right = left + s; /* initial value */ - - if (0 <= left && right < width) { - int visible; - int t; - double zt; - - t = 1; /* initial value */ - - do { - double const dof = depthOfField; - zt = Z(col) + 2.0*(2.0 - dof*Z(col))*t/(dof*pixelEyesep); - visible = Z(col-t) < zt && Z(col+t) < zt; - ++t; - } while (visible && zt < 1); - if (visible) { - int l; - - l = same[left]; - while (l != left && l != right) { - if (l < right) { - left = l; - l = same[left]; - } else { - same[left] = right; - left = right; - l = same[left]; - right = l; - } - } - same[left] = right; + for (col = 0; col < inPamP->width; ++col) { + unsigned int const sep = separation(Z(col), eyesep, dpi, depthOfField); + int const left = col - sep/2; + int const right = left + sep; + + if (left >= 0 && right < inPamP->width) { + bool isVisible; + + if (sameL[right] != right) { + /* Right point already linked */ + if (sameL[right] < left) { + /* Deeper than current */ + sameR[sameL[right]] = sameL[right]; /* Break old links. */ + sameL[right] = right; + isVisible = TRUE; + } else + isVisible = FALSE; + } else + isVisible = TRUE; + + if (sameR[left] != left) { + /* Left point already linked */ + if (sameR[left] > right) { + /* Deeper than current */ + sameL[sameR[left]] = sameR[left]; /* Break old links. */ + sameR[left] = left; + isVisible = TRUE; + } else + isVisible = FALSE; + } else + isVisible = TRUE; + + if (isVisible) { + /* Make a link. */ + sameL[right] = left; + sameR[left] = right; } } } + + /* If smoothing is enabled, replace each non-duplicate pixel with + the pixel adjacent to its right neighbor. + */ + if (smoothing > 0) { + int const baseCol = inPamP->width - optWidth - 1; + + int col; + + for (col = inPamP->width - 1; col >= 0; --col) + sameR[col] = sameR[sameR[col]]; + for (col = baseCol; col >= 0; --col) { + if (sameR[col] == col) + sameR[col] = sameR[col+1] - 1; + } + } } static void -makeMaskRow(const struct pam * const outPamP, - const int * const same, - const tuple * const outRow) { +makeMaskRow(const struct pam * const outPamP, + unsigned int const xbegin, + const unsigned int * const sameL, + const unsigned int * const sameR, + const tuple * const outRow) { int col; - for (col = outPamP->width-1; col >= 0; --col) { - bool const duplicate = (same[col] != col); + for (col = (int)xbegin; col < outPamP->width; ++col) { + bool const duplicate = (sameL[col] != col && sameL[col] >= xbegin); + + unsigned int plane; + + for (plane = 0; plane < outPamP->depth; ++plane) + outRow[col][plane] = duplicate ? outPamP->maxval : 0; + } + + for (col = (int)xbegin - 1; col >= 0; --col) { + bool const duplicate = (sameR[col] != col); + unsigned int plane; for (plane = 0; plane < outPamP->depth; ++plane) @@ -709,37 +947,284 @@ makeMaskRow(const struct pam * const outPamP, static void -makeImageRow(outGenerator * const outGenP, - int const row, - const int * const same, - const tuple * const outRow) { +computeFixedPoint(const unsigned int * const same, + unsigned int * const sameFp, + unsigned int const width) { /*---------------------------------------------------------------------------- - same[N] is one of two things: + Compute the fixed point of same[] (i.e., sameFp[x] is + same[same[same[...[same[x]]...]]]). +-----------------------------------------------------------------------------*/ + int col; - same[N] == N means to generate a value for Column N independent of + for (col = width-1; col >= 0; --col) { + if (same[col] != col) + sameFp[col] = sameFp[same[col]]; + else { + if (col < width-1) + sameFp[col] = sameFp[col + 1] - 1; + else + sameFp[col] = col; + } + } +} + + + +static void +averageFromPattern(struct pam * const pamP, + tuple const bgColor, + const tuple * const textureRow, + const unsigned int * const same, + unsigned int * const sameFp, + const tuple * const outRow, + unsigned int * const tuplesInCol) { +/*---------------------------------------------------------------------------- + Average the color of each non-background pattern tuple to every column that + should have the same color. +-----------------------------------------------------------------------------*/ + int col; + + /* Initialize the tuple sums to zero. */ + + for (col = 0; col < pamP->width; ++col) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + outRow[col][plane] = 0; + tuplesInCol[col] = 0; + } + + /* Accumulate the color of each non-background pattern tuple to + every column that should have the same color. + */ + for (col = pamP->width-1; col >= 0; --col) { + tuple const onetuple = textureRow[(col+same[col])/2]; + unsigned int const targetcol = sameFp[col]; + int eqcol; + + if (!pnm_tupleequal(pamP, onetuple, bgColor)) { + for (eqcol = pamP->width-1; eqcol >= 0; --eqcol) { + if (sameFp[eqcol] == targetcol) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + outRow[eqcol][plane] += onetuple[plane]; + tuplesInCol[eqcol]++; + } + } + } + } + /* Take the average of all colors associated with each column. + Tuples that can be any color are assigned the same color as was + previously assigned to their fixed-point column. + */ + for (col = 0; col < pamP->width; ++col) { + if (tuplesInCol[col] > 0) { + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + outRow[col][plane] /= tuplesInCol[col]; + } else + pnm_assigntuple(pamP, outRow[col], bgColor); + } +} + + + +static void +smoothOutSpeckles(struct pam * const pamP, + tuple const bgColor, + unsigned int const smoothing, + unsigned int * const tuplesInCol, + tuple * const rowBuffer, + const tuple * const outRow) { +/*---------------------------------------------------------------------------- + Smooth out small speckles of the background color lying between other + colors. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; i < smoothing; ++i) { + int col; + tuple * const scratchrow = rowBuffer; + for (col = pamP->width-2; col >= 1; --col) { + if (tuplesInCol[col] == 0) { + /* Replace a background tuple with the average of its + left and right neighbors. + */ + unsigned int plane; + for (plane = 0; plane < pamP->depth; ++plane) + scratchrow[col][plane] = 0; + if (!pnm_tupleequal(pamP, outRow[col-1], bgColor)) { + for (plane = 0; plane < pamP->depth; ++plane) + scratchrow[col][plane] += outRow[col-1][plane]; + ++tuplesInCol[col]; + } + if (!pnm_tupleequal(pamP, outRow[col+1], bgColor)) { + for (plane = 0; plane < pamP->depth; ++plane) + scratchrow[col][plane] += outRow[col+1][plane]; + ++tuplesInCol[col]; + } + if (tuplesInCol[col] > 0) + for (plane = 0; plane < pamP->depth; ++plane) + scratchrow[col][plane] /= tuplesInCol[col]; + else + pnm_assigntuple(pamP, scratchrow[col], outRow[col]); + } else + pnm_assigntuple(pamP, scratchrow[col], outRow[col]); + } + for (col = 1; col < pamP->width-1; ++col) + pnm_assigntuple(pamP, outRow[col], scratchrow[col]); + } +} + + + +static void +replaceRemainingBackgroundWithPattern(outGenerator * const outGenP, + const unsigned int * const same, + unsigned int const row, + const tuple * const outRow) { + + const struct pam * const pamP = &outGenP->pam; + tuple const bgColor = outGenP->textureP->bgColor; + + if (outGenP->textureP->replaceBgColor) { + int col; + for (col = outGenP->pam.width-1; col >= 0; --col) { + if (pnm_tupleequal(pamP, outRow[col], bgColor)) { + bool const duplicate = (same[col] != col); + + tuple newtuple; + + if (duplicate) { + assert(same[col] > col); + assert(same[col] < outGenP->pam.width); + + newtuple = outRow[same[col]]; + } else + newtuple = outGenP->getTuple(outGenP, col, row); + + pnm_assigntuple(pamP, outRow[col], newtuple); + } + } + } +} + + + +static void +makeImageRowMts(outGenerator * const outGenP, + unsigned int const row, + const unsigned int * const same, + unsigned int * const sameFp, + tuple * const rowBuffer, + const tuple * const outRow) { +/*---------------------------------------------------------------------------- + Make a row of a mapped-texture stereogram. +-----------------------------------------------------------------------------*/ + unsigned int * tuplesInCol; + /* tuplesInCol[C] is the number of tuples averaged together to make + Column C. + */ + MALLOCARRAY(tuplesInCol, outGenP->pam.width); + if (tuplesInCol == NULL) + pm_error("Unable to allocate space for \"tuplesInCol\" array."); + + assert(outGenP->textureP); + /* This is an original algorithm by Scott Pakin. */ + + /* + Compute the fixed point of same[] (i.e., sameFp[x] is + same[same[same[...[same[x]]...]]]). + */ + computeFixedPoint(same, sameFp, outGenP->pam.width); + + /* Average the color of each non-background pattern tuple to + every column that should have the same color. + */ + + averageFromPattern(&outGenP->pam, outGenP->textureP->bgColor, + outGenP->textureP->imageData[row], + same, sameFp, + outRow, tuplesInCol); + + /* Smooth out small speckles of the background color lying between + other colors. + */ + smoothOutSpeckles(&outGenP->pam, outGenP->textureP->bgColor, + outGenP->textureP->smoothing, + tuplesInCol, rowBuffer, outRow); + + /* Replace any remaining background tuples with a pattern tuple. */ + + replaceRemainingBackgroundWithPattern(outGenP, same, row, outRow); + + free(tuplesInCol); +} + + + +static void +makeImageRow(outGenerator * const outGenP, + unsigned int const row, + unsigned int const optWidth, + unsigned int const xbegin, + const unsigned int * const sameL, + const unsigned int * const sameR, + const tuple * const outRow) { +/*---------------------------------------------------------------------------- + sameR[N] is one of two things: + + sameR[N] == N means to generate a value for Column N independent of other columns in the row. - same[N] > N means Column N should be identical to Column same[N]. - - same[N] < N is not allowed. + sameR[N] > N means Column N should be identical to Column sameR[N]. + + sameR[N] < N is not allowed. + + sameL[N] is one of two things: + + sameL[N] == N means to generate a value for Column N independent of + other columns in the row. + + sameL[N] < N means Column N should be identical to Column sameL[N]. + + sameL[N] > N is not allowed. -----------------------------------------------------------------------------*/ int col; - for (col = outGenP->pam.width-1; col >= 0; --col) { - bool const duplicate = (same[col] != col); - + int lastLinked; + + for (col = (int)xbegin, lastLinked = INT_MIN; + col < outGenP->pam.width; + ++col) { + tuple newtuple; - unsigned int plane; - if (duplicate) { - assert(same[col] > col); - assert(same[col] < outGenP->pam.width); + if (sameL[col] == col || sameL[col] < (int)xbegin) { + if (lastLinked == col - 1) + newtuple = outRow[col - 1]; + else + newtuple = outGenP->getTuple(outGenP, col, row); + } else { + newtuple = outRow[sameL[col]]; + lastLinked = col; + /* Keep track of the last pixel to be constrained. */ + } + pnm_assigntuple(&outGenP->pam, outRow[col], newtuple); + } - newtuple = outRow[same[col]]; - } else - newtuple = outGenP->getTuple(outGenP, col, row); + for (col = (int)xbegin - 1, lastLinked = INT_MIN; col >= 0; --col) { + tuple newtuple; - for (plane = 0; plane < outGenP->pam.depth; ++plane) - outRow[col][plane] = newtuple[plane]; + if (sameR[col] == col) { + if (lastLinked == col + 1) + newtuple = outRow[col + 1]; + else + newtuple = outGenP->getTuple(outGenP, col, row); + } else { + newtuple = outRow[sameR[col]]; + lastLinked = col; + /* Keep track of the last pixel to be constrained. */ + } + pnm_assigntuple(&outGenP->pam, outRow[col], newtuple); } } @@ -764,26 +1249,40 @@ makeImageRows(const struct pam * const inPamP, double const eyesep, unsigned int const dpi, bool const crossEyed, - bool const makeMask) { + bool const makeMask, + 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 */ - int * same; - /* Array: same[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 * 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 */ - int row; /* Current row in the input and output files */ + unsigned int * sameRfp; + /* Malloced array: Fixed point of sameR[] */ + tuple * rowBuffer; /* Scratch row needed for texture manipulation */ + unsigned int row; /* Current row in the input and output files */ inRow = pnm_allocpamrow(inPamP); outRow = pnm_allocpamrow(&outputGeneratorP->pam); - MALLOCARRAY(same, inPamP->width); - if (same == NULL) - pm_error("Unable to allocate space for \"same\" array."); - - /* See the paper cited above for code comments. All I (Scott) did was - * transcribe the code and make minimal changes for Netpbm. And some - * style changes by Bryan to match Netpbm style. - */ + 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); + for (row = 0; row < inPamP->height; ++row) { pnm_readpamrow(inPamP, inRow); if (crossEyed) @@ -793,17 +1292,29 @@ makeImageRows(const struct pam * const inPamP, invertHeightRow(inPamP, inRow); /* Determine color constraints. */ - makeStereoRow(inPamP, inRow, same, depthOfField, eyesep, dpi); + makeStereoRow(inPamP, inRow, sameL, sameR, depthOfField, eyesep, dpi, + ROUNDU(eyesep * dpi)/(magnifypat * 2), + smoothing); if (makeMask) - makeMaskRow(&outputGeneratorP->pam, same, outRow); - else - makeImageRow(outputGeneratorP, row, same, outRow); - + 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); } - free(same); + + pnm_freepamrow(rowBuffer); + free(sameRfp); + free(sameL); + free(sameR); pnm_freepamrow(outRow); pnm_freepamrow(inRow); } @@ -817,7 +1328,9 @@ produceStereogram(FILE * const ifP, struct pam inPam; /* PAM information for the height-map file */ outGenerator * outputGeneratorP; /* Handle of an object that generates background pixels */ - + unsigned int xbegin; + /* x coordinate separating left-to-right from right-to-left coloring */ + pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type)); createoutputGenerator(cmdline, &inPam, &outputGeneratorP); @@ -832,21 +1345,34 @@ produceStereogram(FILE * const ifP, pnm_writepaminit(&outputGeneratorP->pam); - /* Draw guide boxes at the top, if desired. */ - if (cmdline.guidesize < 0) - drawguides(-cmdline.guidesize, &outputGeneratorP->pam, + if (cmdline.xbeginSpec == 0) + xbegin = outputGeneratorP->pam.width/2; + else { + xbegin = cmdline.xbegin; + if (xbegin >= outputGeneratorP->pam.width) + pm_error("-xbegin must be less than the image width (%d)", + outputGeneratorP->pam.width); + } + + if (cmdline.guidetop) + drawguides(cmdline.guidesize, &outputGeneratorP->pam, cmdline.eyesep, cmdline.dpi, cmdline.depth); makeImageRows(&inPam, outputGeneratorP, cmdline.depth, cmdline.eyesep, cmdline.dpi, - cmdline.crosseyed, cmdline.makemask); + cmdline.crosseyed, cmdline.makemask, cmdline.magnifypat, + cmdline.smoothing, xbegin); - /* Draw guide boxes at the bottom, if desired. */ - if (cmdline.guidesize > 0) + if (cmdline.guidebottom) drawguides(cmdline.guidesize, &outputGeneratorP->pam, cmdline.eyesep, cmdline.dpi, cmdline.depth); + if (cmdline.texfile) { + pnm_freepamarray(outputGeneratorP->textureP->imageData, + &outputGeneratorP->textureP->pam); + free(outputGeneratorP->textureP); + } destroyoutputGenerator(outputGeneratorP); } @@ -855,7 +1381,12 @@ produceStereogram(FILE * const ifP, static void reportParameters(struct cmdlineInfo const cmdline) { - unsigned int const pixelEyesep = round2int(cmdline.eyesep * cmdline.dpi); + unsigned int const pixelEyesep = + ROUNDU(cmdline.eyesep * cmdline.dpi); + unsigned int const sep0 = + separation(0, cmdline.eyesep, cmdline.dpi, cmdline.depth); + unsigned int const sep1 = + separation(1, cmdline.eyesep, cmdline.dpi, cmdline.depth); pm_message("Eye separation: %.4g inch * %d DPI = %u pixels", cmdline.eyesep, cmdline.dpi, pixelEyesep); @@ -863,40 +1394,42 @@ reportParameters(struct cmdlineInfo const cmdline) { if (cmdline.magnifypat > 1) pm_message("Background magnification: %uX * %uX", cmdline.magnifypat, cmdline.magnifypat); - pm_message("\"Optimal\" pattern width: %u / (%u * 2) = %u pixels", + pm_message("\"Optimal\" (far) pattern width: %u / (%u * 2) = %u pixels", pixelEyesep, cmdline.magnifypat, pixelEyesep/(cmdline.magnifypat * 2)); - pm_message("Unique 3-D depth levels possible: %u", - separation(0, cmdline.eyesep, cmdline.dpi, cmdline.depth) - - separation(1, cmdline.eyesep, cmdline.dpi, cmdline.depth) + 1); - if (cmdline.patFilespec && (cmdline.xshift || cmdline.yshift)) - pm_message("Pattern shift: (%u, %u)", cmdline.xshift, cmdline.yshift); + pm_message("Near pattern width: %u / %u = %u pixels", + sep1, cmdline.magnifypat, sep1 / cmdline.magnifypat); + pm_message("Unique 3-D depth levels possible: %u", sep0 - sep1 + 1); + if (cmdline.patfile && (cmdline.xshift || cmdline.yshift)) + pm_message("Pattern shift: (%d, %d)", cmdline.xshift, cmdline.yshift); } int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; /* Parsed command line */ FILE * ifP; - - /* Parse the command line. */ - pnm_init(&argc, argv); + + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); - + if (cmdline.verbose) reportParameters(cmdline); - - srand(cmdline.randomseed); + + srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); ifP = pm_openr(cmdline.inputFilespec); - + /* Produce a stereogram. */ produceStereogram(ifP, cmdline); pm_close(ifP); - + return 0; } + + + diff --git a/generator/pbmmake.c b/generator/pbmmake.c index 41d80274..600440f0 100644 --- a/generator/pbmmake.c +++ b/generator/pbmmake.c @@ -37,7 +37,7 @@ parseCommandLine(int argc, char ** argv, was passed to us as the argv array. -----------------------------------------------------------------------------*/ optEntry *option_def; - /* Instructions to optParseOptions3 on how to parse our options. + /* Instructions to pm_optParseOptions3 on how to parse our options. */ optStruct3 opt; @@ -56,7 +56,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (blackOpt + whiteOpt + grayOpt > 1) @@ -101,13 +101,11 @@ writeGrayRaster(unsigned int const cols, bitrow1[i] = (PBM_WHITE*0x55) | (PBM_BLACK*0xaa); /* 0xaa = 10101010 ; 0x55 = 01010101 */ } - if (cols % 8 > 0) { - bitrow0[lastCol] >>= 8 - cols % 8; - bitrow0[lastCol] <<= 8 - cols % 8; - bitrow1[lastCol] >>= 8 - cols % 8; - bitrow1[lastCol] <<= 8 - cols % 8; - } - if (rows > 1) { + + pbm_cleanrowend_packed(bitrow0, cols); + pbm_cleanrowend_packed(bitrow1, cols); + + if (rows > 1) { unsigned int row; for (row = 1; row < rows; row += 2) { pbm_writepbmrow_packed(ofP, bitrow0, cols, 0); @@ -139,11 +137,10 @@ writeSingleColorRaster(unsigned int const cols, for (i = 0; i <= lastCol; ++i) bitrow0[i] = color*0xff; - if (cols % 8 > 0) { - bitrow0[lastCol] >>= 8 - cols % 8; - bitrow0[lastCol] <<= 8 - cols % 8; - /* row end trimming, really not necessary with white */ - } + if (color != 0) + pbm_cleanrowend_packed(bitrow0, cols); + /* row end trimming, not necessary with white */ + { unsigned int row; for (row = 0; row < rows; ++row) diff --git a/generator/pbmmake.test b/generator/pbmmake.test deleted file mode 100644 index 0fd99ccd..00000000 --- a/generator/pbmmake.test +++ /dev/null @@ -1,9 +0,0 @@ -echo Test 1. Should print 3892756435 12 -./pbmmake -white 16 2 | cksum -echo Test 2. Should print 1576602925 8 -./pbmmake -black 1 1 | cksum -echo Test 3. Should print 4272952448 14 -./pbmmake -gray 7 7 | cksum -echo Test 4. Should print 1634688086 15 -./pbmmake -grey 8 8 | cksum -echo Tests done. diff --git a/generator/pbmpage.c b/generator/pbmpage.c index fcf7af42..a2f47bcc 100644 --- a/generator/pbmpage.c +++ b/generator/pbmpage.c @@ -17,6 +17,7 @@ #include <math.h> #include <stdio.h> +#include "pm_c_util.h" #include "pbm.h" /* US is 8.5 in by 11 in */ @@ -31,10 +32,9 @@ struct bitmap { - int Width; /* width and height in 600ths of an inch */ - int Height; - int Pwidth; /* width in bytes */ - char *bitmap; + unsigned int Width; /* width and height in 600ths of an inch */ + unsigned int Height; + bit ** bitmap; }; static struct bitmap bitmap; @@ -42,11 +42,10 @@ static struct bitmap bitmap; static void -setpixel(int const x, - int const y, - int const c) { +setpixel(unsigned int const x, + unsigned int const y, + unsigned int const c) { - int const charidx = y * bitmap.Pwidth + x/8; char const bitmask = 128 >> (x % 8); if (x < 0 || x >= bitmap.Width) @@ -55,151 +54,148 @@ setpixel(int const x, return; if (c) - bitmap.bitmap[charidx] |= bitmask; + bitmap.bitmap[y][x/8] |= bitmask; else - bitmap.bitmap[charidx] &= ~bitmask; + bitmap.bitmap[y][x/8] &= ~bitmask; } static void -setplus(int x,int y,int s) +setplus(unsigned int const x, + unsigned int const y, + unsigned int const s) { /*---------------------------------------------------------------------------- Draw a black plus sign centered at (x,y) with arms 's' pixels long. Leave the exact center of the plus white. -----------------------------------------------------------------------------*/ -{ - int i; - - for(i=0; i<s; i++) - { - setpixel(x+i,y,1); - setpixel(x-i,y,1); - setpixel(x,y+i,1); - setpixel(x,y-i,1); - } + unsigned int i; + + for (i = 0; i < s; ++i) { + setpixel(x+i, y, 1); + setpixel(x-i, y, 1); + setpixel(x, y+i, 1); + setpixel(x, y-i, 1); + } } static void -setblock(int x,int y,int s) -{ - int i,j; +setblock(unsigned int const x, + unsigned int const y, + unsigned int const s) { + + unsigned int i; + + for (i = 0; i < s; ++i) { + unsigned int j; - for(i=0; i<s; i++) - for(j=0; j<s; j++) - setpixel(x+i,y+j,1); + for (j = 0; j < s; ++j) + setpixel(x+i, y+j, 1); + } } static void -setchar(int x,int y,char c) -{ - int xo,yo; - static char charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e }, - { 0x00, 0x42, 0x7f, 0x40, 0x00 }, - { 0x42, 0x61, 0x51, 0x49, 0x46 }, - { 0x22, 0x41, 0x49, 0x49, 0x36 }, - { 0x18, 0x14, 0x12, 0x7f, 0x10 }, - { 0x27, 0x45, 0x45, 0x45, 0x39 }, - { 0x3e, 0x49, 0x49, 0x49, 0x32 }, - { 0x01, 0x01, 0x61, 0x19, 0x07 }, - { 0x36, 0x49, 0x49, 0x49, 0x36 }, - { 0x26, 0x49, 0x49, 0x49, 0x3e } }; - - if(c<='9' && c>='0') - for(xo=0; xo<5; xo++) - for(yo=0; yo<8; yo++) - if((charmap[c-'0'][xo]>>yo)&1) - setblock(x+xo*3,y+yo*3,3); +setchar(unsigned int const x, + unsigned int const y, + char const c) { + + static char const charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e }, + { 0x00, 0x42, 0x7f, 0x40, 0x00 }, + { 0x42, 0x61, 0x51, 0x49, 0x46 }, + { 0x22, 0x41, 0x49, 0x49, 0x36 }, + { 0x18, 0x14, 0x12, 0x7f, 0x10 }, + { 0x27, 0x45, 0x45, 0x45, 0x39 }, + { 0x3e, 0x49, 0x49, 0x49, 0x32 }, + { 0x01, 0x01, 0x61, 0x19, 0x07 }, + { 0x36, 0x49, 0x49, 0x49, 0x36 }, + { 0x26, 0x49, 0x49, 0x49, 0x3e } }; + + if (c <= '9' && c >= '0') { + unsigned int xo; + + for (xo = 0; xo < 5; ++xo) { + unsigned int yo; + + for (yo = 0; yo < 8; ++yo) { + if ((charmap[c-'0'][xo] >> yo) & 0x01) + setblock(x + xo*3, y + yo*3, 3); + } + } + } } static void -setstring(int x,int y,char* s) -{ - char* p; - int xo; +setstring(unsigned int const x, + unsigned int const y, + const char * const s) { - for(xo=0, p=s; *p; xo+=21, p++) - setchar(x+xo,y,*p); + const char * p; + unsigned int xo; + + for (xo = 0, p = s; *p; xo += 21, ++p) + setchar(x + xo, y, *p); } static void -setCG(int x,int y) -{ - int xo,yo,zo; - - for(xo=0; xo<=50; xo++) - { - yo=sqrt(50.0*50.0-xo*xo); - setpixel(x+xo,y+yo,1); - setpixel(x+yo,y+xo,1); - setpixel(x-1-xo,y-1-yo,1); - setpixel(x-1-yo,y-1-xo,1); - setpixel(x+xo,y-1-yo,1); - setpixel(x-1-xo,y+yo,1); - for(zo=0; zo<yo; zo++) - { - setpixel(x+xo,y-1-zo,1); - setpixel(x-1-xo,y+zo,1); +setCG(unsigned int const x, + unsigned int const y) { + + unsigned int xo; + + for (xo = 0; xo <= 50; ++xo) { + unsigned int const yo = sqrt(SQR(50.0) - SQR(xo)); + + unsigned int zo; + + setpixel(x + xo, y + yo, 1); + setpixel(x+yo, y + xo, 1); + setpixel(x-1-xo, y-1-yo, 1); + setpixel(x-1-yo, y-1-xo, 1); + setpixel(x+xo, y-1-yo, 1); + setpixel(x-1-xo, y+yo, 1); + + for(zo = 0; zo < yo; ++zo) { + setpixel(x + xo, y-1-zo, 1); + setpixel(x-1-xo, y+zo, 1); + } } - } } static void -outputPbm(FILE * const file, +outputPbm(FILE * const ofP, struct bitmap const bitmap) { /*---------------------------------------------------------------------------- Create a pbm file containing the image from the global variable bitmap[]. -----------------------------------------------------------------------------*/ int const forceplain = 0; - bit *pbmrow; - int row; - int bitmap_cursor; + + unsigned int row; - pbm_writepbminit(file, bitmap.Width, bitmap.Height, forceplain); - - /* We round the allocated row space up to a multiple of 8 so the ugly - fast code below can work. - */ - pbmrow = pbm_allocrow(((bitmap.Width+7)/8)*8); + pbm_writepbminit(ofP, bitmap.Width, bitmap.Height, forceplain); - bitmap_cursor = 0; - for (row = 0; row < bitmap.Height; row++) { - int col; - for (col = 0; col < bitmap.Width;) { - /* A little ugliness makes a big speed difference here. */ - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<7); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<6); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<5); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<4); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<3); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<2); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<1); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<0); - - bitmap_cursor++; - } - pbm_writepbmrow(file, pbmrow, bitmap.Width, forceplain); + for (row = 0; row < bitmap.Height; ++row) { + pbm_writepbmrow_packed(ofP, bitmap.bitmap[row], + bitmap.Width, forceplain); } - pbm_freerow(pbmrow); - pm_close(file); } + static void framePerimeter(unsigned int const Width, unsigned int const Height) { - unsigned int x,y; + unsigned int x, y; /* Top edge */ for (x = 0; x < Width; ++x) @@ -221,57 +217,66 @@ framePerimeter(unsigned int const Width, int -main(int argc,char** argv) { +main(int argc, const char** argv) { - int TP=1; - int x,y; + int TP; + unsigned int x, y; char buf[128]; - int Width; /* width and height in 600ths of an inch */ - int Height; + /* width and height in 600ths of an inch */ + unsigned int Width; + unsigned int Height; - pbm_init(&argc, argv); + pm_proginit(&argc, argv); if (argc > 1 && strcmp(argv[1], "-a4") == 0) { - Width = A4WIDTH; + Width = A4WIDTH; Height = A4HEIGHT; - argc--; - argv++; + --argc; + ++argv; } else { - Width = USWIDTH; + Width = USWIDTH; Height = USHEIGHT; } - bitmap.Width = Width; + if (argc > 1) + TP = atoi(argv[1]); + else + TP = 1; + + bitmap.Width = Width; bitmap.Height = Height; - bitmap.Pwidth = (Width + 7) / 8; - bitmap.bitmap = malloc(bitmap.Pwidth * bitmap.Height); + bitmap.bitmap = pbm_allocarray_packed(Width, bitmap.Height); - for (x = 0; x < bitmap.Pwidth * bitmap.Height; ++x) - bitmap.bitmap[x] = 0x00; - - if (argc>1) - TP = atoi(argv[1]); + for (y = 0; y < bitmap.Height; ++y) { + unsigned int x; + for (x = 0; x < pbm_packed_bytes(bitmap.Width); ++x) + bitmap.bitmap[y][x] = 0x00; + } switch (TP) { case 1: framePerimeter(Width, Height); - for (x = 0; x < Width; x += 100) + for (x = 0; x < Width; x += 100) { + unsigned int y; for(y = 0; y < Height; y += 100) setplus(x, y, 4); + } for(x = 0; x < Width; x += 100) { sprintf(buf,"%d", x); setstring(x + 3, (Height/200) * 100 + 3, buf); } - for (y=0; y < Height; y += 100) { + for (y = 0; y < Height; y += 100) { sprintf(buf, "%d", y); setstring((Width/200) * 100 + 3, y + 3, buf); } for (x = 0; x < Width; x += 10) for (y = 0; y < Height; y += 100) setplus(x, y, ((x%100) == 50) ? 2 : 1); - for (x=0; x < Width; x += 100) + for (x = 0; x < Width; x += 100) { + unsigned int y; for (y = 0; y < Height; y += 10) setplus(x, y, ((y%100) == 50) ? 2 : 1); + } setCG(Width/2, Height/2); break; case 2: @@ -290,5 +295,9 @@ main(int argc,char** argv) { outputPbm(stdout, bitmap); + pbm_freearray(bitmap.bitmap, Height); + + pm_close(stdout); + return 0; } diff --git a/generator/pbmtext.c b/generator/pbmtext.c index 693c3f59..9f4366d4 100644 --- a/generator/pbmtext.c +++ b/generator/pbmtext.c @@ -79,7 +79,7 @@ parseCommandLine(int argc, const char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - optParseOptions3(&argc, (char **)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 (argc-1 == 0) @@ -236,7 +236,8 @@ fixControlChars(const char * const input, MALLOCARRAY(output, outputSize); if (output == NULL) - pm_error("Couldn't allocate %u bytes for a line of text.", outputSize); + pm_error("Couldn't allocate %u bytes for a line of text.", + (unsigned)outputSize); for (inCursor = 0, outCursor = 0; input[inCursor] != '\0'; ++inCursor) { if (outCursor + 1 + tabSize > outputSize) { @@ -244,7 +245,7 @@ fixControlChars(const char * const input, REALLOCARRAY(output, outputSize); if (output == NULL) pm_error("Couldn't allocate %u bytes for a line of text.", - outputSize); + (unsigned)outputSize); } if (input[inCursor] == '\n' && input[inCursor+1] == '\0') { /* This is a terminating newline. We don't do those. */ @@ -709,7 +710,7 @@ getText(const char cmdline_text[], while (fgets(buf, sizeof(buf), stdin) != NULL) { if (strlen(buf) + 1 >= sizeof(buf)) pm_error("A line of input text is longer than %u characters." - "Cannot process.", sizeof(buf)-1); + "Cannot process.", (unsigned)sizeof(buf)-1); if (lineCount >= maxlines) { maxlines *= 2; REALLOCARRAY(text_array, maxlines); diff --git a/generator/pbmtextps.c b/generator/pbmtextps.c index eab6780a..e6367530 100644 --- a/generator/pbmtextps.c +++ b/generator/pbmtextps.c @@ -181,7 +181,7 @@ buildTextFromArgs(int const argc, asciiHexEncode(text, asciiHexText); *asciiHexTextP = asciiHexText; } - strfree(text); + pm_strfree(text); } @@ -219,7 +219,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); validateFontName(cmdlineP->font); @@ -231,7 +231,7 @@ parseCommandLine(int argc, char ** argv, static void termCmdline(struct cmdlineInfo const cmdline) { - strfree(cmdline.text); + pm_strfree(cmdline.text); } @@ -263,11 +263,11 @@ construct_postscript(struct cmdlineInfo const cmdline) { "showpage\n"; if (cmdline.stroke < 0) - asprintfN(&retval, template, cmdline.font, cmdline.fontsize, - cmdline.text); + pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, + cmdline.text); else - asprintfN(&retval, template, cmdline.font, cmdline.fontsize, - cmdline.stroke, cmdline.text); + pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, + cmdline.stroke, cmdline.text); return retval; } @@ -362,10 +362,11 @@ gsCommand(const char * const psFname, pm_error("Absurdly fine resolution (%u) and/or huge font size (%u). " "Output height too large.", cmdline.res, cmdline.fontsize); - asprintfN(&retval, "%s -g%dx%d -r%d -sDEVICE=pbm " - "-sOutputFile=%s -q -dBATCH -dNOPAUSE %s </dev/null >/dev/null", - gsExecutableName(), (int) x, (int) y, cmdline.res, - outputFilename, psFname); + pm_asprintf(&retval, "%s -g%dx%d -r%d -sDEVICE=pbm " + "-sOutputFile=%s -q -dBATCH -dNOPAUSE %s " + "</dev/null >/dev/null", + gsExecutableName(), (int) x, (int) y, cmdline.res, + outputFilename, psFname); return retval; } @@ -379,9 +380,9 @@ cropCommand(const char * const inputFileName) { const char * plainOpt = pm_plain_output ? "-plain" : "" ; if (cropExecutableName()) { - asprintfN(&retval, "%s -top -right %s %s", - cropExecutableName(), plainOpt, inputFileName); - if (retval == strsol) + pm_asprintf(&retval, "%s -top -right %s %s", + cropExecutableName(), plainOpt, inputFileName); + if (retval == pm_strsol) pm_error("Unable to allocate memory"); } else retval = NULL; @@ -413,7 +414,7 @@ writeProgram(const char * const psFname, fclose(psfile); - strfree(ps); + pm_strfree(ps); } @@ -437,7 +438,7 @@ executeProgram(const char * const psFname, if (rc != 0) pm_error("Failed to run Ghostscript process. rc=%d", rc); - strfree(com); + pm_strfree(com); } @@ -486,7 +487,7 @@ cropToStdout(const char * const inputFileName, } fclose(pnmcrop); } - strfree(com); + pm_strfree(com); } } @@ -500,25 +501,25 @@ createOutputFile(struct cmdlineInfo const cmdline) { const char * psFname; const char * uncroppedPbmFname; - asprintfN(&psFname, template, getpid(), "ps"); + pm_asprintf(&psFname, template, getpid(), "ps"); if (psFname == NULL) pm_error("Unable to allocate memory"); writeProgram(psFname, cmdline); - asprintfN(&uncroppedPbmFname, template, getpid(), "pbm"); + pm_asprintf(&uncroppedPbmFname, template, getpid(), "pbm"); if (uncroppedPbmFname == NULL) pm_error("Unable to allocate memory"); executeProgram(psFname, uncroppedPbmFname, cmdline); unlink(psFname); - strfree(psFname); + pm_strfree(psFname); cropToStdout(uncroppedPbmFname, cmdline.verbose); unlink(uncroppedPbmFname); - strfree(uncroppedPbmFname); + pm_strfree(uncroppedPbmFname); } diff --git a/generator/pgmcrater b/generator/pgmcrater new file mode 100755 index 00000000..1c22ed70 --- /dev/null +++ b/generator/pgmcrater @@ -0,0 +1,94 @@ +#!/bin/sh + +############################################################################## +# This is essentially a Perl program. We exec the Perl interpreter specifying +# this same file as the Perl program and use the -x option to cause the Perl +# interpreter to skip down to the Perl code. The reason we do this instead of +# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is +# that the user may have multiple Perl interpreters and the one he wants to +# use is properly located in the PATH. The user's choice of Perl interpreter +# may be crucial, such as when the user also has a PERL5LIB environment +# variable and it selects modules that work with only a certain main +# interpreter program. +# +# An alternative some people use is to have /usr/bin/env as the script +# interpreter. We don't do that because we think the existence and +# compatibility of /bin/sh is more reliable. +# +# Note that we aren't concerned about efficiency because the user who needs +# high efficiency can use directly the programs that this program invokes. +# +############################################################################## + +exec perl -w -x -S -- "$0" "$@" + +#!/usr/bin/perl +############################################################################## +# This is nothing but a compatibility interface for +# Pamcrater/Pamshadedrelief. An old program coded to call Pgmcrater will +# continue working because this interface exists. All new (or newly +# modified) programs should call Pamcrater and Pamshadedrelief instead. +# +# In days past, Pamcrater and Pamshadedrelief did not exist. Pgmcrater did +# both jobs together, with PGM output. +############################################################################## + +use strict; + +use Getopt::Long; + +my @pgmcraterArgv = @ARGV; + +my $validOptions = GetOptions( + 'number=i' => \my $numberOpt, + 'height=i' => \my $heightOpt, + 'ysize=i' => \my $ysizeOpt, + 'width=i' => \my $widthOpt, + 'xsize=i' => \my $xsizeOpt, + 'gamma=i' => \my $gammaOpt, + 'randomseed=i' => \my $randomseedOpt); + +if (!$validOptions) { + print STDERR "Invalid syntax\n"; + exit(100); +} + +my $pamcraterArgs; + +$pamcraterArgs = ''; # initial value + +if (defined($numberOpt)) { + $pamcraterArgs .= "'-number=$numberOpt' "; +} + +if (defined($heightOpt)) { + $pamcraterArgs .= "'-height=$heightOpt' "; +} elsif (defined($ysizeOpt)) { + $pamcraterArgs .= "'-height=$ysizeOpt' "; +} + +if (defined($widthOpt)) { + $pamcraterArgs .= "'-width=$widthOpt' "; +} elsif (defined($xsizeOpt)) { + $pamcraterArgs .= "'-width=$xsizeOpt' "; +} + +if (defined($randomseedOpt)) { + $pamcraterArgs .= "'-randomseed=$randomseedOpt' "; +} + +my $pamshadedreliefArgs; + +$pamshadedreliefArgs = ''; # initial value + +if (defined($gammaOpt)) { + $pamshadedreliefArgs .= "'-gamma=$gammaOpt' "; +} + +my $termStatus = + system( + "pamcrater $pamcraterArgs | " . + "pamshadedrelief $pamshadedreliefArgs |" . + "pamtopnm"); + +exit($termStatus == 0 ? 0 : 1); diff --git a/generator/pgmcrater.c b/generator/pgmcrater.c deleted file mode 100644 index ec592381..00000000 --- a/generator/pgmcrater.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - - Fractal cratering - - Designed and implemented in November of 1989 by: - - John Walker - Autodesk SA - Avenue des Champs-Montants 14b - CH-2074 MARIN - Switzerland - Usenet: kelvin@Autodesk.com - Fax: 038/33 88 15 - Voice: 038/33 76 33 - - The algorithm used to determine crater size is as described on - pages 31 and 32 of: - - Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal - Images, New York: Springer Verlag, 1988. - - The mathematical technique used to calculate crater radii that - obey the proper area law distribution from a uniformly distributed - pseudorandom sequence was developed by Rudy Rucker. - - Permission to use, copy, modify, and distribute this software and - its documentation for any purpose and without fee is hereby - granted, without any conditions or restrictions. This software is - provided "as is" without express or implied warranty. - - PLUGWARE! - - If you like this kind of stuff, you may also enjoy "James Gleick's - Chaos--The Software" for MS-DOS, available for $59.95 from your - local software store or directly from Autodesk, Inc., Attn: Science - Series, 2320 Marinship Way, Sausalito, CA 94965, USA. Telephone: - (800) 688-2344 toll-free or, outside the U.S. (415) 332-2344 Ext - 4886. Fax: (415) 289-4718. "Chaos--The Software" includes a more - comprehensive fractal forgery generator which creates - three-dimensional landscapes as well as clouds and planets, plus - five more modules which explore other aspects of Chaos. The user - guide of more than 200 pages includes an introduction by James - Gleick and detailed explanations by Rudy Rucker of the mathematics - and algorithms used by each program. - -*/ - -/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at right - * edge. Make craters wrap around the image (enables tiling of image). - */ - -#define _XOPEN_SOURCE /* get M_PI in math.h */ - -#include <assert.h> -#include <math.h> - -#include "pm_c_util.h" -#include "pgm.h" -#include "mallocvar.h" - -static void gencraters ARGS((void)); -static void initseed ARGS((void)); - -/* Definitions for obtaining random numbers. */ - -#define Cast(low, high) ((low)+((high)-(low)) * ((rand() & 0x7FFF) / arand)) - -/* Data types */ - -typedef int Boolean; -#define FALSE 0 -#define TRUE 1 - -#define V (void) - -/* Display parameters */ - -#define SCRX screenxsize /* Screen width */ -#define SCRY screenysize /* Screen height */ -#define SCRGAMMA 1.0 /* Display gamma */ - -/* Local variables */ - -#define ImageGamma 0.5 /* Inherent gamma of mapped image */ - -static int screenxsize = 256; /* Screen X size */ -static int screenysize = 256; /* Screen Y size */ -static double dgamma = SCRGAMMA; /* Display gamma */ -static double arand = 32767.0; /* Random number parameters */ -static long ncraters = 50000L; /* Number of craters to generate */ -static double CdepthPower = 1.5; /* Crater depth power factor */ -static double DepthBias = 0.707107; /* Depth bias */ - -static int modulo(int t, int n) -{ - int m; - assert(n>0); - m = t % n; - while (m<0) { - m+=n; - } - return m; -} - -/* INITSEED -- Generate initial random seed, if needed. */ - -static void initseed() -{ - unsigned int i; - - srand(pm_randseed()); - for (i = 0; i < 7; ++i) - V rand(); -} - -/* GENCRATERS -- Generate cratered terrain. */ - -static void gencraters() -{ - int i, j, x, y; - long l; - unsigned short *aux; - int slopemin = -52, slopemax = 52; -#define RGBQuant 255 - unsigned char *slopemap; /* Slope to pixel map */ - gray *pixels; /* Pixel vector */ - -#define Auxadr(x, y) &aux[modulo(y, SCRY)*SCRX+modulo(x, SCRX)] - - /* Acquire the elevation array and initialize it to mean - surface elevation. */ - - MALLOCARRAY(aux, SCRX * SCRY); - if (aux == NULL) - pm_error("out of memory allocating elevation array"); - - /* Acquire the elevation buffer and initialize to mean - initial elevation. */ - - for (i = 0; i < SCRY; i++) { - unsigned short *zax = aux + (((long) SCRX) * i); - - for (j = 0; j < SCRX; j++) { - *zax++ = 32767; - } - } - - /* Every time we go around this loop we plop another crater - on the surface. */ - - for (l = 0; l < ncraters; l++) { - double g; - int cx = Cast(0.0, ((double) SCRX - 1)), - cy = Cast(0.0, ((double) SCRY - 1)), - gx, gy, x, y; - unsigned long amptot = 0, axelev; - unsigned int npatch = 0; - - - /* Phase 1. Compute the mean elevation of the impact - area. We assume the impact area is a - fraction of the total crater size. */ - - /* Thanks, Rudy, for this equation that maps the uniformly - distributed numbers from Cast into an area-law - distribution as observed on cratered bodies. */ - - g = sqrt(1 / (M_PI * (1 - Cast(0, 0.9999)))); - - /* If the crater is tiny, handle it specially. */ - -#if 0 - fprintf(stderr, "crater=%lu ", (unsigned long)l); - fprintf(stderr, "cx=%d ", cx); - fprintf(stderr, "cy=%d ", cy); - fprintf(stderr, "size=%g\n", g); -#endif - - if (g < 3) { - - /* Set pixel to the average of its Moore neighbourhood. */ - - for (y = cy - 1; y <= cy + 1; y++) { - for (x = cx - 1; x <= cx + 1; x++) { - amptot += *Auxadr(x, y); - npatch++; - } - } - axelev = amptot / npatch; - - /* Perturb the mean elevation by a small random factor. */ - - x = (g >= 1) ? ((rand() >> 8) & 3) - 1 : 0; - *Auxadr(cx, cy) = axelev + x; - - /* Jam repaint sizes to correct patch. */ - - gx = 1; - gy = 0; - - } else { - - /* Regular crater. Generate an impact feature of the - correct size and shape. */ - - /* Determine mean elevation around the impact area. */ - - gx = MAX(2, (g / 3)); - gy = MAX(2, g / 3); - - for (y = cy - gy; y <= cy + gy; y++) { - for (x = cx-gx; x <= cx + gx; x++) { - amptot += *Auxadr(x,y); - npatch++; - } - } - axelev = amptot / npatch; - - gy = MAX(2, g); - g = gy; - gx = MAX(2, g); - - for (y = cy - gy; y <= cy + gy; y++) { - double dy = (cy - y) / (double) gy, - dysq = dy * dy; - - for (x = cx - gx; x <= cx + gx; x++) { - double dx = ((cx - x) / (double) gx), - cd = (dx * dx) + dysq, - cd2 = cd * 2.25, - tcz = DepthBias - sqrt(fabs(1 - cd2)), - cz = MAX((cd2 > 1) ? 0.0 : -10, tcz), - roll, iroll; - unsigned short av; - - cz *= pow(g, CdepthPower); - if (dy == 0 && dx == 0 && ((int) cz) == 0) { - cz = cz < 0 ? -1 : 1; - } - -#define rollmin 0.9 - roll = (((1 / (1 - MIN(rollmin, cd))) / - (1 / (1 - rollmin))) - (1 - rollmin)) / rollmin; - iroll = 1 - roll; - - av = (axelev + cz) * iroll + (*Auxadr(x,y) + cz) * roll; - av = MAX(1000, MIN(64000, av)); - *Auxadr(x,y) = av; - } - } - } - if ((l % 5000) == 4999) { - pm_message( "%ld craters generated of %ld (%ld%% done)", - l + 1, ncraters, ((l + 1) * 100) / ncraters); - } - } - - i = MAX((slopemax - slopemin) + 1, 1); - MALLOCARRAY(slopemap, i); - if (slopemap == NULL) - pm_error("out of memory allocating slope map"); - - for (i = slopemin; i <= slopemax; i++) { - - /* Confused? OK, we're using the left-to-right slope to - calculate a shade based on the sine of the angle with - respect to the vertical (light incident from the left). - Then, with one exponentiation, we account for both the - inherent gamma of the image (ad-hoc), and the - user-specified display gamma, using the identity: - - (x^y)^z = (x^(y*z)) */ - - slopemap[i - slopemin] = i > 0 ? - (128 + 127.0 * - pow(sin((M_PI / 2) * i / slopemax), - dgamma * ImageGamma)) : - (128 - 127.0 * - pow(sin((M_PI / 2) * i / slopemin), - dgamma * ImageGamma)); - } - - /* Generate the screen image. */ - - pgm_writepgminit(stdout, SCRX, SCRY, RGBQuant, FALSE); - pixels = pgm_allocrow(SCRX); - - for (y = 0; y < SCRY; y++) { - gray *pix = pixels; - - for (x = 0; x < SCRX; x++) { - int j = *Auxadr(x+1, y) - *Auxadr(x, y); - j = MIN(MAX(slopemin, j), slopemax); - *pix++ = slopemap[j - slopemin]; - } - pgm_writepgmrow(stdout, pixels, SCRX, RGBQuant, FALSE); - } - pm_close(stdout); - pgm_freerow(pixels); - -#undef Auxadr -#undef Scradr - free((char *) slopemap); - free((char *) aux); -} - -/* MAIN -- Main program. */ - -int main(argc, argv) - int argc; - char *argv[]; -{ - int i; - Boolean gammaspec = FALSE, numspec = FALSE, - widspec = FALSE, hgtspec = FALSE; - const char * const usage = "[-number <n>] [-width|-xsize <w>]\n\ - [-height|-ysize <h>] [-gamma <f>]"; - - DepthBias = sqrt(0.5); /* Get exact value for depth bias */ - - - pgm_init(&argc, argv); - - i = 1; - while ((i < argc) && (argv[i][0] == '-') && (argv[i][1] != '\0')) { - if (pm_keymatch(argv[i], "-gamma", 2)) { - if (gammaspec) { - pm_error("already specified gamma correction"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &dgamma) != 1)) - pm_usage(usage); - if (dgamma <= 0.0) { - pm_error("gamma correction must be greater than 0"); - } - gammaspec = TRUE; - } else if (pm_keymatch(argv[i], "-number", 2)) { - if (numspec) { - pm_error("already specified number of craters"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%ld", &ncraters) != 1)) - pm_usage(usage); - if (ncraters <= 0) { - pm_error("number of craters must be greater than 0!"); - } - numspec = TRUE; - } else if (pm_keymatch(argv[i], "-xsize", 2) || - pm_keymatch(argv[i], "-width", 2)) { - if (widspec) { - pm_error("already specified a width/xsize"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &screenxsize) != 1)) - pm_usage(usage); - if (screenxsize <= 0) { - pm_error("screen width must be greater than 0"); - } - widspec = TRUE; - } else if (pm_keymatch(argv[i], "-ysize", 2) || - pm_keymatch(argv[i], "-height", 2)) { - if (hgtspec) { - pm_error("already specified a height/ysize"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &screenysize) != 1)) - pm_usage(usage); - if (screenxsize <= 0) { - pm_error("screen height must be greater than 0"); - } - hgtspec = TRUE; - } else { - pm_usage(usage); - } - i++; - } - - initseed(); - gencraters(); - - exit(0); -} diff --git a/generator/pgmkernel.c b/generator/pgmkernel.c index b741d596..ec634c16 100644 --- a/generator/pgmkernel.c +++ b/generator/pgmkernel.c @@ -1,8 +1,8 @@ -/* pgmkernel.c - generate a portable graymap convolution kernel +/* pgmkernel.c - generate a PGM convolution kernel ** -** Creates a Portable Graymap file containing a convolution filter -** with max value = 255 and minimum value > 127 that can be used as a -** smoothing kernel for pnmconvol. +** Creates a PGM image containing a convolution filter with max value = 255 +** and minimum value > 127 that can be used as a smoothing kernel for +** pnmconvol. ** ** Copyright (C) 1992 by Alberto Accomazzi, Smithsonian Astrophysical ** Observatory. @@ -16,76 +16,228 @@ */ #include <math.h> -#include "pgm.h" +#include "pm_c_util.h" +#include "shhopt.h" #include "mallocvar.h" +#include "pgm.h" + + + +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + unsigned int cols; + unsigned int rows; + float weight; + gray maxval; +}; + + + +static void +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Convert program invocation arguments (argc,argv) into a format the + program can use easily, struct cmdlineInfo. Validate arguments along + the way and exit program with message if invalid. + + Note that some string information we return as *cmdlineP is in the storage + argv[] points to. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct3 opt; + + unsigned int weightSpec, maxvalSpec; + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "weight", OPT_FLOAT, &cmdlineP->weight, + &weightSpec, 0); + OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, + &maxvalSpec, 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 */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!weightSpec) + cmdlineP->weight = 6.0; + + if (cmdlineP->weight < 0.0) + pm_error("-weight cannot be negative. You specified %f", + cmdlineP->weight); + + if (!maxvalSpec) + cmdlineP->maxval = PGM_MAXMAXVAL; + + if (cmdlineP->maxval > PGM_OVERALLMAXVAL) + pm_error("-maxval is too large: %u. Maximum is %u", + cmdlineP->maxval, PGM_OVERALLMAXVAL); + + if (cmdlineP->maxval == 0) + pm_error("-maxval cannot be zero"); + + if (argc-1 < 1) + pm_error("Need at least one argument: size of (square) kernel"); + else if (argc-1 == 1) { + if (atoi(argv[1]) <= 0) + pm_error("Dimension must be a positive number. " + "You specified '%s'", argv[1]); + cmdlineP->cols = atoi(argv[1]); + cmdlineP->rows = atoi(argv[1]); + } else if (argc-1 == 2) { + if (atoi(argv[1]) <= 0) + pm_error("Width must be a positive number. " + "You specified '%s'", argv[1]); + if (atoi(argv[2]) <= 0) + pm_error("Height must be a positive number. " + "You specified '%s'", argv[2]); + cmdlineP->cols = atoi(argv[1]); + cmdlineP->rows = atoi(argv[2]); + } else + pm_error("At most two arguments allowed. " + "You specified %u", argc-1); +} + + + +static double +t(double const dx2, + double const dy2, + double const weight) { +/*---------------------------------------------------------------------------- + The t value for a pixel that is (dx, dy) pixels away from the center of + the kernel, where 'dx2' is SQR(dx) and 'dy2' is SQR(dy), if the distance is + weighted by 'weight'. +-----------------------------------------------------------------------------*/ + + return 1.0 / (1.0 + weight * sqrt(dx2 + dy2)); +} + + + +static double +tMaxAllKernel(unsigned int const cols, + unsigned int const rows, + double const weight) { +/*---------------------------------------------------------------------------- + The maximum t value over all pixels in the kernel, if the kernel is + 'cols' by 'rows' pixels and distance is weighted by 'weight'. +-----------------------------------------------------------------------------*/ + + /* It depends upon whether there is an even or odd number of rows + and columns. If both dimensions are odd, there is a pixel right + at the center, and it has the greatest t value. If both dimensions + are even, the center of the image is in the center of a 4-pixel + square and each of those 4 pixels has the greatest t value. If + one dimension is even and the other odd, the center of the kernel + is midway between two pixels, horizontally or vertically, and one + of those two pixels has the greatest t value. + */ + + double dxMax, dyMax; + + switch (cols % 2 + rows % 2) { + case 0: + dxMax = 0.5; + dyMax = 0.5; + break; + case 1: + dxMax = 0.5; + dyMax = 0.0; + break; + case 2: + dxMax = 0.0; + dyMax = 0.0; + } + + return t(SQR(dxMax), SQR(dyMax), weight); +} + + + +static void +writeKernel(FILE * const ofP, + unsigned int const cols, + unsigned int const rows, + gray const maxval, + gray ** const halfKernel, + unsigned int const halfRows) { + + unsigned int row; + + pgm_writepgminit(stdout, cols, rows, maxval, 0); + + for (row = 0; row < halfRows; ++row) + pgm_writepgmrow(stdout, halfKernel[row], cols, maxval, 0); + + /* Now write out the same rows in reverse order. */ + + for (; row < rows; ++row) + pgm_writepgmrow(stdout, halfKernel[rows-1-row], cols, maxval, 0); +} + + int -main ( argc, argv ) - int argc; - char *argv[]; -{ - register int i, j; - int argn = 1, ixsize, iysize, maxval = 255; - double fxsize = 0.0, fysize = 0.0, w = 6.0, kxcenter, kycenter, - tmax = 0, *fkernel; - const char *usage = "[-weight f] width [height]"; - - pgm_init( &argc, argv ); - - while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) - { - if ( pm_keymatch( argv[argn], "-weight", 2 )) { - if (++argn >= argc) - pm_usage( usage ); - else if (sscanf(argv[argn], "%lf", &w) != 1) - pm_usage( usage ); +main(int argc, const char * argv[]) { + + struct CmdlineInfo cmdline; + unsigned int arows; + unsigned int arow; + double xcenter, ycenter; + /* row, column "number" of center of kernel */ + double tMax; + /* The maximum t value over all pixels */ + gray ** halfKernel; + /* The upper half of the kernel we generate. The lower half is + just the mirror image of this. + */ + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + xcenter = ((double) cmdline.cols - 1) / 2.0; + ycenter = ((double) cmdline.rows - 1) / 2.0; + + tMax = tMaxAllKernel(cmdline.cols, cmdline.rows, cmdline.weight); + + /* Output matrix is symmetric vertically and horizontally. */ + + arows = (cmdline.rows + 1) / 2; + /* Half the number of rows. Add 1 if odd. */ + halfKernel = pgm_allocarray(cmdline.cols, arows); + + for (arow = 0; arow < arows; ++arow) { + double const dy2 = SQR(arow - ycenter); + + unsigned int col; + for (col = 0; col < (cmdline.cols +1) / 2; ++col) { + double const dx2 = SQR(col - xcenter); + + double const normalized = t(dx2, dy2, cmdline.weight) / 2 / tMax; + + gray const grayval = ROUNDU(cmdline.maxval * (0.5 + normalized)); + + halfKernel[arow][col ] = grayval; + halfKernel[arow][cmdline.cols - col - 1] = grayval; } - else - pm_usage( usage ); - argn++; } - if (argn == argc) - pm_usage( usage ); - - if (sscanf(argv[argn], "%lf", &fxsize) != 1) - pm_error( "error reading input kernel x size, (%s)\n", argv[argn]); + writeKernel(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, + halfKernel, arows); - ++argn; - if (argn == argc - 1) { - if (sscanf(argv[argn], "%lf", &fysize) != 1) - pm_error( "error reading input kernel y size, (%s)\n", argv[argn]); - } - else if (argn == argc) - fysize = fxsize; - else - pm_usage( usage ); - - if (fxsize <= 1 || fysize <= 1) - pm_usage( usage ); - - kxcenter = (fxsize - 1) / 2.0; - kycenter = (fysize - 1) / 2.0; - ixsize = fxsize + 0.999; - iysize = fysize + 0.999; - MALLOCARRAY(fkernel, ixsize * iysize); - for (i = 0; i < iysize; i++) - for (j = 0; j < ixsize; j++) { - fkernel[i*ixsize+j] = 1.0 / (1.0 + w * sqrt((double) - (i-kycenter)*(i-kycenter)+ - (j-kxcenter)*(j-kxcenter))); - if (tmax < fkernel[i*ixsize+j]) - tmax = fkernel[i*ixsize+j]; - } + pgm_freearray(halfKernel, arows); - /* output PGM header + data (ASCII format only) */ - printf("P2\n%d %d\n%d\n", ixsize, iysize, maxval); - - for (i = 0; i < iysize; i++, printf("\n")) - for (j = 0; j < ixsize; j++) - printf(" %3d", (int)(maxval * (fkernel[i*ixsize+j] / - (2*tmax) + 0.5))); - - exit(0); + return 0; } - diff --git a/generator/pgmmake.c b/generator/pgmmake.c index bc7f025c..f8f8b09c 100644 --- a/generator/pgmmake.c +++ b/generator/pgmmake.c @@ -43,19 +43,21 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = false; /* We have no short (old-fashioned) options */ opt.allowNegNum = false; /* We have no parms that are negative numbers */ - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + free (option_def); + if (!maxvalSpec) cmdlineP->maxval = PGM_MAXMAXVAL; else { if (cmdlineP->maxval > PGM_OVERALLMAXVAL) pm_error("The value you specified for -maxval (%u) is too big. " "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL); - + if (cmdlineP->maxval < 1) pm_error("You cannot specify 0 for -maxval"); - } + } if (argc-1 < 3) pm_error("Need 3 arguments: gray level, width, height."); @@ -82,7 +84,7 @@ main(int argc, char *argv[]) { struct cmdlineInfo cmdline; gray * grayrow; - unsigned int row; + unsigned int col, row; pgm_init(&argc, argv); @@ -91,12 +93,12 @@ main(int argc, char *argv[]) { pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0); grayrow = pgm_allocrow(cmdline.cols); - for (row = 0; row < cmdline.rows; ++row) { - unsigned int col; - for (col = 0; col < cmdline.cols; ++col) - grayrow[col] = cmdline.grayLevel; + /* All rows are identical. Fill once. */ + for (col = 0; col < cmdline.cols; ++col) + grayrow[col] = cmdline.grayLevel; + + for (row = 0; row < cmdline.rows; ++row) pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0); - } pgm_freerow(grayrow); pm_close(stdout); diff --git a/generator/pgmnoise.c b/generator/pgmnoise.c index 215cbfeb..442edc59 100644 --- a/generator/pgmnoise.c +++ b/generator/pgmnoise.c @@ -7,6 +7,8 @@ #include "mallocvar.h" #include "shhopt.h" #include "pgm.h" +#include <assert.h> + struct cmdlineInfo { @@ -15,13 +17,13 @@ struct cmdlineInfo { */ unsigned int width; unsigned int height; + unsigned int maxval; unsigned int randomseed; unsigned int randomseedSpec; }; - static void parseCommandLine(int argc, const char ** const argv, struct cmdlineInfo * const cmdlineP) { @@ -34,19 +36,32 @@ parseCommandLine(int argc, const char ** const argv, */ optStruct3 opt; unsigned int option_def_index; + unsigned int maxvalSpec; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ - OPTENT3(0, "randomseed", OPT_INT, &cmdlineP->randomseed, + OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); + OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, + &maxvalSpec, 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 */ - optParseOptions3(&argc, (char **)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. */ + free(option_def); + + if (maxvalSpec) { + if (cmdlineP->maxval > PGM_OVERALLMAXVAL) + pm_error("Maxval too large: %u. Maximu is %u", + cmdlineP->maxval, PGM_OVERALLMAXVAL); + else if (cmdlineP->maxval == 0) + pm_error("Maxval must not be zero"); + } else + cmdlineP->maxval = PGM_MAXMAXVAL; if (argc-1 != 2) pm_error("Wrong number of arguments: %u. " @@ -70,25 +85,91 @@ parseCommandLine(int argc, const char ** const argv, +static unsigned int +randPool(unsigned int const digits) { +/*---------------------------------------------------------------------------- + Draw 'digits' bits from pool of random bits. If the number of random bits + in pool is insufficient, call rand() and add 31 bits to it. + + 'digits' must be at most 16. + + We assume that each call to rand() generates 31 bits, or RAND_MAX == + 2147483647. + + The underlying logic is flexible and endian-free. The above conditions + can be relaxed. +-----------------------------------------------------------------------------*/ + static unsigned long int hold=0; /* entropy pool */ + static unsigned int len=0; /* number of valid bits in pool */ + + unsigned int const mask = (1 << digits) - 1; + + unsigned int retval; + + assert(RAND_MAX == 2147483647 && digits <= 16); + + retval = hold; /* initial value */ + + if (len > digits) { /* Enough bits in hold to satisfy request */ + hold >>= digits; + len -= digits; + } else { /* Load another 31 bits into hold */ + hold = rand(); + retval |= (hold << len); + hold >>= (digits - len); + len = 31 - digits + len; + } + return (retval & mask); +} + + static void -pgmnoise(FILE * const ofP, +pgmnoise(FILE * const ofP, unsigned int const cols, unsigned int const rows, gray const maxval) { + bool const usingPool = !(RAND_MAX==2147483647 && (maxval & (maxval+1))); + unsigned int const bitLen = pm_maxvaltobits(maxval); + unsigned int row; gray * destrow; + /* If maxval is 2^n-1, we draw exactly n bits from the pool. + Otherwise call rand() and determine gray value by modulo. + + In the latter case, there is a miniscule skew toward 0 (=black) + because smaller numbers are produced more frequently by modulo. + Thus we employ the pool method only when it is certain that no + skew will ensue. + + To illustrate the point, consider converting the outcome of one + roll of a fair, six-sided die to 5 values (0 to 4) by N % 5. The + probability for values 1, 2, 3, 4 are 1/6, but 0 alone is 2/6. + Average is 10/6 or 1.6667, compared to 2.0 from an ideal + generator which produces exactly 5 values. With two dice + average improves to 70/36 or 1.9444. + + The more (distinct) dice we roll, or the more binary digits we + draw, the smaller the skew. + */ + destrow = pgm_allocrow(cols); pgm_writepgminit(ofP, cols, rows, maxval, 0); for (row = 0; row < rows; ++row) { - unsigned int col; - for (col = 0; col < cols; ++col) - destrow[col] = rand() % (maxval + 1); - + if (usingPool) { + unsigned int col; + for (col = 0; col < cols; ++col) + destrow[col] = randPool(bitLen); + } + else { + unsigned int col; + for (col = 0; col < cols; ++col) + destrow[col] = rand() % (maxval + 1); + } pgm_writepgmrow(ofP, destrow, cols, maxval, 0); } @@ -97,8 +178,9 @@ pgmnoise(FILE * const ofP, -int main(int argc, - const char * argv[]) { +int +main(int argc, + const char * argv[]) { struct cmdlineInfo cmdline; @@ -108,8 +190,7 @@ int main(int argc, srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); - pgmnoise(stdout, cmdline.width, cmdline.height, PGM_MAXMAXVAL); + pgmnoise(stdout, cmdline.width, cmdline.height, cmdline.maxval); return 0; } - diff --git a/generator/pgmramp.c b/generator/pgmramp.c index c0fb42b1..225542fe 100644 --- a/generator/pgmramp.c +++ b/generator/pgmramp.c @@ -16,7 +16,7 @@ #include "pgm.h" #include "shhopt.h" -enum ramptype {RT_LR, RT_TB, RT_RECT, RT_ELLIP}; +enum ramptype {RT_LR, RT_TB, RT_DIAG, RT_RECT, RT_ELLIP}; struct cmdlineInfo { @@ -39,7 +39,7 @@ parseCommandLine(int argc, char ** argv, program can use easily, struct cmdlineInfo. Validate arguments along the way and exit program with message if invalid. - Note that some string information we return as *cmdlineP is in the storage + Note that some string information we return as *cmdlineP is in the storage argv[] points to. -----------------------------------------------------------------------------*/ optEntry *option_def = malloc(100*sizeof(optEntry)); @@ -47,13 +47,14 @@ parseCommandLine(int argc, char ** argv, */ optStruct3 opt; - unsigned int lrSpec, tbSpec, rectangleSpec, ellipseSpec; + unsigned int lrSpec, tbSpec, diagonalSpec, rectangleSpec, ellipseSpec; unsigned int maxvalSpec; unsigned int option_def_index; option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "lr", OPT_FLAG, NULL, &lrSpec, 0); OPTENT3(0, "tb", OPT_FLAG, NULL, &tbSpec, 0); + OPTENT3(0, "diagonal", OPT_FLAG, NULL, &diagonalSpec, 0); OPTENT3(0, "rectangle", OPT_FLAG, NULL, &rectangleSpec, 0); OPTENT3(0, "ellipse", OPT_FLAG, NULL, &ellipseSpec, 0); OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); @@ -62,18 +63,23 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ - if (lrSpec + tbSpec + rectangleSpec + ellipseSpec == 0) - pm_error("You must specify one of -lr, -tb, -rectangle, or -ellipse"); - if (lrSpec + tbSpec + rectangleSpec + ellipseSpec > 1) + free (option_def); + + if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec == 0) + pm_error("You must specify one of " + "-lr, -tb, -diagonal, -rectangle, or -ellipse"); + if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec > 1) pm_error("You may specify at most one of " - "-lr, -tb, -rectangle, or -ellipse"); + "-lr, -tb, -diagonal, -rectangle, or -ellipse"); if (lrSpec) cmdlineP->ramptype = RT_LR; else if (tbSpec) cmdlineP->ramptype = RT_TB; + else if (diagonalSpec) + cmdlineP->ramptype = RT_DIAG; else if (rectangleSpec) cmdlineP->ramptype = RT_RECT; else if (ellipseSpec) @@ -87,10 +93,10 @@ parseCommandLine(int argc, char ** argv, if (cmdlineP->maxval > PGM_OVERALLMAXVAL) pm_error("The value you specified for -maxval (%u) is too big. " "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL); - + if (cmdlineP->maxval < 1) pm_error("You cannot specify 0 for -maxval"); - } + } if (argc-1 < 2) pm_error("Need two arguments: width and height."); @@ -105,7 +111,7 @@ parseCommandLine(int argc, char ** argv, -int +int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; @@ -119,31 +125,36 @@ main(int argc, char *argv[]) { colso2 = MAX(1, cmdline.cols / 2); rowso2 = MAX(1, cmdline.rows / 2); - + pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0); grayrow = pgm_allocrow(cmdline.cols); - + for (row = 0; row < cmdline.rows; ++row) { unsigned int col; for (col = 0; col < cmdline.cols; ++col) { switch (cmdline.ramptype) { case RT_LR: - grayrow[col] = - col * cmdline.maxval / MAX(cmdline.cols-1, 1); + /* Fill row buffer once. All rows are identical. */ + if (row == 0) + grayrow[col] = + (float) col * cmdline.maxval / MAX(cmdline.cols-1, 1); break; case RT_TB: - grayrow[col] = - row * cmdline.maxval / MAX(cmdline.rows-1, 1); + grayrow[col] = + (float) row * cmdline.maxval / MAX(cmdline.rows-1, 1); + break; + case RT_DIAG: + grayrow[col] = + ((float) col + row) * cmdline.maxval / + MAX((float) cmdline.cols + cmdline.rows-2, 1); break; - case RT_RECT: { float const r = fabs((int)(rowso2 - row)) / rowso2; float const c = fabs((int)(colso2 - col)) / colso2; - grayrow[col] = + grayrow[col] = cmdline.maxval - (r + c) / 2.0 * cmdline.maxval; - } - break; - + } break; + case RT_ELLIP: { float const r = fabs((int)(rowso2 - row)) / rowso2; float const c = fabs((int)(colso2 - col)) / colso2; @@ -153,12 +164,11 @@ main(int argc, char *argv[]) { if ( v < 0.0 ) v = 0.0; else if ( v > 1.0 ) v = 1.0; grayrow[col] = cmdline.maxval - v * cmdline.maxval; + } break; } - break; - } - } + } pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0); - } + } pgm_freerow(grayrow); pm_close(stdout); diff --git a/generator/ppmcie.c b/generator/ppmcie.c index fda0ab7c..717ed13b 100644 --- a/generator/ppmcie.c +++ b/generator/ppmcie.c @@ -26,6 +26,7 @@ Introduced option to plot 1976 u' v' chromaticities. */ +#include <assert.h> #include <math.h> #include "pm_c_util.h" @@ -34,10 +35,8 @@ #include "nstring.h" #define CLAMP(v, l, h) ((v) < (l) ? (l) : (v) > (h) ? (h) : (v)) -#define TRUE 1 -#define FALSE 0 -#define Maxval 255 /* Maxval to use in generated pixmaps */ +pixval const cieMaxval = 255; /* Maxval to use in generated pixmaps */ /* A color system is defined by the CIE x and y coordinates of its three primary illuminants and the x and y coordinates of the white @@ -462,8 +461,18 @@ gamma_correct_rgb(const struct colorSystem * const cs, -#define Sz(x) (((x) * MIN(pixcols, pixrows)) / 512) +/* Sz(X) is the displacement in pixels of a displacement of X normalized + distance units. (A normalized distance unit is 1/512 of the smaller + dimension of the canvas) +*/ +#define Sz(x) (((x) * (int)MIN(pixcols, pixrows)) / 512) + +/* B(X, Y) is a pair of function arguments (for a libppmd function) that + give a pixel position on the canvas. That position is (X, Y), biased + horizontally 'xBias' pixels. +*/ #define B(x, y) ((x) + xBias), (y) + #define Bixels(y, x) pixels[y][x + xBias] @@ -511,12 +520,12 @@ makeAllBlack(pixel ** const pixels, static void drawTongueOutline(pixel ** const pixels, - int const pixcols, - int const pixrows, - pixval const maxval, - bool const upvp, - int const xBias, - int const yBias) { + int const pixcols, + int const pixrows, + pixval const maxval, + bool const upvp, + int const xBias, + int const yBias) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; @@ -535,7 +544,7 @@ drawTongueOutline(pixel ** const pixels, &icx, &icy); if (wavelength > 380) - ppmd_line(pixels, pixcols, pixrows, Maxval, + ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly), B(icx, icy), PPMD_NULLDRAWPROC, (char *) &rgbcolor); else { @@ -574,12 +583,12 @@ findTongue(pixel ** const pixels, ++i); if (i >= pxcols) - *presentP = FALSE; + *presentP = false; else { int j; int const leftEdge = i; - *presentP = TRUE; + *presentP = true; for (j = pxcols - 1; j >= leftEdge && PPM_GETR(Bixels(row, j)) == 0; @@ -641,16 +650,16 @@ fillInTongue(pixel ** const pixels, xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb); - mx = Maxval; + mx = maxval; /* Check whether the requested color is within the gamut achievable with the given color system. If not, draw it in a reduced intensity, interpolated by desaturation to the closest within-gamut color. */ - if (constrain_rgb(&jr, &jg, &jb)) { - mx = highlightGamut ? Maxval : ((Maxval + 1) * 3) / 4; - } + if (constrain_rgb(&jr, &jg, &jb)) + mx = highlightGamut ? maxval : ((maxval + 1) * 3) / 4; + /* Scale to max(rgb) = 1. */ jmax = MAX(jr, MAX(jg, jb)); if (jmax > 0) { @@ -672,67 +681,175 @@ fillInTongue(pixel ** const pixels, static void -drawAxes(pixel ** const pixels, - int const pixcols, - int const pixrows, - pixval const maxval, - bool const upvp, - int const xBias, - int const yBias) { +drawYAxis(pixel ** const pixels, + unsigned int const pixcols, + unsigned int const pixrows, + pixval const maxval, + unsigned int const xBias, + unsigned int const yBias, + pixel const axisColor) { + + unsigned int const pxrows = pixrows - yBias; - int const pxcols = pixcols - xBias; - int const pxrows = pixrows - yBias; - - pixel rgbcolor; /* Color of axes */ - int i; - - PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); ppmd_line(pixels, pixcols, pixrows, maxval, B(0, 0), B(0, pxrows - 1), PPMD_NULLDRAWPROC, - (char *) &rgbcolor); + (char *) &axisColor); +} + + + +static void +drawXAxis(pixel ** const pixels, + unsigned int const pixcols, + unsigned int const pixrows, + pixval const maxval, + unsigned int const xBias, + unsigned int const yBias, + pixel const axisColor) { + + unsigned int const pxcols = pixcols - xBias; + unsigned int const pxrows = pixrows - yBias; + ppmd_line(pixels, pixcols, pixrows, maxval, B(0, pxrows - 1), B(pxcols - 1, pxrows - 1), - PPMD_NULLDRAWPROC, (char *) &rgbcolor); - - /* Draw tick marks on X and Y axes every 0.1 units. Also - label axes. - */ + PPMD_NULLDRAWPROC, (char *) &axisColor); +} + + + +static void +tickX(pixel ** const pixels, + unsigned int const pixcols, + unsigned int const pixrows, + pixval const maxval, + unsigned int const xBias, + unsigned int const yBias, + pixel const axisColor, + unsigned int const tenth) { +/*---------------------------------------------------------------------------- + Put a tick mark 'tenth' tenths of the way along the X axis + and label it. + + 'pixels' is the canvas on which to draw it; its dimensions are + 'pixcols' by 'pixrows' and has maxval 'maxval'. +-----------------------------------------------------------------------------*/ + unsigned int const pxcols = pixcols - xBias; + unsigned int const pxrows = pixrows - yBias; + unsigned int const tickCol = (tenth * (pxcols - 1)) / 10; + /* Pixel column where the left edge of the tick goes */ + unsigned int const tickThickness = Sz(3); + /* Thickness of the tick in pixels */ + unsigned int const tickBottom = pxrows - Sz(1); + /* Pixel row of the bottom of the tick */ + + char s[20]; + + assert(tenth < 10); + + sprintf(s, "0.%u", tenth); + ppmd_line(pixels, pixcols, pixrows, maxval, + B(tickCol, tickBottom), + B(tickCol, tickBottom - tickThickness), + PPMD_NULLDRAWPROC, (char *) &axisColor); + ppmd_text(pixels, pixcols, pixrows, maxval, + B(tickCol - Sz(11), pxrows + Sz(12)), + Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &axisColor); +} + + + +static void +tickY(pixel ** const pixels, + unsigned int const pixcols, + unsigned int const pixrows, + pixval const maxval, + unsigned int const xBias, + unsigned int const yBias, + pixel const axisColor, + unsigned int const tenth) { +/*---------------------------------------------------------------------------- + Put a tick mark 'tenth' tenths of the way along the Y axis and label it. + + 'pixels' is the canvas on which to draw it; its dimensions are + 'pixcols' by 'pixrows' and has maxval 'maxval'. +-----------------------------------------------------------------------------*/ + unsigned int const pxrows = pixrows - yBias; + unsigned int const tickRow = (tenth * (pxrows - 1)) / 10; + /* Pixel row where the top of the tick goes */ + unsigned int const tickThickness = Sz(3); + /* Thickness of the tick in pixels */ - for (i = 1; i <= 9; i += 1) { - char s[20]; - - /* X axis tick */ - - sprintf(s, "0.%d", i); - ppmd_line(pixels, pixcols, pixrows, maxval, - B((i * (pxcols - 1)) / 10, pxrows - Sz(1)), - B((i * (pxcols - 1)) / 10, pxrows - Sz(4)), - PPMD_NULLDRAWPROC, (char *) &rgbcolor); - ppmd_text(pixels, pixcols, pixrows, maxval, - B((i * (pxcols - 1)) / 10 - Sz(11), pxrows + Sz(12)), - Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &rgbcolor); - - /* Y axis tick */ - - sprintf(s, "0.%d", 10 - i); - ppmd_line(pixels, pixcols, pixrows, maxval, - B(0, (i * (pxrows - 1)) / 10), - B(Sz(3), (i * (pxrows - 1)) / 10), - PPMD_NULLDRAWPROC, (char *) &rgbcolor); - - ppmd_text(pixels, pixcols, pixrows, maxval, - B(Sz(-30), (i * (pxrows - 1)) / 10 + Sz(5)), - Sz(10), 0, s, - PPMD_NULLDRAWPROC, (char *) &rgbcolor); - } + char s[20]; + + assert(tenth < 10); + + sprintf(s, "0.%d", 10 - tenth); + ppmd_line(pixels, pixcols, pixrows, maxval, + B(0, tickRow), B(tickThickness, tickRow), + PPMD_NULLDRAWPROC, (char *) &axisColor); + + ppmd_text(pixels, pixcols, pixrows, maxval, + B(Sz(-30), tickRow + Sz(5)), + Sz(10), 0, s, + PPMD_NULLDRAWPROC, (char *) &axisColor); +} + + + +static void +labelAxes(pixel ** const pixels, + unsigned int const pixcols, + unsigned int const pixrows, + pixval const maxval, + unsigned int const xBias, + unsigned int const yBias, + pixel const axisColor, + bool const upvp) { +/*---------------------------------------------------------------------------- + Label the axes "x" and "y" or "u" and "v". +-----------------------------------------------------------------------------*/ + unsigned int const pxcols = pixcols - xBias; + unsigned int const pxrows = pixrows - yBias; + ppmd_text(pixels, pixcols, pixrows, maxval, B((98 * (pxcols - 1)) / 100 - Sz(11), pxrows + Sz(12)), Sz(10), 0, (upvp ? "u'" : "x"), - PPMD_NULLDRAWPROC, (char *) &rgbcolor); + PPMD_NULLDRAWPROC, (char *) &axisColor); ppmd_text(pixels, pixcols, pixrows, maxval, B(Sz(-22), (2 * (pxrows - 1)) / 100 + Sz(5)), Sz(10), 0, (upvp ? "v'" : "y"), - PPMD_NULLDRAWPROC, (char *) &rgbcolor); + PPMD_NULLDRAWPROC, (char *) &axisColor); +} + + + +static void +drawAxes(pixel ** const pixels, + unsigned int const pixcols, + unsigned int const pixrows, + pixval const maxval, + bool const upvp, + unsigned int const xBias, + unsigned int const yBias) { +/*---------------------------------------------------------------------------- + Draw the axes, with tick marks every .1 units and labels. +-----------------------------------------------------------------------------*/ + pixel axisColor; /* Color of axes and labels */ + unsigned int i; + + PPM_ASSIGN(axisColor, maxval, maxval, maxval); + + drawYAxis(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor); + drawXAxis(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor); + + for (i = 1; i <= 9; i += 1) { + tickX(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, i); + + tickY(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, i); + } + + labelAxes(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, + upvp); } @@ -840,14 +957,14 @@ plotBlackBodyCurve(pixel ** const pixels, } if (t > 1000) { - ppmd_line(pixels, pixcols, pixrows, Maxval, + ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly), B(xb, yb), PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* Draw tick mark every 1000 kelvins */ if ((((int) t) % 1000) == 0) { - ppmd_line(pixels, pixcols, pixrows, Maxval, + ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly - Sz(2)), B(lx, ly + Sz(2)), PPMD_NULLDRAWPROC, (char *) &rgbcolor); @@ -859,7 +976,7 @@ plotBlackBodyCurve(pixel ** const pixels, char bb[20]; sprintf(bb, "%g", t); - ppmd_text(pixels, pixcols, pixrows, Maxval, + ppmd_text(pixels, pixcols, pixrows, maxval, B(lx - Sz(12), ly - Sz(4)), Sz(6), 0, bb, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } @@ -938,7 +1055,7 @@ plotMonochromeWavelengths( PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); tx = icx + ((x < 520) ? Sz(-2) : ((x >= 535) ? Sz(2) : 0)); ty = icy + ((x < 520) ? 0 : ((x >= 535) ? Sz(-1) : Sz(-2))); - ppmd_line(pixels, pixcols, pixrows, Maxval, + ppmd_line(pixels, pixcols, pixrows, maxval, B(icx, icy), B(tx, ty), PPMD_NULLDRAWPROC, (char *) &rgbcolor); @@ -970,13 +1087,13 @@ plotMonochromeWavelengths( } /* gamma correct from linear rgb to nonlinear rgb. */ gamma_correct_rgb(cs, &jr, &jg, &jb); - r = Maxval * jr; - g = Maxval * jg; - b = Maxval * jb; + r = maxval * jr; + g = maxval * jg; + b = maxval * jb; PPM_ASSIGN(rgbcolor, (pixval) r, (pixval) g, (pixval) b); sprintf(wl, "%d", x); - ppmd_text(pixels, pixcols, pixrows, Maxval, + ppmd_text(pixels, pixcols, pixrows, maxval, B(icx + bx, icy + by), Sz(6), 0, wl, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } @@ -997,18 +1114,18 @@ writeLabel(pixel ** const pixels, PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); - snprintfN(sysdesc, sizeof(sysdesc), - "System: %s\n" - "Primary illuminants (X, Y)\n" - " Red: %0.4f, %0.4f\n" - " Green: %0.4f, %0.4f\n" - " Blue: %0.4f, %0.4f\n" - "White point (X, Y): %0.4f, %0.4f", - cs->name, cs->xRed, cs->yRed, cs->xGreen, cs->yGreen, - cs->xBlue, cs->yBlue, cs->xWhite, cs->yWhite); + pm_snprintf(sysdesc, sizeof(sysdesc), + "System: %s\n" + "Primary illuminants (X, Y)\n" + " Red: %0.4f, %0.4f\n" + " Green: %0.4f, %0.4f\n" + " Blue: %0.4f, %0.4f\n" + "White point (X, Y): %0.4f, %0.4f", + cs->name, cs->xRed, cs->yRed, cs->xGreen, cs->yGreen, + cs->xBlue, cs->yBlue, cs->xWhite, cs->yWhite); sysdesc[sizeof(sysdesc)-1] = '\0'; /* for robustness */ - ppmd_text(pixels, pixcols, pixrows, Maxval, + ppmd_text(pixels, pixcols, pixrows, maxval, pixcols / 3, Sz(24), Sz(12), 0, sysdesc, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } @@ -1016,8 +1133,8 @@ writeLabel(pixel ** const pixels, int -main(int argc, - char * argv[]) { +main(int argc, + const char * argv[]) { int argn; const char * const usage = "[-[no]black] [-[no]wpoint] [-[no]label] [-no[axes]] [-full]\n\ @@ -1027,24 +1144,24 @@ main(int argc, [-size <s>] [-xsize|-width <x>] [-ysize|-height <y>]"; const struct colorSystem *cs; - int widspec = FALSE, hgtspec = FALSE; - int xBias, yBias; - int upvp = FALSE; /* xy or u'v' color coordinates? */ - int showWhite = TRUE; /* Show white point ? */ - int showBlack = TRUE; /* Show black body curve ? */ - int fullChart = FALSE; /* Fill entire tongue ? */ - int showLabel = TRUE; /* Show labels ? */ - int showAxes = TRUE; /* Plot axes ? */ + bool widspec = false, hgtspec = false; + unsigned int xBias, yBias; + bool upvp = false; /* xy or u'v' color coordinates? */ + bool showWhite = true; /* Show white point ? */ + bool showBlack = true; /* Show black body curve ? */ + bool fullChart = false; /* Fill entire tongue ? */ + bool showLabel = true; /* Show labels ? */ + bool showAxes = true; /* Plot axes ? */ - ppm_init(&argc, argv); + pm_proginit(&argc, argv); argn = 1; cs = &Rec709system; /* default */ while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { if (pm_keymatch(argv[argn], "-xy", 2)) { - upvp = FALSE; + upvp = false; } else if (pm_keymatch(argv[argn], "-upvp", 1)) { - upvp = TRUE; + upvp = true; } else if (pm_keymatch(argv[argn], "-xsize", 1) || pm_keymatch(argv[argn], "-width", 2)) { if (widspec) { @@ -1053,7 +1170,7 @@ main(int argc, argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1)) pm_usage(usage); - widspec = TRUE; + widspec = true; } else if (pm_keymatch(argv[argn], "-ysize", 1) || pm_keymatch(argv[argn], "-height", 2)) { if (hgtspec) { @@ -1062,7 +1179,7 @@ main(int argc, argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) pm_usage(usage); - hgtspec = TRUE; + hgtspec = true; } else if (pm_keymatch(argv[argn], "-size", 2)) { if (hgtspec || widspec) { pm_error("already specified a size/height/ysize"); @@ -1071,7 +1188,7 @@ main(int argc, if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) pm_usage(usage); sxsize = sysize; - hgtspec = widspec = TRUE; + hgtspec = widspec = true; } else if (pm_keymatch(argv[argn], "-rec709", 1)) { cs = &Rec709system; } else if (pm_keymatch(argv[argn], "-ntsc", 1)) { @@ -1085,23 +1202,23 @@ main(int argc, } else if (pm_keymatch(argv[argn], "-cie", 1)) { cs = &CIEsystem; } else if (pm_keymatch(argv[argn], "-black", 3)) { - showBlack = TRUE; /* Show black body curve */ + showBlack = true; /* Show black body curve */ } else if (pm_keymatch(argv[argn], "-wpoint", 2)) { - showWhite = TRUE; /* Show white point of color system */ + showWhite = true; /* Show white point of color system */ } else if (pm_keymatch(argv[argn], "-noblack", 3)) { - showBlack = FALSE; /* Don't show black body curve */ + showBlack = false; /* Don't show black body curve */ } else if (pm_keymatch(argv[argn], "-nowpoint", 3)) { - showWhite = FALSE; /* Don't show white point of system */ + showWhite = false; /* Don't show white point of system */ } else if (pm_keymatch(argv[argn], "-label", 1)) { - showLabel = TRUE; /* Show labels. */ + showLabel = true; /* Show labels. */ } else if (pm_keymatch(argv[argn], "-nolabel", 3)) { - showLabel = FALSE; /* Don't show labels */ + showLabel = false; /* Don't show labels */ } else if (pm_keymatch(argv[argn], "-axes", 1)) { - showAxes = TRUE; /* Show axes. */ + showAxes = true; /* Show axes. */ } else if (pm_keymatch(argv[argn], "-noaxes", 3)) { - showAxes = FALSE; /* Don't show axes */ + showAxes = false; /* Don't show axes */ } else if (pm_keymatch(argv[argn], "-full", 1)) { - fullChart = TRUE; /* Fill whole tongue full-intensity */ + fullChart = true; /* Fill whole tongue full-intensity */ } else if (pm_keymatch(argv[argn], "-gamma", 2)) { cs = &Customsystem; argn++; @@ -1170,32 +1287,32 @@ main(int argc, makeAllBlack(pixels, pixcols, pixrows); - drawTongueOutline(pixels, pixcols, pixrows, Maxval, upvp, xBias, yBias); + drawTongueOutline(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias); - fillInTongue(pixels, pixcols, pixrows, Maxval, cs, upvp, xBias, yBias, + fillInTongue(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias, fullChart); if (showAxes) - drawAxes(pixels, pixcols, pixrows, Maxval, upvp, xBias, yBias); + drawAxes(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias); if (showWhite) - plotWhitePoint(pixels, pixcols, pixrows, Maxval, + plotWhitePoint(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias); if (showBlack) - plotBlackBodyCurve(pixels, pixcols, pixrows, Maxval, + plotBlackBodyCurve(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias); /* Plot wavelengths around periphery of the tongue. */ if (showAxes) - plotMonochromeWavelengths(pixels, pixcols, pixrows, Maxval, + plotMonochromeWavelengths(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias); if (showLabel) - writeLabel(pixels, pixcols, pixrows, Maxval, cs); + writeLabel(pixels, pixcols, pixrows, cieMaxval, cs); - ppm_writeppm(stdout, pixels, pixcols, pixrows, Maxval, FALSE); + ppm_writeppm(stdout, pixels, pixcols, pixrows, cieMaxval, 0); return 0; } diff --git a/generator/ppmcolors.c b/generator/ppmcolors.c index ecae2285..701812d1 100644 --- a/generator/ppmcolors.c +++ b/generator/ppmcolors.c @@ -45,7 +45,7 @@ parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP) { /* defaults */ cmdlineP->maxval = 5; - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (cmdlineP->maxval < 1) @@ -72,15 +72,16 @@ main(int argc, char *argv[]) { parseCommandLine(argc, argv, &cmdline); - asprintfN(&cmd, "pamseq 3 %u -tupletype=RGB | pamtopnm", cmdline.maxval); + pm_asprintf(&cmd, "pamseq 3 %u -tupletype=RGB | pamtopnm", cmdline.maxval); rc = system(cmd); if (rc != 0) pm_error("pamseq|pamtopnm pipeline failed. system() rc = %d", rc); - strfree(cmd); - exit(rc); + pm_strfree(cmd); + + return rc; } diff --git a/generator/ppmforge.c b/generator/ppmforge.c index c77076e3..8ea86429 100644 --- a/generator/ppmforge.c +++ b/generator/ppmforge.c @@ -39,12 +39,9 @@ #include "pm_c_util.h" #include "ppm.h" #include "mallocvar.h" +#include "shhopt.h" -#ifdef VMS -static double const hugeVal = HUGE_VAL; -#else static double const hugeVal = 1e50; -#endif /* Definitions used to address real and imaginary parts in a two-dimensional array of complex numbers as stored by fourn(). */ @@ -78,12 +75,6 @@ static double arand, gaussadd, gaussfac; /* Gaussian random parameters */ static double fracdim; /* Fractal dimension */ static double powscale; /* Power law scaling exponent */ static int meshsize = 256; /* FFT mesh size */ -static unsigned int seedarg; /* Seed specified by user */ -static bool seedspec = FALSE; /* Did the user specify a seed ? */ -static bool clouds = FALSE; /* Just generate clouds */ -static bool stars = FALSE; /* Just generate stars */ -static int screenxsize = 256; /* Screen X size */ -static int screenysize = 256; /* Screen Y size */ static double inclangle, hourangle; /* Star position relative to planet */ static bool inclspec = FALSE; /* No inclination specified yet */ static bool hourspec = FALSE; /* No hour specified yet */ @@ -92,6 +83,166 @@ static double glaciers; /* Glacier level */ static int starfraction; /* Star fraction */ static int starcolor; /* Star color saturation */ + +struct CmdlineInfo { + unsigned int clouds; + unsigned int night; + float dimension; + float hourAngle; + unsigned int hourSpec; + float inclAngle; + unsigned int inclinationSpec; + unsigned int meshSize; + unsigned int meshSpec; + float power; + float glaciers; + float ice; + int saturation; + unsigned int seed; + int stars; + unsigned int starsSpec; + unsigned int width; + unsigned int height; +}; + + + +static void +parseCommandLine(int argc, const char **argv, + struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Convert program invocation arguments (argc,argv) into a format the + program can use easily, struct cmdlineInfo. Validate arguments along + the way and exit program with message if invalid. + + Note that some string information we return as *cmdlineP is in the storage + argv[] points to. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int dimensionSpec, seedSpec, + meshSpec, powerSpec, glaciersSpec, iceSpec, saturationSpec, + starsSpec, widthSpec, heightSpec; + float hour; + float inclination; + unsigned int mesh; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; + OPTENT3(0, "clouds", OPT_FLAG, NULL, &cmdlineP->clouds, 0); + OPTENT3(0, "night", OPT_FLAG, NULL, &cmdlineP->night, 0); + OPTENT3(0, "dimension", OPT_FLOAT, &cmdlineP->dimension, + &dimensionSpec, 0); + OPTENT3(0, "hour", OPT_FLOAT, &hour, + &cmdlineP->hourSpec, 0); + OPTENT3(0, "inclination", OPT_FLOAT, &inclination, + &cmdlineP->inclinationSpec, 0); + OPTENT3(0, "tilt", OPT_FLOAT, &inclination, + &cmdlineP->inclinationSpec, 0); + OPTENT3(0, "mesh", OPT_UINT, &mesh, + &meshSpec, 0); + OPTENT3(0, "power", OPT_FLOAT, &cmdlineP->power, + &powerSpec, 0); + OPTENT3(0, "glaciers", OPT_FLOAT, &cmdlineP->glaciers, + &glaciersSpec, 0); + OPTENT3(0, "ice", OPT_FLOAT, &cmdlineP->ice, + &iceSpec, 0); + OPTENT3(0, "saturation", OPT_INT, &cmdlineP->saturation, + &saturationSpec, 0); + OPTENT3(0, "seed", OPT_UINT, &cmdlineP->seed, + &seedSpec, 0); + OPTENT3(0, "stars", OPT_INT, &cmdlineP->stars, + &starsSpec, 0); + OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, + &widthSpec, 0); + OPTENT3(0, "xsize", OPT_UINT, &cmdlineP->width, + &widthSpec, 0); + OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, + &heightSpec, 0); + OPTENT3(0, "ysize", OPT_UINT, &cmdlineP->height, + &heightSpec, 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 */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (dimensionSpec) { + if (cmdlineP->dimension <= 0.0) + pm_error("-dimension must be greater than zero. " + "You specified %f", cmdlineP->dimension); + } else + cmdlineP->dimension = cmdlineP->clouds ? 2.15 : 2.4; + + if (cmdlineP->hourSpec) + cmdlineP->hourAngle = (M_PI / 12.0) * (hour + 12.0); + + if (cmdlineP->inclinationSpec) + cmdlineP->inclAngle = (M_PI / 180.0) * inclination; + + if (meshSpec) { + unsigned int i; + if (mesh < 2) + pm_error("-mesh value must be at least 2. " + "You specified %u", mesh); + /* Force FFT mesh to the next larger power of 2. */ + for (i = 2; i < mesh; i <<= 1); + cmdlineP->meshSize = i; + } else + cmdlineP->meshSize = 256; + + if (powerSpec) { + if (cmdlineP->power <= 0.0) + pm_error("-power must be greater than zero. " + "You specified %f", cmdlineP->power); + } else + cmdlineP->power = cmdlineP->clouds ? 0.75 : 1.2; + + if (iceSpec) { + if (cmdlineP->ice <= 0.0) + pm_error("-ice must be greater than zero. " + "You specified %f", cmdlineP->ice); + } else + cmdlineP->ice = 0.4; + + if (glaciersSpec) { + if (cmdlineP->glaciers <= 0.0) + pm_error("-glaciers must be greater than 0. " + "You specified %f", cmdlineP->glaciers); + } else + cmdlineP->glaciers = 0.75; + + if (!starsSpec) + cmdlineP->stars = 100; + + if (!saturationSpec) + cmdlineP->saturation = 125; + + if (!seedSpec) + cmdlineP->seed = pm_randseed(); + + if (!widthSpec) + cmdlineP->width = 256; + + if (!heightSpec) + cmdlineP->height = 256; + + if (argc-1 > 0) + pm_error("There are no non-option arguments. " + "You specified %u", argc-1); + + free(option_def); +} + + /* FOURN -- Multi-dimensional fast Fourier transform Called with arguments: @@ -278,19 +429,6 @@ static void spectralsynth(x, n, h) } -static unsigned int -initseed(void) { - /* Generate initial random seed. */ - - unsigned int i; - - srand(pm_randseed()); - for (i = 0; i < 7; ++i) - rand(); - return rand(); -} - - /* TEMPRGB -- Calculate the relative R, G, and B components for a black body emitting light at a given temperature. @@ -421,7 +559,8 @@ makeCp(float * const a, static void -createPlanetStuff(float * const a, +createPlanetStuff(bool const clouds, + float * const a, unsigned int const n, double ** const uP, double ** const u1P, @@ -460,7 +599,9 @@ createPlanetStuff(float * const a, " -inclination %.0f -hour %d -ice %.2f -glaciers %.2f", (siang * (180.0 / M_PI)), (int) (((shang * (12.0 / M_PI)) + 12 + - (flipped ? 12 : 0)) + 0.5) % 24, icelevel, glaciers); + (flipped ? 12 : 0)) + 0.5) % 24, + icelevel, + glaciers); pm_message(" -stars %d -saturation %d.", starfraction, starcolor); } @@ -535,9 +676,9 @@ generateCloudRow(pixel * const pixels, /* Render the FFT output as clouds. */ - unsigned int j; + unsigned int col; - for (j = 0; j < cols; j++) { + for (col = 0; col < cols; ++col) { double r; pixval w; @@ -546,15 +687,15 @@ generateCloudRow(pixel * const pixels, referenced below does not exist. */ if (t1 > 0.0) - r += t1 * u1[j] * cp[byf + bxf[j]] + - t1 * u[j] * cp[byf + bxc[j]]; + r += t1 * u1[col] * cp[byf + bxf[col]] + + t1 * u[col] * cp[byf + bxc[col]]; if (t > 0.0) - r += t * u1[j] * cp[byc + bxf[j]] + - t * u[j] * cp[byc + bxc[j]]; + r += t * u1[col] * cp[byc + bxf[col]] + + t * u[col] * cp[byc + bxc[col]]; w = (r > 127.0) ? (maxval * ((r - 127.0) / 128.0)) : 0; - PPM_ASSIGN(*(pixels + j), w, w, maxval); + PPM_ASSIGN(pixels[col], w, w, maxval); } } @@ -823,7 +964,7 @@ genplanet(bool const stars, pm_message("%s: -seed %d -dimension %.2f -power %.2f -mesh %d", clouds ? "clouds" : "planet", rseed, fracdim, powscale, meshsize); - createPlanetStuff(a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, + createPlanetStuff(clouds, a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, cols, maxval); } @@ -944,19 +1085,14 @@ static bool planet(unsigned int const cols, unsigned int const rows, bool const stars, - bool const clouds) { + bool const clouds, + unsigned int const rseed) { /*---------------------------------------------------------------------------- Make a planet. -----------------------------------------------------------------------------*/ float * a; bool error; - unsigned int rseed; /* Current random seed */ - - if (seedspec) - rseed = seedarg; - else - rseed = initseed(); - + initgauss(rseed); if (stars) { @@ -985,200 +1121,39 @@ planet(unsigned int const cols, - int -main(int argc, char ** argv) { +main(int argc, const char ** argv) { + struct CmdlineInfo cmdline; bool success; - int i; - const char * const usage = "\n\ -[-width|-xsize <x>] [-height|-ysize <y>] [-mesh <n>]\n\ -[-clouds] [-dimension <f>] [-power <f>] [-seed <n>]\n\ -[-hour <f>] [-inclination|-tilt <f>] [-ice <f>] [-glaciers <f>]\n\ -[-night] [-stars <n>] [-saturation <n>]"; - bool dimspec = FALSE, meshspec = FALSE, powerspec = FALSE, - widspec = FALSE, hgtspec = FALSE, icespec = FALSE, - glacspec = FALSE, starspec = FALSE, starcspec = FALSE; - - int cols, rows; /* Dimensions of our output image */ - - ppm_init(&argc, argv); - i = 1; - - while ((i < argc) && (argv[i][0] == '-') && (argv[i][1] != '\0')) { - - if (pm_keymatch(argv[i], "-clouds", 2)) { - clouds = TRUE; - } else if (pm_keymatch(argv[i], "-night", 2)) { - stars = TRUE; - } else if (pm_keymatch(argv[i], "-dimension", 2)) { - if (dimspec) { - pm_error("already specified a dimension"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &fracdim) != 1)) - pm_usage(usage); - if (fracdim <= 0.0) { - pm_error("fractal dimension must be greater than 0"); - } - dimspec = TRUE; - } else if (pm_keymatch(argv[i], "-hour", 3)) { - if (hourspec) { - pm_error("already specified an hour"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &hourangle) != 1)) - pm_usage(usage); - hourangle = (M_PI / 12.0) * (hourangle + 12.0); - hourspec = TRUE; - } else if (pm_keymatch(argv[i], "-inclination", 3) || - pm_keymatch(argv[i], "-tilt", 2)) { - if (inclspec) { - pm_error("already specified an inclination/tilt"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &inclangle) != 1)) - pm_usage(usage); - inclangle = (M_PI / 180.0) * inclangle; - inclspec = TRUE; - } else if (pm_keymatch(argv[i], "-mesh", 2)) { - unsigned int j; - if (meshspec) { - pm_error("already specified a mesh size"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &meshsize) != 1)) - pm_usage(usage); + unsigned int cols, rows; /* Dimensions of our output image */ - if (meshsize < 2) - pm_error("mesh must be at least 2"); + pm_proginit(&argc, argv); - /* Force FFT mesh to the next larger power of 2. */ + parseCommandLine(argc, argv, &cmdline); - for (j = meshsize; (j & 1) == 0; j >>= 1) ; - - if (j != 1) { - for (j = 2; j < meshsize; j <<= 1) ; - meshsize = j; - } - meshspec = TRUE; - } else if (pm_keymatch(argv[i], "-power", 2)) { - if (powerspec) { - pm_error("already specified a power factor"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &powscale) != 1)) - pm_usage(usage); - if (powscale <= 0.0) { - pm_error("power factor must be greater than 0"); - } - powerspec = TRUE; - } else if (pm_keymatch(argv[i], "-ice", 3)) { - if (icespec) { - pm_error("already specified ice cap level"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &icelevel) != 1)) - pm_usage(usage); - if (icelevel <= 0.0) { - pm_error("ice cap level must be greater than 0"); - } - icespec = TRUE; - } else if (pm_keymatch(argv[i], "-glaciers", 2)) { - if (glacspec) { - pm_error("already specified glacier level"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &glaciers) != 1)) - pm_usage(usage); - if (glaciers <= 0.0) { - pm_error("glacier level must be greater than 0"); - } - glacspec = TRUE; - } else if (pm_keymatch(argv[i], "-stars", 3)) { - if (starspec) { - pm_error("already specified a star fraction"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &starfraction) != 1)) - pm_usage(usage); - starspec = TRUE; - } else if (pm_keymatch(argv[i], "-saturation", 3)) { - if (starcspec) { - pm_error("already specified a star color saturation"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &starcolor) != 1)) - pm_usage(usage); - starcspec = TRUE; - } else if (pm_keymatch(argv[i], "-seed", 3)) { - if (seedspec) { - pm_error("already specified a random seed"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%u", &seedarg) != 1)) - pm_usage(usage); - seedspec = TRUE; - } else if (pm_keymatch(argv[i], "-xsize", 2) || - pm_keymatch(argv[i], "-width", 2)) { - if (widspec) { - pm_error("already specified a width/xsize"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &screenxsize) != 1)) - pm_usage(usage); - widspec = TRUE; - } else if (pm_keymatch(argv[i], "-ysize", 2) || - pm_keymatch(argv[i], "-height", 3)) { - if (hgtspec) { - pm_error("already specified a height/ysize"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &screenysize) != 1)) - pm_usage(usage); - hgtspec = TRUE; - } else { - pm_usage(usage); - } - i++; - } - - - /* Set defaults when explicit specifications were not given. - - The default fractal dimension and power scale depend upon - whether we are generating a planet or clouds. - */ - - if (!dimspec) { - fracdim = clouds ? 2.15 : 2.4; - } - if (!powerspec) { - powscale = clouds ? 0.75 : 1.2; - } - if (!icespec) { - icelevel = 0.4; - } - if (!glacspec) { - glaciers = 0.75; - } - if (!starspec) { - starfraction = 100; - } - if (!starcspec) { - starcolor = 125; - } + fracdim = cmdline.dimension; + hourspec = cmdline.hourSpec; + hourangle = cmdline.hourAngle; + inclspec = cmdline.inclinationSpec; + inclangle = cmdline.inclAngle; + meshsize = cmdline.meshSize; + powscale = cmdline.power; + icelevel = cmdline.ice; + glaciers = cmdline.glaciers; + starfraction = cmdline.stars; + starcolor = cmdline.saturation; /* Force screen to be at least as wide as it is high. Long, skinny screens cause crashes because picture width is calculated based on height. */ - cols = (MAX(screenysize, screenxsize) + 1) & (~1); - rows = screenysize; + cols = (MAX(cmdline.height, cmdline.width) + 1) & (~1); + rows = cmdline.height; - success = planet(cols, rows, stars, clouds); + success = planet(cols, rows, cmdline.night, cmdline.clouds, cmdline.seed); exit(success ? 0 : 1); } diff --git a/generator/ppmmake.c b/generator/ppmmake.c index e59b47d5..2d4bbca8 100644 --- a/generator/ppmmake.c +++ b/generator/ppmmake.c @@ -55,19 +55,21 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = false; /* We have no short (old-fashioned) options */ opt.allowNegNum = false; /* We have no parms that are negative numbers */ - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + free (option_def); + if (!maxvalSpec) cmdlineP->maxval = PPM_MAXMAXVAL; else { if (cmdlineP->maxval > PPM_OVERALLMAXVAL) pm_error("The value you specified for -maxval (%u) is too big. " "Max allowed is %u", cmdlineP->maxval, PPM_OVERALLMAXVAL); - + if (cmdlineP->maxval < 1) pm_error("You cannot specify 0 for -maxval"); - } + } if (argc-1 < 3) pm_error("Need 3 arguments: color, width, height."); @@ -88,7 +90,7 @@ main(int argc, char *argv[]) { struct cmdlineInfo cmdline; pixel * pixrow; - unsigned int row; + unsigned int row, col; ppm_init(&argc, argv); @@ -97,12 +99,12 @@ main(int argc, char *argv[]) { ppm_writeppminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0); pixrow = ppm_allocrow(cmdline.cols); - for (row = 0; row < cmdline.rows; ++row) { - unsigned int col; - for (col = 0; col < cmdline.cols; ++col) - pixrow[col] = cmdline.color; + /* All rows are identical. Fill once. */ + for (col = 0; col < cmdline.cols; ++col) + pixrow[col] = cmdline.color; + + for (row = 0; row < cmdline.rows; ++row) ppm_writeppmrow(stdout, pixrow, cmdline.cols, cmdline.maxval, 0); - } ppm_freerow(pixrow); pm_close(stdout); diff --git a/generator/ppmpat.c b/generator/ppmpat.c index 772fa51d..fe1a1d27 100644 --- a/generator/ppmpat.c +++ b/generator/ppmpat.c @@ -41,6 +41,8 @@ struct cmdlineInfo { pattern basePattern; unsigned int width; unsigned int height; + unsigned int randomseed; + unsigned int randomseedSpec; }; @@ -71,22 +73,34 @@ parseCommandLine(int argc, const char ** argv, MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ - OPTENT3(0, "gingham2", OPT_FLAG, NULL, &gingham2, 0); - OPTENT3(0, "g2", OPT_FLAG, NULL, &gingham2, 0); - OPTENT3(0, "gingham3", OPT_FLAG, NULL, &gingham3, 0); - OPTENT3(0, "g3", OPT_FLAG, NULL, &gingham3, 0); - OPTENT3(0, "madras", OPT_FLAG, NULL, &madras, 0); - OPTENT3(0, "tartan", OPT_FLAG, NULL, &tartan, 0); - OPTENT3(0, "poles", OPT_FLAG, NULL, &poles, 0); - OPTENT3(0, "squig", OPT_FLAG, NULL, &squig, 0); - OPTENT3(0, "camo", OPT_FLAG, NULL, &camo, 0); - OPTENT3(0, "anticamo", OPT_FLAG, NULL, &anticamo, 0); + OPTENT3(0, "gingham2", OPT_FLAG, NULL, + &gingham2, 0); + OPTENT3(0, "g2", OPT_FLAG, NULL, + &gingham2, 0); + OPTENT3(0, "gingham3", OPT_FLAG, NULL, + &gingham3, 0); + OPTENT3(0, "g3", OPT_FLAG, NULL, + &gingham3, 0); + OPTENT3(0, "madras", OPT_FLAG, NULL, + &madras, 0); + OPTENT3(0, "tartan", OPT_FLAG, NULL, + &tartan, 0); + OPTENT3(0, "poles", OPT_FLAG, NULL, + &poles, 0); + OPTENT3(0, "squig", OPT_FLAG, NULL, + &squig, 0); + OPTENT3(0, "camo", OPT_FLAG, NULL, + &camo, 0); + OPTENT3(0, "anticamo", OPT_FLAG, NULL, + &anticamo, 0); + OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, + &cmdlineP->randomseedSpec, 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 */ - optParseOptions3(&argc, (char **)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. */ basePatternCount = @@ -136,6 +150,7 @@ parseCommandLine(int argc, const char ** argv, if (cmdlineP->height < 1) pm_error("Height must be at least 1 pixel"); } + free(option_def); } @@ -1140,7 +1155,8 @@ main(int argc, const char ** argv) { validateComputableDimensions(cmdline.width, cmdline.height); - srand(pm_randseed()); + srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); + pixels = ppm_allocarray(cmdline.width, cmdline.height); switch (cmdline.basePattern) { @@ -1188,3 +1204,5 @@ main(int argc, const char ** argv) { return 0; } + + diff --git a/generator/ppmrainbow b/generator/ppmrainbow index 96e304ac..c0568d9b 100755 --- a/generator/ppmrainbow +++ b/generator/ppmrainbow @@ -1,4 +1,28 @@ -#!/usr/bin/perl -wl +#!/bin/sh + +############################################################################## +# This is essentially a Perl program. We exec the Perl interpreter specifying +# this same file as the Perl program and use the -x option to cause the Perl +# interpreter to skip down to the Perl code. The reason we do this instead of +# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is +# that the user may have multiple Perl interpreters and the one he wants to +# use is properly located in the PATH. The user's choice of Perl interpreter +# may be crucial, such as when the user also has a PERL5LIB environment +# variable and it selects modules that work with only a certain main +# interpreter program. +# +# An alternative some people use is to have /usr/bin/env as the script +# interpreter. We don't do that because we think the existence and +# compatibility of /bin/sh is more reliable. +# +# Note that we aren't concerned about efficiency because the user who needs +# high efficiency can use directly the programs that this program invokes. +# +############################################################################## + +exec perl -w -x -S -- "$0" "$@" + +#!/usr/bin/perl use strict; use Getopt::Long; diff --git a/generator/ppmrough.c b/generator/ppmrough.c index b21adedf..e749c9c2 100644 --- a/generator/ppmrough.c +++ b/generator/ppmrough.c @@ -15,6 +15,7 @@ #include <sys/time.h> #include "pm_c_util.h" +#include "mallocvar.h" #include "shhopt.h" #include "ppm.h" @@ -22,274 +23,309 @@ static pixel** PIX; static pixval BG_RED, BG_GREEN, BG_BLUE; -struct cmdlineInfo { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - unsigned int left, right, top, bottom; - unsigned int width, height, var; - const char *bg_rgb; - const char *fg_rgb; - unsigned init; - unsigned int verbose; + unsigned int left, right, top, bottom; + unsigned int width, height, var; + const char * bg_rgb; + const char * fg_rgb; + unsigned int randomseed; + unsigned int randomseedSpec; + unsigned int verbose; }; static void -/*-------------------------------------------------------------------------- */ - parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP) -/*-------------------------------------------------------------------------- */ -{ - optEntry *option_def = malloc(100*sizeof(optEntry)); - /* Instructions to OptParseOptions2 on how to parse our options. */ - optStruct3 opt; - - unsigned int option_def_index; - - option_def_index = 0; /* incremented by OPTENTRY */ - OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, NULL, 0); - OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, NULL, 0); - OPTENT3(0, "left", OPT_UINT, &cmdlineP->left, NULL, 0); - OPTENT3(0, "right", OPT_UINT, &cmdlineP->right, NULL, 0); - OPTENT3(0, "top", OPT_UINT, &cmdlineP->top, NULL, 0); - OPTENT3(0, "bottom", OPT_UINT, &cmdlineP->bottom, NULL, 0); - OPTENT3(0, "bg", OPT_STRING, &cmdlineP->bg_rgb, NULL, 0); - OPTENT3(0, "fg", OPT_STRING, &cmdlineP->fg_rgb, NULL, 0); - OPTENT3(0, "var", OPT_UINT, &cmdlineP->var, NULL, 0); - OPTENT3(0, "init", OPT_UINT, &cmdlineP->init, NULL, 0); - OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); - - /* Set the defaults */ - cmdlineP->width = 100; - cmdlineP->height = 100; - cmdlineP->left = cmdlineP->right = cmdlineP->top = cmdlineP->bottom = -1; - cmdlineP->bg_rgb = NULL; - cmdlineP->fg_rgb = NULL; - cmdlineP->var = 10; - - 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 */ - - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); - - if (argc-1 != 0) - pm_error("There are no arguments. You specified %d.", argc-1); +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { + + optEntry * option_def; + /* Instructions to OptParseOptions2 on how to parse our options. */ + optStruct3 opt; + + unsigned int option_def_index; + + MALLOCARRAY(option_def, 100); + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, NULL, 0); + OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, NULL, 0); + OPTENT3(0, "left", OPT_UINT, &cmdlineP->left, NULL, 0); + OPTENT3(0, "right", OPT_UINT, &cmdlineP->right, NULL, 0); + OPTENT3(0, "top", OPT_UINT, &cmdlineP->top, NULL, 0); + OPTENT3(0, "bottom", OPT_UINT, &cmdlineP->bottom, NULL, 0); + OPTENT3(0, "bg", OPT_STRING, &cmdlineP->bg_rgb, NULL, 0); + OPTENT3(0, "fg", OPT_STRING, &cmdlineP->fg_rgb, NULL, 0); + OPTENT3(0, "var", OPT_UINT, &cmdlineP->var, NULL, 0); + OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, + &cmdlineP->randomseedSpec, 0); + OPTENT3(0, "init", OPT_UINT, &cmdlineP->randomseed, + &cmdlineP->randomseedSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); + + /* Set the defaults */ + cmdlineP->width = 100; + cmdlineP->height = 100; + cmdlineP->left = cmdlineP->right = cmdlineP->top = cmdlineP->bottom = -1; + cmdlineP->bg_rgb = NULL; + cmdlineP->fg_rgb = NULL; + cmdlineP->var = 10; + + 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 */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + + if (argc-1 != 0) + pm_error("There are no arguments. You specified %d.", argc-1); + + free(option_def); } + + static void -/* ----------------------------------------- */ - proc_left(int const r1, int const r2, int const c1, int const c2, - unsigned int const var) -/* ----------------------------------------- */ -{ - int cm, rm, c; - - if (r1 + 1 == r2) return; - rm = (r1 + r2) >> 1; - cm = (c1 + c2) >> 1; - cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); - - for (c = 0; c < cm; c++) - PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE); - - proc_left(r1, rm, c1, cm, var); - proc_left(rm, r2, cm, c2, var); +procLeft(int const r1, + int const r2, + int const c1, + int const c2, + unsigned int const var) { + + int cm, rm, c; + + if (r1 + 1 == r2) return; + rm = (r1 + r2) >> 1; + cm = (c1 + c2) >> 1; + cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); + + for (c = 0; c < cm; c++) + PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE); + + procLeft(r1, rm, c1, cm, var); + procLeft(rm, r2, cm, c2, var); } + + static void -/* ------------------------------------------ */ - proc_right(int const r1, int const r2, int const c1, int const c2, - unsigned int const width, unsigned int const var) -/* ------------------------------------------ */ -{ - int cm, rm, c; - - if (r1 + 1 == r2) return; - rm = (r1 + r2) >> 1; - cm = (c1 + c2) >> 1; - cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); - - for (c = cm; c < width; c++) - PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE); - - proc_right(r1, rm, c1, cm, width, var); - proc_right(rm, r2, cm, c2, width, var); +procRight(int const r1, + int const r2, + int const c1, + int const c2, + unsigned int const width, + unsigned int const var) { + + int cm, rm, c; + + if (r1 + 1 == r2) return; + rm = (r1 + r2) >> 1; + cm = (c1 + c2) >> 1; + cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); + + for (c = cm; c < width; c++) + PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE); + + procRight(r1, rm, c1, cm, width, var); + procRight(rm, r2, cm, c2, width, var); } + + static void -/* ---------------------------------------- */ - proc_top(int const c1, int const c2, int const r1, int const r2, - unsigned int const var) -/* ---------------------------------------- */ -{ - int rm, cm, r; - - if (c1 + 1 == c2) return; - cm = (c1 + c2) >> 1; - rm = (r1 + r2) >> 1; - rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); - - for (r = 0; r < rm; r++) - PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE); - - proc_top(c1, cm, r1, rm, var); - proc_top(cm, c2, rm, r2, var); +procTop(int const c1, + int const c2, + int const r1, + int const r2, + unsigned int const var) { + + int rm, cm, r; + + if (c1 + 1 == c2) return; + cm = (c1 + c2) >> 1; + rm = (r1 + r2) >> 1; + rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); + + for (r = 0; r < rm; r++) + PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE); + + procTop(c1, cm, r1, rm, var); + procTop(cm, c2, rm, r2, var); } + + static void -/* ------------------------------------------- */ - proc_bottom(int const c1, int const c2, int const r1, int const r2, - unsigned int const height, unsigned int const var) -/* ------------------------------------------- */ -{ - int rm, cm, r; - - if (c1 + 1 == c2) return; - cm = (c1 + c2) >> 1; - rm = (r1 + r2) >> 1; - rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); - - for (r = rm; r < height; r++) - PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE); - - proc_bottom(c1, cm, r1, rm, height, var); - proc_bottom(cm, c2, rm, r2, height, var); +procBottom(int const c1, + int const c2, + int const r1, + int const r2, + unsigned int const height, + unsigned int const var) { + + int rm, cm, r; + + if (c1 + 1 == c2) return; + cm = (c1 + c2) >> 1; + rm = (r1 + r2) >> 1; + rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5); + + for (r = rm; r < height; r++) + PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE); + + procBottom(c1, cm, r1, rm, height, var); + procBottom(cm, c2, rm, r2, height, var); } + int -/* ============================ */ - main(int argc, char* argv[]) -/* ============================ */ -{ - struct cmdlineInfo cmdline; - pixel bgcolor, fgcolor; - pixval fg_red, fg_green, fg_blue; - int rows, cols, row; - int left, right, top, bottom; - int left_r1, left_r2, left_c1, left_c2; - int right_r1, right_r2, right_c1, right_c2; - int top_r1, top_r2, top_c1, top_c2; - int bottom_r1, bottom_r2, bottom_c1, bottom_c2; - unsigned init; - struct timeval tv; - int col; - - ppm_init(&argc, argv); - - parseCommandLine(argc, argv, &cmdline); - - init = cmdline.init; - if (init == 0) { - gettimeofday(&tv, NULL); - srand((unsigned int)tv.tv_usec); - } - else - srand(init); - - cols = cmdline.width; - rows = cmdline.height; - left = cmdline.left; - right = cmdline.right; - top = cmdline.top; - bottom = cmdline.bottom; - - if (cmdline.bg_rgb) - bgcolor = ppm_parsecolor(cmdline.bg_rgb, PPM_MAXMAXVAL); - else - PPM_ASSIGN(bgcolor, 0, 0, 0); - BG_RED = PPM_GETR(bgcolor); - BG_GREEN = PPM_GETG(bgcolor); - BG_BLUE = PPM_GETB(bgcolor); - - if (cmdline.fg_rgb) - fgcolor = ppm_parsecolor(cmdline.fg_rgb, PPM_MAXMAXVAL); - else - PPM_ASSIGN(fgcolor, PPM_MAXMAXVAL, PPM_MAXMAXVAL, PPM_MAXMAXVAL); - fg_red = PPM_GETR(fgcolor); - fg_green = PPM_GETG(fgcolor); - fg_blue = PPM_GETB(fgcolor); - - if (cmdline.verbose) { - pm_message("width is %d, height is %d, variance is %d.", - cols, rows, cmdline.var); - if (left >= 0) pm_message("ragged left border is required"); - if (right >= 0) pm_message("ragged right border is required"); - if (top >= 0) pm_message("ragged top border is required"); - if (bottom >= 0) pm_message("ragged bottom border is required"); - pm_message("background is %s", ppm_colorname(&bgcolor, PPM_MAXMAXVAL, 1)); - pm_message("foreground is %s", ppm_colorname(&fgcolor, PPM_MAXMAXVAL, 1)); - if (init >= 0) pm_message("srand() initialized with seed %u", init); - } - - /* Allocate memory for the whole pixmap */ - PIX = ppm_allocarray(cols, rows); - - /* First, set all pixel to foreground color */ - for (row = 0; row < rows; row++) - for (col = 0; col < cols; col++) - PPM_ASSIGN(PIX[row][col], fg_red, fg_green, fg_blue); - - /* Make a ragged left border */ - if (left >= 0) { - left_c1 = left_c2 = left; - left_r1 = 0; - left_r2 = rows - 1; - for (col = 0; col < left_c1; col++) - PPM_ASSIGN(PIX[left_r1][col], BG_RED, BG_GREEN, BG_BLUE); - for (col = 0; col < left_c2; col++) - PPM_ASSIGN(PIX[left_r2][col], BG_RED, BG_GREEN, BG_BLUE); - - proc_left(left_r1, left_r2, left_c1, left_c2, cmdline.var); - } - - /* Make a ragged right border */ - if (right >= 0) { - right_c1 = right_c2 = cols - right - 1; - right_r1 = 0; - right_r2 = rows - 1; - for (col = right_c1; col < cols; col++) - PPM_ASSIGN(PIX[right_r1][col], BG_RED, BG_GREEN, BG_BLUE); - for (col = right_c2; col < cols; col++) - PPM_ASSIGN(PIX[right_r2][col], BG_RED, BG_GREEN, BG_BLUE); - - proc_right(right_r1, right_r2, right_c1, right_c2, - cmdline.width, cmdline.var); - } - - /* Make a ragged top border */ - if (top >= 0) { - top_r1 = top_r2 = top; - top_c1 = 0; - top_c2 = cols - 1; - for (row = 0; row < top_r1; row++) - PPM_ASSIGN(PIX[row][top_c1], BG_RED, BG_GREEN, BG_BLUE); - for (row = 0; row < top_r2; row++) - PPM_ASSIGN(PIX[row][top_c2], BG_RED, BG_GREEN, BG_BLUE); - - proc_top(top_c1, top_c2, top_r1, top_r2, cmdline.var); - } - - /* Make a ragged bottom border */ - if (bottom >= 0) { - bottom_r1 = bottom_r2 = rows - bottom - 1; - bottom_c1 = 0; - bottom_c2 = cols - 1; - for (row = bottom_r1; row < rows; row++) - PPM_ASSIGN(PIX[row][bottom_c1], BG_RED, BG_GREEN, BG_BLUE); - for (row = bottom_r2; row < rows; row++) - PPM_ASSIGN(PIX[row][bottom_c2], BG_RED, BG_GREEN, BG_BLUE); - - proc_bottom(bottom_c1, bottom_c2, bottom_r1, bottom_r2, - cmdline.height, cmdline.var); - } - - /* Write pixmap */ - ppm_writeppm(stdout, PIX, cols, rows, PPM_MAXMAXVAL, 0); - - ppm_freearray(PIX, rows); - - pm_close(stdout); - exit(0); +main(int argc, const char * argv[]) { + + struct CmdlineInfo cmdline; + pixel bgcolor, fgcolor; + pixval fg_red, fg_green, fg_blue; + int rows, cols, row; + int left, right, top, bottom; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); + + cols = cmdline.width; + rows = cmdline.height; + left = cmdline.left; + right = cmdline.right; + top = cmdline.top; + bottom = cmdline.bottom; + + if (cmdline.bg_rgb) + bgcolor = ppm_parsecolor(cmdline.bg_rgb, PPM_MAXMAXVAL); + else + PPM_ASSIGN(bgcolor, 0, 0, 0); + BG_RED = PPM_GETR(bgcolor); + BG_GREEN = PPM_GETG(bgcolor); + BG_BLUE = PPM_GETB(bgcolor); + + if (cmdline.fg_rgb) + fgcolor = ppm_parsecolor(cmdline.fg_rgb, PPM_MAXMAXVAL); + else + PPM_ASSIGN(fgcolor, PPM_MAXMAXVAL, PPM_MAXMAXVAL, PPM_MAXMAXVAL); + fg_red = PPM_GETR(fgcolor); + fg_green = PPM_GETG(fgcolor); + fg_blue = PPM_GETB(fgcolor); + + if (cmdline.verbose) { + pm_message("width is %d, height is %d, variance is %d.", + cols, rows, cmdline.var); + if (left >= 0) + pm_message("ragged left border is required"); + if (right >= 0) + pm_message("ragged right border is required"); + if (top >= 0) + pm_message("ragged top border is required"); + if (bottom >= 0) + pm_message("ragged bottom border is required"); + pm_message("background is %s", + ppm_colorname(&bgcolor, PPM_MAXMAXVAL, 1)); + pm_message("foreground is %s", + ppm_colorname(&fgcolor, PPM_MAXMAXVAL, 1)); + if (cmdline.randomseedSpec) + pm_message("srand() initialized with seed %u", cmdline.randomseed); + } + + /* Allocate memory for the whole pixmap */ + PIX = ppm_allocarray(cols, rows); + + /* First, set all pixel to foreground color */ + for (row = 0; row < rows; row++) { + unsigned int col; + for (col = 0; col < cols; ++col) + PPM_ASSIGN(PIX[row][col], fg_red, fg_green, fg_blue); + } + /* Make a ragged left border */ + if (left >= 0) { + int const left_c1 = left; + int const left_c2 = left; + int const left_r1 = 0; + int const left_r2 = rows - 1; + + unsigned int col; + + for (col = 0; col < left_c1; ++col) + PPM_ASSIGN(PIX[left_r1][col], BG_RED, BG_GREEN, BG_BLUE); + for (col = 0; col < left_c2; ++col) + PPM_ASSIGN(PIX[left_r2][col], BG_RED, BG_GREEN, BG_BLUE); + + procLeft(left_r1, left_r2, left_c1, left_c2, cmdline.var); + } + + /* Make a ragged right border */ + if (right >= 0) { + int const right_c1 = cols - right - 1; + int const right_c2 = cols - right - 1; + int const right_r1 = 0; + int const right_r2 = rows - 1; + + unsigned int col; + + for (col = right_c1; col < cols; col++) + PPM_ASSIGN(PIX[right_r1][col], BG_RED, BG_GREEN, BG_BLUE); + for (col = right_c2; col < cols; col++) + PPM_ASSIGN(PIX[right_r2][col], BG_RED, BG_GREEN, BG_BLUE); + + procRight(right_r1, right_r2, right_c1, right_c2, + cmdline.width, cmdline.var); + } + + /* Make a ragged top border */ + if (top >= 0) { + int const top_r1 = top; + int const top_r2 = top; + int const top_c1 = 0; + int const top_c2 = cols - 1; + + unsigned int row; + + for (row = 0; row < top_r1; ++row) + PPM_ASSIGN(PIX[row][top_c1], BG_RED, BG_GREEN, BG_BLUE); + for (row = 0; row < top_r2; ++row) + PPM_ASSIGN(PIX[row][top_c2], BG_RED, BG_GREEN, BG_BLUE); + + procTop(top_c1, top_c2, top_r1, top_r2, cmdline.var); + } + + /* Make a ragged bottom border */ + if (bottom >= 0) { + int const bottom_r1 = rows - bottom - 1; + int const bottom_r2 = rows - bottom - 1; + int const bottom_c1 = 0; + int const bottom_c2 = cols - 1; + + unsigned int row; + + for (row = bottom_r1; row < rows; ++row) + PPM_ASSIGN(PIX[row][bottom_c1], BG_RED, BG_GREEN, BG_BLUE); + for (row = bottom_r2; row < rows; ++row) + PPM_ASSIGN(PIX[row][bottom_c2], BG_RED, BG_GREEN, BG_BLUE); + + procBottom(bottom_c1, bottom_c2, bottom_r1, bottom_r2, + cmdline.height, cmdline.var); + } + + /* Write pixmap */ + ppm_writeppm(stdout, PIX, cols, rows, PPM_MAXMAXVAL, 0); + + ppm_freearray(PIX, rows); + + pm_close(stdout); + + return 0; } + + + |