diff options
71 files changed, 3754 insertions, 1257 deletions
diff --git a/buildtools/configure.pl b/buildtools/configure.pl index 6a152a10..a2a1fd22 100755 --- a/buildtools/configure.pl +++ b/buildtools/configure.pl @@ -9,15 +9,15 @@ use Cwd 'abs_path'; use Fcntl; use Config; #use File::Temp "tempfile"; Not available before Perl 5.6.1 - + my ($TRUE, $FALSE) = (1,0); # This program generates config.mk, which is included by all of the -# Netpbm makefiles. You run this program as the first step in building +# Netpbm makefiles. You run this program as the first step in building # Netpbm. (The second step is 'make'). -# This program is only a convenience. It is supported to create +# This program is only a convenience. It is supported to create # config.mk any way you want. In fact, an easy way is to copy # config.mk.in and follow the instructions in the comments therein # to uncomment certain lines and make other changes. @@ -28,7 +28,7 @@ my ($TRUE, $FALSE) = (1,0); # user, but it isn't the normal build process. # The argument to this program is the filepath of the config.mk.in -# file. If unspecified, the default is 'config.mk.in' in the +# file. If unspecified, the default is 'config.mk.in' in the # Netpbm source directory. # For explanations of the stuff we put in the make files, see the comments @@ -105,7 +105,7 @@ sub promptYesNo($) { while (!defined($retval)) { my $response = prompt("(y)es or (n)o", $default); - + if (uc($response) =~ m{ ^ (Y|YES) $ }x) { $retval = $TRUE; } elsif (uc($response) =~ m{ ^ (N|NO) $ }x) { @@ -131,11 +131,11 @@ sub flow($) { my $retval; my @words = split(m{\s+}, $unflowed); - + my $currentLine; - + $currentLine = ""; - + foreach my $word (@words) { my $mustSpill; if (length($currentLine) == 0) { @@ -148,7 +148,7 @@ sub flow($) { } else { $separator = " "; } - + if (length($currentLine) + length($separator) + length($word) <= 72) { $currentLine .= $separator; @@ -194,7 +194,7 @@ sub tmpdir() { # basic Perl some time after Perl 5.005_03. my $retval; - + if ($ENV{'TMPDIR'}) { $retval = $ENV{'TMPDIR'}; } elsif ($ENV{'TEMP'}) { @@ -217,8 +217,8 @@ sub tempFile($) { # Here's what we'd do if we could expect Perl 5.6.1 or later, instead # of calling this subroutine: -# my ($cFile, $cFileName) = tempfile("netpbmXXXX", -# SUFFIX=>".c", +# my ($cFile, $cFileName) = tempfile("netpbmXXXX", +# SUFFIX=>".c", # DIR=>File::Spec->tmpdir(), # UNLINK=>0); my ($suffix) = @_; @@ -275,7 +275,7 @@ sub commandExists($) { # from system() a lot different than if system() were to try to # execute the program directly. - return(system("$command 1</dev/null 1>/dev/null 2>/dev/null")/256 != 127); + return(system("$command 1</dev/null 1>/dev/null 2>/dev/null")/256 != 127); } @@ -304,20 +304,20 @@ sub testCflags() { my $cflags; - $cflags = ""; # initial value - + $cflags = ""; # initial value + if ($ENV{"CPPFLAGS"}) { $cflags = $ENV{"CPPFLAGS"}; } else { $cflags = ""; } - + if ($ENV{"CFLAGS"}) { $cflags .= " " . $ENV{"CFLAGS"}; } - + return $cflags; -} +} @@ -325,21 +325,21 @@ sub testCompile($$$) { my ($cflags, $cSourceCodeR, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile of the program in @{$cSourceCodeR}. -# +# # Return $$successR == $TRUE iff the compile succeeds (exit code 0). #----------------------------------------------------------------------------- my ($cFile, $cFileName) = tempFile(".c"); print $cFile @{$cSourceCodeR}; - + my ($oFile, $oFileName) = tempFile(".o"); # Note: we tried using /dev/null for the output file and got complaints # from the Sun compiler that it has the wrong suffix. 2002.08.09. - + my $compileCommand = "$testCc -c -o $oFileName $cflags $cFileName"; print ("Doing test compile: $compileCommand\n"); my $rc = system($compileCommand); - + unlink($oFileName); close($oFile); unlink($cFileName); @@ -354,22 +354,22 @@ sub testCompileLink($$$) { my ($flags, $cSourceCodeR, $successR) = @_; #----------------------------------------------------------------------------- # Do a test compile and link of the program in @{$cSourceCodeR}. -# +# # Return $$successR == $TRUE iff the compile succeeds (exit code 0). #----------------------------------------------------------------------------- my ($cFile, $cFileName) = tempFile('.c'); print $cFile @{$cSourceCodeR}; - + my ($oFile, $oFileName) = tempFile(''); # Note that $flags may contain -l options, which where static linking # is involved have to go _after_ the base object file ($oFileName). - + my $compileCommand = "$testCc -o $oFileName $cFileName $flags"; print ("Doing test compile/link: $compileCommand\n"); my $rc = system($compileCommand); - + unlink($oFileName); close($oFile); unlink($cFileName); @@ -410,14 +410,14 @@ sub askAboutCygwin() { print("Are you building in/for the Cygwin environment?\n"); print("\n"); - + my $default; if ($OSNAME eq "cygwin") { $default = "y"; } else { $default = "n"; } - + my $retval = promptYesNo($default); return $retval; @@ -429,14 +429,14 @@ sub askAboutDjgpp() { print("Are you building in/for the DJGPP environment?\n"); print("\n"); - + my $default; if ($OSNAME eq "dos") { $default = "y"; } else { $default = "n"; } - + my $retval = promptYesNo($default); } @@ -446,7 +446,7 @@ sub askAboutMingw() { print("Are you building for the MinGW environment?\n"); print("\n"); - + my $retval = promptYesNo("n"); return $retval; @@ -488,7 +488,7 @@ sub getPlatform() { computePlatformDefault(\$default); print("Which of the following best describes your platform?\n"); - + print("gnu GNU/Linux\n"); print("win Windows/DOS (Cygwin, DJGPP, Mingw32)\n"); print("sun Solaris or SunOS\n"); @@ -658,7 +658,7 @@ sub getCompiler($$$) { # make variable (e.g. make CC=gcc2). # # - Configure needs to do test compiles. The test is best if it uses -# the same compiler that the build eventually will use, but it's +# the same compiler that the build eventually will use, but it's # useful even if not. # # The value this subroutine returns is NOT the command name to invoke the @@ -714,11 +714,11 @@ sub gccLinker() { # First, we assume that the compiler calls 'collect2' as the linker # front end. The specs file might specify some other program, but # it usually says 'collect2'. - + my $retval; my $collect2 = qx{gcc --print-prog-name=collect2}; - + if (defined($collect2)) { chomp($collect2); my $linker = qx{$collect2 -v 2>&1}; @@ -749,14 +749,14 @@ sub getLinker($$$$) { print("\n"); my $default; - + if ($compiler eq "gcc") { $default = gccLinker(); } else { $default = "sun"; } my $response = prompt("sun or gnu", $default); - + if ($response eq "gnu") { $$baseLinkerR = "GNU"; } elsif ($response eq "sun") { @@ -811,11 +811,11 @@ sub getLibTypes($$$$$$$$) { my $default = ($default_target eq "merge") ? "static" : "shared"; - my ($netpbmlibtype, $netpbmlibsuffix, $shlibprefixlist, + my ($netpbmlibtype, $netpbmlibsuffix, $shlibprefixlist, $willBuildShared, $staticlib_too); - + my $response = prompt("static or shared", $default); - + if ($response eq "shared") { $willBuildShared = $TRUE; if ($platform eq "WINDOWS") { @@ -823,7 +823,7 @@ sub getLibTypes($$$$$$$$) { $netpbmlibsuffix = "dll"; if ($subplatform eq "cygwin") { $shlibprefixlist = "cyg lib"; - } + } } elsif ($platform eq "DARWIN") { $netpbmlibtype = "dylib"; $netpbmlibsuffix = "dylib"; @@ -846,7 +846,7 @@ sub getLibTypes($$$$$$$$) { $netpbmlibtype = "unixstatic"; $netpbmlibsuffix = "a"; # targets, but needed for building - # libopt + # libopt } else { print("'$response' isn't one of the choices. \n" . "You must choose 'static' or 'shared'.\n"); @@ -857,18 +857,18 @@ sub getLibTypes($$$$$$$$) { # Note that we can't do both a static and shared library for AIX, because # they both have the same name: libnetpbm.a. - - if (($netpbmlibtype eq "unixshared" or - $netpbmlibtype eq "irixshared" or + + if (($netpbmlibtype eq "unixshared" or + $netpbmlibtype eq "irixshared" or $netpbmlibtype eq "dll") and $netpbmlibsuffix ne "a") { print("Do you want to build static libraries too (for linking to \n"); print("programs not in the Netpbm package?\n"); print("\n"); - + my $default = "y"; - + my $response = prompt("(y)es or (n)o", $default); - + if (uc($response) =~ /^(Y|YES)$/) { $staticlib_too = "Y"; } elsif (uc($response) =~ /^(N|NO)$/) { @@ -919,16 +919,16 @@ sub inttypesDefault() { my @candidateList = ("<inttypes.h>", "<sys/inttypes.h>", "<types.h>", "<sys/types.h>"); - + for (my $i = 0; $i < @candidateList && !$works; ++$i) { my $candidate = $candidateList[$i]; my @cSourceCode = ( "#include $candidate\n", "int_fast32_t testvar;\n" ); - + testCompile($cflags, \@cSourceCode, \my $success); - + if ($success) { $works = $candidate; } @@ -961,7 +961,7 @@ sub getInttypes($) { print("\n"); my $default = inttypesDefault(); - + while (!$gotit) { my $response = prompt("'#include' argument or NONE", $default); @@ -1000,9 +1000,9 @@ sub getInt64($$) { "#include $inttypes_h\n", "int64_t testvar;\n" ); - + testCompile($cflags, \@cSourceCode, \my $success); - + if ($success) { print("You do.\n"); $$haveInt64R = 'Y'; @@ -1034,9 +1034,9 @@ sub determineSseCapability($) { my @cSourceCode = ( "#include <emmintrin.h>\n", ); - + testCompile($cflags, \@cSourceCode, \my $success); - + if ($success) { print("It does.\n"); $$haveEmmintrinR = $TRUE; @@ -1115,9 +1115,9 @@ sub getTiffLibrary($@) { my $default = "libtiff" . libSuffix($platform); print("What is your TIFF (graphics format) library?\n"); - + my $response = prompt("library filename or 'none'", $default); - + if ($response ne "none") { $tifflib = $response; } @@ -1137,9 +1137,9 @@ sub getTiffLibrary($@) { $default = "default"; } print("Where are the interface headers for it?\n"); - + my $response = prompt("TIFF header directory", $default); - + if ($response ne "default") { $tiffhdr_dir = $response; } @@ -1161,9 +1161,9 @@ sub getJpegLibrary($@) { my $default = "libjpeg" . libSuffix($platform); print("What is your JPEG (graphics format) library?\n"); - + my $response = prompt("library filename or 'none'", $default); - + if ($response ne "none") { $jpeglib = $response; } @@ -1178,9 +1178,9 @@ sub getJpegLibrary($@) { $default = "default"; } print("Where are the interface headers for it?\n"); - + my $response = prompt("JPEG header directory", $default); - + if ($response ne "default") { $jpeghdr_dir = $response; } @@ -1218,9 +1218,9 @@ sub getPngLibrary($@) { my $default = "libpng" . libSuffix($platform); print("What is your PNG (graphics format) library?\n"); - + my $response = prompt("library filename or 'none'", $default); - + if ($response ne "none") { $pnglib = $response; } @@ -1233,9 +1233,9 @@ sub getPngLibrary($@) { } else { $default = "default"; } - + print("Where are the interface headers for it?\n"); - + my $response = prompt("PNG header directory", $default); if ($response ne "default") { @@ -1258,9 +1258,9 @@ sub getZLibrary($@) { my $default = "libz" . libSuffix($platform); print("What is your Z (compression) library?\n"); - + my $response = prompt("library filename or 'none'", $default); - + if ($response ne "none") { $zlib = $response; } @@ -1273,11 +1273,11 @@ sub getZLibrary($@) { } else { $default = "default"; } - + print("Where are the interface headers for it?\n"); - + my $response = prompt("Z header directory", $default); - + if ($response ne "default") { $zhdr_dir = $response; } @@ -1315,9 +1315,9 @@ sub getX11Library($@) { $default = "libX11" . libSuffix($platform); } print("What is your X11 (X client) library?\n"); - + my $response = prompt("library filename or 'none'", $default); - + if ($response ne "none") { $x11lib = $response; } @@ -1328,9 +1328,9 @@ sub getX11Library($@) { $default = "default"; print("Where are the interface headers for it?\n"); - + my $response = prompt("X11 header directory", $default); - + if ($response ne "default") { $x11hdr_dir = $response; } @@ -1371,27 +1371,27 @@ sub getLinuxsvgaLibrary($@) { } else { $default = 'none'; } - + print("What is your Svgalib library?\n"); - + my $response = prompt("library filename or 'none'", $default); - + if ($response ne 'none') { $svgalib = $response; } } if (defined($svgalib) && $svgalib ne 'none') { my $default; - + if (-d('/usr/include/svgalib')) { $default = '/usr/include/svgalib'; } else { $default = "default"; } print("Where are the interface headers for it?\n"); - + my $response = prompt("Svgalib header directory", $default); - + if ($response ne "default") { $svgalibhdr_dir = $response; } @@ -1414,13 +1414,13 @@ sub symlink_command() { # simulation via a "ln" command, but have a "cp" command which works # in a pinch. Some Windows environments have "ln", but it won't do # symbolic links. - + if (commandExists("ln")) { # We assume if Perl can do symbolic links, so can Ln, and vice # versa. my $symlink_exists = eval { symlink("",""); 1 }; - + if ($symlink_exists) { $retval = "ln -s"; } else { @@ -1459,7 +1459,7 @@ sub gnuOptimizeOpt($) { # that causes -O3 to generate incorrect code (symptom: pnmtojpeg --quality=95 # generates a syntax error message from shhopt). #----------------------------------------------------------------------------- -# I don't know what are exactly the cases that Gcc is broken. I know +# I don't know what are exactly the cases that Gcc is broken. I know # Red Hat 7.1 and 7.2 and Mandrake 8.2, running gcc 2.96.1, commonly have # the problem. But it may be limited to a certain subrelease level or # CPU type or other environment. People who have reported the problem have @@ -1472,7 +1472,7 @@ sub gnuOptimizeOpt($) { my @gccVerboseResp = `$gccCommandName --verbose 2>&1`; my $brokenCompiler; - + if (@gccVerboseResp ==2) { if ($gccVerboseResp[1] =~ m{gcc version 2.96}) { $brokenCompiler = $TRUE; @@ -1513,13 +1513,13 @@ sub wnostrictoverflowWorks($) { my ($cFile, $cFileName) = tempFile(".c"); print $cFile "int x;"; - + my $compileCommand = "$gccCommandName -c -o /dev/null -Wno-strict-overflow $cFileName"; print("Doing test compile to see if -Wno-strict-overflow works: " . "$compileCommand\n"); my $rc = system($compileCommand); - + unlink($cFileName); close($cFile); @@ -1534,7 +1534,7 @@ sub gnuCflags($) { my $flags; $flags = gnuOptimizeOpt($gccCommandName) . " -ffast-math " . - " -pedantic -fno-common " . + " -pedantic -fno-common " . "-Wall -Wno-uninitialized -Wmissing-declarations -Wimplicit " . "-Wwrite-strings -Wmissing-prototypes -Wundef " . "-Wno-unknown-pragmas "; @@ -1577,7 +1577,7 @@ sub findProcessManagement($) { my @cSourceCode = ( "#include <sys/wait.h>\n", ); - + testCompile($cflags, \@cSourceCode, \my $success); if (!$success) { @@ -1637,7 +1637,7 @@ sub testCompileJpeglibH($$) { "#include <stdio.h>\n", "#include <jpeglib.h>\n", ); - + testCompile($cflags, \@cSourceCode, $successR); } @@ -1646,7 +1646,7 @@ sub testCompileJpeglibH($$) { sub testCompileJpegMarkerStruct($$) { my ($cflags, $successR) = @_; #----------------------------------------------------------------------------- -# Do a test compile to see if struct jpeg_marker_struct is defined in +# Do a test compile to see if struct jpeg_marker_struct is defined in # jpeglib.h. Assume it is already established that the compiler works # and can find jpeglib.h. #----------------------------------------------------------------------------- @@ -1668,7 +1668,7 @@ sub printMissingHdrWarning($$) { warnUser("You said the compile-time part of the $name library " . "(the header files) is in " . - + (defined($hdr_dir) ? "directory '$hdr_dir', " : "the compiler's default search path, ") . @@ -1719,7 +1719,7 @@ sub testJpegHdr($) { } else { # We can get to something named jpeglib.h, but maybe it's an old # version we can't use. Check it out. - testCompileJpegMarkerStruct("$generalCflags $jpegIOpt", + testCompileJpegMarkerStruct("$generalCflags $jpegIOpt", \my $success); if (!$success) { print("\n"); @@ -1789,7 +1789,7 @@ sub testCompileZlibH($$) { my @cSourceCode = ( "#include <zlib.h>\n", ); - + testCompile($cflags, \@cSourceCode, $successR); } @@ -1804,7 +1804,7 @@ sub testCompilePngH($$) { my @cSourceCode = ( "#include <png.h>\n", ); - + testCompile($cflags, \@cSourceCode, $successR); } @@ -1829,7 +1829,7 @@ sub testPngHdr($$) { } else { my $pngIOpt = $pnghdr_dir ? "-I$pnghdr_dir" : ""; - testCompilePngH("$generalCflags $zlibIOpt $pngIOpt", + testCompilePngH("$generalCflags $zlibIOpt $pngIOpt", \my $success); if (!$success) { @@ -1903,10 +1903,10 @@ sub testLinkPnglib($$) { } else { my $pngLdflags = qx{libpng-config --ldflags}; chomp($pngLdflags); - + testCompileLink("$generalCflags $pngCflags $pngLdflags", \@cSourceCode, \my $success); - + if (!$success) { testCompileLink("$generalCflags $pngCflags $pngLdflags -lz -lm", \@cSourceCode, \my $lzLmSuccess); @@ -1947,7 +1947,7 @@ sub testCompileXmlreaderH($$) { my @cSourceCode = ( "#include <libxml/xmlreader.h>\n", ); - + testCompile($cflags, \@cSourceCode, $successR); } @@ -1979,7 +1979,7 @@ sub testCompileXmlReaderTypes($$) { "#include <libxml/xmlreader.h>\n", "xmlReaderTypes dummy;\n", ); - + testCompile($cflags, \@cSourceCode, $successR); } @@ -2083,7 +2083,7 @@ if (@ARGV > 0) { } else { die("Unrecognized option: $ARGV[0]"); } - } + } $configInPathArg = $ARGV[0]; } @@ -2161,7 +2161,7 @@ print("\n"); { my $default = "regular"; my $response = prompt("regular or merge", $default); - + if ($response eq "regular") { $default_target = "nonmerge"; } elsif ($response eq "merge") { @@ -2220,18 +2220,18 @@ my ($jpeglib, $jpeghdr_dir) = getJpegLibrary($platform); print("\n"); my ($tifflib, $tiffhdr_dir) = getTiffLibrary($platform, $jpeghdr_dir); print("\n"); -my ($pnglib, $pnghdr_dir) = getPngLibrary($platform, +my ($pnglib, $pnghdr_dir) = getPngLibrary($platform, $tiffhdr_dir, $jpeghdr_dir); print("\n"); -my ($zlib, $zhdr_dir) = getZLibrary($platform, +my ($zlib, $zhdr_dir) = getZLibrary($platform, $pnghdr_dir, $tiffhdr_dir, $jpeghdr_dir); print("\n"); -my ($x11lib, $x11hdr_dir) = getX11Library($platform); +my ($x11lib, $x11hdr_dir) = getX11Library($platform); print("\n"); -my ($linuxsvgalib, $linuxsvgahdr_dir) = getLinuxsvgaLibrary($platform); +my ($linuxsvgalib, $linuxsvgahdr_dir) = getLinuxsvgaLibrary($platform); print("\n"); @@ -2274,7 +2274,7 @@ if (-f("GNUmakefile")) { while (!$done) { print("Where is the Netpbm source code?\n"); - $srcdir = prompt("Netpbm source directory", + $srcdir = prompt("Netpbm source directory", abs_path(dirname($0) . "/..")); if (-f("$srcdir/GNUmakefile")) { @@ -2283,7 +2283,7 @@ if (-f("GNUmakefile")) { print("That doesn't appear to contain Netpbm source code.\n"); print("There is no file named 'GNUmakefile' in it.\n"); print("\n"); - } + } } unlink("GNUmakefile"); symlink("$srcdir/GNUmakefile", "GNUmakefile"); @@ -2293,7 +2293,7 @@ if (-f("GNUmakefile")) { open(SRCDIR, ">srcdir.mk"); print(SRCDIR "SRCDIR = $srcdir\n"); close(SRCDIR); - + $defaultConfigInPath = "$srcdir/config.mk.in"; } @@ -2320,7 +2320,7 @@ open (CONFIG_IN,"<$configInPath") or @config_mk = <CONFIG_IN>; -unshift(@config_mk, +unshift(@config_mk, "####This file was automatically created by 'configure.'\n", "####Many variables are set twice -- a generic setting, then \n", "####a system-specific override at the bottom of the file.\n", @@ -2360,7 +2360,7 @@ if ($platform eq "GNU") { push(@config_mk, gnuCflags('cc')); } # The merged programs have a main_XXX subroutine instead of main(), -# which would cause a warning with -Wmissing-declarations or +# which would cause a warning with -Wmissing-declarations or # -Wmissing-prototypes. push(@config_mk, "CFLAGS_MERGE = " . "-Wno-missing-declarations -Wno-missing-prototypes\n"); @@ -2376,7 +2376,7 @@ if ($platform eq "GNU") { } else { makeCompilerGcc(\@config_mk); } - # Before Netpbm 10.20 (January 2004), we set this to -R for + # Before Netpbm 10.20 (January 2004), we set this to -R for # $compiler == cc and -rpath otherwise. But now we know that the GNU # compiler can also invoke a linker that needs -R, so we're more flexible. if ($baseLinker eq "GNU") { @@ -2416,7 +2416,7 @@ if ($platform eq "GNU") { "-L/usr/local/lib\n"); push(@config_mk, "LDSHLIB = -shared -expect_unresolved \"*\"\n"); } else { - # We've never tested this. This is just here to give a user a + # We've never tested this. This is just here to give a user a # headstart on submitting to us the necessary information. 2002.07.04. push(@config_mk, "CC = gcc\n"); push(@config_mk, 'CFLAGS = -O3', "\n"); @@ -2452,7 +2452,7 @@ if ($platform eq "GNU") { # } push(@config_mk, 'SYMLINK = ', symlink_command(), "\n"); push(@config_mk, 'DLLVER=$(NETPBM_MAJOR_RELEASE)', "\n"); - push(@config_mk, "LDSHLIB = " . + push(@config_mk, "LDSHLIB = " . '-shared -Wl,--image-base=0x10000000 -Wl,--enable-auto-import', "\n"); if ($subplatform ne "cygwin") { push(@config_mk, "MSVCRT = Y\n"); @@ -2463,7 +2463,7 @@ if ($platform eq "GNU") { } elsif ($platform eq "BEOS") { push(@config_mk, "LDSHLIB = -nostart\n"); } elsif ($platform eq "OPENBSD") { - # vedge@vedge.com.ar says on 2001.04.29 that there are a ton of + # vedge@vedge.com.ar says on 2001.04.29 that there are a ton of # undefined symbols in the Fiasco stuff on OpenBSD. So we'll just # cut it out of the build until someone feels like fixing it. push(@config_mk, "BUILD_FIASCO = N\n"); @@ -2482,7 +2482,7 @@ if ($platform eq "GNU") { push(@config_mk, "SHLIB_CLIB =\n"); } else { makeCompilerGcc(\@config_mk); - push(@config_mk, "LDSHLIB = -shared\n"); + push(@config_mk, "LDSHLIB = -shared\n"); } push(@config_mk, "NETWORKLD = -lsocket -lresolve\n"); } elsif ($platform eq "DARWIN") { @@ -2520,7 +2520,7 @@ if (!$flex_result) { print("You do not appear to have the 'flex' or 'lex' pattern \n"); print("matcher generator on your system, so we will not build \n"); print("programs that need it (Thinkjettopbm)\n"); - + print("\n"); print("Press ENTER to continue.\n"); my $key = <STDIN>; @@ -2534,7 +2534,7 @@ if (!$flex_result) { print("find 'flex' on your system.\n"); print("\n"); - push(@config_mk, "LEX = lex\n"); + push(@config_mk, "LEX = lex\n"); } } @@ -2628,4 +2628,4 @@ if ($warned) { print("\n"); -exit 0; +exit 0; diff --git a/common.mk b/common.mk index 749488c2..91b476b9 100644 --- a/common.mk +++ b/common.mk @@ -150,7 +150,7 @@ IMPORTINC_LIB_HEADERS := \ IMPORTINC_LIB_UTIL_HEADERS := \ bitarith.h bitio.h bitreverse.h filename.h intcode.h floatcode.h io.h \ matrix.h mallocvar.h \ - nsleep.h nstring.h pm_c_util.h runlength.h shhopt.h token.h + nsleep.h nstring.h pm_c_util.h rand.h runlength.h shhopt.h token.h IMPORTINC_HEADERS := \ $(IMPORTINC_ROOT_HEADERS) \ @@ -470,7 +470,7 @@ empty.c: # 2000.06.15 # DJGPP can do SYMKINKs for programs but not for ordinary files, so -# it define SYMLINKEXE, other system don't need it +# it defines SYMLINKEXE, other system don't need it ifeq ($(SYMLINKEXE)x,x) SYMLINKEXE := $(SYMLINK) endif diff --git a/converter/other/jpeg2000/libjasper_compat.h b/converter/other/jpeg2000/libjasper_compat.h index 45dd904b..103b1d09 100644 --- a/converter/other/jpeg2000/libjasper_compat.h +++ b/converter/other/jpeg2000/libjasper_compat.h @@ -49,7 +49,7 @@ pmjas_image_decode(jas_stream_t * const in, if (jasperP) { *imagePP = jasperP; - *errorP = errorP; + *errorP = NULL; } else { pm_asprintf(errorP, "Failed. Details may have been written to " "Standard Error"); diff --git a/converter/other/jpeg2000/pamtojpeg2k.c b/converter/other/jpeg2000/pamtojpeg2k.c index a886c390..ee5189ce 100644 --- a/converter/other/jpeg2000/pamtojpeg2k.c +++ b/converter/other/jpeg2000/pamtojpeg2k.c @@ -35,7 +35,7 @@ enum compmode {COMPMODE_INTEGER, COMPMODE_REAL}; enum progression {PROG_LRCP, PROG_RLCP, PROG_RPCL, PROG_PCRL, PROG_CPRL}; -struct cmdlineInfo { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ @@ -73,7 +73,7 @@ struct cmdlineInfo { static void parseCommandLine(int argc, char ** argv, - struct cmdlineInfo * const cmdlineP) { + struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that many of the strings that this function returns in the *cmdline_p structure are actually in the supplied argv array. And @@ -372,9 +372,11 @@ convertToJasperImage(struct pam * const inpamP, static void writeJpc(jas_image_t * const jasperP, - struct cmdlineInfo const cmdline, - FILE * const ofP) { - + struct CmdlineInfo const cmdline, + int const ofd) { +/*---------------------------------------------------------------------------- + Write the image *jasperP to open file 'ofd'. +-----------------------------------------------------------------------------*/ jas_stream_t * outStreamP; const char * options; const char * ilyrratesOpt; @@ -459,7 +461,7 @@ writeJpc(jas_image_t * const jasperP, pm_strfree(ilyrratesOpt); /* Open the output image file (Standard Output) */ - outStreamP = jas_stream_fdopen(fileno(ofP), "w+b"); + outStreamP = jas_stream_fdopen(ofd, "w+b"); if (outStreamP == NULL) pm_error("Unable to open output stream. jas_stream_fdopen() " "failed"); @@ -500,7 +502,7 @@ writeJpc(jas_image_t * const jasperP, int main(int argc, char **argv) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; FILE * ifP; struct pam inpam; jas_image_t * jasperP; @@ -526,7 +528,7 @@ main(int argc, char **argv) convertToJasperImage(&inpam, &jasperP); - writeJpc(jasperP, cmdline, stdout); + writeJpc(jasperP, cmdline, fileno(stdout)); jas_image_destroy(jasperP); diff --git a/converter/other/pamtowinicon.c b/converter/other/pamtowinicon.c index 7df8f60d..3c2c06bf 100644 --- a/converter/other/pamtowinicon.c +++ b/converter/other/pamtowinicon.c @@ -974,6 +974,10 @@ convertOneImage(unsigned int const imageNum, doingPng = pam.width * pam.height >= pngThreshold; + if (verbose) + pm_message("Image %2u: encoding as %s", + imageNum, doingPng ? "PNG" : "BMP"); + readAndScalePam(&pam, doingPng, tuples); determineImageType(&pam, tuples, &getPixel, diff --git a/converter/other/pgmtopbm.c b/converter/other/pgmtopbm.c index 64dc814b..d5f67a06 100644 --- a/converter/other/pgmtopbm.c +++ b/converter/other/pgmtopbm.c @@ -17,6 +17,7 @@ #include "pgm.h" #include "dithers.h" #include "mallocvar.h" +#include "rand.h" enum halftone {QT_FS, QT_THRESH, QT_DITHER8, QT_CLUSTER, QT_HILBERT}; @@ -462,14 +463,19 @@ createFsConverter(unsigned int const cols, /* Initialize Floyd-Steinberg error vectors. */ MALLOCARRAY_NOFAIL(stateP->thiserr, cols + 2); MALLOCARRAY_NOFAIL(stateP->nexterr, cols + 2); - srand(randomSeedSpec ? randomSeed : pm_randseed()); { /* (random errors in [-fs_scale/8 .. fs_scale/8]) */ unsigned int col; + struct pm_randSt randSt; + pm_randinit(&randSt); + pm_srand2(&randSt, randomSeedSpec, randomSeed); + for (col = 0; col < cols + 2; ++col) stateP->thiserr[col] = - (long)(rand() % fs_scale - half_fs_scale) / 4; + (long)(pm_rand(&randSt) % fs_scale - half_fs_scale) / 4; + + pm_randterm(&randSt); } stateP->fs_forward = TRUE; diff --git a/converter/other/pngtopam.c b/converter/other/pngtopam.c index 9098154a..3c0f81a5 100644 --- a/converter/other/pngtopam.c +++ b/converter/other/pngtopam.c @@ -668,10 +668,16 @@ dumpPngInfo(struct pngx * const pngxP) { background.blue); } - if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) - pm_message("tRNS chunk (transparency): %u entries", - pngx_trns(pngxP).numTrans); - else + if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) { + struct pngx_trns const trns = pngx_trns(pngxP); + + pm_message("tRNS chunk (transparency):"); + pm_message(" %u palette entries", trns.numTrans); + pm_message(" transparent color = (%u,%u,%u)", + trns.transColor.red, + trns.transColor.green, + trns.transColor.blue); + } else pm_message("tRNS chunk (transparency): not present"); if (pngx_chunkIsPresent(pngxP, PNG_INFO_gAMA)) @@ -868,17 +874,17 @@ paletteHasPartialTransparency(struct pngx * const pngxP) { if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) { struct pngx_trns const trans = pngx_trns(pngxP); - bool foundGray; + bool foundPartial; unsigned int i; - for (i = 0, foundGray = FALSE; - i < trans.numTrans && !foundGray; + for (i = 0, foundPartial = FALSE; + i < trans.numTrans && !foundPartial; ++i) { if (trans.trans[i] != 0 && trans.trans[i] != pngxP->maxval) { - foundGray = TRUE; + foundPartial = TRUE; } } - retval = foundGray; + retval = foundPartial; } else retval = FALSE; } else diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c index 8ad21a6a..23bd7d66 100644 --- a/converter/other/pnmtopng.c +++ b/converter/other/pnmtopng.c @@ -56,6 +56,7 @@ #endif /* 2 for warnings (1 == error) */ #include <assert.h> +#include <stdbool.h> #include <string.h> /* strcat() */ #include <limits.h> #include <png.h> @@ -140,12 +141,6 @@ typedef struct _jmpbuf_wrapper { jmp_buf jmpbuf; } jmpbuf_wrapper; -#ifndef TRUE -# define TRUE 1 -#endif -#ifndef FALSE -# define FALSE 0 -#endif #ifndef NONE # define NONE 0 #endif @@ -394,8 +389,8 @@ parseCommandLine(int argc, const char ** argv, 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 */ + 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. */ @@ -511,6 +506,8 @@ parseCommandLine(int argc, const char ** argv, cmdlineP->inputFileName = argv[1]; else pm_error("Program takes at most one argument: input file name"); + + free(option_def); } @@ -671,7 +668,7 @@ lookupColorAlpha(coloralphahash_table const caht, /* The following variables belong to getChv() and freeChv() */ -static bool getChv_computed = FALSE; +static bool getChv_computed = false; static colorhist_vector getChv_chv; @@ -722,7 +719,7 @@ getChv(FILE * const ifP, else pm_message("Too many colors (more than %u) found", maxColors); } - getChv_computed = TRUE; + getChv_computed = true; } *chvP = getChv_chv; *colorsP = getChv_colors; @@ -736,7 +733,7 @@ static void freeChv(void) { if (getChv_chv) ppm_freecolorhist(getChv_chv); - getChv_computed = FALSE; + getChv_computed = false; } @@ -750,7 +747,7 @@ pgmBitsAreRepeated(unsigned int const repeatedSize, xelval const maxval, int const format) { /*---------------------------------------------------------------------------- - Return TRUE iff all the samples in the image in file 'ifP', + Return true iff all the samples in the image in file 'ifP', described by 'cols', 'rows', 'maxval', and 'format', consist in the rightmost 'repeatedSize' * 2 bits of two identical sets of 'repeatedSize' bits. @@ -774,7 +771,7 @@ pgmBitsAreRepeated(unsigned int const repeatedSize, pm_seek2(ifP, &rasterPos, sizeof(rasterPos)); - mayscale = TRUE; /* initial assumption */ + mayscale = true; /* initial assumption */ for (row = 0; row < rows && mayscale; ++row) { unsigned int col; @@ -785,7 +782,7 @@ pgmBitsAreRepeated(unsigned int const repeatedSize, xelval const testbits1 = testbits2 & mask1; /* The lower half of the bits of interest in the sample */ if (((testbits1 << repeatedSize) | testbits1) != testbits2) - mayscale = FALSE; + mayscale = false; } } pnm_freerow(xelrow); @@ -878,7 +875,7 @@ meaningful_bits_ppm(FILE * const ifp, maxMeaningfulBits = pm_maxvaltobits(maxval); if (maxval == 65535) { - mayscale = TRUE; /* initial assumption */ + mayscale = true; /* initial assumption */ pm_seek2(ifp, &rasterPos, sizeof(rasterPos)); for (row = 0; row < rows && mayscale; ++row) { unsigned int col; @@ -888,7 +885,7 @@ meaningful_bits_ppm(FILE * const ifp, if ((PPM_GETR(p) & 0xff) * 0x101 != PPM_GETR(p) || (PPM_GETG(p) & 0xff) * 0x101 != PPM_GETG(p) || (PPM_GETB(p) & 0xff) * 0x101 != PPM_GETB(p)) - mayscale = FALSE; + mayscale = false; } } if (mayscale) @@ -902,7 +899,7 @@ meaningful_bits_ppm(FILE * const ifp, static void -tryTransparentColor(FILE * const ifp, +tryTransparentColor(FILE * const ifP, pm_filepos const rasterPos, int const cols, int const rows, @@ -912,7 +909,14 @@ tryTransparentColor(FILE * const ifp, gray const alphaMaxval, pixel const transcolor, bool * const singleColorIsTransP) { +/*---------------------------------------------------------------------------- + Find out if the transparent pixels identified by alpha mask 'alphaMask' + (whose maxval is 'alphaMaxval') are exactly the pixels of color + 'transcolor'. Return answer as *singleColorIsTransP. + The image we analyze is that on input stream *ifP, starting at position + 'rasterPos', and we leave that stream positioned arbitrarily. +-----------------------------------------------------------------------------*/ int const pnmType = PNM_FORMAT_TYPE(format); xel * xelrow; @@ -922,13 +926,13 @@ tryTransparentColor(FILE * const ifp, xelrow = pnm_allocrow(cols); - pm_seek2(ifp, &rasterPos, sizeof(rasterPos)); + pm_seek2(ifP, &rasterPos, sizeof(rasterPos)); - singleColorIsTrans = TRUE; /* initial assumption */ + singleColorIsTrans = true; /* initial assumption */ for (row = 0; row < rows && singleColorIsTrans; ++row) { int col; - pnm_readpnmrow(ifp, xelrow, cols, maxval, format); + pnm_readpnmrow(ifP, xelrow, cols, maxval, format); for (col = 0 ; col < cols && singleColorIsTrans; ++col) { if (alphaMask[row][col] == 0) { /* transparent */ /* If we have a second transparent color, we're @@ -936,16 +940,16 @@ tryTransparentColor(FILE * const ifp, */ if (pnmType == PPM_TYPE) { if (!PPM_EQUAL(xelrow[col], transcolor)) - singleColorIsTrans = FALSE; + singleColorIsTrans = false; } else { if (PNM_GET1(xelrow[col]) != PNM_GET1(transcolor)) - singleColorIsTrans = FALSE; + singleColorIsTrans = false; } } else if (alphaMask[row][col] != alphaMaxval) { /* Here's an area of the mask that is translucent. That disqualified us. */ - singleColorIsTrans = FALSE; + singleColorIsTrans = false; } else { /* Here's an area of the mask that is opaque. If it's the same color as our candidate transparent color, @@ -953,10 +957,10 @@ tryTransparentColor(FILE * const ifp, */ if (pnmType == PPM_TYPE) { if (PPM_EQUAL(xelrow[col], transcolor)) - singleColorIsTrans = FALSE; + singleColorIsTrans = false; } else { if (PNM_GET1(xelrow[col]) == PNM_GET1(transcolor)) - singleColorIsTrans = FALSE; + singleColorIsTrans = false; } } } @@ -990,8 +994,8 @@ analyzeAlpha(FILE * const ifP, of a certain color fully transparent and every other pixel opaque, we can simply identify that color in the PNG. - We have to do this before any scaling occurs, since alpha is only - possible with 8 and 16-bit. + We have to do this before any scaling occurs, since alpha is possible + only with 8 and 16-bit. -----------------------------------------------------------------------------*/ xel * xelrow; bool foundTransparentPixel; @@ -1034,7 +1038,7 @@ analyzeAlpha(FILE * const ifP, pnm_readpnmrow(ifP, xelrow, cols, maxval, format); for (col = 0; col < cols && !foundTransparentPixel; ++col) { if (alphaMask[row][col] == 0) { - foundTransparentPixel = TRUE; + foundTransparentPixel = true; transcolor = pnm_xeltopixel(xelrow[col], format); } } @@ -1115,16 +1119,16 @@ determineTransparency(struct cmdlineInfo const cmdline, if (alphaCanBeTransparencyIndex && !cmdline.force) { if (verbose) pm_message("converting alpha mask to transparency index"); - *alphaP = FALSE; + *alphaP = false; *transparentP = 2; *transColorP = alphaTranscolor; } else if (allOpaque) { if (verbose) pm_message("Skipping alpha because mask is all opaque"); - *alphaP = FALSE; + *alphaP = false; *transparentP = -1; } else { - *alphaP = TRUE; + *alphaP = true; *transparentP = -1; } *alphaMaxvalP = alphaMaxval; @@ -1134,17 +1138,17 @@ determineTransparency(struct cmdlineInfo const cmdline, use with trans[], which can have stuff in it if the user specified a transparent color. */ - *alphaP = FALSE; + *alphaP = false; *alphaMaxvalP = 255; if (cmdline.transparent) { const char * transstring2; /* The -transparent value, but with possible leading '=' removed */ if (cmdline.transparent[0] == '=') { - *transExactP = TRUE; + *transExactP = true; transstring2 = &cmdline.transparent[1]; } else { - *transExactP = FALSE; + *transExactP = false; transstring2 = cmdline.transparent; } /* We do this funny PPM_DEPTH thing instead of just passing 'maxval' @@ -1206,7 +1210,7 @@ hasColor(FILE * const ifP, for (col = 0; col < cols && isGray; ++col) { xel const p = xelrow[col]; if (PPM_GETR(p) != PPM_GETG(p) || PPM_GETG(p) != PPM_GETB(p)) - isGray = FALSE; + isGray = false; } } @@ -1335,10 +1339,10 @@ compute_nonalpha_palette(colorhist_vector const chv, int j; bool found; - found = FALSE; + found = false; for (j = 0; j < ordered_palette_size && !found; ++j) { if (PNM_EQUAL(ordered_palette[j], chv[colorIndex].color)) - found = TRUE; + found = true; } if (!found) pm_error("failed to find color (%d, %d, %d), which is in the " @@ -1636,7 +1640,7 @@ compute_alpha_palette(FILE * const ifP, MAXPALETTEENTRIES elements. If there are more than MAXPALETTEENTRIES color/alpha pairs in the image, - don't return any palette information -- just return *tooBigP == TRUE. + don't return any palette information -- just return *tooBigP == true. -----------------------------------------------------------------------------*/ colorhist_vector chv; unsigned int colors; @@ -1714,9 +1718,9 @@ makeOneColorTransparentInPalette(xel const transColor, can do a better job when the opaque entries are all last in the color/alpha palette). - If the specified color is not there and exact == TRUE, return + If the specified color is not there and exact == true, return without changing anything, but issue a warning message. If it's - not there and exact == FALSE, just find the closest color. + not there and exact == false, just find the closest color. We assume every entry in the palette is opaque upon entry. @@ -2369,7 +2373,8 @@ writeRaster(struct pngx * const pngxP, /* max: 3 color channels, one alpha channel, 16-bit */ MALLOCARRAY(line, cols * 8); if (line == NULL) - pm_error("out of memory allocating PNG row buffer"); + pm_error("out of memory allocating PNG row buffer for %u columns", + cols); for (pass = 0; pass < pngxP->numPassesRequired; ++pass) { unsigned int row; @@ -2386,6 +2391,7 @@ writeRaster(struct pngx * const pngxP, pngx_writeRow(pngxP, line); } } + free(line); pnm_freerow(xelrow); } @@ -2837,9 +2843,9 @@ convertpnm(struct cmdlineInfo const cmdline, if (verbose) pm_message("Not using color map. %s", noColormapReason); pm_strfree(noColormapReason); - colorMapped = FALSE; + colorMapped = false; } else - colorMapped = TRUE; + colorMapped = true; computeColorMapLookupTable(colorMapped, palettePnm, paletteSize, transPnm, transSize, alpha, alphaMaxval, diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c index 8c9167fb..3ca158b5 100644 --- a/converter/other/pnmtops.c +++ b/converter/other/pnmtops.c @@ -9,7 +9,7 @@ We use methods we learned from Dirk Krause's program Bmeps. Previous versions used raster encoding code based on Bmeps - code. This program does not used any code from Bmeps. + code. This program does not use any code from Bmeps. 2) Use our own filters and redefine /readstring . This is aboriginal Netpbm code, from when Postscript was young. The filters are @@ -1268,7 +1268,7 @@ putFilters(unsigned int const postscriptLevel, assert(postscriptLevel > 1); /* We say to decode flate, then rle, so Caller must ensure it encodes - rel, then flate. + rle, then flate. */ if (ascii85) diff --git a/converter/pbm/pbmtox10bm b/converter/pbm/pbmtox10bm index deb3aeab..ca82fcd2 100644 --- a/converter/pbm/pbmtox10bm +++ b/converter/pbm/pbmtox10bm @@ -37,6 +37,16 @@ exec perl -w -x -S -- "$0" "$@" use strict; use File::Basename; use Cwd 'abs_path'; +use IO::Handle; + + + +sub pm_message($) { + STDERR->print("pbmtox10bm: $_[0]\n"); +} + + + sub doVersionHack($) { my ($argvR) = @_; @@ -56,13 +66,12 @@ my $infile; foreach (@ARGV) { if (/^-/) { # It's an option. But Pbmtox10bm didn't have any options. - print(STDERR "Invalid option '$_'\n"); + pm_message("Invalid option '$_'"); exit(10); } else { # It's a parameter if (defined($infile)) { - print(STDERR - "You may specify at most one non-option parameter.\n"); + pm_message("You may specify at most one non-option parameter."); exit(10); } else { $infile = $_; diff --git a/converter/ppm/hpcdtoppm/Makefile b/converter/ppm/hpcdtoppm/Makefile index 59ba3630..5777a84f 100644 --- a/converter/ppm/hpcdtoppm/Makefile +++ b/converter/ppm/hpcdtoppm/Makefile @@ -19,7 +19,7 @@ install.bin install.merge: install.bin.local install.bin.local: $(PKGDIR)/bin # In June 2002, pcdovtoppm replaced pcdindex cd $(PKGDIR)/bin ; \ - $(SYMLINK) pcdovtoppm$(EXE) pcdindex$(EXE) + $(SYMLINK) pcdovtoppm pcdindex FORCE: diff --git a/doc/HISTORY b/doc/HISTORY index 922320de..e150a3e2 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -4,29 +4,40 @@ Netpbm. CHANGE HISTORY -------------- -21.03.22 BJH Release 10.93.03 +21.03.27 BJH Release 10.94.00 - pnmtops: Fix incorrect output (arithmetic overflow) when - bounding box is exactly INT_MAX high or wide. Always broken. - Pnmtops was in primordial Netpbm. + Add pamhomography: Thanks Scott Pakin. + + pamstereogram: Add -yfillshift . + + pamtowinicon: Add BMP/PNG encoding to verbose output. -21.03.10 BJH Release 10.93.02 + Use internal random number generator everywhere random numbers + are used except ppmtoilbm, so seeded results are the same on + all platforms. + + pamexec: Issue message instead of being killed by a signal when + the exec'ed program does not read the whole image" + + ppmforge: Fail if -dimension is greater than 5, which is + useless. + + pamscale: Fix bogus "bad magic number" or similar failure most + of the time with -nomix. Broken since Netpbm 10.49 (December + 2009). - pnmtopng: fix incorrect transparency in output when requesting + pnmtopng: Fix incorrect transparency in output when requesting transparency. Introduced after Netpbm 10.35 (August 2006) but not after Netpbm 10.47 (June 2009). - pnmtopng: fix buffer overrun or bogus "too many color/ + pnmtopng: Fix buffer overrun or bogus "too many color/ transparency pairs" failure when requesting transparency. Introduced after Netpbm 10.26 (January 2005) but not after Netpbm 10.35 (August 2006). -21.03.07 BJH Release 10.93.01 + pamtojpeg2k: Fix constant failure with message about file + close failing. - pamscale: fix bogus "bad magic number" or similar failure most - of the time with -nomix. Broken since Netpbm 10.49 (December - 2009). - libnetpbm: pm_system: Fix bug: standard input feeder process repositions unrelated files. Always broken (pm_system was new in Netpbm 10.13 (September 2003). @@ -35,6 +46,15 @@ CHANGE HISTORY (result of pm_system bug above). Always broken (Pamtowinicon was new in Netpbm 10.63 (June 2013). + pnmtopng: Fix trivial memory leaks. + + pnmtops: Fix incorrect output (arithmetic overflow) when + bounding box is exactly INT_MAX high or wide. Always broken. + Pnmtops was in primordial Netpbm. + + make package: fix no such file pcdovtoppm.exe failure on + Windows. + 20.12.28 BJH Release 10.93.00 pamarith: Add -equal. @@ -43,18 +63,18 @@ CHANGE HISTORY it makes sense (all but -subtract, -difference, -compare, -divide, -shiftleft, and -shiftright). - pamrith: fail if operand images have different depth and not + pamarith: fail if operand images have different depth and not depth 1. ppmshift: Add -seed . pamaddnoise: Fix incorrect output for -type poisson. Always - broken. (pamaddnoise was new to Netpbm in Netpbm 10.29 (August - 2005)). + broken. (pamaddnoise's precursor pnmaddnoies was new to Netpbm + in Netpbm 10.29 (August 2005)). pamaddnoise: fix bug: garbage output with -type impulse. Always - broken (pamaddnoise's precursors pnmaddnoise was new to Netpbm - in Netpbm 10.29 (August 2005). + broken (pamaddnoise's precursor pnmaddnoise was new to Netpbm + in Netpbm 10.29 (August 2005)). 20.09.26 BJH Release 10.92.00 diff --git a/editor/Makefile b/editor/Makefile index 88409dad..395deaf4 100644 --- a/editor/Makefile +++ b/editor/Makefile @@ -19,7 +19,8 @@ SUBDIRS = pamflip specialty PORTBINARIES = pamaddnoise pamaltsat pambackground pambrighten pamcomp pamcut \ pamdice pamditherbw pamedge \ pamenlarge \ - pamfunc pamhue pamlevels pammasksharpen pammixmulti \ + pamfunc pamhomography pamhue pamlevels \ + pammasksharpen pammixmulti \ pamperspective pamrecolor pamrubber \ pamscale pamsistoaglyph pamstretch pamthreshold pamundice \ pamwipeout \ diff --git a/editor/pamaddnoise.c b/editor/pamaddnoise.c index 20c68d99..9ca80394 100644 --- a/editor/pamaddnoise.c +++ b/editor/pamaddnoise.c @@ -33,18 +33,20 @@ #include "pm_c_util.h" #include "mallocvar.h" +#include "rand.h" #include "shhopt.h" #include "pm_gamma.h" #include "pam.h" static double const EPSILON = 1.0e-5; +static double const SALT_RATIO = 0.5; static double -rand1() { +rand1(struct pm_randSt * const randStP) { - return (double)rand()/RAND_MAX; + return (double)pm_rand(randStP)/RAND_MAX; } @@ -67,6 +69,7 @@ struct CmdlineInfo { enum NoiseType noiseType; + unsigned int seedSpec; unsigned int seed; float lambda; @@ -119,7 +122,7 @@ parseCommandLine(int argc, const char ** const argv, unsigned int option_def_index; - unsigned int typeSpec, seedSpec, lambdaSpec, lsigmaSpec, mgsigmaSpec, + unsigned int typeSpec, lambdaSpec, lsigmaSpec, mgsigmaSpec, sigma1Spec, sigma2Spec, toleranceSpec; const char * type; @@ -128,21 +131,21 @@ parseCommandLine(int argc, const char ** const argv, option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "type", OPT_STRING, &type, - &typeSpec, 0); + &typeSpec, 0); OPTENT3(0, "seed", OPT_UINT, &cmdlineP->seed, - &seedSpec, 0); + &cmdlineP->seedSpec, 0); OPTENT3(0, "lambda", OPT_FLOAT, &cmdlineP->lambda, - &lambdaSpec, 0); + &lambdaSpec, 0); OPTENT3(0, "lsigma", OPT_FLOAT, &cmdlineP->lsigma, - &lsigmaSpec, 0); + &lsigmaSpec, 0); OPTENT3(0, "mgsigma", OPT_FLOAT, &cmdlineP->mgsigma, - &mgsigmaSpec, 0); + &mgsigmaSpec, 0); OPTENT3(0, "sigma1", OPT_FLOAT, &cmdlineP->sigma1, - &sigma1Spec, 0); + &sigma1Spec, 0); OPTENT3(0, "sigma2", OPT_FLOAT, &cmdlineP->sigma2, - &sigma2Spec, 0); + &sigma2Spec, 0); OPTENT3(0, "tolerance", OPT_FLOAT, &cmdlineP->tolerance, - &toleranceSpec, 0); + &toleranceSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -193,7 +196,7 @@ parseCommandLine(int argc, const char ** const argv, if (!toleranceSpec) cmdlineP->tolerance = 0.10; - if (!seedSpec) + if (!cmdlineP->seedSpec) cmdlineP->seed = pm_randseed(); if (argc-1 > 1) @@ -211,11 +214,12 @@ parseCommandLine(int argc, const char ** const argv, static void -addGaussianNoise(sample const maxval, - sample const origSample, - sample * const newSampleP, - float const sigma1, - float const sigma2) { +addGaussianNoise(sample const maxval, + sample const origSample, + sample * const newSampleP, + float const sigma1, + float const sigma2, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- Add Gaussian noise. @@ -225,11 +229,11 @@ addGaussianNoise(sample const maxval, double x1, x2, xn, yn; double rawNewSample; - x1 = rand1(); + x1 = rand1(randStP); if (x1 == 0.0) x1 = 1.0; - x2 = rand1(); + x2 = rand1(randStP); xn = sqrt(-2.0 * log(x1)) * cos(2.0 * M_PI * x2); yn = sqrt(-2.0 * log(x1)) * sin(2.0 * M_PI * x2); @@ -242,40 +246,42 @@ addGaussianNoise(sample const maxval, static void -addImpulseNoise(sample const maxval, - sample const origSample, - sample * const newSampleP, - float const tolerance) { +addImpulseNoise(sample const maxval, + sample const origSample, + sample * const newSampleP, + float const tolerance, + double const saltRatio, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- Add impulse (salt and pepper) noise -----------------------------------------------------------------------------*/ - double const low_tol = tolerance / 2.0; - double const high_tol = 1.0 - (tolerance / 2.0); - double const sap = rand1(); + double const pepperRatio = 1.0 - saltRatio; + double const loTolerance = tolerance * pepperRatio; + double const hiTolerance = 1.0 - tolerance * saltRatio; + double const sap = rand1(randStP); - if (sap < low_tol) - *newSampleP = 0; - else if ( sap >= high_tol ) - *newSampleP = maxval; - else - *newSampleP = origSample; + *newSampleP = + sap < loTolerance ? 0 : + sap >= hiTolerance? maxval : + origSample; } static void -addLaplacianNoise(sample const maxval, - double const infinity, - sample const origSample, - sample * const newSampleP, - float const lsigma) { +addLaplacianNoise(sample const maxval, + double const infinity, + sample const origSample, + sample * const newSampleP, + float const lsigma, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- Add Laplacian noise From Pitas' book. -----------------------------------------------------------------------------*/ - double const u = rand1(); + double const u = rand1(randStP); double rawNewSample; @@ -297,11 +303,12 @@ addLaplacianNoise(sample const maxval, static void -addMultiplicativeGaussianNoise(sample const maxval, - double const infinity, - sample const origSample, - sample * const newSampleP, - float const mgsigma) { +addMultiplicativeGaussianNoise(sample const maxval, + double const infinity, + sample const origSample, + sample * const newSampleP, + float const mgsigma, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- Add multiplicative Gaussian noise @@ -311,14 +318,14 @@ addMultiplicativeGaussianNoise(sample const maxval, double rawNewSample; { - double const uniform = rand1(); + double const uniform = rand1(randStP); if (uniform <= EPSILON) rayleigh = infinity; else rayleigh = sqrt(-2.0 * log( uniform)); } { - double const uniform = rand1(); + double const uniform = rand1(randStP); gauss = rayleigh * cos(2.0 * M_PI * uniform); } rawNewSample = origSample + (origSample * mgsigma * gauss); @@ -363,10 +370,11 @@ poissonPmf(double const lambda, static void -addPoissonNoise(struct pam * const pamP, - sample const origSample, - sample * const newSampleP, - float const lambdaOfMaxval) { +addPoissonNoise(struct pam * const pamP, + sample const origSample, + sample * const newSampleP, + float const lambdaOfMaxval, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- Add Poisson noise -----------------------------------------------------------------------------*/ @@ -376,7 +384,7 @@ addPoissonNoise(struct pam * const pamP, double const lambda = origSampleIntensity * lambdaOfMaxval; - double const u = rand1(); + double const u = rand1(randStP); /* We now apply the inverse CDF (cumulative distribution function) of the Poisson distribution to uniform random variable 'u' to get a Poisson @@ -416,12 +424,14 @@ main(int argc, const char ** argv) { const tuple * newtuplerow; unsigned int row; double infinity; + struct pm_randSt randSt; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); - srand(cmdline.seed); + pm_randinit(&randSt); + pm_srand2(&randSt, cmdline.seedSpec, cmdline.seed); ifP = pm_openr(cmdline.inputFileName); @@ -448,35 +458,40 @@ main(int argc, const char ** argv) { addGaussianNoise(inpam.maxval, tuplerow[col][plane], &newtuplerow[col][plane], - cmdline.sigma1, cmdline.sigma2); + cmdline.sigma1, cmdline.sigma2, + &randSt); break; case NOISETYPE_IMPULSE: addImpulseNoise(inpam.maxval, tuplerow[col][plane], &newtuplerow[col][plane], - cmdline.tolerance); + cmdline.tolerance, SALT_RATIO, + &randSt); break; case NOISETYPE_LAPLACIAN: addLaplacianNoise(inpam.maxval, infinity, tuplerow[col][plane], &newtuplerow[col][plane], - cmdline.lsigma); + cmdline.lsigma, + &randSt); break; case NOISETYPE_MULTIPLICATIVE_GAUSSIAN: addMultiplicativeGaussianNoise(inpam.maxval, infinity, tuplerow[col][plane], &newtuplerow[col][plane], - cmdline.mgsigma); + cmdline.mgsigma, + &randSt); break; case NOISETYPE_POISSON: addPoissonNoise(&inpam, tuplerow[col][plane], &newtuplerow[col][plane], - cmdline.lambda); + cmdline.lambda, + &randSt); break; } @@ -484,6 +499,7 @@ main(int argc, const char ** argv) { } pnm_writepamrow(&outpam, newtuplerow); } + pm_randterm(&randSt); pnm_freepamrow(newtuplerow); pnm_freepamrow(tuplerow); diff --git a/editor/pamditherbw.c b/editor/pamditherbw.c index ae91a26f..694b2c21 100644 --- a/editor/pamditherbw.c +++ b/editor/pamditherbw.c @@ -14,12 +14,14 @@ #include <string.h> #include "pm_c_util.h" -#include "pam.h" -#include "dithers.h" +#include "rand.h" #include "mallocvar.h" #include "shhopt.h" +#include "pam.h" +#include "dithers.h" #include "pm_gamma.h" + enum halftone {QT_FS, QT_ATKINSON, QT_THRESH, @@ -588,7 +590,9 @@ fsDestroy(struct converter * const converterP) { static struct converter createFsConverter(struct pam * const graypamP, - float const threshFraction) { + float const threshFraction, + bool const randomseedSpec, + unsigned int const randomseed) { struct fsState * stateP; struct converter converter; @@ -605,9 +609,17 @@ createFsConverter(struct pam * const graypamP, { /* (random errors in [-1/8 .. 1/8]) */ + unsigned int col; + struct pm_randSt randSt; + + pm_randinit(&randSt); + pm_srand2(&randSt, randomseedSpec, randomseed); + for (col = 0; col < graypamP->width + 2; ++col) - stateP->thiserr[col] = ((float)rand()/RAND_MAX - 0.5) / 4; + stateP->thiserr[col] = (pm_drand(&randSt) - 0.5) / 4; + + pm_randterm(&randSt); } stateP->halfWhite = threshFraction; @@ -725,7 +737,9 @@ atkinsonDestroy(struct converter * const converterP) { static struct converter createAtkinsonConverter(struct pam * const graypamP, - float const threshFraction) { + float const threshFraction, + bool const randomseedSpec, + unsigned int const randomseed) { struct atkinsonState * stateP; struct converter converter; @@ -743,11 +757,18 @@ createAtkinsonConverter(struct pam * const graypamP, { /* (random errors in [-1/8 .. 1/8]) */ unsigned int col; + struct pm_randSt randSt; + + pm_randinit(&randSt); + pm_srand2(&randSt, randomseedSpec, randomseed); + for (col = 0; col < graypamP->width + 2; ++col) { - stateP->error[0][col] = ((float)rand()/RAND_MAX - 0.5) / 4; + stateP->error[0][col] = (pm_drand(&randSt) - 0.5) / 4; stateP->error[1][col] = 0.0; stateP->error[2][col] = 0.0; } + + pm_randterm(&randSt); } stateP->halfWhite = threshFraction; @@ -934,8 +955,6 @@ main(int argc, char *argv[]) { parseCommandLine(argc, argv, &cmdline); - srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); - ifP = pm_openr(cmdline.inputFilespec); if (cmdline.halftone == QT_HILBERT) @@ -956,10 +975,14 @@ main(int argc, char *argv[]) { switch (cmdline.halftone) { case QT_FS: - converter = createFsConverter(&graypam, cmdline.threshval); + converter = createFsConverter(&graypam, cmdline.threshval, + cmdline.randomseedSpec, + cmdline.randomseed); break; case QT_ATKINSON: - converter = createAtkinsonConverter(&graypam, cmdline.threshval); + converter = createAtkinsonConverter(&graypam, cmdline.threshval, + cmdline.randomseedSpec, + cmdline.randomseed); break; case QT_THRESH: converter = createThreshConverter(&graypam, cmdline.threshval); diff --git a/editor/pamhomography.c b/editor/pamhomography.c new file mode 100644 index 00000000..59b59ed7 --- /dev/null +++ b/editor/pamhomography.c @@ -0,0 +1,677 @@ +/* ---------------------------------------------------------------------- + * + * Map one quadrilateral to another + * by Scott Pakin <scott+pbm@pakin.org> + * + * ---------------------------------------------------------------------- + * + * Copyright (C) 2020 Scott Pakin <scott+pbm@pakin.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ---------------------------------------------------------------------- + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include "pm_c_util.h" +#include "shhopt.h" +#include "mallocvar.h" +#include "pam.h" + +#define MIN4(A, B, C, D) MIN(MIN(A, B), MIN(C, D)) +#define MAX4(A, B, C, D) MAX(MAX(A, B), MAX(C, D)) + +/* A point on the image plane. It may or may not lie within the + bounds of the image itself. */ +typedef struct Point { + int x; + int y; +} Point; + +/* A quadrilateral on the image plane */ +typedef struct Quad { + Point ul; + Point ur; + Point lr; + Point ll; +} Quad; + +/* A user-specified mapping from one quadrilateral to another */ +typedef struct QuadMap { + Quad from; + Quad to; +} QuadMap; + +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFilespec; /* "-" if stdin */ + QuadMap qmap; /* Source and target quadrilaterals */ + Quad bbox; /* Bounding box for the target image */ + const char * fillColor; /* Fill color for unused coordinates */ +}; + + + +static unsigned int +parseCoords(const char * const str, + int * const coords) { +/*---------------------------------------------------------------------------- + Parse a list of up to 16 integers. The function returns the number + of integers encountered. +-----------------------------------------------------------------------------*/ + + const char * p; + char * pnext; + unsigned int i; + + for (i = 0, p = str; i < 16; ++i, p = pnext) { + long int val; + + /* Skip punctuation, except "+" and "-", and white space. */ + while (*p != '\0' && *p != '+' && *p != '-' && + (isspace(*p) || ispunct(*p))) + ++p; + + /* Parse the next integer. */ + errno = 0; /* strtol() sets errno on error. */ + val = strtol(p, &pnext, 10); + if (errno == ERANGE) + return i; /* Integer lies out of long int range */ + if (errno != 0 || pnext == p) + return i; /* Too few integers */ + coords[i] = (int)val; + if ((long int)coords[i] != val) + return i; /* Integer lies out of int range */ + } + return i; +} + + + +static void +parseViewString(const char * const str, + Quad * const quad) { +/*---------------------------------------------------------------------------- + Parse a list of four integers in the order {ulx, uly, lrx, lry} into a + quadrilateral. The function aborts on error. +-----------------------------------------------------------------------------*/ + + int coords[16]; + + if (parseCoords(str, coords) != 4) + pm_error("failed to parse \"%s\" as a list of four integers", str); + quad->ul.x = quad->ll.x = coords[0]; + quad->ul.y = quad->ur.y = coords[1]; + quad->lr.x = quad->ur.x = coords[2]; + quad->lr.y = quad->ll.y = coords[3]; +} + + + +static void +parseQuadString(const char * const str, + Quad * const quad) { +/*---------------------------------------------------------------------------- + Parse a list of eight integers in the order {ulx, uly, urx, ury, + lrx, lry, llx, lly} into a quadrilateral. The function aborts on + error. +-----------------------------------------------------------------------------*/ + + int coords[16]; + + if (parseCoords(str, coords) != 8) + pm_error("failed to parse \"%s\" as a list of eight integers", str); + quad->ul.x = coords[0]; + quad->ul.y = coords[1]; + quad->ur.x = coords[2]; + quad->ur.y = coords[3]; + quad->lr.x = coords[4]; + quad->lr.y = coords[5]; + quad->ll.x = coords[6]; + quad->ll.y = coords[7]; +} + + + +static void +readMapFile(const char * const fname, + QuadMap * const qmap) { +/*---------------------------------------------------------------------------- + Read from a file either 16 numbers in the order {ulx1, uly1, urx1, ury1, + lrx1, lry1, llx1, lly1, ulx2, uly2, urx2, ury2, lrx2, lry2, llx2, lly2} + or 8 numbers in the order {ulx2, uly2, urx2, ury2, lrx2, lry2, llx2, + lly2}. This function aborts on error. +-----------------------------------------------------------------------------*/ + + FILE * fp; + char * str; /* Entire file contents */ + int coords[16]; /* File as a list of up to 16 coordinates */ + char * c; + long int nread; + + /* Read the entire file. */ + fp = pm_openr(fname); + str = pm_read_unknown_size(fp, &nread); + REALLOCARRAY_NOFAIL(str, nread + 1); + str[nread] = '\0'; + pm_close(fp); + + /* Replace newlines and tabs with spaces to prettify error reporting. */ + for (c = str; *c != '\0'; ++c) + if (isspace(*c)) + *c = ' '; + + /* Read either {from, to} or just a {to} quadrilateral. */ + switch (parseCoords(str, coords)) { + case 16: + /* 16 integers: assign both the "from" and the "to" quadrilateral. */ + qmap->from.ul.x = coords[0]; + qmap->from.ul.y = coords[1]; + qmap->from.ur.x = coords[2]; + qmap->from.ur.y = coords[3]; + qmap->from.lr.x = coords[4]; + qmap->from.lr.y = coords[5]; + qmap->from.ll.x = coords[6]; + qmap->from.ll.y = coords[7]; + qmap->to.ul.x = coords[8]; + qmap->to.ul.y = coords[9]; + qmap->to.ur.x = coords[10]; + qmap->to.ur.y = coords[11]; + qmap->to.lr.x = coords[12]; + qmap->to.lr.y = coords[13]; + qmap->to.ll.x = coords[14]; + qmap->to.ll.y = coords[15]; + break; + case 8: + /* 8 integers: assign only the "to" quadrilateral. */ + memset((void *)&qmap->from, 0, sizeof(Quad)); + qmap->to.ul.x = coords[0]; + qmap->to.ul.y = coords[1]; + qmap->to.ur.x = coords[2]; + qmap->to.ur.y = coords[3]; + qmap->to.lr.x = coords[4]; + qmap->to.lr.y = coords[5]; + qmap->to.ll.x = coords[6]; + qmap->to.ll.y = coords[7]; + break; + default: + /* Any other number of integers: issue an error message. */ + pm_error("failed to parse \"%s\" as a list of either 8 or 16 integers", + str); + break; + } + + free(str); +} + + + +static void +parseCommandLine(int argc, + const char ** const argv, + struct CmdlineInfo * const cmdlineP ) { +/*---------------------------------------------------------------------------- + Parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. +-----------------------------------------------------------------------------*/ + + optEntry *option_def; + /* Instructions to pm_optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int mapFileSpec = 0, fromSpec = 0, toSpec = 0; + unsigned int viewSpec = 0, fillColorSpec = 0; + char *mapFile, *from, *to, *view; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "mapfile", OPT_STRING, &mapFile, + &mapFileSpec, 0); + OPTENT3(0, "from", OPT_STRING, &from, + &fromSpec, 0); + OPTENT3(0, "to", OPT_STRING, &to, + &toSpec, 0); + OPTENT3(0, "view", OPT_STRING, &view, + &viewSpec, 0); + OPTENT3(0, "fill", OPT_STRING, &cmdlineP->fillColor, + &fillColorSpec, 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 local variables. */ + + if (!fillColorSpec) + cmdlineP->fillColor = NULL; + + memset((void *)&cmdlineP->qmap, 0, sizeof(QuadMap)); + if (mapFileSpec) + readMapFile(mapFile, &cmdlineP->qmap); + if (fromSpec) + parseQuadString(from, &cmdlineP->qmap.from); + if (toSpec) + parseQuadString(to, &cmdlineP->qmap.to); + if (!mapFileSpec && !fromSpec && !toSpec && !viewSpec) + pm_error("You must specify at least one of " + "-mapfile, -qin, -qout, and -view"); + if (viewSpec) + parseViewString(view, &cmdlineP->bbox); + else + memset((void *)&cmdlineP->bbox, 0, sizeof(Quad)); + + if (argc < 2) + cmdlineP->inputFilespec = "-"; + else if (argc == 2) + cmdlineP->inputFilespec = argv[1]; + else + pm_error("Too many non-option arguments: %u. " + "Only argument is input file name", argc - 1); + + free((void *) option_def); +} + + + +static tuple +parseFillColor(const struct pam * const pamP, + const struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Parse the fill color into the correct format for the given PAM metadata. +-----------------------------------------------------------------------------*/ + + tuple rgb; + tuple fillColor; + + if (!cmdlineP->fillColor) { + pnm_createBlackTuple(pamP, &fillColor); + return fillColor; + } + + rgb = pnm_parsecolor(cmdlineP->fillColor, pamP->maxval); + fillColor = pnm_allocpamtuple(pamP); + switch (pamP->depth) { + case 1: + /* Grayscale */ + fillColor[0] = (rgb[PAM_RED_PLANE]*299 + + rgb[PAM_GRN_PLANE]*587 + + rgb[PAM_BLU_PLANE]*114)/1000; + break; + case 2: + /* Grayscale + alpha */ + fillColor[0] = (rgb[PAM_RED_PLANE]*299 + + rgb[PAM_GRN_PLANE]*587 + + rgb[PAM_BLU_PLANE]*114)/1000; + fillColor[PAM_GRAY_TRN_PLANE] = pamP->maxval; + break; + case 3: + /* RGB */ + pnm_assigntuple(pamP, fillColor, rgb); + break; + case 4: + /* RGB + alpha */ + pnm_assigntuple(pamP, fillColor, rgb); + fillColor[PAM_TRN_PLANE] = pamP->maxval; + break; + default: + pm_error("unexpected image depth %d", pamP->depth); + break; + } + + return fillColor; +} + + + +static tuple ** +initOutputImage(const struct pam * const pamP, + const struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Allocate and initialize the output image. +-----------------------------------------------------------------------------*/ + + tuple fillColor; /* Fill color to use for unused coordinates */ + tuple ** outImg; /* Output image */ + unsigned int row; + + outImg = pnm_allocpamarray(pamP); + + fillColor = parseFillColor(pamP, cmdlineP); + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + + for (col = 0; col < pamP->width; ++col) { + pnm_assigntuple(pamP, outImg[row][col], fillColor); + } + } + + free((void *) fillColor); + return outImg; +} + + + +static void +computeSteps(const Quad * const qfrom, + const Quad * const qto, + double * const ustep, + double * const vstep) { +/*---------------------------------------------------------------------------- + Compute increments for u and v as these range from 0.0 to 1.0. +-----------------------------------------------------------------------------*/ + + double fx0, fx1, fxd; + double tx0, tx1, txd; + double fy0, fy1, fyd; + double ty0, ty1, tyd; + + /* Compute ustep as the inverse of the maximum possible x delta across + either the "from" or "to" quadrilateral. */ + fx0 = MIN4((double)qfrom->ur.x, + (double)qfrom->ul.x, + (double)qfrom->lr.x, + (double)qfrom->ll.x); + fx1 = MAX4((double)qfrom->ur.x, + (double)qfrom->ul.x, + (double)qfrom->lr.x, + (double)qfrom->ll.x); + fxd = fx1 - fx0; + tx0 = MIN4((double)qto->ur.x, + (double)qto->ul.x, + (double)qto->lr.x, + (double)qto->ll.x); + tx1 = MAX4((double)qto->ur.x, + (double)qto->ul.x, + (double)qto->lr.x, + (double)qto->ll.x); + txd = tx1 - tx0; + if (fxd == 0.0 && txd == 0.0) + *ustep = 1.0; /* Arbitrary nonzero step */ + *ustep = 0.5/MAX(fxd, txd); + /* Divide into 0.5 instead of 1.0 for additional smoothing. */ + + /* Compute vstep as the inverse of the maximum possible y delta across + either the "from" or "to" quadrilateral + . */ + fy0 = MIN4((double)qfrom->ur.y, + (double)qfrom->ul.y, + (double)qfrom->lr.y, + (double)qfrom->ll.y); + fy1 = MAX4((double)qfrom->ur.y, + (double)qfrom->ul.y, + (double)qfrom->lr.y, + (double)qfrom->ll.y); + fyd = fy1 - fy0; + ty0 = MIN4((double)qto->ur.y, + (double)qto->ul.y, + (double)qto->lr.y, + (double)qto->ll.y); + ty1 = MAX4((double)qto->ur.y, + (double)qto->ul.y, + (double)qto->lr.y, + (double)qto->ll.y); + tyd = ty1 - ty0; + if (fyd == 0.0 && tyd == 0.0) + *vstep = 1.0; /* Arbitrary nonzero step */ + *vstep = 0.5/MAX(fyd, tyd); + /* Divide into 0.5 instead of 1.0 for additional smoothing. */ +} + + + +static Quad * +prepareQuadrilateral(const struct pam * const pamP, + const Quad * const qdata) { +/*---------------------------------------------------------------------------- + If a quadrilateral has all zero points, replace it with a quadrilateral + of the full size of the image. The caller should free the result. +-----------------------------------------------------------------------------*/ + + Quad * qcopy; + + MALLOCVAR_NOFAIL(qcopy); + + if (qdata->ul.x == 0 && qdata->ul.y == 0 && + qdata->ur.x == 0 && qdata->ur.y == 0 && + qdata->ll.x == 0 && qdata->ll.y == 0 && + qdata->lr.x == 0 && qdata->lr.y == 0) { + /* Set the quadrilateral to the image's bounding box. */ + memset((void *)qcopy, 0, sizeof(Quad)); + qcopy->ur.x = pamP->width - 1; + qcopy->lr.x = pamP->width - 1; + qcopy->lr.y = pamP->height - 1; + qcopy->ll.y = pamP->height - 1; + } else { + /* Use the quadrilateral as specified. */ + memcpy(qcopy, qdata, sizeof(Quad)); + } + + return qcopy; +} + + +static void +coordsAtPercent(const Quad * const quad, + double const u, + double const v, + int * const x, + int * const y) { +/*---------------------------------------------------------------------------- + Return the (x, y) coordinates that lie at (u%, v%) from the upper left to + the lower right of a given quadrilateral. +-----------------------------------------------------------------------------*/ + + *x = (int) nearbyint((1.0 - u)*(1.0 - v)*quad->ul.x + + u*(1.0 - v)*quad->ur.x + + u*v*quad->lr.x + + (1.0 - u)*v*quad->ll.x); + *y = (int) nearbyint((1.0 - u)*(1.0 - v)*quad->ul.y + + u*(1.0 - v)*quad->ur.y + + u*v*quad->lr.y + + (1.0 - u)*v*quad->ll.y); +} + + + +static void +computeBoundingBox(const Quad * const q, + Quad * const bbox) { +/*---------------------------------------------------------------------------- + Compute the bounding box of a given quadrilateral. +-----------------------------------------------------------------------------*/ + + bbox->ul.x = bbox->ll.x = MIN4(q->ul.x, q->ur.x, q->lr.x, q->ll.x); + bbox->ul.y = bbox->ur.y = MIN4(q->ul.y, q->ur.y, q->lr.y, q->ll.y); + bbox->ur.x = bbox->lr.x = MAX4(q->ul.x, q->ur.x, q->lr.x, q->ll.x); + bbox->ll.y = bbox->lr.y = MAX4(q->ul.y, q->ur.y, q->lr.y, q->ll.y); +} + + + +static void +mapQuadrilaterals(const struct pam * const inPamP, + const struct pam * const outPamP, + const Quad * const qfrom, + const Quad * const qto, + tuple ** const inImg, + tuple ** const outImg, + int const xofs, + int const yofs) { +/*---------------------------------------------------------------------------- + Map the quadrilateral in the source image to the quadrilateral in the + target image. This is the function that implemens pamhomography's + primary functionality. +-----------------------------------------------------------------------------*/ + + sample ** channel; + /* Aggregated values for a single channel */ + unsigned long ** tally; + /* Number of values at each coordinate in the above */ + double ustep, vstep; + /* Steps to use when iterating from 0.0 to 1.0 */ + double u, v; + unsigned int plane, row, col; + + MALLOCARRAY2_NOFAIL(channel, outPamP->height, outPamP->width); + MALLOCARRAY2_NOFAIL(tally, outPamP->height, outPamP->width); + + computeSteps(qfrom, qto, &ustep, &vstep); + + for (plane = 0; plane < outPamP->depth; ++plane) { + /* Reset the channel colors and tally for each plane, */ + for (row = 0; row < outPamP->height; ++row) + for (col = 0; col < outPamP->width; ++col) { + channel[row][col] = 0; + tally[row][col] = 0; + } + + /* Iterate from 0% to 100% in the y dimension. */ + for (v = 0.0; v <= 1.0; v += vstep) { + /* Iterate from 0% to 100% in the x dimension. */ + for (u = 0.0; u <= 1.0; u += ustep) { + int x0, y0; /* "From" coordinate */ + int x1, y1; /* "To" coordinate */ + + /* Map (u%, v%) of one quadrilateral to (u%, v%) of the + other quadrilateral. */ + coordsAtPercent(qfrom, u, v, &x0, &y0); + coordsAtPercent(qto, u, v, &x1, &y1); + + /* Copy the source image's (x0, y0) to the destination + image's (x1, y1) in the current plane. */ + x1 += xofs; + y1 += yofs; + if (x0 >= 0 && y0 >= 0 && + x0 < inPamP->width && y0 < inPamP->height && + x1 >= 0 && y1 >= 0 && + x1 < outPamP->width && y1 < outPamP->height) { + channel[y1][x1] += inImg[y0][x0][plane]; + tally[y1][x1]++; + } + } + } + + /* Assign the current plane in the output image the average color + at each point. */ + for (row = 0; row < outPamP->height; ++row) + for (col = 0; col < outPamP->width; ++col) + if (tally[row][col] != 0) + outImg[row][col][plane] = + (channel[row][col] + tally[row][col]/2) / + tally[row][col]; + } + + pm_freearray2((void ** const)tally); + pm_freearray2((void ** const)channel); + free((void *)qto); + free((void *)qfrom); +} + + + +static void +processFile(FILE * const ifP, + const struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Read the input image, create the output image, and map a quadrilateral in + the former to a quadrilateral in the latter. +-----------------------------------------------------------------------------*/ + + struct pam inPam; /* PAM metadata for the input file */ + struct pam outPam; /* PAM metadata for the output file */ + tuple ** inImg; /* Input image */ + tuple ** outImg; /* Output image */ + Quad *qfrom, *qto; /* Source and target quadrilaterals */ + Quad bbox; /* Bounding box around the transformed input image */ + + inImg = pnm_readpam(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type)); + + /* Extract quadrilaterals and populate them with the image bounds + if necessary. */ + qfrom = prepareQuadrilateral(&inPam, &cmdlineP->qmap.from); + qto = prepareQuadrilateral(&inPam, &cmdlineP->qmap.to); + + /* Allocate storage for the target image. */ + if (cmdlineP->bbox.ul.x == 0 && cmdlineP->bbox.ul.y == 0 && + cmdlineP->bbox.lr.x == 0 && cmdlineP->bbox.lr.y == 0) + /* User did not specify a target bounding box. Compute optimal + dimensions. */ + computeBoundingBox(qto, &bbox); + else + /* User specified a target bounding box. Use it. */ + bbox = cmdlineP->bbox; + outPam = inPam; + outPam.file = stdout; + outPam.width = bbox.lr.x - bbox.ul.x + 1; + outPam.height = bbox.lr.y - bbox.ul.y + 1; + outImg = initOutputImage(&outPam, cmdlineP); + + mapQuadrilaterals(&inPam, &outPam, + qfrom, qto, + inImg, outImg, + -bbox.ul.x, -bbox.ul.y); + + pnm_writepam(&outPam, outImg); + + pnm_freepamarray(outImg, &outPam); + pnm_freepamarray(inImg, &inPam); +} + + + +int +main(int argc, const char *argv[]) { + + struct CmdlineInfo cmdline; /* Parsed command line */ + FILE * ifP; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + + processFile(ifP, &cmdline); + + pm_close(ifP); + + return 0; +} diff --git a/editor/pammixmulti.c b/editor/pammixmulti.c index f5012d7a..2b45d807 100644 --- a/editor/pammixmulti.c +++ b/editor/pammixmulti.c @@ -13,6 +13,7 @@ #include "shhopt.h" #include "mallocvar.h" #include "nstring.h" +#include "rand.h" typedef enum { BLEND_AVERAGE, /* Take the average color of all pixels */ @@ -36,6 +37,8 @@ struct ProgramState { /* Standard deviation when selecting images via a mask */ unsigned long ** imageWeights; /* Per-image weights as a function of grayscale level */ + struct pm_randSt randSt; + /* Random number generator parameters and internal state */ }; @@ -134,9 +137,9 @@ parseCommandLine(int argc, const char ** argv, } static void -openInputFiles(unsigned int const inFileCt, - const char ** const inFileName, - struct ProgramState * const stateP) { +initInput(unsigned int const inFileCt, + const char ** const inFileName, + struct ProgramState * const stateP) { /*---------------------------------------------------------------------------- Open all of the input files. @@ -180,6 +183,25 @@ openInputFiles(unsigned int const inFileCt, } + +static void +termInput(struct ProgramState * const stateP) { +/*---------------------------------------------------------------------------- + Deallocate all of the resources we allocated. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; i < stateP->inFileCt; ++i) { + pnm_freepamrow(stateP->inTupleRows[i]); + pm_close(stateP->inPam[i].file); + } + + free(stateP->inTupleRows); + free(stateP->inPam); +} + + + static void initMask(const char * const maskFileName, struct ProgramState * const stateP) { @@ -233,6 +255,15 @@ initOutput(FILE * const ofP, } +static void +termOutput(struct ProgramState * const stateP) { + + free(stateP->outTupleRow); + + pm_close(stateP->outPam.file); +} + + static void blendTuplesRandom(struct ProgramState * const stateP, @@ -243,8 +274,8 @@ blendTuplesRandom(struct ProgramState * const stateP, from a random input image. -----------------------------------------------------------------------------*/ unsigned int const depth = stateP->inPam[0].depth; - unsigned int const img = (unsigned int) (rand() % stateP->inFileCt); - + unsigned int const img = (unsigned int) (pm_rand(&stateP->randSt) % + stateP->inFileCt); unsigned int samp; for (samp = 0; samp < depth; ++samp) @@ -276,23 +307,26 @@ blendTuplesAverage(struct ProgramState * const stateP, +#if 0 static void -randomNormal2(double * const r1P, - double * const r2P) { +randomNormal2(double * const r1P, + double * const r2P, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- Return two normally distributed random numbers. -----------------------------------------------------------------------------*/ double u1, u2; do { - u1 = drand48(); - u2 = drand48(); + u1 = drand48(randStP); + u2 = drand48(randStP); } while (u1 <= DBL_EPSILON); *r1P = sqrt(-2.0*log(u1)) * cos(2.0*M_PI*u2); *r2P = sqrt(-2.0*log(u1)) * sin(2.0*M_PI*u2); } +#endif @@ -332,7 +366,8 @@ precomputeImageWeights(struct ProgramState * const stateP, double r[2]; unsigned int k; - randomNormal2(&r[0], &r[1]); + pm_gaussrand2(&stateP->randSt, &r[0], &r[1]); + for (k = 0; k < 2; ++k) { int const img = r[k] * sigma + pctGray * stateP->inFileCt * 0.999999; @@ -453,26 +488,6 @@ blendImages(BlendType const blend, -static void -termState(struct ProgramState * const stateP) { -/*---------------------------------------------------------------------------- - Deallocate all of the resources we allocated. ------------------------------------------------------------------------------*/ - unsigned int i; - - for (i = 0; i < stateP->inFileCt; ++i) { - pnm_freepamrow(stateP->inTupleRows[i]); - pm_close(stateP->inPam[i].file); - } - - free(stateP->outTupleRow); - free(stateP->inTupleRows); - free(stateP->inPam); - pm_close(stateP->outPam.file); -} - - - int main(int argc, const char * argv[]) { @@ -483,13 +498,14 @@ main(int argc, const char * argv[]) { parseCommandLine(argc, argv, &cmdline); - srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); - - openInputFiles(cmdline.inFileNameCt, cmdline.inFileName, &state); + initInput(cmdline.inFileNameCt, cmdline.inFileName, &state); if (cmdline.blend == BLEND_MASK) initMask(cmdline.maskfile, &state); + pm_randinit(&state.randSt); + pm_srand2(&state.randSt, cmdline.randomseedSpec, cmdline.randomseed); + initOutput(stdout, &state); if (cmdline.blend == BLEND_MASK) @@ -497,10 +513,14 @@ main(int argc, const char * argv[]) { blendImages(cmdline.blend, &state); + termOutput(&state); + + pm_randterm(&state.randSt); + if (cmdline.blend == BLEND_MASK) termMask(&state); - termState(&state); + termInput(&state); freeCmdline(&cmdline); diff --git a/editor/pamrecolor.c b/editor/pamrecolor.c index 8c5bce12..86c1965c 100644 --- a/editor/pamrecolor.c +++ b/editor/pamrecolor.c @@ -1,3 +1,4 @@ + /* ---------------------------------------------------------------------- * * Replace every pixel in an image with one of equal luminance @@ -32,6 +33,7 @@ #include "mallocvar.h" #include "nstring.h" +#include "rand.h" #include "shhopt.h" #include "pam.h" @@ -42,7 +44,7 @@ #define CLAMPxy(N, A, B) MAX(MIN((float)(N), (float)(B)), (float)(A)) -struct rgbfrac { +struct Rgbfrac { /* This structure represents red, green, and blue, each expressed as a fraction from 0.0 to 1.0. */ @@ -51,19 +53,19 @@ struct rgbfrac { float bfrac; }; -struct cmdlineInfo { +struct CmdlineInfo { /* This structure represents all of the information the user supplied in the command line but in a form that's easy for the program to use. */ const char * inputFileName; /* '-' if stdin */ const char * colorfile; /* NULL if unspecified */ - struct rgbfrac color2gray; + struct Rgbfrac color2gray; /* colorspace/rmult/gmult/bmult options. Negative numbers if unspecified. */ unsigned int targetcolorSpec; - struct rgbfrac targetcolor; + struct Rgbfrac targetcolor; unsigned int randomseed; unsigned int randomseedSpec; }; @@ -71,7 +73,7 @@ struct cmdlineInfo { static float -rgb2gray(struct rgbfrac * const color2grayP, +rgb2gray(struct Rgbfrac * const color2grayP, float const red, float const grn, float const blu) { @@ -122,7 +124,7 @@ getColorRow(struct pam * const pamP, static void convertRowToGray(struct pam * const pamP, - struct rgbfrac * const color2gray, + struct Rgbfrac * const color2gray, tuplen * const tupleRow, samplen * const grayRow) { /*---------------------------------------------------------------------- @@ -160,7 +162,7 @@ convertRowToGray(struct pam * const pamP, static void explicitlyColorRow(struct pam * const pamP, tuplen * const rowData, - struct rgbfrac const tint) { + struct Rgbfrac const tint) { unsigned int col; @@ -175,17 +177,25 @@ explicitlyColorRow(struct pam * const pamP, static void randomlyColorRow(struct pam * const pamP, - tuplen * const rowData) { + tuplen * const rowData, + bool const randomseedSpec, + unsigned int const randomseed) { /*---------------------------------------------------------------------- Assign each tuple in a row a random color. ------------------------------------------------------------------------*/ unsigned int col; + struct pm_randSt randSt; + + pm_randinit(&randSt); + pm_srand2(&randSt, randomseedSpec, randomseed); for (col = 0; col < pamP->width; ++col) { - rowData[col][PAM_RED_PLANE] = rand() / (float)RAND_MAX; - rowData[col][PAM_GRN_PLANE] = rand() / (float)RAND_MAX; - rowData[col][PAM_BLU_PLANE] = rand() / (float)RAND_MAX; + rowData[col][PAM_RED_PLANE] = pm_drand(&randSt); + rowData[col][PAM_GRN_PLANE] = pm_drand(&randSt); + rowData[col][PAM_BLU_PLANE] = pm_drand(&randSt); } + + pm_randterm(&randSt); } @@ -193,7 +203,7 @@ randomlyColorRow(struct pam * const pamP, static void recolorRow(struct pam * const inPamP, tuplen * const inRow, - struct rgbfrac * const color2grayP, + struct Rgbfrac * const color2grayP, tuplen * const colorRow, struct pam * const outPamP, tuplen * const outRow) { @@ -293,10 +303,10 @@ recolorRow(struct pam * const inPamP, -static struct rgbfrac +static struct Rgbfrac color2GrayFromCsName(const char * const csName) { - struct rgbfrac retval; + struct Rgbfrac retval; /* Thanks to http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html @@ -352,7 +362,7 @@ color2GrayFromCsName(const char * const csName) { static void parseCommandLine(int argc, const char ** const argv, - struct cmdlineInfo * const cmdlineP ) { + struct CmdlineInfo * const cmdlineP ) { optEntry * option_def; /* Instructions to OptParseOptions3 on how to parse our options */ @@ -445,7 +455,7 @@ parseCommandLine(int argc, const char ** const argv, int main(int argc, const char *argv[]) { - struct cmdlineInfo cmdline; /* Command-line parameters */ + struct CmdlineInfo cmdline; /* Command-line parameters */ struct pam inPam; struct pam outPam; struct pam colorPam; @@ -462,8 +472,6 @@ main(int argc, const char *argv[]) { parseCommandLine(argc, argv, &cmdline); - srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); - ifP = pm_openr(cmdline.inputFileName); inPam.comment_p = &comments; pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(comment_p)); @@ -474,7 +482,6 @@ main(int argc, const char *argv[]) { outPam.depth = 4 - (inPam.depth % 2); outPam.allocation_depth = outPam.depth; strcpy(outPam.tuple_type, PAM_PPM_TUPLETYPE); - pnm_writepaminit(&outPam); if (cmdline.colorfile) { colorfP = pm_openr(cmdline.colorfile); @@ -492,6 +499,8 @@ main(int argc, const char *argv[]) { colorRowBuffer = pnm_allocpamrown(&outPam); + pnm_writepaminit(&outPam); + for (row = 0; row < inPam.height; ++row) { tuplen * colorRow; @@ -505,7 +514,8 @@ main(int argc, const char *argv[]) { if (cmdline.targetcolorSpec) explicitlyColorRow(&colorPam, colorRow, cmdline.targetcolor); else - randomlyColorRow(&colorPam, colorRow); + randomlyColorRow(&colorPam, colorRow, + cmdline.randomseedSpec, cmdline.randomseed); } recolorRow(&inPam, inRow, &cmdline.color2gray, colorRow, diff --git a/editor/pamrubber.c b/editor/pamrubber.c index 602701ec..fda31203 100644 --- a/editor/pamrubber.c +++ b/editor/pamrubber.c @@ -1,20 +1,18 @@ -/*----------------------------------------------------------------------------*/ - -/* pamrubber.c - transform images using Rubber Sheeting algorithm -** see: http://www.schaik.com/netpbm/rubber/ -** -** Copyright (C) 2011 by Willem van Schaik (willem@schaik.com) -** -** Permission to use, copy, modify, and distribute this software and its -** documentation for any purpose and without fee is hereby granted, provided -** that the above copyright notice appear in all copies and that both that -** copyright notice and this permission notice appear in supporting -** documentation. This software is provided "as is" without express or -** implied warranty. -*/ - -/*----------------------------------------------------------------------------*/ - +/*============================================================================= + pamrubber +=============================================================================== + Transform images using Rubber Sheeting algorithm + See: http://www.schaik.com/netpbm/rubber/ + + Copyright (C) 2011 by Willem van Schaik (willem@schaik.com) + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. This software is provided "as is" without express or + implied warranty. +=============================================================================*/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -25,12 +23,12 @@ #include "pm_c_util.h" #include "mallocvar.h" +#include "rand.h" #include "shhopt.h" #include "pam.h" #include "pamdraw.h" - typedef struct { double x; double y; @@ -54,7 +52,7 @@ typedef struct { point br; /* bottom right */ } quadrilateral; -struct cmdlineInfo { +struct CmdlineInfo { unsigned int nCP; point oldCP[4]; point newCP[4]; @@ -64,14 +62,14 @@ struct cmdlineInfo { unsigned int frame; unsigned int linear; unsigned int verbose; - unsigned int randseedSpec; - unsigned int randseed; + unsigned int randomseedSpec; + unsigned int randomseed; }; static void parseCmdline(int argc, const char ** argv, - struct cmdlineInfo * const cmdlineP) { + struct CmdlineInfo * const cmdlineP) { /* parse all parameters from the command line */ @@ -92,10 +90,10 @@ parseCmdline(int argc, const char ** argv, OPTENT3(0, "frame", OPT_FLAG, NULL, &cmdlineP->frame, 0); OPTENT3(0, "linear", OPT_FLAG, NULL, &cmdlineP->linear, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); - OPTENT3(0, "randseed", OPT_UINT, &cmdlineP->randseed, - &cmdlineP->randseedSpec, 0); - OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randseed, - &cmdlineP->randseedSpec, 0); + OPTENT3(0, "randseed", OPT_UINT, &cmdlineP->randomseed, + &cmdlineP->randomseedSpec, 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 */ @@ -339,28 +337,29 @@ windtriangle(triangle * const tP, static double -tiny(void) { +tiny(struct pm_randSt * const randStP) { - if (rand() % 2) - return +1E-6 * (double) ((rand() % 90) + 9); + if (pm_rand(randStP) % 2) + return +1E-6 * (double) ((pm_rand(randStP) % 90) + 9); else - return -1E-6 * (double) ((rand() % 90) + 9); + return -1E-6 * (double) ((pm_rand(randStP) % 90) + 9); } static void angle(point * const p1P, - point * const p2P) { + point * const p2P, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- Move *p2P slightly if necessary to make sure the line (*p1P, *p2P) is not horizontal or vertical. -----------------------------------------------------------------------------*/ if (p1P->x == p2P->x) { /* vertical line */ - p2P->x += tiny(); + p2P->x += tiny(randStP); } if (p1P->y == p2P->y) { /* horizontal line */ - p2P->y += tiny(); + p2P->y += tiny(randStP); } } @@ -720,8 +719,10 @@ static void drawClippedTriangle(const struct pam * const pamP, static void -prepTrig(int const wd, - int const ht) { +prepTrig(int const wd, + int const ht, + bool const randomseedSpec, + unsigned int const randomseed) { /* create triangles using control points */ @@ -731,16 +732,20 @@ prepTrig(int const wd, point c2p1, c2p2, c2p3, c2p4; line l1, l2; point p0; + struct pm_randSt randSt; + + pm_randinit(&randSt); + pm_srand2(&randSt, randomseedSpec, randomseed); - rtl1 = makepoint(0.0 + tiny(), 0.0 + tiny()); - rtr1 = makepoint((double) wd - 1.0 + tiny(), 0.0 + tiny()); - rbl1 = makepoint(0.0 + tiny(), (double) ht - 1.0 + tiny()); - rbr1 = makepoint((double) wd - 1.0 + tiny(), (double) ht - 1.0 + tiny()); + rtl1 = makepoint(0.0 + tiny(&randSt), 0.0 + tiny(&randSt)); + rtr1 = makepoint((double) wd - 1.0 + tiny(&randSt), 0.0 + tiny(&randSt)); + rbl1 = makepoint(0.0 + tiny(&randSt), (double) ht - 1.0 + tiny(&randSt)); + rbr1 = makepoint((double) wd - 1.0 + tiny(&randSt), (double) ht - 1.0 + tiny(&randSt)); - rtl2 = makepoint(0.0 + tiny(), 0.0 + tiny()); - rtr2 = makepoint((double) wd - 1.0 + tiny(), 0.0 + tiny()); - rbl2 = makepoint(0.0 + tiny(), (double) ht - 1.0 + tiny()); - rbr2 = makepoint((double) wd - 1.0 + tiny(), (double) ht - 1.0 + tiny()); + rtl2 = makepoint(0.0 + tiny(&randSt), 0.0 + tiny(&randSt)); + rtr2 = makepoint((double) wd - 1.0 + tiny(&randSt), 0.0 + tiny(&randSt)); + rbl2 = makepoint(0.0 + tiny(&randSt), (double) ht - 1.0 + tiny(&randSt)); + rbr2 = makepoint((double) wd - 1.0 + tiny(&randSt), (double) ht - 1.0 + tiny(&randSt)); if (nCP == 1) { c1p1 = oldCP[0]; @@ -772,8 +777,8 @@ prepTrig(int const wd, c2p2 = newCP[1]; /* check for hor/ver edges */ - angle (&c1p1, &c1p2); - angle (&c2p1, &c2p2); + angle (&c1p1, &c1p2, &randSt); + angle (&c2p1, &c2p2, &randSt); /* connect two control points to corners to get 6 triangles */ /* left side */ @@ -811,13 +816,13 @@ prepTrig(int const wd, /* Move vertices slightly if necessary to make sure no edge is horizontal or vertical. */ - angle(&c1p1, &c1p2); - angle(&c1p2, &c1p3); - angle(&c1p3, &c1p1); + angle(&c1p1, &c1p2, &randSt); + angle(&c1p2, &c1p3, &randSt); + angle(&c1p3, &c1p1, &randSt); - angle(&c2p1, &c2p2); - angle(&c2p2, &c2p3); - angle(&c2p3, &c2p1); + angle(&c2p1, &c2p2, &randSt); + angle(&c2p2, &c2p3, &randSt); + angle(&c2p3, &c2p1, &randSt); if (windtriangle(&tri1s[0], c1p1, c1p2, c1p3)) { tri2s[0] = maketriangle(c2p1, c2p2, c2p3); @@ -871,19 +876,19 @@ prepTrig(int const wd, c2p4 = newCP[3]; /* check for hor/ver edges */ - angle (&c1p1, &c1p2); - angle (&c1p2, &c1p3); - angle (&c1p3, &c1p4); - angle (&c1p4, &c1p1); - angle (&c1p1, &c1p3); - angle (&c1p2, &c1p4); - - angle (&c2p1, &c2p2); - angle (&c2p2, &c2p3); - angle (&c2p3, &c2p4); - angle (&c2p4, &c2p1); - angle (&c2p1, &c2p3); - angle (&c2p2, &c2p4); + angle (&c1p1, &c1p2, &randSt); + angle (&c1p2, &c1p3, &randSt); + angle (&c1p3, &c1p4, &randSt); + angle (&c1p4, &c1p1, &randSt); + angle (&c1p1, &c1p3, &randSt); + angle (&c1p2, &c1p4, &randSt); + + angle (&c2p1, &c2p2, &randSt); + angle (&c2p2, &c2p3, &randSt); + angle (&c2p3, &c2p4, &randSt); + angle (&c2p4, &c2p1, &randSt); + angle (&c2p1, &c2p3, &randSt); + angle (&c2p2, &c2p4, &randSt); /*-------------------------------------------------------------------*/ /* -1- -2- -3- -4- -5- -6- */ @@ -979,6 +984,8 @@ prepTrig(int const wd, &tri2s[9], c2p3, c2p1, rtl2, rtr2, rbl2, rbr2); nTri = 10; } + + pm_randterm(&randSt); } @@ -1277,7 +1284,7 @@ warpQuad(point const p2, static void -setGlobalCP(struct cmdlineInfo const cmdline) { +setGlobalCP(struct CmdlineInfo const cmdline) { unsigned int i; @@ -1392,7 +1399,7 @@ pix(tuple ** const tuples, int main(int argc, const char ** const argv) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; FILE * ifP; struct pam inpam, outpam; tuple ** inTuples; @@ -1405,8 +1412,6 @@ main(int argc, const char ** const argv) { setGlobalCP(cmdline); - srand(cmdline.randseedSpec ? cmdline.randseed : pm_randseed()); - ifP = pm_openr(cmdline.fileName); inTuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); @@ -1421,7 +1426,8 @@ main(int argc, const char ** const argv) { makeAllWhite(&outpam, outTuples); if (cmdline.tri) - prepTrig(inpam.width, inpam.height); + prepTrig(inpam.width, inpam.height, + cmdline.randomseedSpec, cmdline.randomseed); if (cmdline.quad) prepQuad(); diff --git a/editor/pbmreduce.c b/editor/pbmreduce.c index 3a0968fe..70caa581 100644 --- a/editor/pbmreduce.c +++ b/editor/pbmreduce.c @@ -11,9 +11,11 @@ */ #include "pm_c_util.h" -#include "pbm.h" #include "mallocvar.h" +#include "rand.h" #include "shhopt.h" +#include "pbm.h" + #include <assert.h> #define SCALE 1024 @@ -105,7 +107,7 @@ parseCommandLine(int argc, const char ** argv, unsigned int scale; scale = strtol(argv[1], &endptr, 10); - if (*argv[1] == '\0') + if (*argv[1] == '\0') pm_error("Scale argument is a null string. Must be a number."); else if (*endptr != '\0') pm_error("Scale argument contains non-numeric character '%c'.", @@ -115,7 +117,7 @@ parseCommandLine(int argc, const char ** argv, "You specified %d", scale); else if (scale > INT_MAX / scale) pm_error("Scale argument too large. You specified %d", scale); - else + else cmdlineP->scale = scale; if (argc-1 > 1) { @@ -145,10 +147,11 @@ struct FS { static void initializeFloydSteinberg(struct FS * const fsP, int const newcols, - unsigned int const seed, - bool const seedSpec) { + bool const seedSpec, + unsigned int const seed) { unsigned int col; + struct pm_randSt randSt; MALLOCARRAY(fsP->thiserr, newcols + 2); MALLOCARRAY(fsP->nexterr, newcols + 2); @@ -156,33 +159,36 @@ initializeFloydSteinberg(struct FS * const fsP, if (fsP->thiserr == NULL || fsP->nexterr == NULL) pm_error("out of memory"); - srand(seedSpec ? seed : pm_randseed()); + pm_randinit(&randSt); + pm_srand2(&randSt, seedSpec, seed); for (col = 0; col < newcols + 2; ++col) - fsP->thiserr[col] = (rand() % SCALE - HALFSCALE) / 4; + fsP->thiserr[col] = ((int) (pm_rand(&randSt) % SCALE) - HALFSCALE) / 4; /* (random errors in [-SCALE/8 .. SCALE/8]) */ + + pm_randterm(&randSt); } /* Scanning method - + In Floyd-Steinberg dithering mode horizontal direction of scan alternates between rows; this is called "serpentine scanning". - + Example input (14 x 7), N=3: - + 111222333444xx Fractional pixels on the right edge and bottom edge (x) - 111222333444xx are ignored; their values do not influence output. + 111222333444xx are ignored; their values do not influence output. 111222333444xx 888777666555xx 888777666555xx 888777666555xx xxxxxxxxxxxxxx - + Output (4 x 2): - + 1234 8765 @@ -196,11 +202,14 @@ enum Direction { RIGHT_TO_LEFT, LEFT_TO_RIGHT }; static enum Direction oppositeDir(enum Direction const arg) { + enum Direction retval; + switch (arg) { - case LEFT_TO_RIGHT: return RIGHT_TO_LEFT; - case RIGHT_TO_LEFT: return LEFT_TO_RIGHT; + case LEFT_TO_RIGHT: retval = RIGHT_TO_LEFT; break; + case RIGHT_TO_LEFT: retval = LEFT_TO_RIGHT; break; } - assert(false); /* All cases handled above */ + + return retval; } @@ -240,7 +249,7 @@ main(int argc, const char * argv[]) { if (cmdline.halftone == QT_FS) initializeFloydSteinberg(&fs, newcols, - cmdline.randomseed, cmdline.randomseedSpec); + cmdline.randomseedSpec, cmdline.randomseed); else { /* These variables are meaningless in this case, and the values should never be used. @@ -258,10 +267,10 @@ main(int argc, const char * argv[]) { int limitCol; int startCol; int step; - + for (colChar = 0; colChar < colChars; ++colChar) newbitrow[colChar] = 0x00; /* Clear to white */ - + for (subrow = 0; subrow < cmdline.scale; ++subrow) pbm_readpbmrow(ifP, bitslice[subrow], cols, format); @@ -274,7 +283,7 @@ main(int argc, const char * argv[]) { case LEFT_TO_RIGHT: { startCol = 0; limitCol = newcols; - step = +1; + step = +1; } break; case RIGHT_TO_LEFT: { startCol = newcols - 1; diff --git a/editor/pnmflip b/editor/pnmflip index 07d4ddb9..962198a2 100755 --- a/editor/pnmflip +++ b/editor/pnmflip @@ -46,6 +46,13 @@ exec perl -w -x -S -- "$0" "$@" use strict; use File::Basename; use Cwd 'abs_path'; +use IO::Handle; + +sub pm_message($) { + STDERR->print("pnmflip: $_[0]\n"); +} + + my $xformOpt; my @miscOptions; @@ -78,8 +85,7 @@ foreach (@ARGV) { } else { # It's a parameter if (defined($infile)) { - print(STDERR - "You may specify at most one non-option parameter.\n"); + pm_message("You may specify at most one non-option parameter."); exit(10); } else { $infile = $_; diff --git a/editor/pnmquant b/editor/pnmquant index f7af9e7a..0bebce69 100755 --- a/editor/pnmquant +++ b/editor/pnmquant @@ -37,9 +37,16 @@ use Getopt::Long; use File::Spec; #use Fcntl ":seek"; # not available in Perl 5.00503 use Fcntl; # gets open flags +use IO::Handle; my ($TRUE, $FALSE) = (1,0); +sub pm_message($) { + STDERR->print("pnmquant: $_[0]\n"); +} + + + my ($SEEK_SET, $SEEK_CUR, $SEEK_END) = (0, 1, 2); @@ -102,17 +109,16 @@ sub parseCommandLine(@) { "plain"); if (!$optsAreValid) { - print(STDERR "Invalid option syntax.\n"); + pm_message("Invalid option syntax"); exit(1); } if (@ARGV > 2) { - print(STDERR "This program takes at most 2 arguments. You specified ", - scalar(@ARGV), "\n"); + pm_message("This program takes at most 2 arguments. You specified " . + scalar(@ARGV)); exit(1); } elsif (@ARGV < 1) { - print(STDERR - "You must specify the number of colors as an argument.\n"); + pm_message("You must specify the number of colors as an argument."); exit(1); } my $infile; @@ -120,9 +126,8 @@ sub parseCommandLine(@) { if (!($cmdline{ncolors} =~ m{ ^[[:digit:]]+$ }x ) || $cmdline{ncolors} == 0) { - print(STDERR - "Number of colors argument '$cmdline{ncolors}' " . - "is not a positive integer.\n"); + pm_message("Number of colors argument '$cmdline{ncolors}' " . + "is not a positive integer."); exit(1); } @@ -210,8 +215,8 @@ sub makeColormap($$$$$$$) { my ($mapfileFh, $mapfileSpec) = tempFile(".pnm"); if (!defined($mapfileFh)) { - print(STDERR "Unable to create temporary file for colormap. " . - "errno = $ERRNO\n"); + pm_message("Unable to create temporary file for colormap. " . + "errno = $ERRNO"); exit(1); } @@ -223,8 +228,8 @@ sub makeColormap($$$$$$$) { (defined($opt_center) ? 1 : 0); if ($colorSummaryOptCt > 1) { - print(STDERR "You can specify only one of " . - "-meanpixel, -meancolor, and -center\n"); + pm_message("You can specify only one of " . + "-meanpixel, -meancolor, and -center"); exit(1); } if (defined($opt_meanpixel)) { @@ -240,8 +245,8 @@ sub makeColormap($$$$$$$) { (defined($opt_spreadbrightness) ? 1 : 0); if ($spreadOptCt > 1) { - print(STDERR "You can specify only one of " . - "-spreadluminosity and -spreadbrightness\n"); + pm_message("You can specify only one of " . + "-spreadluminosity and -spreadbrightness"); exit(1); } @@ -263,7 +268,7 @@ sub makeColormap($$$$$$$) { my $maprc = system("pnmcolormap", $ncolors, @options); if ($maprc != 0) { - print(STDERR "pnmcolormap failed, rc=$maprc\n"); + pm_message("pnmcolormap failed, rc=$maprc"); exit(1); } return $mapfileSpec; @@ -287,15 +292,15 @@ sub remap($$$$$$) { } if ($opt_norandom) { if (defined($opt_randomseed)) { - print(STDERR "You cannot specify -randomseed with -norandom\n"); + pm_message("You cannot specify -randomseed with -norandom"); exit(1); } push(@options, "-norandom"); } if (defined($opt_randomseed)) { if ($opt_randomseed < 0) { - print(STDERR "-randomseed value must not be negative. " . - "You specified $opt_randomseed\n"); + pm_message("-randomseed value must not be negative. " . + "You specified $opt_randomseed"); exit(10); } push(@options, "-randomseed=$opt_randomseed"); @@ -310,7 +315,7 @@ sub remap($$$$$$) { my $remaprc = system("pnmremap", "-mapfile=$mapfileSpec", @options); if ($remaprc != 0) { - print(STDERR "pnmremap failed, rc=$remaprc\n"); + pm_message("pnmremap failed, rc=$remaprc"); exit(1); } } diff --git a/editor/pnmquantall b/editor/pnmquantall index aea6cc84..80d00fce 100755 --- a/editor/pnmquantall +++ b/editor/pnmquantall @@ -57,11 +57,18 @@ use warnings; use English; use Fcntl; # gets open flags use File::Copy; +use IO::Handle; my $TRUE=1; my $FALSE = 0; +sub pm_message($) { + STDERR->print("pnmquantall: $_[0]\n"); +} + + + sub doVersionHack($) { my ($argvR) = @_; @@ -84,7 +91,7 @@ sub parseArgs($$$$) { if (@argv > 0 && $argv[0] eq "-ext") { if (@argv < 2) { - print STDERR ("-ext requires a value\n"); + pm_message("-ext requires a value"); exit(100); } else { $$extR = $argv[1]; @@ -96,8 +103,8 @@ sub parseArgs($$$$) { } if (@argv < $firstArgPos + 2) { - print STDERR ("Not enough arguments. You need at least the number " . - "of colors and one file name\n"); + pm_message("Not enough arguments. You need at least the number " . + "of colors and one file name"); exit(100); } @@ -212,7 +219,7 @@ if (!$progError) { my $exitStatus; if ($progError) { - print STDERR ("Failed. $progError\n"); + pm_message("Failed. $progError"); $exitStatus = 1; } else { $exitStatus = 0; diff --git a/editor/pnmremap.c b/editor/pnmremap.c index 0c0096ba..e5b59d04 100644 --- a/editor/pnmremap.c +++ b/editor/pnmremap.c @@ -28,6 +28,7 @@ #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" +#include "rand.h" #include "shhopt.h" #include "pam.h" #include "ppm.h" @@ -399,20 +400,21 @@ randomizeError(long ** const err, Set a random error in the range [-1 .. 1] (normalized via FS_SCALE) in the error array err[][]. -----------------------------------------------------------------------------*/ - unsigned int const seed = (random.init == RANDOM_WITHSEED) ? - random.seed : pm_randseed(); - unsigned int col; + struct pm_randSt randSt; assert(random.init != RANDOM_NONE); - srand(seed); + pm_randinit(&randSt); + pm_srand2(&randSt, random.init == RANDOM_WITHSEED, random.seed); for (col = 0; col < width; ++col) { unsigned int plane; for (plane = 0; plane < depth; ++plane) - err[plane][col] = rand() % (FS_SCALE * 2) - FS_SCALE; + err[plane][col] = pm_rand(&randSt) % (FS_SCALE * 2) - FS_SCALE; } + + pm_randterm(&randSt); } diff --git a/editor/specialty/pampaintspill.c b/editor/specialty/pampaintspill.c index c7994723..5cd482d5 100644 --- a/editor/specialty/pampaintspill.c +++ b/editor/specialty/pampaintspill.c @@ -45,11 +45,11 @@ #include "mallocvar.h" #include "nstring.h" +#include "rand.h" #include "shhopt.h" #include "pam.h" #include "pammap.h" - static time_t const timeUpdateDelta = 30; /* Seconds between progress updates */ static int const minUpdates = 4; @@ -67,6 +67,8 @@ struct cmdlineInfo { unsigned int all; float power; unsigned int downsample; + unsigned int randomseedSpec; + unsigned int randomseed; }; struct coords { @@ -98,16 +100,18 @@ parseCommandLine(int argc, const char ** const argv, MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* Incremented by OPTENTRY */ - OPTENT3(0, "bgcolor", OPT_STRING, &cmdlineP->bgcolor, + OPTENT3(0, "bgcolor", OPT_STRING, &cmdlineP->bgcolor, &bgcolorSpec, 0); OPTENT3(0, "wrap", OPT_FLAG, NULL, &cmdlineP->wrap, 0); OPTENT3(0, "all", OPT_FLAG, NULL, &cmdlineP->all, 0); - OPTENT3(0, "power", OPT_FLOAT, &cmdlineP->power, + OPTENT3(0, "power", OPT_FLOAT, &cmdlineP->power, &powerSpec, 0); - OPTENT3(0, "downsample", OPT_UINT, &cmdlineP->downsample, + OPTENT3(0, "downsample", OPT_UINT, &cmdlineP->downsample, &downsampleSpec, 0); + OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, + &cmdlineP->randomseedSpec, 0); opt.opt_table = option_def; opt.short_allowed = 0; @@ -223,7 +227,9 @@ locatePaintSources(struct pam * const pamP, tuple ** const tuples, tuple const bgColor, unsigned int const downsample, - struct paintSourceSet * const paintSourcesP) { + struct paintSourceSet * const paintSourcesP, + bool const randomseedSpec, + unsigned int const randomseed) { /*-------------------------------------------------------------------- Construct a list of all pixel coordinates in the input image that represent a non-background color. @@ -248,21 +254,24 @@ locatePaintSources(struct pam * const pamP, pm_message("Image contains %u background + %u non-background pixels", pamP->width * pamP->height - paintSources.size, paintSources.size); - + /* Reduce the number of paint sources to reduce execution time. */ if (downsample > 0 && downsample < paintSources.size) { + struct pm_randSt randSt; unsigned int i; - srand(pm_randseed()); + pm_randinit(&randSt); + pm_srand2(&randSt, randomseedSpec, randomseed); for (i = 0; i < downsample; ++i) { unsigned int const swapIdx = - i + rand() % (paintSources.size - i); + i + pm_rand(&randSt) % (paintSources.size - i); struct coords const swapVal = paintSources.list[i]; paintSources.list[i] = paintSources.list[swapIdx]; paintSources.list[swapIdx] = swapVal; } + pm_randterm(&randSt); paintSources.size = downsample; } @@ -426,7 +435,7 @@ produceOutputImage(struct pam * const pamP, for (row = 0; row < pamP->height; ++row) { struct coords target; double * newColor; - + MALLOCARRAY(newColor, pamP->depth); target.y = row; @@ -484,7 +493,8 @@ main(int argc, const char *argv[]) { pnm_colorname(&inPam, bgColor, PAM_COLORNAME_HEXOK)); locatePaintSources(&inPam, inTuples, bgColor, cmdline.downsample, - &paintSources); + &paintSources, + cmdline.randomseedSpec, cmdline.randomseed); produceOutputImage(&inPam, inTuples, bgColor, paintSources, distFunc, cmdline.power, cmdline.all, &outTuples); @@ -498,3 +508,5 @@ main(int argc, const char *argv[]) { return 0; } + + diff --git a/editor/specialty/ppmshift.c b/editor/specialty/ppmshift.c index cdb0f173..27cbb78c3 100644 --- a/editor/specialty/ppmshift.c +++ b/editor/specialty/ppmshift.c @@ -12,11 +12,11 @@ #include <stdbool.h> #include "mallocvar.h" +#include "rand.h" #include "shhopt.h" #include "ppm.h" - struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. @@ -24,6 +24,7 @@ struct CmdlineInfo { const char * inputFileName; unsigned int shift; + unsigned int seedSpec; unsigned int seed; }; @@ -42,8 +43,6 @@ parseCommandLine(int argc, const char ** const argv, unsigned int option_def_index; - unsigned int seedSpec; - MALLOCARRAY(option_def, 100); opt.opt_table = option_def; @@ -52,14 +51,11 @@ parseCommandLine(int argc, const char ** const argv, option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "seed", OPT_UINT, &cmdlineP->seed, - &seedSpec, 0); + &cmdlineP->seedSpec, 0); pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ - if (!seedSpec) - cmdlineP->seed = pm_randseed(); - if (argc-1 < 1) pm_error("You must specify the shift factor as an argument"); else { @@ -84,6 +80,62 @@ parseCommandLine(int argc, const char ** const argv, +static void +shiftRow(pixel * const srcrow, + unsigned int const cols, + unsigned int const shift, + pixel * const destrow, + struct pm_randSt * const randStP) { + + /* the range by which a line is shifted lays in the range from */ + /* -shift/2 .. +shift/2 pixels; however, within this range it is */ + /* randomly chosen */ + + pixel * pP; + pixel * pP2; + int nowshift; + + if (shift != 0) + nowshift = (pm_rand(randStP) % (shift+1)) - ((shift+1) / 2); + else + nowshift = 0; + + pP = &srcrow[0]; + pP2 = &destrow[0]; + + /* if the shift value is less than zero, we take the original + pixel line and copy it into the destination line translated + to the left by x pixels. The empty pixels on the right end + of the destination line are filled up with the pixel that + is the right-most in the original pixel line. + */ + if (nowshift < 0) { + unsigned int col; + pP += abs(nowshift); + for (col = 0; col < cols; ++col) { + PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP)); + ++pP2; + if (col < (cols + nowshift) - 1) + ++pP; + } + } else { + unsigned int col; + /* The shift value is 0 or positive, so fill the first + <nowshift> pixels of the destination line with the + first pixel from the source line, and copy the rest of + the source line to the dest line + */ + for (col = 0; col < cols; ++col) { + PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP)); + ++pP2; + if (col >= nowshift) + ++pP; + } + } +} + + + int main(int argc, const char ** argv) { @@ -95,13 +147,15 @@ main(int argc, const char ** argv) { pixel * destrow; unsigned int row; unsigned int shift; + struct pm_randSt randSt; /* parse in 'default' parameters */ pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); - srand(cmdline.seed); + pm_randinit(&randSt); + pm_srand2(&randSt, cmdline.seedSpec, cmdline.seed); ifP = pm_openr(cmdline.inputFileName); @@ -115,67 +169,23 @@ main(int argc, const char ** argv) { } else shift = cmdline.shift; - srcrow = ppm_allocrow(cols); - + srcrow = ppm_allocrow(cols); destrow = ppm_allocrow(cols); ppm_writeppminit(stdout, cols, rows, maxval, 0); - /** now do the shifting **/ - /* the range by which a line is shifted lays in the range from */ - /* -shift/2 .. +shift/2 pixels; however, within this range it is */ - /* randomly chosen */ for (row = 0; row < rows; ++row) { - pixel * pP; - pixel * pP2; - unsigned int nowshift; - - if (shift != 0) - nowshift = (rand() % (shift+1)) - ((shift+1) / 2); - else - nowshift = 0; - ppm_readppmrow(ifP, srcrow, cols, maxval, format); - pP = &srcrow[0]; - pP2 = &destrow[0]; - - /* if the shift value is less than zero, we take the original - pixel line and copy it into the destination line translated - to the left by x pixels. The empty pixels on the right end - of the destination line are filled up with the pixel that - is the right-most in the original pixel line. - */ - if (nowshift < 0) { - unsigned int col; - pP += abs(nowshift); - for (col = 0; col < cols; ++col) { - PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP)); - ++pP2; - if (col < (cols + nowshift) - 1) - ++pP; - } - } else { - unsigned int col; - /* The shift value is 0 or positive, so fill the first - <nowshift> pixels of the destination line with the - first pixel from the source line, and copy the rest of - the source line to the dest line - */ - for (col = 0; col < cols; ++col) { - PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP)); - ++pP2; - if (col >= nowshift) - ++pP; - } - } + shiftRow(srcrow, cols, shift, destrow, &randSt); ppm_writeppmrow(stdout, destrow, cols, maxval, 0); } - pm_close(ifP); - ppm_freerow(srcrow); ppm_freerow(destrow); + ppm_freerow(srcrow); + pm_close(ifP); + pm_randterm(&randSt); return 0; } diff --git a/editor/specialty/ppmspread.c b/editor/specialty/ppmspread.c index 6753f4fe..7b9558e3 100644 --- a/editor/specialty/ppmspread.c +++ b/editor/specialty/ppmspread.c @@ -10,102 +10,158 @@ #include <string.h> +#include "nstring.h" +#include "rand.h" +#include "shhopt.h" #include "ppm.h" +struct CmdlineInfo { + /* This structure represents all of the information the user + supplied in the command line but in a form that's easy for the + program to use. + */ + const char * inputFilename; /* '-' if stdin */ + unsigned int spread; + unsigned int randomseedSpec; + unsigned int randomseed; +}; + + + +static void +parseCommandLine(int argc, const char ** const argv, + struct CmdlineInfo * const cmdlineP ) { + + optEntry * option_def; + /* Instructions to OptParseOptions3 on how to parse our options */ + optStruct3 opt; + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + option_def_index = 0; /* Incremented by OPTENTRY */ + + OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, + &cmdlineP->randomseedSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = 0; + opt.allowNegNum = 1; + + pm_optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 ); + + if (argc-1 < 1) + pm_error("You must specify the spread factor as an argument"); + else { + const char * error; + pm_string_to_uint(argv[1], &cmdlineP->spread, &error); + + if (error) + pm_error("Spread factor '%s' is not an unsigned integer. %s", + argv[1], error); + + if (argc-1 < 2) + cmdlineP->inputFilename = "-"; + else { + cmdlineP->inputFilename = argv[2]; + if (argc-1 >2) + pm_error("Too many arguments: %u. " + "The only possible arguments are " + "the spread factor and the optional input file name", + argc-1); + } + } +} + + + +static void +spreadRow(pixel ** const srcarray, + unsigned int const cols, + unsigned int const rows, + unsigned int const spread, + unsigned int const row, + pixel ** const destarray, + struct pm_randSt * const randStP) { + + unsigned int col; + + for (col = 0; col < cols; ++col) { + pixel const p = srcarray[row][col]; + + int const xdis = (pm_rand(randStP) % (spread + 1) ) + - ((spread + 1) / 2); + int const ydis = (pm_rand(randStP) % (spread + 1)) + - ((spread + 1) / 2); + + int const xnew = col + xdis; + int const ynew = row + ydis; + + /* only set the displaced pixel if it's within the bounds + of the image + */ + if (xnew >= 0 && xnew < cols && ynew >= 0 && ynew < rows) { + /* Displacing a pixel is accomplished by swapping it + with another pixel in its vicinity. + */ + pixel const p2 = srcarray[ynew][xnew]; + /* Original value of second pixel */ + + /* Set second pixel to new value */ + PPM_ASSIGN(destarray[ynew][xnew], + PPM_GETR(p), PPM_GETG(p), PPM_GETB(p)); + + /* Set first pixel to (old) value of second */ + PPM_ASSIGN(destarray[row][col], + PPM_GETR(p2), PPM_GETG(p2), PPM_GETB(p2)); + } else { + /* Displaced pixel is out of bounds; leave the old pixel there. + */ + PPM_ASSIGN(destarray[row][col], + PPM_GETR(p), PPM_GETG(p), PPM_GETB(p)); + } + } +} + + int -main(int argc, - char * argv[]) { +main(int argc, + const char * argv[]) { + struct CmdlineInfo cmdline; FILE * ifP; - int argn, rows, cols; + int rows, cols; unsigned int row; - pixel ** destarray, ** srcarray; - pixel * pP; - pixel * pP2; + pixel ** destarray; + pixel ** srcarray; pixval maxval; - pixval r1, g1, b1; - int amount; - const char * const usage = "amount [ppmfile]\n amount: # of pixels to displace a pixel by at most\n"; - - /* parse in 'default' parameters */ - ppm_init(&argc, argv); - - argn = 1; - - /* parse in amount & seed */ - if (argn == argc) - pm_usage(usage); - if (sscanf(argv[argn], "%d", &amount) != 1) - pm_usage(usage); - if (amount < 0) - pm_error("amount should be a positive number"); - ++argn; - - /* parse in filename (if present, stdin otherwise) */ - if (argn != argc) - { - ifP = pm_openr(argv[argn]); - ++argn; - } - else - ifP = stdin; + struct pm_randSt randSt; - if (argn != argc) - pm_usage(usage); + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilename); srcarray = ppm_readppm(ifP, &cols, &rows, &maxval); destarray = ppm_allocarray(cols, rows); + pm_randinit(&randSt); + pm_srand2(&randSt, cmdline.randomseedSpec, cmdline.randomseed); + /* clear out the buffer */ for (row = 0; row < rows; ++row) memset(destarray[row], 0, cols * sizeof(pixel)); - srand(pm_randseed()); - - /* start displacing pixels */ + /* Displace pixels */ for (row = 0; row < rows; ++row) { - unsigned int col; - pP = &srcarray[row][0]; - - for (col = 0; col < cols; ++col) { - int const xdis = (rand() % (amount+1)) - ((amount+1) / 2); - int const ydis = (rand() % (amount+1)) - ((amount+1) / 2); + spreadRow(srcarray, cols, rows, cmdline.spread, row, + destarray, &randSt); - int const xnew = col + xdis; - int const ynew = row + ydis; - - /* only set the displaced pixel if it's within the bounds - of the image - */ - if (xnew >= 0 && xnew < cols && ynew >= 0 && ynew < rows) { - /* displacing a pixel is accomplished by swapping it - with another pixel in its vicinity - so, first - store other pixel's RGB - */ - pP2 = &srcarray[ynew][xnew]; - r1 = PPM_GETR(*pP2); - g1 = PPM_GETG(*pP2); - b1 = PPM_GETB(*pP2); - /* set second pixel to new value */ - pP2 = &destarray[ynew][xnew]; - PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP)); - - /* now, set first pixel to (old) value of second */ - pP2 = &destarray[row][col]; - PPM_ASSIGN(*pP2, r1, g1, b1); - } else { - /* displaced pixel is out of bounds; leave the old - pixel there - */ - pP2 = &destarray[row][col]; - PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP)); - } - ++pP; - } } + pm_randterm(&randSt); ppm_writeppm(stdout, destarray, cols, rows, maxval, 0); @@ -115,3 +171,5 @@ main(int argc, return 0; } + + diff --git a/generator/pamcrater.c b/generator/pamcrater.c index 43c27dbc..b8ceafa5 100644 --- a/generator/pamcrater.c +++ b/generator/pamcrater.c @@ -48,6 +48,7 @@ #include "pm_c_util.h" #include "mallocvar.h" +#include "rand.h" #include "shhopt.h" #include "nstring.h" #include "pam.h" @@ -169,11 +170,12 @@ static double const DepthBias2 = 0.5; /* Square of depth bias */ static double const -cast(double const high) { +cast(double const high, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- A random number in the range [0, 'high']. -----------------------------------------------------------------------------*/ - return high * ((rand() & 0x7FFF) / arand); + return high * ((pm_rand(randStP) & 0x7FFF) / arand); } @@ -252,11 +254,12 @@ setElev(struct pam * const pamP, static void -smallCrater(struct pam * const pamP, - tuple ** const terrain, - int const cx, - int const cy, - double const radius) { +smallCrater(struct pam * const pamP, + tuple ** const terrain, + int const cx, + int const cy, + double const radius, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- Generate a crater with a special method for tiny craters. @@ -283,10 +286,10 @@ smallCrater(struct pam * const pamP, /* 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; + int const x = radius >= 1 ? ((pm_rand(randStP) >> 8) & 0x3) - 1 : 0; assert(axelev > 0); @@ -374,7 +377,7 @@ normalCrater(struct pam * const pamP, av = (axelev + cz) * (1 - roll) + (terrainMod(pamP, terrain, x, y) + cz) * roll; av = MAX(1000, MIN(64000, av)); - + setElev(pamP, terrain, x, y, av); } } @@ -388,19 +391,20 @@ normalCrater(struct pam * const pamP, static void -plopCrater(struct pam * const pamP, - tuple ** const terrain, - int const cx, - int const cy, - double const radius, - bool const verbose) { +plopCrater(struct pam * const pamP, + tuple ** const terrain, + int const cx, + int const cy, + double const radius, + bool const verbose, + struct pm_randSt * const randStP) { 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); + smallCrater (pamP, terrain, cx, cy, radius, randStP); else normalCrater(pamP, terrain, cx, cy, radius); } @@ -448,6 +452,7 @@ genCraters(struct CmdlineInfo const cmdline) { -----------------------------------------------------------------------------*/ tuple ** terrain; /* elevation array */ struct pam pam; + struct pm_randSt randSt; /* Allocate the elevation array and initialize it to mean surface elevation. @@ -455,18 +460,22 @@ genCraters(struct CmdlineInfo const cmdline) { initCanvas(cmdline.width, cmdline.height, &pam, &terrain); + pm_randinit(&randSt); + pm_srand2(&randSt, cmdline.randomseedSpec, cmdline.randomseed); + if (cmdline.test) plopCrater(&pam, terrain, pam.width/2 + cmdline.offset, pam.height/2 + cmdline.offset, - (double) cmdline.radius, cmdline.verbose); + (double) cmdline.radius, cmdline.verbose, + &randSt); 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); + int const cx = cast((double) pam.width - 1, &randSt); + int const cy = cast((double) pam.height - 1, &randSt); /* Thanks, Rudy, for this equation that maps the uniformly distributed numbers from cast() into an area-law distribution @@ -475,9 +484,11 @@ genCraters(struct CmdlineInfo const cmdline) { Produces values within the interval: 0.56419 <= radius <= 56.419 */ - double const radius = sqrt(1 / (M_PI * (1 - cast(0.9999)))); + double const radius = + sqrt(1 / (M_PI * (1 - cast(0.9999, &randSt)))); - plopCrater(&pam, terrain, cx, cy, radius, cmdline.verbose); + plopCrater(&pam, terrain, cx, cy, radius, + cmdline.verbose, &randSt); if (((l + 1) % 100000) == 0) pm_message("%u craters generated of %u (%u%% done)", @@ -485,6 +496,8 @@ genCraters(struct CmdlineInfo const cmdline) { } } + pm_randterm(&randSt); + pnm_writepam(&pam, terrain); pnm_freepamarray(terrain, &pam); @@ -503,12 +516,9 @@ main(int argc, const char ** argv) { parseCommandLine(argc, argv, &cmdline); - srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); - genCraters(cmdline); return 0; } - diff --git a/generator/pamstereogram.c b/generator/pamstereogram.c index 9b861b51..6e64b127 100644 --- a/generator/pamstereogram.c +++ b/generator/pamstereogram.c @@ -16,7 +16,7 @@ * * ---------------------------------------------------------------------- * - * Copyright (C) 2006-2020 Scott Pakin <scott+pbm@pakin.org> + * Copyright (C) 2006-2021 Scott Pakin <scott+pbm@pakin.org> * * All rights reserved. * @@ -58,11 +58,12 @@ #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" +#include "rand.h" #include "shhopt.h" #include "pam.h" -enum outputType {OUTPUT_BW, OUTPUT_GRAYSCALE, OUTPUT_COLOR}; +enum OutputType {OUTPUT_BW, OUTPUT_GRAYSCALE, OUTPUT_COLOR}; /* ---------------------------------------------------------------------- */ @@ -85,13 +86,14 @@ struct cmdlineInfo { unsigned int magnifypat; /* -magnifypat option */ int xshift; /* -xshift option */ int yshift; /* -yshift option */ + int yfillshift; /* -yfillshift option */ const char * patfile; /* -patfile option. Null if none */ const char * texfile; /* -texfile option. Null if none */ const char * bgcolor; /* -bgcolor option */ unsigned int smoothing; /* -smoothing option */ unsigned int randomseed; /* -randomseed option */ unsigned int randomseedSpec; /* -randomseed option count */ - enum outputType outputType; /* Type of output file */ + enum OutputType outputType; /* Type of output file */ unsigned int xbegin; /* -xbegin option */ unsigned int xbeginSpec; /* -xbegin option count */ unsigned int tileable; /* -tileable option */ @@ -153,8 +155,8 @@ parseCommandLine(int argc, unsigned int option_def_index; unsigned int patfileSpec, texfileSpec, dpiSpec, eyesepSpec, depthSpec, - guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec, - bgcolorSpec, smoothingSpec, planesSpec; + guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec, yfillshiftSpec, + bgcolorSpec, smoothingSpec, planesSpec; unsigned int blackandwhite, grayscale, color; const char ** planes; @@ -194,6 +196,8 @@ parseCommandLine(int argc, &xshiftSpec, 0); OPTENT3(0, "yshift", OPT_INT, &cmdlineP->yshift, &yshiftSpec, 0); + OPTENT3(0, "yfillshift", OPT_INT, &cmdlineP->yfillshift, + &yfillshiftSpec, 0); OPTENT3(0, "patfile", OPT_STRING, &cmdlineP->patfile, &patfileSpec, 0); OPTENT3(0, "texfile", OPT_STRING, &cmdlineP->texfile, @@ -282,9 +286,10 @@ parseCommandLine(int argc, if (!xshiftSpec) cmdlineP->xshift = 0; - if (!yshiftSpec) cmdlineP->yshift = 0; + if (!yfillshiftSpec) + cmdlineP->yfillshift = 0; if (xshiftSpec && !cmdlineP->patfile) pm_error("-xshift is valid only with -patfile"); @@ -402,11 +407,12 @@ typedef struct outGenerator { -struct randomState { +struct RandomState { /* The state of a randomColor generator. */ unsigned int magnifypat; tuple * currentRow; unsigned int prevy; + struct pm_randSt * randStP; }; @@ -421,7 +427,7 @@ randomColor(outGenerator * const outGenP, /*---------------------------------------------------------------------------- Return a random RGB value. -----------------------------------------------------------------------------*/ - struct randomState * const stateP = outGenP->stateP; + struct RandomState * const stateP = outGenP->stateP; /* Every time we start a new row, we select a new sequence of random colors. @@ -436,7 +442,7 @@ randomColor(outGenerator * const outGenP, unsigned int plane; for (plane = 0; plane < outGenP->pam.depth; ++plane) { - unsigned int const randval = rand(); + unsigned int const randval = pm_rand(stateP->randStP); thisTuple[plane] = randval % modulus; } } @@ -456,9 +462,13 @@ static outGenStateTerm termRandomColor; static void termRandomColor(outGenerator * const outGenP) { - struct randomState * const stateP = outGenP->stateP; + struct RandomState * const stateP = outGenP->stateP; pnm_freepamrow(stateP->currentRow); + + pm_randterm(stateP->randStP); + + free(stateP->randStP); } @@ -468,7 +478,7 @@ initRandomColor(outGenerator * const outGenP, const struct pam * const inPamP, struct cmdlineInfo const cmdline) { - struct randomState * stateP; + struct RandomState * stateP; outGenP->pam.format = PAM_FORMAT; outGenP->pam.plainformat = 0; @@ -499,6 +509,10 @@ initRandomColor(outGenerator * const outGenP, stateP->magnifypat = cmdline.magnifypat; stateP->prevy = (unsigned int)(-cmdline.magnifypat); + MALLOCVAR_NOFAIL(stateP->randStP); + pm_randinit(stateP->randStP); + pm_srand2(stateP->randStP, cmdline.randomseedSpec, cmdline.randomseed); + outGenP->stateP = stateP; outGenP->getTuple = &randomColor; outGenP->terminateState = &termRandomColor; @@ -1178,6 +1192,8 @@ static void makeImageRow(outGenerator * const outGenP, unsigned int const row, unsigned int const xbegin, + unsigned int const farWidth, + int const yfillshift, const unsigned int * const sameL, const unsigned int * const sameR, const tuple * const outRow) { @@ -1200,20 +1216,26 @@ makeImageRow(outGenerator * const outGenP, sameL[N] > N is not allowed. -----------------------------------------------------------------------------*/ - int col; + unsigned int const width = outGenP->pam.width; + unsigned int const height = outGenP->pam.height; + + unsigned int col; int lastLinked; - for (col = (int)xbegin, lastLinked = INT_MIN; - col < outGenP->pam.width; - ++col) { + for (col = xbegin, lastLinked = -1; col < width; ++col) { tuple newtuple; - if (sameL[col] == col || sameL[col] < (int)xbegin) { + if (sameL[col] == col || sameL[col] < xbegin) { if (lastLinked == col - 1) newtuple = outRow[col - 1]; - else - newtuple = outGenP->getTuple(outGenP, col, row); + else { + if (col < xbegin + farWidth) + newtuple = outGenP->getTuple(outGenP, col, row); + else + newtuple = outGenP->getTuple( + outGenP, col, (row + height - yfillshift) % height); + } } else { newtuple = outRow[sameL[col]]; lastLinked = col; @@ -1222,20 +1244,25 @@ makeImageRow(outGenerator * const outGenP, pnm_assigntuple(&outGenP->pam, outRow[col], newtuple); } - for (col = (int)xbegin - 1, lastLinked = INT_MIN; col >= 0; --col) { + for (col = xbegin, lastLinked = -1; col > 0; --col) { tuple newtuple; - if (sameR[col] == col) { - if (lastLinked == col + 1) - newtuple = outRow[col + 1]; - else - newtuple = outGenP->getTuple(outGenP, col, row); + if (sameR[col-1] == col-1) { + if (lastLinked == col) + newtuple = outRow[col]; + else { + if (col > xbegin - farWidth) + newtuple = outGenP->getTuple(outGenP, col-1, row); + else + newtuple = outGenP->getTuple( + outGenP, col-1, (row + height - yfillshift) % height); + } } else { - newtuple = outRow[sameR[col]]; - lastLinked = col; + newtuple = outRow[sameR[col-1]]; + lastLinked = col-1; /* Keep track of the last pixel to be constrained. */ } - pnm_assigntuple(&outGenP->pam, outRow[col], newtuple); + pnm_assigntuple(&outGenP->pam, outRow[col-1], newtuple); } } @@ -1261,6 +1288,8 @@ makeOneImageRow(unsigned int const row, const unsigned int * const sameR, unsigned int * const colNumBuffer, unsigned int const xbegin, + unsigned int const farWidth, + int const yfillshift, tuple * const rowBuffer, tuple * const outRow) { @@ -1272,7 +1301,7 @@ makeOneImageRow(unsigned int const row, rowBuffer, outRow); else makeImageRow(outputGeneratorP, row, - xbegin, sameL, sameR, outRow); + xbegin, farWidth, yfillshift, sameL, sameR, outRow); } } @@ -1284,6 +1313,8 @@ constructRowTileable(const struct pam * const inPamP, bool const makeMask, const unsigned int * const sameL, const unsigned int * const sameR, + unsigned int const farWidth, + int const yfillshift, unsigned int const row, tuple * const outRow, tuple * const outRowBuf1, @@ -1300,10 +1331,10 @@ constructRowTileable(const struct pam * const inPamP, xbegin=maximum case. */ makeOneImageRow(row, outputGeneratorP, makeMask, sameL, sameR, colNumBuf2, - 0, outRowBuf2, outRow); + farWidth, yfillshift, 0, outRowBuf2, outRow); makeOneImageRow(row, outputGeneratorP, makeMask, sameL, sameR, colNumBuf2, - inPamP->width - 1, outRowBuf2, outRowMax); + farWidth, yfillshift, inPamP->width - 1, outRowBuf2, outRowMax); for (col = 0; col < inPamP->width; ++col) { unsigned int plane; @@ -1362,6 +1393,8 @@ doRow(const struct pam * const inPamP, unsigned int const magnifypat, unsigned int const smoothing, unsigned int const xbegin, + unsigned int const farWidth, + int const yfillshift, unsigned int const row, tuple * const inRow, tuple * const outRowBuf0, @@ -1397,13 +1430,16 @@ doRow(const struct pam * const inPamP, /* Construct a single row. */ if (tileable) { constructRowTileable(inPamP, outputGeneratorP, makeMask, - sameL, sameR, row, outRow, + sameL, sameR, + farWidth, yfillshift, + row, outRow, outRowBuf1, outRowBuf2, colNumBuf2); } else { makeOneImageRow(row, outputGeneratorP, makeMask, sameL, sameR, colNumBuf2, - xbegin, outRowBuf1, outRow); + xbegin, farWidth, yfillshift, + outRowBuf1, outRow); } /* Write the resulting row. */ @@ -1423,7 +1459,8 @@ makeImageRows(const struct pam * const inPamP, bool const tileable, unsigned int const magnifypat, unsigned int const smoothing, - unsigned int const xbegin) { + unsigned int const xbegin, + int const yfillshift) { tuple * inRow; /* Buffer for use in reading from the height-map image */ tuple * outRowBuf0; /* Buffer for use in generating output rows */ @@ -1433,6 +1470,8 @@ makeImageRows(const struct pam * const inPamP, unsigned int * colNumBuf1; unsigned int * colNumBuf2; unsigned int row; /* Current row in the input and output files */ + unsigned int pixelEyesep; + unsigned int farWidth; inRow = pnm_allocpamrow(inPamP); outRowBuf0 = pnm_allocpamrow(&outputGeneratorP->pam); @@ -1451,10 +1490,13 @@ makeImageRows(const struct pam * const inPamP, pm_error("Unable to allocate space for %u column buffer", inPamP->width); + pixelEyesep = ROUNDU(eyesep * dpi); + farWidth = pixelEyesep/(magnifypat * 2); + for (row = 0; row < inPamP->height; ++row) { doRow(inPamP, outputGeneratorP, depthOfField, eyesep, dpi, crossEyed, makeMask, tileable, magnifypat, smoothing, xbegin, - row, + farWidth, yfillshift, row, inRow, outRowBuf0, outRowBuf1, outRowBuf2, colNumBuf0, colNumBuf1, colNumBuf2); } @@ -1510,7 +1552,8 @@ produceStereogram(FILE * const ifP, makeImageRows(&inPam, outputGeneratorP, cmdline.depth, cmdline.eyesep, cmdline.dpi, cmdline.crosseyed, cmdline.makemask, cmdline.tileable, - cmdline.magnifypat, cmdline.smoothing, xbegin); + cmdline.magnifypat, cmdline.smoothing, xbegin, + cmdline.yfillshift); if (cmdline.guidebottom) drawguides(cmdline.guidesize, &outputGeneratorP->pam, @@ -1567,8 +1610,6 @@ main(int argc, const char *argv[]) { if (cmdline.verbose) reportParameters(cmdline); - srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); - ifP = pm_openr(cmdline.inputFilespec); /* Produce a stereogram. */ @@ -1580,4 +1621,3 @@ main(int argc, const char *argv[]) { } - diff --git a/generator/pgmcrater b/generator/pgmcrater index c66f5576..6d81df39 100755 --- a/generator/pgmcrater +++ b/generator/pgmcrater @@ -36,6 +36,16 @@ exec perl -w -x -S -- "$0" "$@" use strict; use Getopt::Long; +use IO::Handle; + + + +sub pm_message($) { + STDERR->print("pgmcrater: $_[0]\n"); +} + + + sub doVersionHack($) { my ($argvR) = @_; @@ -69,7 +79,7 @@ my $validOptions = GetOptions( 'randomseed=i' => \my $randomseedOpt); if (!$validOptions) { - print STDERR "Invalid syntax\n"; + pm_message("Invalid syntax"); exit(100); } diff --git a/generator/pgmnoise.c b/generator/pgmnoise.c index e3e4eca6..ea35956b 100644 --- a/generator/pgmnoise.c +++ b/generator/pgmnoise.c @@ -7,12 +7,17 @@ #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" +#include "rand.h" #include "shhopt.h" #include "pgm.h" +/* constants */ +static unsigned long int const ceil31bits = 0x7fffffffUL; +static unsigned long int const ceil32bits = 0xffffffffUL; -struct cmdlineInfo { + +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ @@ -21,13 +26,15 @@ struct cmdlineInfo { unsigned int maxval; unsigned int randomseed; unsigned int randomseedSpec; + unsigned int verbose; }; static void -parseCommandLine(int argc, const char ** const argv, - struct cmdlineInfo * const cmdlineP) { +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. @@ -46,6 +53,8 @@ parseCommandLine(int argc, const char ** const argv, &cmdlineP->randomseedSpec, 0); OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -57,7 +66,7 @@ parseCommandLine(int argc, const char ** const argv, if (maxvalSpec) { if (cmdlineP->maxval > PGM_OVERALLMAXVAL) - pm_error("Maxval too large: %u. Maximu is %u", + pm_error("Maxval too large: %u. Maximum is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL); else if (cmdlineP->maxval == 0) pm_error("Maxval must not be zero"); @@ -95,38 +104,43 @@ parseCommandLine(int argc, const char ** const argv, static unsigned int -randPool(unsigned int const digits) { +randPool(unsigned int const nDigits, + struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- - 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. + Draw 'nDigits' bits from pool of random bits. If the number of random bits + in pool is insufficient, call pm_rand() and add N bits to it. + + N is 31 or 32. In raw mode we use N = 32 regardless of the actual number of + available bits. If there are only 31 available, we use zero for the MSB. - 'digits' must be at most 16. + 'nDigits' must be at most 16. - We assume that each call to rand() generates 31 bits, or RAND_MAX == - 2147483647. + We assume that each call to pm_rand() generates 31 or 32 bits, or + randStP->max == 2147483647 or 4294967295. - The underlying logic is flexible and endian-free. The above conditions - can be relaxed. + 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 const mask = (1 << nDigits) - 1; + unsigned int const randbits = (randStP->max == ceil31bits) ? 31 : 32; unsigned int retval; - assert(RAND_MAX == 2147483647 && digits <= 16); + assert(randStP->max == ceil31bits || randStP->max == ceil32bits); + assert(nDigits <= 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(); + if (len > nDigits) { /* Enough bits in hold to satisfy request */ + hold >>= nDigits; + len -= nDigits; + } else { /* Load another 31 or 32 bits into hold */ + hold = pm_rand(randStP); retval |= (hold << len); - hold >>= (digits - len); - len = 31 - digits + len; + hold >>= (nDigits - len); + len = randbits - nDigits + len; } return (retval & mask); } @@ -134,36 +148,61 @@ randPool(unsigned int const digits) { static void -pgmnoise(FILE * const ofP, - unsigned int const cols, - unsigned int const rows, - gray const maxval) { +reportVerbose(struct pm_randSt * const randStP, + gray const maxval, + bool const usingPool) { + + pm_message("random seed: %u", randStP->seed); + pm_message("random max: %u maxval: %u", randStP->max, maxval); + pm_message("method: %s", usingPool ? "pool" : "modulo"); +} + + - bool const usingPool = !(RAND_MAX==2147483647 && (maxval & (maxval+1))); +static void +pgmnoise(FILE * const ofP, + unsigned int const cols, + unsigned int const rows, + gray const maxval, + bool const verbose, + struct pm_randSt * const randStP) { + + bool const usingPool = + (randStP->max==ceil31bits || randStP->max==ceil32bits) && + !(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. + Otherwise call pm_rand() and determine gray value by modulo. In the latter case, there is a minuscule 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. + skew will result. 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. + probability for values 1, 2, 3, 4 is 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. + + The pool method is economical. But there is an additional merit: + No bits are lost this way. This gives us a means to check the + integrity of the random number generator. + + - Akira Urushibata, March 2021 */ + if (verbose) + reportVerbose(randStP, maxval, usingPool); + destrow = pgm_allocrow(cols); pgm_writepgminit(ofP, cols, rows, maxval, 0); @@ -172,12 +211,11 @@ pgmnoise(FILE * const ofP, if (usingPool) { unsigned int col; for (col = 0; col < cols; ++col) - destrow[col] = randPool(bitLen); - } - else { + destrow[col] = randPool(bitLen, randStP); + } else { unsigned int col; for (col = 0; col < cols; ++col) - destrow[col] = rand() % (maxval + 1); + destrow[col] = pm_rand(randStP) % (maxval + 1); } pgm_writepgmrow(ofP, destrow, cols, maxval, 0); } @@ -191,18 +229,22 @@ int main(int argc, const char * argv[]) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; + struct pm_randSt randSt; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); - srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); + pm_randinit(&randSt); + pm_srand2(&randSt, cmdline.randomseedSpec, cmdline.randomseed); + + pgmnoise(stdout, cmdline.width, cmdline.height, cmdline.maxval, + cmdline.verbose, &randSt); - pgmnoise(stdout, cmdline.width, cmdline.height, cmdline.maxval); + pm_randterm(&randSt); return 0; } - diff --git a/generator/ppmforge.c b/generator/ppmforge.c index 114f7f18..7cef571f 100644 --- a/generator/ppmforge.c +++ b/generator/ppmforge.c @@ -34,12 +34,14 @@ #define _XOPEN_SOURCE 500 /* get M_PI in math.h */ #include <math.h> +#include <float.h> #include <assert.h> #include "pm_c_util.h" -#include "ppm.h" #include "mallocvar.h" +#include "rand.h" #include "shhopt.h" +#include "ppm.h" static double const hugeVal = 1e50; @@ -59,12 +61,8 @@ typedef struct { /* Definition for obtaining random numbers. */ -#define nrand 4 /* Gauss() sample count */ -#define Cast(low, high) ((low)+(((high)-(low)) * ((rand() & 0x7FFF) / arand))) - /* Local variables */ -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 */ @@ -172,6 +170,11 @@ parseCommandLine(int argc, const char **argv, if (cmdlineP->dimension <= 0.0) pm_error("-dimension must be greater than zero. " "You specified %f", cmdlineP->dimension); + else if (cmdlineP->dimension > 5.0 + FLT_EPSILON) + pm_error("-dimension must not be greater than 5. " + "Results are not interesting with higher numbers, so " + "we assume it is a mistake. " + "You specified %f", cmdlineP->dimension); } else cmdlineP->dimension = cmdlineP->clouds ? 2.15 : 2.4; @@ -335,45 +338,73 @@ fourn(float * const data, #undef SWAP +struct Gauss { + struct pm_randSt randSt; + unsigned int nrand; /* Gauss() sample count */ + double arand; + double gaussadd; + double gaussfac; +}; + + static void -initgauss(unsigned int const seed) { +initgauss(struct Gauss * const gaussP, + unsigned int const seed) { /*---------------------------------------------------------------------------- Initialize random number generators. As given in Peitgen & Saupe, page 77. -----------------------------------------------------------------------------*/ + gaussP->nrand = 4; + /* Range of random generator */ - arand = pow(2.0, 15.0) - 1.0; - gaussadd = sqrt(3.0 * nrand); - gaussfac = 2 * gaussadd / (nrand * arand); - srand(seed); + gaussP->arand = pow(2.0, 15.0) - 1.0; + gaussP->gaussadd = sqrt(3.0 * gaussP->nrand); + gaussP->gaussfac = 2 * gaussP->gaussadd / (gaussP->nrand * gaussP->arand); + + pm_randinit(&gaussP->randSt); + pm_srand(&gaussP->randSt, seed); } static double -gauss() { +gauss(struct Gauss * const gaussP) { /*---------------------------------------------------------------------------- A Gaussian random number. As given in Peitgen & Saupe, page 77. -----------------------------------------------------------------------------*/ - int i; + unsigned int i; double sum; - for (i = 1, sum = 0.0; i <= nrand; ++i) { - sum += (rand() & 0x7FFF); + for (i = 1, sum = 0.0; i <= gaussP->nrand; ++i) { + sum += (pm_rand(&gaussP->randSt) & 0x7FFF); } - return gaussfac * sum - gaussadd; + return gaussP->gaussfac * sum - gaussP->gaussadd; +} + + + +static double +cast(double const low, + double const high, + struct Gauss * const gaussP) { + + return + low + + ((high-low) * ((pm_rand(&gaussP->randSt) & 0x7FFF) / gaussP->arand)); + } static void -spectralsynth(float ** const x, - unsigned int const n, - double const h) { +spectralsynth(float ** const x, + unsigned int const n, + double const h, + struct Gauss * const gaussP) { /*---------------------------------------------------------------------------- Spectrally synthesized fractal motion in two dimensions. @@ -395,9 +426,10 @@ spectralsynth(float ** const x, for (i = 0; i <= n / 2; i++) { for (j = 0; j <= n / 2; j++) { - phase = 2 * M_PI * ((rand() & 0x7FFF) / arand); + phase = 2 * M_PI * ((pm_rand(&gaussP->randSt) & 0x7FFF) / + gaussP->arand); if (i != 0 || j != 0) { - rad = pow((double) (i * i + j * j), -(h + 1) / 2) * gauss(); + rad = pow((double) (i*i + j*j), -(h + 1) / 2) * gauss(gaussP); } else { rad = 0; } @@ -416,8 +448,9 @@ spectralsynth(float ** const x, Imag(a, n / 2, n / 2) = 0; for (i = 1; i <= n / 2 - 1; i++) { for (j = 1; j <= n / 2 - 1; j++) { - phase = 2 * M_PI * ((rand() & 0x7FFF) / arand); - rad = pow((double) (i * i + j * j), -(h + 1) / 2) * gauss(); + phase = 2 * M_PI * ((pm_rand(&gaussP->randSt) & 0x7FFF) / + gaussP->arand); + rad = pow((double) (i * i + j * j), -(h + 1) / 2) * gauss(gaussP); rcos = rad * cos(phase); rsin = rad * sin(phase); Real(a, i, n - j) = rcos; @@ -469,22 +502,22 @@ temprgb(double const temp, static void -etoile(pixel * const pix) { +etoile(pixel * const pix, + struct Gauss * const gaussP) { /*---------------------------------------------------------------------------- Set a pixel in the starry sky. -----------------------------------------------------------------------------*/ - if ((rand() % 1000) < starfraction) { -#define StarQuality 0.5 /* Brightness distribution exponent */ -#define StarIntensity 8 /* Brightness scale factor */ -#define StarTintExp 0.5 /* Tint distribution exponent */ - double v = StarIntensity * pow(1 / (1 - Cast(0, 0.9999)), - (double) StarQuality), - temp, - r, g, b; - - if (v > 255) { - v = 255; - } + if ((pm_rand(&gaussP->randSt) % 1000) < starfraction) { + double const starQuality = 0.5; + /* Brightness distribution exponent */ + double const starIntensity = 8; + /* Brightness scale factor */ + double const starTintExp = 0.5; + /* Tint distribution exponent */ + double const v = + MIN(255.0, + starIntensity * pow(1 / (1 - cast(0, 0.9999, gaussP)), + (double) starQuality)); /* We make a special case for star color of zero in order to prevent floating point roundoff which would otherwise @@ -493,13 +526,17 @@ etoile(pixel * const pix) { 256 shades in the image. */ if (starcolor == 0) { - int vi = v; + pixval const vi = v; PPM_ASSIGN(*pix, vi, vi, vi); } else { + double temp; + double r, g, b; + temp = 5500 + starcolor * - pow(1 / (1 - Cast(0, 0.9999)), StarTintExp) * - ((rand() & 7) ? -1 : 1); + pow(1 / (1 - cast(0, 0.9999, gaussP)), starTintExp) * + ((pm_rand(&gaussP->randSt) & 7) ? -1 : 1); + /* Constrain temperature to a reasonable value: >= 2600K (S Cephei/R Andromedae), <= 28,000 (Spica). */ temp = MAX(2600, MIN(28000, temp)); @@ -575,7 +612,8 @@ createPlanetStuff(bool const clouds, unsigned char ** const cpP, Vector * const sunvecP, unsigned int const cols, - pixval const maxval) { + pixval const maxval, + struct Gauss * const gaussP) { double *u, *u1; unsigned int *bxf, *bxc; @@ -585,8 +623,8 @@ createPlanetStuff(bool const clouds, /* Compute incident light direction vector. */ - shang = hourspec ? hourangle : Cast(0, 2 * M_PI); - siang = inclspec ? inclangle : Cast(-M_PI * 0.12, M_PI * 0.12); + shang = hourspec ? hourangle : cast(0, 2 * M_PI, gaussP); + siang = inclspec ? inclangle : cast(-M_PI * 0.12, M_PI * 0.12, gaussP); sunvecP->x = sin(shang) * cos(siang); sunvecP->y = sin(siang); @@ -594,7 +632,7 @@ createPlanetStuff(bool const clouds, /* Allow only 25% of random pictures to be crescents */ - if (!hourspec && ((rand() % 100) < 75)) { + if (!hourspec && ((pm_rand(&gaussP->randSt) % 100) < 75)) { flipped = (sunvecP->z < 0); sunvecP->z = fabs(sunvecP->z); } else @@ -634,7 +672,7 @@ createPlanetStuff(bool const clouds, pm_error("Cannot allocate %u element interpolation tables.", cols); { unsigned int j; - for (j = 0; j < cols; j++) { + for (j = 0; j < cols; ++j) { double const bx = (n - 1) * uprj(j, cols); bxf[j] = floor(bx); @@ -651,8 +689,9 @@ createPlanetStuff(bool const clouds, static void -generateStarrySkyRow(pixel * const pixels, - unsigned int const cols) { +generateStarrySkyRow(pixel * const pixels, + unsigned int const cols, + struct Gauss * const gaussP) { /*---------------------------------------------------------------------------- Generate a starry sky. Note that no FFT is performed; the output is generated directly from a power law @@ -660,8 +699,8 @@ generateStarrySkyRow(pixel * const pixels, -----------------------------------------------------------------------------*/ unsigned int j; - for (j = 0; j < cols; j++) - etoile(pixels + j); + for (j = 0; j < cols; ++j) + etoile(pixels + j, gaussP); } @@ -881,7 +920,8 @@ generatePlanetRow(pixel * const pixelrow, int const byc, int const byf, Vector const sunvec, - pixval const maxval) { + pixval const maxval, + struct Gauss * const gaussP) { unsigned int const StarClose = 2; @@ -924,24 +964,25 @@ generatePlanetRow(pixel * const pixelrow, /* Left stars */ for (col = 0; (int)col < (int)(cols/2 - (lcos + StarClose)); ++col) - etoile(&pixelrow[col]); + etoile(&pixelrow[col], gaussP); /* Right stars */ for (col = cols/2 + (lcos + StarClose); col < cols; ++col) - etoile(&pixelrow[col]); + etoile(&pixelrow[col], gaussP); } static void -genplanet(bool const stars, - bool const clouds, - float * const a, - unsigned int const cols, - unsigned int const rows, - unsigned int const n, - unsigned int const rseed) { +genplanet(bool const stars, + bool const clouds, + float * const a, + unsigned int const cols, + unsigned int const rows, + unsigned int const n, + unsigned int const rseed, + struct Gauss * const gaussP) { /*---------------------------------------------------------------------------- Generate planet from elevation array. @@ -971,13 +1012,13 @@ genplanet(bool const stars, clouds ? "clouds" : "planet", rseed, fracdim, powscale, meshsize); createPlanetStuff(clouds, a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, - cols, maxval); + cols, maxval, gaussP); } pixelrow = ppm_allocrow(cols); for (row = 0; row < rows; ++row) { if (stars) - generateStarrySkyRow(pixelrow, cols); + generateStarrySkyRow(pixelrow, cols, gaussP); else { double const by = (n - 1) * uprj(row, rows); int const byf = floor(by) * n; @@ -993,7 +1034,8 @@ genplanet(bool const stars, generatePlanetRow(pixelrow, row, rows, cols, t, t1, u, u1, cp, bxc, bxf, byc, byf, sunvec, - maxval); + maxval, + gaussP); } ppm_writeppmrow(stdout, pixelrow, cols, maxval, FALSE); } @@ -1098,14 +1140,15 @@ planet(unsigned int const cols, -----------------------------------------------------------------------------*/ float * a; bool error; + struct Gauss gauss; - initgauss(rseed); + initgauss(&gauss, rseed); if (stars) { a = NULL; error = FALSE; } else { - spectralsynth(&a, meshsize, 3.0 - fracdim); + spectralsynth(&a, meshsize, 3.0 - fracdim, &gauss); if (a == NULL) { error = TRUE; } else { @@ -1117,7 +1160,7 @@ planet(unsigned int const cols, } } if (!error) - genplanet(stars, clouds, a, cols, rows, meshsize, rseed); + genplanet(stars, clouds, a, cols, rows, meshsize, rseed, &gauss); if (a != NULL) free(a); @@ -1159,10 +1202,10 @@ main(int argc, const char ** argv) { cols = (MAX(cmdline.height, cmdline.width) + 1) & (~1); rows = cmdline.height; - success = planet(cols, rows, cmdline.night, cmdline.clouds, cmdline.seed); + success = planet(cols, rows, cmdline.night, + cmdline.clouds, cmdline.seed); exit(success ? 0 : 1); } - diff --git a/generator/ppmpat.c b/generator/ppmpat.c index 908c200f..9473afeb 100644 --- a/generator/ppmpat.c +++ b/generator/ppmpat.c @@ -14,7 +14,6 @@ #define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ /* get M_PI in math.h */ #define _BSD_SOURCE /* Make sure strdup() is in <string.h> */ -#define SPIROGRAPHS 0 /* Spirograph to be added soon */ #include <assert.h> #include <math.h> @@ -23,8 +22,9 @@ #include "pm_c_util.h" #include "mallocvar.h" -#include "shhopt.h" #include "nstring.h" +#include "rand.h" +#include "shhopt.h" #include "ppm.h" #include "ppmdraw.h" @@ -40,9 +40,6 @@ typedef enum { PAT_SQUIG, PAT_CAMO, PAT_ANTICAMO, - PAT_SPIRO1, - PAT_SPIRO2, - PAT_SPIRO3 } Pattern; typedef struct { @@ -80,7 +77,6 @@ validateColorCount(Pattern const basePattern, switch (basePattern) { case PAT_GINGHAM2: case PAT_ARGYLE1: - case PAT_SPIRO1: if (colorCount != 2) pm_error("Wrong number of colors: %u. " "2 colors are required for the specified pattern.", @@ -112,8 +108,6 @@ validateColorCount(Pattern const basePattern, colorCount); break; - case PAT_SPIRO2: - case PAT_SPIRO3: default: pm_error("INTERNAL ERROR."); } @@ -188,9 +182,6 @@ parseCommandLine(int argc, const char ** argv, unsigned int squig; unsigned int camo; unsigned int anticamo; - unsigned int spiro1; - unsigned int spiro2; - unsigned int spiro3; MALLOCARRAY_NOFAIL(option_def, 100); @@ -219,16 +210,6 @@ parseCommandLine(int argc, const char ** argv, &camo, 0); OPTENT3(0, "anticamo", OPT_FLAG, NULL, &anticamo, 0); -#if SPIROGRAPHS != 0 - OPTENT3(0, "spiro1", OPT_FLAG, NULL, - &spiro1, 0); - OPTENT3(0, "spiro2", OPT_FLAG, NULL, - &spiro1, 0); - OPTENT3(0, "spiro3", OPT_FLAG, NULL, - &spiro1, 0); -#else - spiro1 = spiro2 = spiro3 = 0; -#endif OPTENT3(0, "color", OPT_STRINGLIST, &colorText, &cmdlineP->colorSpec, 0); OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, @@ -246,8 +227,7 @@ parseCommandLine(int argc, const char ** argv, gingham2 + gingham3 + madras + tartan + argyle1 + argyle2 + poles + squig + - camo + anticamo + - spiro1 + spiro2 + spiro3; + camo + anticamo; if (basePatternCount < 1) pm_error("You must specify a base pattern option such as -gingham2"); @@ -275,12 +255,6 @@ parseCommandLine(int argc, const char ** argv, cmdlineP->basePattern = PAT_CAMO; else if (anticamo) cmdlineP->basePattern = PAT_ANTICAMO; - else if (spiro1) - cmdlineP->basePattern = PAT_SPIRO1; - else if (spiro2) - cmdlineP->basePattern = PAT_SPIRO2; - else if (spiro3) - cmdlineP->basePattern = PAT_SPIRO3; else assert(false); /* Every possibility is accounted for */ } @@ -339,14 +313,15 @@ validateComputableDimensions(unsigned int const cols, static pixel -randomColor(pixval const maxval) { +randomColor(struct pm_randSt * const randStP, + pixval const maxval) { pixel p; PPM_ASSIGN(p, - rand() % (maxval + 1), - rand() % (maxval + 1), - rand() % (maxval + 1) + pm_rand(randStP) % (maxval + 1), + pm_rand(randStP) % (maxval + 1), + pm_rand(randStP) % (maxval + 1) ); return p; @@ -354,15 +329,18 @@ randomColor(pixval const maxval) { -#define DARK_THRESH 0.25 +static double const DARK_THRESH = 0.25; + + static pixel -randomBrightColor(pixval const maxval) { +randomBrightColor(struct pm_randSt * const randStP, + pixval const maxval) { pixel p; do { - p = randomColor(maxval); + p = randomColor(randStP, maxval); } while (PPM_LUMIN(p) <= maxval * DARK_THRESH); return p; @@ -371,12 +349,13 @@ randomBrightColor(pixval const maxval) { static pixel -randomDarkColor(pixval const maxval) { +randomDarkColor(struct pm_randSt * const randStP, + pixval const maxval) { pixel p; do { - p = randomColor(maxval); + p = randomColor(randStP, maxval); } while (PPM_LUMIN(p) > maxval * DARK_THRESH); return p; @@ -448,7 +427,8 @@ nextColorBg(ColorTable * const colorTableP) { static pixel -randomAnticamoColor(pixval const maxval) { +randomAnticamoColor(struct pm_randSt * const randStP, + pixval const maxval) { int v1, v2, v3; pixel p; @@ -457,37 +437,49 @@ randomAnticamoColor(pixval const maxval) { v2 = (maxval + 1) / 2; v3 = 3 * v1; - switch (rand() % 15) { + switch (pm_rand(randStP) % 15) { case 0: case 1: - PPM_ASSIGN(p, rand() % v1 + v3, rand() % v2, rand() % v2); + PPM_ASSIGN(p, pm_rand(randStP) % v1 + v3, + pm_rand(randStP) % v2, + pm_rand(randStP) % v2); break; case 2: case 3: - PPM_ASSIGN(p, rand() % v2, rand() % v1 + v3, rand() % v2); + PPM_ASSIGN(p, pm_rand(randStP) % v2, + pm_rand(randStP) % v1 + v3, + pm_rand(randStP) % v2); break; case 4: case 5: - PPM_ASSIGN(p, rand() % v2, rand() % v2, rand() % v1 + v3); + PPM_ASSIGN(p, pm_rand(randStP) % v2, + pm_rand(randStP) % v2, + pm_rand(randStP) % v1 + v3); break; case 6: case 7: case 8: - PPM_ASSIGN(p, rand() % v2, rand() % v1 + v3, rand() % v1 + v3); + PPM_ASSIGN(p, pm_rand(randStP) % v2, + pm_rand(randStP) % v1 + v3, + pm_rand(randStP) % v1 + v3); break; case 9: case 10: case 11: - PPM_ASSIGN(p, rand() % v1 + v3, rand() % v2, rand() % v1 + v3); + PPM_ASSIGN(p, pm_rand(randStP) % v1 + v3, + pm_rand(randStP) % v2, + pm_rand(randStP) % v1 + v3); break; case 12: case 13: case 14: - PPM_ASSIGN(p, rand() % v1 + v3, rand() % v1 + v3, rand() % v2); + PPM_ASSIGN(p, pm_rand(randStP) % v1 + v3, + pm_rand(randStP) % v1 + v3, + pm_rand(randStP) % v2); break; } @@ -497,7 +489,8 @@ randomAnticamoColor(pixval const maxval) { static pixel -randomCamoColor(pixval const maxval) { +randomCamoColor(struct pm_randSt * const randStP, + pixval const maxval) { int const v1 = (maxval + 1 ) / 8; int const v2 = (maxval + 1 ) / 4; @@ -505,31 +498,39 @@ randomCamoColor(pixval const maxval) { pixel p; - switch (rand() % 10) { + switch (pm_rand(randStP) % 10) { case 0: case 1: case 2: /* light brown */ - PPM_ASSIGN(p, rand() % v3 + v3, rand() % v3 + v2, rand() % v3 + v2); + PPM_ASSIGN(p, pm_rand(randStP) % v3 + v3, + pm_rand(randStP) % v3 + v2, + pm_rand(randStP) % v3 + v2); break; case 3: case 4: case 5: /* dark green */ - PPM_ASSIGN(p, rand() % v2, rand() % v2 + 3 * v1, rand() % v2); + PPM_ASSIGN(p, pm_rand(randStP) % v2, + pm_rand(randStP) % v2 + 3 * v1, + pm_rand(randStP) % v2); break; case 6: case 7: /* brown */ - PPM_ASSIGN(p, rand() % v2 + v2, rand() % v2, rand() % v2); + PPM_ASSIGN(p, pm_rand(randStP) % v2 + v2, + pm_rand(randStP) % v2, + pm_rand(randStP) % v2); break; case 8: case 9: /* dark brown */ - PPM_ASSIGN(p, rand() % v1 + v1, rand() % v1, rand() % v1); + PPM_ASSIGN(p, pm_rand(randStP) % v1 + v1, + pm_rand(randStP) % v1, + pm_rand(randStP) % v1); break; } @@ -539,28 +540,30 @@ randomCamoColor(pixval const maxval) { static float -rnduni(void) { - return rand() % 32767 / 32767.0; +rnduni(struct pm_randSt * const randStP) { + + return pm_rand(randStP) % 32767 / 32767.0; } static void -clearBackgroundCamo(pixel ** const pixels, - unsigned int const cols, - unsigned int const rows, - pixval const maxval, - ColorTable * const colorTableP, - bool const antiflag) { +clearBackgroundCamo(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ColorTable * const colorTableP, + struct pm_randSt * const randStP, + bool const antiflag) { pixel color; if (colorTableP->count > 0) { color = colorTableP->color[0]; } else if (antiflag) - color = randomAnticamoColor(maxval); + color = randomAnticamoColor(randStP, maxval); else - color = randomCamoColor(maxval); + color = randomCamoColor(randStP,maxval); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC, @@ -570,13 +573,14 @@ clearBackgroundCamo(pixel ** const pixels, static void -camoFill(pixel ** const pixels, - unsigned int const cols, - unsigned int const rows, - pixval const maxval, - struct fillobj * const fh, - ColorTable * const colorTableP, - bool const antiflag) { +camoFill(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + struct fillobj * const fh, + ColorTable * const colorTableP, + struct pm_randSt * const randStP, + bool const antiflag) { pixel color; @@ -585,9 +589,9 @@ camoFill(pixel ** const pixels, color = colorTableP->color[colorTableP->index]; nextColorBg(colorTableP); } else if (antiflag) - color = randomAnticamoColor(maxval); + color = randomAnticamoColor(randStP, maxval); else - color = randomCamoColor(maxval); + color = randomCamoColor(randStP, maxval); ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color); } @@ -608,25 +612,29 @@ camoFill(pixel ** const pixels, static void -computeXsYs(int * const xs, - int * const ys, - unsigned int const cols, - unsigned int const rows, - unsigned int const pointCt) { - - unsigned int const cx = rand() % cols; - unsigned int const cy = rand() % rows; - double const a = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) + - MIN_ELLIPSE_FACTOR; - double const b = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) + - MIN_ELLIPSE_FACTOR; - double const theta = rnduni() * 2.0 * M_PI; +computeXsYs(int * const xs, + int * const ys, + unsigned int const cols, + unsigned int const rows, + unsigned int const pointCt, + struct pm_randSt * const randStP) { + + unsigned int const cx = pm_rand(randStP) % cols; + unsigned int const cy = pm_rand(randStP) % rows; + double const a = rnduni(randStP) * + (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) + + MIN_ELLIPSE_FACTOR; + double const b = rnduni(randStP) * + (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) + + MIN_ELLIPSE_FACTOR; + double const theta = rnduni(randStP) * 2.0 * M_PI; unsigned int p; for (p = 0; p < pointCt; ++p) { - double const c = rnduni() * (MAX_POINT_FACTOR - MIN_POINT_FACTOR) + - MIN_POINT_FACTOR; + double const c = rnduni(randStP) * + (MAX_POINT_FACTOR - MIN_POINT_FACTOR) + + MIN_POINT_FACTOR; double const tx = a * sin(p * 2.0 * M_PI / pointCt); double const ty = b * cos(p * 2.0 * M_PI / pointCt); double const tang = atan2(ty, tx) + theta; @@ -638,18 +646,20 @@ computeXsYs(int * const xs, static void -camo(pixel ** const pixels, - unsigned int const cols, - unsigned int const rows, - ColorTable * const colorTableP, - pixval const maxval, - bool const antiflag) { +camo(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + ColorTable * const colorTableP, + struct pm_randSt * const randStP, + pixval const maxval, + bool const antiflag) { unsigned int const n = (rows * cols) / SQR(BLOBRAD) * 5; unsigned int i; - clearBackgroundCamo(pixels, cols, rows, maxval, colorTableP, antiflag); + clearBackgroundCamo(pixels, cols, rows, maxval, + colorTableP, randStP, antiflag); if (colorTableP->count > 0) { assert(colorTableP->count > 1); @@ -658,13 +668,13 @@ camo(pixel ** const pixels, for (i = 0; i < n; ++i) { unsigned int const pointCt = - rand() % (MAX_POINTS - MIN_POINTS + 1) + MIN_POINTS; + pm_rand(randStP) % (MAX_POINTS - MIN_POINTS + 1) + MIN_POINTS; int xs[MAX_POINTS], ys[MAX_POINTS]; int x0, y0; struct fillobj * fh; - computeXsYs(xs, ys, cols, rows, pointCt); + computeXsYs(xs, ys, cols, rows, pointCt, randStP); x0 = (xs[0] + xs[pointCt - 1]) / 2; y0 = (ys[0] + ys[pointCt - 1]) / 2; @@ -675,7 +685,8 @@ camo(pixel ** const pixels, pixels, cols, rows, maxval, x0, y0, pointCt, xs, ys, x0, y0, ppmd_fill_drawproc, fh); - camoFill(pixels, cols, rows, maxval, fh, colorTableP, antiflag); + camoFill(pixels, cols, rows, maxval, fh, + colorTableP, randStP, antiflag); ppmd_fill_destroy(fh); } @@ -688,17 +699,20 @@ camo(pixel ** const pixels, -----------------------------------------------------------------------------*/ static void -gingham2(pixel ** const pixels, - unsigned int const cols, - unsigned int const rows, - ColorTable const colorTable, - pixval const maxval) { +gingham2(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + ColorTable const colorTable, + struct pm_randSt * const randStP, + pixval const maxval) { bool const colorSpec = (colorTable.count > 0); pixel const backcolor = colorSpec ? - colorTable.color[0] : randomDarkColor(maxval); + colorTable.color[0] : + randomDarkColor(randStP, maxval); pixel const forecolor = colorSpec ? - colorTable.color[1] : randomBrightColor(maxval); + colorTable.color[1] : + randomBrightColor(randStP, maxval); unsigned int const colso2 = cols / 2; unsigned int const rowso2 = rows / 2; @@ -722,19 +736,23 @@ gingham2(pixel ** const pixels, static void -gingham3(pixel ** const pixels, - unsigned int const cols, - unsigned int const rows, - ColorTable const colorTable, - pixval const maxval) { +gingham3(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + ColorTable const colorTable, + struct pm_randSt * const randStP, + pixval const maxval) { bool const colorSpec = (colorTable.count > 0); - pixel const backcolor = colorSpec ? - colorTable.color[0] : randomDarkColor(maxval); + pixel const backcolor = colorSpec ? + colorTable.color[0] : + randomDarkColor(randStP, maxval); pixel const fore1color = colorSpec ? - colorTable.color[1] : randomBrightColor(maxval); + colorTable.color[1] : + randomBrightColor(randStP, maxval); pixel const fore2color = colorSpec ? - colorTable.color[2] : randomBrightColor(maxval); + colorTable.color[2] : + randomBrightColor(randStP, maxval); unsigned int const colso4 = cols / 4; unsigned int const rowso4 = rows / 4; @@ -770,19 +788,23 @@ gingham3(pixel ** const pixels, static void -madras(pixel ** const pixels, - unsigned int const cols, - unsigned int const rows, - ColorTable const colorTable, - pixval const maxval) { +madras(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + ColorTable const colorTable, + struct pm_randSt * const randStP, + pixval const maxval) { bool const colorSpec = (colorTable.count > 0); - pixel const backcolor = colorSpec ? - colorTable.color[0] : randomDarkColor(maxval); + pixel const backcolor = colorSpec ? + colorTable.color[0] : + randomDarkColor(randStP, maxval); pixel const fore1color = colorSpec ? - colorTable.color[1] : randomBrightColor(maxval); + colorTable.color[1] : + randomBrightColor(randStP, maxval); pixel const fore2color = colorSpec ? - colorTable.color[2] : randomBrightColor(maxval); + colorTable.color[2] : + randomBrightColor(randStP, maxval); unsigned int const cols2 = cols * 2 / 44; unsigned int const rows2 = rows * 2 / 44; unsigned int const cols3 = cols * 3 / 44; @@ -899,19 +921,23 @@ madras(pixel ** const pixels, static void -tartan(pixel ** const pixels, - unsigned int const cols, - unsigned int const rows, - ColorTable const colorTable, - pixval const maxval) { +tartan(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + ColorTable const colorTable, + struct pm_randSt * const randStP, + pixval const maxval) { bool const colorSpec = (colorTable.count > 0); - pixel const backcolor = colorSpec ? - colorTable.color[0] : randomDarkColor(maxval); + pixel const backcolor = colorSpec ? + colorTable.color[0] : + randomDarkColor(randStP, maxval); pixel const fore1color = colorSpec ? - colorTable.color[1] : randomBrightColor(maxval); + colorTable.color[1] : + randomBrightColor(randStP, maxval); pixel const fore2color = colorSpec ? - colorTable.color[2] : randomBrightColor(maxval); + colorTable.color[2] : + randomBrightColor(randStP, maxval); unsigned int const cols1 = cols / 22; unsigned int const rows1 = rows / 22; unsigned int const cols3 = cols * 3 / 22; @@ -1013,14 +1039,15 @@ argyle(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable const colorTable, + struct pm_randSt * const randStP, pixval const maxval, bool const stripes) { bool const colorSpec = (colorTable.count > 0); pixel const backcolor = colorSpec ? - colorTable.color[0] : randomDarkColor(maxval); + colorTable.color[0] : randomDarkColor(randStP, maxval); pixel const forecolor = colorSpec ? - colorTable.color[1] : randomBrightColor(maxval); + colorTable.color[1] : randomBrightColor(randStP, maxval); /* Fill canvas with background to start */ ppmd_filledrectangle( @@ -1032,7 +1059,8 @@ argyle(pixel ** const pixels, if (stripes) { /* Connect corners with thin stripes */ pixel const stripecolor = - colorSpec ? colorTable.color[2] : randomBrightColor(maxval); + colorSpec ? colorTable.color[2] : + randomBrightColor(randStP, maxval); ppmd_line(pixels, cols, rows, maxval, 0, 0, cols-1, rows-1, PPMD_NULLDRAWPROC, (char *) &stripecolor); @@ -1049,32 +1077,33 @@ argyle(pixel ** const pixels, -#define MAXPOLES 500 +static unsigned int const MAXPOLES = 500; static void -placeAndColorPolesRandomly(int * const xs, - int * const ys, - pixel * const colors, - unsigned int const cols, - unsigned int const rows, - pixval const maxval, - ColorTable * const colorTableP, - unsigned int const poleCt) { +placeAndColorPolesRandomly(int * const xs, + int * const ys, + pixel * const colors, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + ColorTable * const colorTableP, + struct pm_randSt * const randStP, + unsigned int const poleCt) { unsigned int i; for (i = 0; i < poleCt; ++i) { - xs[i] = rand() % cols; - ys[i] = rand() % rows; + xs[i] = pm_rand(randStP) % cols; + ys[i] = pm_rand(randStP) % rows; if (colorTableP->count > 0) { colors[i] = colorTableP->color[colorTableP->index]; nextColor(colorTableP); } else - colors[i] = randomBrightColor(maxval); + colors[i] = randomBrightColor(randStP, maxval); } } @@ -1104,11 +1133,12 @@ assignInterpolatedColor(pixel * const resultP, static void -poles(pixel ** const pixels, - unsigned int const cols, - unsigned int const rows, - ColorTable * const colorTableP, - pixval const maxval) { +poles(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + ColorTable * const colorTableP, + struct pm_randSt * const randStP, + pixval const maxval) { unsigned int const poleCt = MAX(2, MIN(MAXPOLES, cols * rows / 30000)); @@ -1117,7 +1147,7 @@ poles(pixel ** const pixels, unsigned int row; placeAndColorPolesRandomly(xs, ys, colors, cols, rows, maxval, - colorTableP, poleCt); + colorTableP, randStP, poleCt); /* Interpolate points */ @@ -1236,11 +1266,12 @@ sqRainbowCircleDrawproc(pixel ** const pixels, static void -chooseSqPoleColors(ColorTable * const colorTableP, - pixval const maxval, - pixel * const color1P, - pixel * const color2P, - pixel * const color3P) { +chooseSqPoleColors(ColorTable * const colorTableP, + pixval const maxval, + pixel * const color1P, + pixel * const color2P, + pixel * const color3P, + struct pm_randSt * const randStP) { if (colorTableP->count > 0) { *color1P = colorTableP->color[colorTableP->index]; @@ -1250,19 +1281,20 @@ chooseSqPoleColors(ColorTable * const colorTableP, *color3P = colorTableP->color[colorTableP->index]; nextColor(colorTableP); } else { - *color1P = randomBrightColor(maxval); - *color2P = randomBrightColor(maxval); - *color3P = randomBrightColor(maxval); + *color1P = randomBrightColor(randStP, maxval); + *color2P = randomBrightColor(randStP, maxval); + *color3P = randomBrightColor(randStP, maxval); } } static void -sqAssignColors(unsigned int const circlecount, - pixval const maxval, - ColorTable * const colorTableP, - pixel * const colors) { +sqAssignColors(unsigned int const circlecount, + pixval const maxval, + ColorTable * const colorTableP, + pixel * const colors, + struct pm_randSt * const randStP) { float const cco3 = (circlecount - 1) / 3.0; @@ -1271,7 +1303,7 @@ sqAssignColors(unsigned int const circlecount, pixel rc3; unsigned int i; - chooseSqPoleColors(colorTableP, maxval, &rc1, &rc2, &rc3); + chooseSqPoleColors(colorTableP, maxval, &rc1, &rc2, &rc3, randStP); for (i = 0; i < circlecount; ++i) { if (i < cco3) { @@ -1333,24 +1365,25 @@ clearBackgroundSquig(pixel ** const pixels, static void -chooseWrapAroundPoint(unsigned int const cols, - unsigned int const rows, - ppmd_point * const pFirstP, - ppmd_point * const pLastP, - ppmd_point * const p0P, - ppmd_point * const p1P, - ppmd_point * const p2P, - ppmd_point * const p3P) { - - switch (rand() % 4) { +chooseWrapAroundPoint(unsigned int const cols, + unsigned int const rows, + ppmd_point * const pFirstP, + ppmd_point * const pLastP, + ppmd_point * const p0P, + ppmd_point * const p1P, + ppmd_point * const p2P, + ppmd_point * const p3P, + struct pm_randSt * const randStP) { + + switch (pm_rand(randStP) % 4) { case 0: - p1P->x = rand() % cols; + p1P->x = pm_rand(randStP) % cols; p1P->y = 0; if (p1P->x < cols / 2) - pFirstP->x = rand() % (p1P->x * 2 + 1); + pFirstP->x = pm_rand(randStP) % (p1P->x * 2 + 1); else - pFirstP->x = cols - 1 - rand() % ((cols - p1P->x) * 2); - pFirstP->y = rand() % rows; + pFirstP->x = cols - 1 - pm_rand(randStP) % ((cols - p1P->x) * 2); + pFirstP->y = pm_rand(randStP) % rows; p2P->x = p1P->x; p2P->y = rows - 1; pLastP->x = 2 * p2P->x - pFirstP->x; @@ -1362,13 +1395,13 @@ chooseWrapAroundPoint(unsigned int const cols, break; case 1: - p2P->x = rand() % cols; + p2P->x = pm_rand(randStP) % cols; p2P->y = 0; if (p2P->x < cols / 2) - pLastP->x = rand() % (p2P->x * 2 + 1); + pLastP->x = pm_rand(randStP) % (p2P->x * 2 + 1); else - pLastP->x = cols - 1 - rand() % ((cols - p2P->x) * 2); - pLastP->y = rand() % rows; + pLastP->x = cols - 1 - pm_rand(randStP) % ((cols - p2P->x) * 2); + pLastP->y = pm_rand(randStP) % rows; p1P->x = p2P->x; p1P->y = rows - 1; pFirstP->x = 2 * p1P->x - pLastP->x; @@ -1381,12 +1414,12 @@ chooseWrapAroundPoint(unsigned int const cols, case 2: p1P->x = 0; - p1P->y = rand() % rows; - pFirstP->x = rand() % cols; + p1P->y = pm_rand(randStP) % rows; + pFirstP->x = pm_rand(randStP) % cols; if (p1P->y < rows / 2) - pFirstP->y = rand() % (p1P->y * 2 + 1); + pFirstP->y = pm_rand(randStP) % (p1P->y * 2 + 1); else - pFirstP->y = rows - 1 - rand() % ((rows - p1P->y) * 2); + pFirstP->y = rows - 1 - pm_rand(randStP) % ((rows - p1P->y) * 2); p2P->x = cols - 1; p2P->y = p1P->y; pLastP->x = p2P->x - pFirstP->x; @@ -1399,12 +1432,12 @@ chooseWrapAroundPoint(unsigned int const cols, case 3: p2P->x = 0; - p2P->y = rand() % rows; - pLastP->x = rand() % cols; + p2P->y = pm_rand(randStP) % rows; + pLastP->x = pm_rand(randStP) % cols; if (p2P->y < rows / 2) - pLastP->y = rand() % (p2P->y * 2 + 1); + pLastP->y = pm_rand(randStP) % (p2P->y * 2 + 1); else - pLastP->y = rows - 1 - rand() % ((rows - p2P->y) * 2); + pLastP->y = rows - 1 - pm_rand(randStP) % ((rows - p2P->y) * 2); p1P->x = cols - 1; p1P->y = p2P->y; pFirstP->x = p1P->x - pLastP->x; @@ -1420,11 +1453,12 @@ chooseWrapAroundPoint(unsigned int const cols, static void -squig(pixel ** const pixels, - unsigned int const cols, - unsigned int const rows, - ColorTable * const colorTableP, - pixval const maxval) { +squig(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + ColorTable * const colorTableP, + struct pm_randSt * const randStP, + pixval const maxval) { int i; @@ -1453,10 +1487,11 @@ squig(pixel ** const pixels, ppmd_circlep(pixels, cols, rows, maxval, ppmd_makePoint(0, 0), radius, sqMeasureCircleDrawproc, &sqClientData); - sqAssignColors(squig.circleCt, maxval, colorTableP, squig.color); + sqAssignColors(squig.circleCt, maxval, colorTableP, squig.color, + randStP); chooseWrapAroundPoint(cols, rows, &c[0], &c[SQ_POINTS-1], - &p0, &p1, &p2, &p3); + &p0, &p1, &p2, &p3, randStP); { /* Do the middle points */ @@ -1466,8 +1501,8 @@ squig(pixel ** const pixels, /* validateSquigAspect() assures that cols - 2 * radius, rows -2 * radius are positive */ - c[j].x = (rand() % (cols - 2 * radius)) + radius; - c[j].y = (rand() % (rows - 2 * radius)) + radius; + c[j].x = (pm_rand(randStP) % (cols - 2 * radius)) + radius; + c[j].y = (pm_rand(randStP) % (rows - 2 * radius)) + radius; } } @@ -1490,6 +1525,7 @@ main(int argc, const char ** argv) { struct CmdlineInfo cmdline; pixel ** pixels; + struct pm_randSt randSt; pm_proginit(&argc, argv); @@ -1497,65 +1533,68 @@ main(int argc, const char ** argv) { validateComputableDimensions(cmdline.width, cmdline.height); - srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); + pm_randinit(&randSt); + pm_srand2(&randSt, cmdline.randomseedSpec, cmdline.randomseed); pixels = ppm_allocarray(cmdline.width, cmdline.height); switch (cmdline.basePattern) { case PAT_GINGHAM2: gingham2(pixels, cmdline.width, cmdline.height, - cmdline.colorTable, PPM_MAXMAXVAL); + cmdline.colorTable, &randSt, PPM_MAXMAXVAL); break; case PAT_GINGHAM3: gingham3(pixels, cmdline.width, cmdline.height, - cmdline.colorTable, PPM_MAXMAXVAL); + cmdline.colorTable, &randSt, PPM_MAXMAXVAL); break; case PAT_MADRAS: madras(pixels, cmdline.width, cmdline.height, - cmdline.colorTable, PPM_MAXMAXVAL); + cmdline.colorTable, &randSt, PPM_MAXMAXVAL); break; case PAT_TARTAN: tartan(pixels, cmdline.width, cmdline.height, - cmdline.colorTable, PPM_MAXMAXVAL); + cmdline.colorTable, &randSt, PPM_MAXMAXVAL); break; case PAT_ARGYLE1: argyle(pixels, cmdline.width, cmdline.height, - cmdline.colorTable, PPM_MAXMAXVAL, FALSE); + cmdline.colorTable, &randSt, PPM_MAXMAXVAL, FALSE); break; case PAT_ARGYLE2: argyle(pixels, cmdline.width, cmdline.height, - cmdline.colorTable, PPM_MAXMAXVAL, TRUE); + cmdline.colorTable, &randSt, PPM_MAXMAXVAL, TRUE); break; case PAT_POLES: poles(pixels, cmdline.width, cmdline.height, - &cmdline.colorTable, PPM_MAXMAXVAL); + &cmdline.colorTable, &randSt, PPM_MAXMAXVAL); break; case PAT_SQUIG: squig(pixels, cmdline.width, cmdline.height, - &cmdline.colorTable, PPM_MAXMAXVAL); + &cmdline.colorTable, &randSt, PPM_MAXMAXVAL); break; case PAT_CAMO: camo(pixels, cmdline.width, cmdline.height, - &cmdline.colorTable, PPM_MAXMAXVAL, 0); + &cmdline.colorTable, &randSt, PPM_MAXMAXVAL, 0); break; case PAT_ANTICAMO: camo(pixels, cmdline.width, cmdline.height, - &cmdline.colorTable, PPM_MAXMAXVAL, 1); + &cmdline.colorTable, &randSt, PPM_MAXMAXVAL, 1); break; default: pm_error("can't happen!"); } + pm_randterm(&randSt); + ppm_writeppm(stdout, pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 0); @@ -1567,4 +1606,3 @@ main(int argc, const char ** argv) { } - diff --git a/generator/ppmrainbow b/generator/ppmrainbow index e8a329ff..a5c2689d 100755 --- a/generator/ppmrainbow +++ b/generator/ppmrainbow @@ -26,6 +26,19 @@ exec perl -w -x -S -- "$0" "$@" use strict; use Getopt::Long; use File::Temp; +use IO::Handle; + + + +sub pm_message($) { + STDERR->print("ppmrainbow: $_[0]\n"); +} + +sub pm_error($) { + pm_message($_[0]); + exit(1); +} + my ($FALSE, $TRUE) = (0,1); @@ -46,15 +59,6 @@ sub doVersionHack($) { -sub fatal($) { - my ($msg) = @_; - - print(STDERR "ppmrainbow: $msg\n"); - exit(1); -} - - - ############################################################################## # # MAINLINE @@ -79,14 +83,14 @@ GetOptions("width=i" => \$Twid, "verbose!" => \$verbose); if ($Twid < 1 || $Thgt < 1) { - fatal("invalid width and/or height"); + pm_error("invalid width and/or height"); } my $verboseCommand = $verbose ? "set -x;" : ""; if (@ARGV < 1) { - fatal("You must specify at least one color as an argument"); + pm_error("You must specify at least one color as an argument"); } elsif (@ARGV < 2 && ! $repeat) { - fatal("With the -norepeat option, you must specify at least two colors " . + pm_error("With the -norepeat option, you must specify at least two colors " . "as arguments."); } @@ -115,7 +119,7 @@ while (@colorlist >= 2) { my $rc = system("$verboseCommand pgmramp -lr $w $Thgt | " . "pgmtoppm \"$colorlist[0]-$colorlist[1]\" >$outfile"); if ($rc != 0) { - fatal("pgmramp|pgmtoppm pipe failed."); + pm_error("pgmramp|pgmtoppm pipe failed."); } $widthRemaining -= $w; $n++; diff --git a/generator/ppmrough.c b/generator/ppmrough.c index c87a0364..a4a1f14d 100644 --- a/generator/ppmrough.c +++ b/generator/ppmrough.c @@ -16,11 +16,10 @@ #include "pm_c_util.h" #include "mallocvar.h" +#include "rand.h" #include "shhopt.h" #include "ppm.h" -static pixel** PIX; -static pixval BG_RED, BG_GREEN, BG_BLUE; struct CmdlineInfo { @@ -28,9 +27,12 @@ struct CmdlineInfo { 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 int leftSpec, rightSpec, topSpec, bottomSpec; + unsigned int width; + unsigned int height; + unsigned int var; + const char * bg; /* Null if not specified */ + const char * fg; /* Null if not specified */ unsigned int randomseed; unsigned int randomseedSpec; unsigned int verbose; @@ -42,6 +44,8 @@ static void parseCommandLine(int argc, const char ** argv, struct CmdlineInfo * const cmdlineP) { + unsigned int widthSpec, heightSpec, bgSpec, fgSpec, varSpec; + optEntry * option_def; /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; @@ -51,28 +55,30 @@ parseCommandLine(int argc, const char ** argv, MALLOCARRAY_NOFAIL(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, "width", OPT_UINT, &cmdlineP->width, + &widthSpec, 0); + OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, + &heightSpec, 0); + OPTENT3(0, "left", OPT_UINT, &cmdlineP->left, + &cmdlineP->leftSpec, 0); + OPTENT3(0, "right", OPT_UINT, &cmdlineP->right, + &cmdlineP->rightSpec, 0); + OPTENT3(0, "top", OPT_UINT, &cmdlineP->top, + &cmdlineP->topSpec, 0); + OPTENT3(0, "bottom", OPT_UINT, &cmdlineP->bottom, + &cmdlineP->bottomSpec, 0); + OPTENT3(0, "bg", OPT_STRING, &cmdlineP->bg, + &bgSpec, 0); + OPTENT3(0, "fg", OPT_STRING, &cmdlineP->fg, + &fgSpec, 0); + OPTENT3(0, "var", OPT_UINT, &cmdlineP->var, + &varSpec, 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; + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -80,6 +86,17 @@ parseCommandLine(int argc, const char ** argv, pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + if (!widthSpec) + cmdlineP->width = 100; + if (!heightSpec) + cmdlineP->height = 100; + if (!bgSpec) + cmdlineP->bg = NULL; + if (!fgSpec) + cmdlineP->fg = NULL; + if (!varSpec) + cmdlineP->var = 10; + if (argc-1 != 0) pm_error("There are no arguments. You specified %d.", argc-1); @@ -89,239 +106,340 @@ parseCommandLine(int argc, const char ** argv, static void -procLeft(int const r1, - int const r2, - int const c1, - int const c2, - unsigned int const var) { +reportParameters(struct CmdlineInfo const cmdline, + pixel const bgcolor, + pixel const fgcolor) { + + pm_message("width is %d, height is %d, variance is %d.", + cmdline.width, cmdline.height, cmdline.var); + if (cmdline.leftSpec) + pm_message("ragged left border is required"); + if (cmdline.rightSpec) + pm_message("ragged right border is required"); + if (cmdline.topSpec) + pm_message("ragged top border is required"); + if (cmdline.bottomSpec) + 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("pm_rand() initialized with seed %u", + cmdline.randomseed); +} - 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); +static void +makeAllForegroundColor(pixel ** const pixels, + unsigned int const rows, + unsigned int const cols, + pixel const fgcolor) { + + pixval const r = PPM_GETR(fgcolor); + pixval const g = PPM_GETG(fgcolor); + pixval const b = PPM_GETB(fgcolor); + + unsigned int row; + + for (row = 0; row < rows; ++row) { + unsigned int col; - procLeft(r1, rm, c1, cm, var); - procLeft(rm, r2, cm, c2, var); + for (col = 0; col < cols; ++col) + PPM_ASSIGN(pixels[row][col], r, g, b); + } } static void -procRight(int const r1, - int const r2, - int const c1, - int const c2, - unsigned int const width, - unsigned int const var) { +procLeft(pixel ** const pixels, + int const r1, + int const r2, + int const c1, + int const c2, + unsigned int const var, + pixel const bgcolor, + struct pm_randSt * const randStP) { + + if (r1 + 1 != r2) { + int const rm = (r1 + r2) >> 1; + int const cm = ((c1 + c2) >> 1) + + (int)floor(((float)pm_rand(randStP) / RAND_MAX - 0.5) * var + 0.5); + + int c; + + for (c = 0; c < cm; c++) + pixels[rm][c] = bgcolor; + + procLeft(pixels, r1, rm, c1, cm, var, bgcolor, randStP); + procLeft(pixels, rm, r2, cm, c2, var, bgcolor, randStP); + } +} + - 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); +static void +procRight(pixel ** const pixels, + int const r1, + int const r2, + int const c1, + int const c2, + unsigned int const width, + unsigned int const var, + pixel const bgcolor, + struct pm_randSt * const randStP) { + + if (r1 + 1 != r2) { + int const rm = (r1 + r2) >> 1; + int const cm = ((c1 + c2) >> 1) + + (int)floor(((float)pm_rand(randStP) / RAND_MAX - 0.5) * var + 0.5); + + int c; + + for (c = cm; c < width; c++) + pixels[rm][c] = bgcolor; + + procRight(pixels, r1, rm, c1, cm, width, var, bgcolor, randStP); + procRight(pixels, rm, r2, cm, c2, width, var, bgcolor, randStP); + } +} + - 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 +procTop(pixel ** const pixels, + int const c1, + int const c2, + int const r1, + int const r2, + unsigned int const var, + pixel const bgcolor, + struct pm_randSt * const randStP) { + + if (c1 + 1 != c2) { + int const cm = (c1 + c2) >> 1; + int const rm = ((r1 + r2) >> 1) + + (int)floor(((float)pm_rand(randStP) / RAND_MAX - 0.5) * var + 0.5); + + int r; + + for (r = 0; r < rm; r++) + pixels[r][cm] = bgcolor; + + procTop(pixels, c1, cm, r1, rm, var, bgcolor, randStP); + procTop(pixels, cm, c2, rm, r2, var, bgcolor, randStP); + } } static void -procTop(int const c1, - int const c2, - int const r1, - int const r2, - unsigned int const var) { +procBottom(pixel ** const pixels, + int const c1, + int const c2, + int const r1, + int const r2, + unsigned int const height, + unsigned int const var, + pixel const bgcolor, + struct pm_randSt * const randStP) { + + if (c1 + 1 != c2) { + int const cm = (c1 + c2) >> 1; + int const rm = ((r1 + r2) >> 1) + + (int)floor(((float)pm_rand(randStP) / RAND_MAX - 0.5) * var + 0.5); + + int r; + + for (r = rm; r < height; ++r) + pixels[r][cm] = bgcolor; + + procBottom(pixels, c1, cm, r1, rm, height, var, bgcolor, randStP); + procBottom(pixels, cm, c2, rm, r2, height, var, bgcolor, randStP); + } +} - 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); +static void +makeRaggedLeftBorder(pixel ** const pixels, + unsigned int const rows, + unsigned int const cols, + bool const leftSpec, + unsigned int const left, + unsigned int const var, + pixel const bgcolor, + struct pm_randSt * const randStP) { + + if (leftSpec) { + int const leftC1 = left; + int const leftC2 = left; + int const leftR1 = 0; + int const leftR2 = rows - 1; - procTop(c1, cm, r1, rm, var); - procTop(cm, c2, rm, r2, var); + unsigned int col; + + for (col = 0; col < leftC1; ++col) + pixels[leftR1][col] = bgcolor; + for (col = 0; col < leftC2; ++col) + pixels[leftR2][col] = bgcolor; + + procLeft(pixels, leftR1, leftR2, leftC1, leftC2, var, + bgcolor, randStP); + } } static void -procBottom(int const c1, - int const c2, - int const r1, - int const r2, - unsigned int const height, - unsigned int const var) { +makeRaggedRightBorder(pixel ** const pixels, + unsigned int const rows, + unsigned int const cols, + bool const rightSpec, + unsigned int const right, + unsigned int const width, + unsigned int const var, + pixel const bgcolor, + struct pm_randSt * const randStP) { + + if (rightSpec) { + int const rightC1 = cols - right - 1; + int const rightC2 = cols - right - 1; + int const rightR1 = 0; + int const rightR2 = rows - 1; - 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); + unsigned int col; - for (r = rm; r < height; r++) - PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE); + for (col = rightC1; col < cols; ++col) + pixels[rightR1][col] = bgcolor; + for (col = rightC2; col < cols; ++col) + pixels[rightR2][col] = bgcolor; - procBottom(c1, cm, r1, rm, height, var); - procBottom(cm, c2, rm, r2, height, var); + procRight(pixels, rightR1, rightR2, rightC1, rightC2, width, var, + bgcolor, randStP); + } } -int -main(int argc, const char * argv[]) { +static void +makeRaggedTopBorder(pixel ** const pixels, + unsigned int const rows, + unsigned int const cols, + bool const topSpec, + unsigned int const top, + unsigned int const var, + pixel const bgcolor, + struct pm_randSt * const randStP) { + + if (topSpec) { + unsigned int const topR1 = top; + unsigned int const topR2 = top; + unsigned int const topC1 = 0; + unsigned int const topC2 = cols - 1; - struct CmdlineInfo cmdline; - pixel bgcolor, fgcolor; - pixval fg_red, fg_green, fg_blue; - int rows, cols, row; - int left, right, top, bottom; + unsigned int row; - pm_proginit(&argc, argv); + for (row = 0; row < topR1; ++row) + pixels[row][topC1] = bgcolor; + for (row = 0; row < topR2; ++row) + pixels[row][topC2] = bgcolor; - parseCommandLine(argc, argv, &cmdline); + procTop(pixels, topC1, topC2, topR1, topR2, var, bgcolor, randStP); + } +} - 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); +static void +makeRaggedBottomBorder(pixel ** const pixels, + unsigned int const rows, + unsigned int const cols, + bool const bottomSpec, + unsigned int const bottom, + unsigned int const height, + unsigned int const var, + pixel const bgcolor, + struct pm_randSt * const randStP) { + + if (bottomSpec) { + unsigned int const bottomR1 = rows - bottom - 1; + unsigned int const bottomR2 = rows - bottom - 1; + unsigned int const bottomC1 = 0; + unsigned int const bottomC2 = cols - 1; - 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); - } + unsigned int row; - /* Allocate memory for the whole pixmap */ - PIX = ppm_allocarray(cols, rows); + for (row = bottomR1; row < rows; ++row) + pixels[row][bottomC1] = bgcolor; + for (row = bottomR2; row < rows; ++row) + pixels[row][bottomC2] = bgcolor; - /* 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); + procBottom(pixels, bottomC1, bottomC2, bottomR1, bottomR2, + height, var, bgcolor, randStP); } - /* 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); - } +int +main(int argc, const char ** const argv) { - /* 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; + struct CmdlineInfo cmdline; + pixel bgcolor, fgcolor; + struct pm_randSt randSt; + static pixel** pixels; - unsigned int col; + pm_proginit(&argc, argv); - 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); + parseCommandLine(argc, argv, &cmdline); - procRight(right_r1, right_r2, right_c1, right_c2, - cmdline.width, cmdline.var); - } + pm_randinit(&randSt); + pm_srand(&randSt, + cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); - /* 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; + if (cmdline.bg) + bgcolor = ppm_parsecolor(cmdline.bg, PPM_MAXMAXVAL); + else + PPM_ASSIGN(bgcolor, 0, 0, 0); - unsigned int row; + if (cmdline.fg) + fgcolor = ppm_parsecolor(cmdline.fg, PPM_MAXMAXVAL); + else + PPM_ASSIGN(fgcolor, PPM_MAXMAXVAL, PPM_MAXMAXVAL, PPM_MAXMAXVAL); - 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); + if (cmdline.verbose) + reportParameters(cmdline, bgcolor, fgcolor); - procTop(top_c1, top_c2, top_r1, top_r2, cmdline.var); - } + pixels = ppm_allocarray(cmdline.width, cmdline.height); - /* 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; + makeAllForegroundColor(pixels, cmdline.height, cmdline.width, fgcolor); - unsigned int row; + makeRaggedLeftBorder(pixels, cmdline.height, cmdline.width, + cmdline.leftSpec, cmdline.left, + cmdline.var, bgcolor, &randSt); - 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); + makeRaggedRightBorder(pixels, cmdline.height, cmdline.width, + cmdline.rightSpec, cmdline.right, + cmdline.width, cmdline.var, bgcolor, &randSt); - procBottom(bottom_c1, bottom_c2, bottom_r1, bottom_r2, - cmdline.height, cmdline.var); - } + makeRaggedTopBorder(pixels, cmdline.height, cmdline.width, + cmdline.topSpec, cmdline.top, + cmdline.var, bgcolor, &randSt); + + makeRaggedBottomBorder(pixels, cmdline.height, cmdline.width, + cmdline.bottomSpec, cmdline.bottom, + cmdline.height, cmdline.var, bgcolor, &randSt); + + pm_randterm(&randSt); /* Write pixmap */ - ppm_writeppm(stdout, PIX, cols, rows, PPM_MAXMAXVAL, 0); + ppm_writeppm(stdout, pixels, cmdline.width, cmdline.height, + PPM_MAXMAXVAL, 0); - ppm_freearray(PIX, rows); + ppm_freearray(pixels, cmdline.height); pm_close(stdout); @@ -329,4 +447,3 @@ main(int argc, const char * argv[]) { } - diff --git a/lib/Makefile b/lib/Makefile index bc758df4..d42658a2 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -48,6 +48,10 @@ LIBOBJECTS_X = \ util/matrix.o \ util/nsleep.o \ util/nstring.o \ + util/rand.o \ + util/randsysrand.o \ + util/randsysrandom.o \ + util/randmersenne.o \ util/runlength.o \ util/shhopt.o \ util/token.o \ diff --git a/lib/util/Makefile b/lib/util/Makefile index 02119edf..d8e2d135 100644 --- a/lib/util/Makefile +++ b/lib/util/Makefile @@ -19,6 +19,10 @@ UTILOBJECTS = \ matrix.o \ nsleep.o \ nstring.o \ + rand.o \ + randsysrand.o \ + randsysrandom.o \ + randmersenne.o \ runlength.o \ shhopt.o \ token.o \ diff --git a/lib/util/rand.c b/lib/util/rand.c new file mode 100644 index 00000000..2f60de83 --- /dev/null +++ b/lib/util/rand.c @@ -0,0 +1,261 @@ +/* + +Pseudo-random number generator for Netpbm + +The interface provided herein should be flexible enough for anybody +who wishes to use some other random number generator. + +--- + +If you desire to implement a different generator, or writing an original +one, first take a look at the random number generator section of the +GNU Scientific Library package (GSL). + +GNU Scientific Library +https://www.gnu.org/software/gsl/ + +GSL Random Number Generators +https://wnww.gnu.org/software/gsl/doc/html/rng.html + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> +#include <strings.h> +#include <time.h> +#include <float.h> +#include <math.h> + +#include "netpbm/pm_c_util.h" +#include "netpbm/mallocvar.h" +#include "netpbm/pm.h" +#include "netpbm/rand.h" + +/*----------------------------------------------------------------------------- + Use +------------------------------------------------------------------------------- + Typical usage: + + #include "rand.h" + + ... + + myfunction( ... , unsigned int const seed , ... ) { + + struct randSt; + + ... + + pm_randinit(&randSt); + pm_srand(&randSt, seed); // pm_srand2() is often more useful + + ... + + pm_rand(&randSt); + + ... + + pm_randterm(&randSt); + + } +-----------------------------------------------------------------------------*/ + + + +/*----------------------------------------------------------------------------- + Design note +------------------------------------------------------------------------------- + + Netpbm code contains multiple random number generators. Stock Netpbm always + uses an internal pseudo-random number generator that implements the Mersenne + Twister method and does not rely on any randomness facility of the operating + system, but it is easy to compile an alternative version that uses others. + + The Mersenne Twister method was new to Netpbm in Netpbm 10.94 + (March 2021). Before that, Netpbm used standard OS-provided facilities. + + Programs that use random numbers have existed in Netpbm since PBMPlus days. + The system rand() function was used in instances randomness was required; + exceptions were rare and all of them appear to be errors on the part of the + original author. + + Although the rand() function is available in every system on which Netpbm + runs, differences exist in the underlying algorithm, so that Netpbm programs + produce different output on different systems even when the user specifies + the same random number seed. + + This was not considered a problem in the early days. Deterministic + operation was not a feature users requested and it was impossible regardless + of the random number generation method on most programs because they did + not allow a user to specify a seed for the generator. + + This state of affairs changed as Netpbm got firmly established as a + base-level system package. Security became critical for many users. A + crucial component of quality control is automated regression tests (="make + check"). Unpredictable behavior gets in the way of testing. One by one + programs were given the -randomseed (or -seed) option to ensure reproducible + results. Often this was done as new tests cases were written. However, + inconsistent output caused by system-level differences in rand() + implementation remained a major obstacle. + + In 2020 the decision was made to replace all calls to rand() in the Netpbm + source code with an internal random number generator. We decided to use the + Mersenne Twister, which is concise, enjoys a fine reputation and is + available under liberal conditions (see below.) +-----------------------------------------------------------------------------*/ + + +void +pm_srand(struct pm_randSt * const randStP, + unsigned int const seed) { +/*---------------------------------------------------------------------------- + Initialize (or "seed") the random number generation sequence with value + 'seed'. +-----------------------------------------------------------------------------*/ + pm_randinit(randStP); + + randStP->vtable.srand(randStP, seed); + + randStP->seed = seed; +} + + + +void +pm_srand2(struct pm_randSt * const randStP, + bool const seedValid, + unsigned int const seed) { +/*---------------------------------------------------------------------------- + Seed the random number generator. If 'seedValid' is true, use 'seed".. + Otherwise, use pm_randseed(). + + For historical reasons pm_randseed() is defined in libpm.c rather than + this source file. +-----------------------------------------------------------------------------*/ + pm_srand(randStP, seedValid ? seed : pm_randseed() ); + +} + + + +unsigned long int +pm_rand(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + An integer random number in the interval [0, randStP->max]. +-----------------------------------------------------------------------------*/ + return randStP->vtable.rand(randStP); +} + + + +double +pm_drand(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + A floating point random number in the interval [0, 1). + + Although the return value is declared as double, the actual value will have + no more precision than a single call to pm_rand() provides. This is 32 bits + for Mersenne Twister. +-----------------------------------------------------------------------------*/ + return (double) pm_rand(randStP) / randStP->max; +} + + + +void +pm_gaussrand2(struct pm_randSt * const randStP, + double * const r1P, + double * const r2P) { +/*---------------------------------------------------------------------------- + Generate two Gaussian (or normally) distributed random numbers *r1P and + *r2P. + + Mean = 0, Standard deviation = 1. + + This is called the Box-Muller method. + + For details of this algorithm and other methods for producing + Gaussian random numbers see: + + http://www.doc.ic.ac.uk/~wl/papers/07/csur07dt.pdf +-----------------------------------------------------------------------------*/ + double u1, u2; + + u1 = pm_drand(randStP); + u2 = pm_drand(randStP); + + if (u1 < DBL_EPSILON) + u1 = DBL_EPSILON; + + *r1P = sqrt(-2.0 * log(u1)) * cos(2.0 * M_PI * u2); + *r2P = sqrt(-2.0 * log(u1)) * sin(2.0 * M_PI * u2); +} + + + +double +pm_gaussrand(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + A Gaussian (or normally) distributed random number. + + Mean = 0, Standard deviation = 1. + + If a randStP->gaussCache has a value, return that value. Otherwise call + pm_gaussrand2; return one generated value, remember the other. +-----------------------------------------------------------------------------*/ + double retval; + + if (!randStP->gaussCacheValid) { + pm_gaussrand2(randStP, &retval, &randStP->gaussCache); + randStP->gaussCacheValid = true; + } else { + retval = randStP->gaussCache; + randStP->gaussCacheValid = false; + } + + return retval; +} + + + +void +pm_randinit(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + Initialize the random number generator. +-----------------------------------------------------------------------------*/ + switch (PM_RANDOM_NUMBER_GENERATOR) { + case PM_RAND_SYS_RAND: + randStP->vtable = pm_randsysrand_vtable; + break; + case PM_RAND_SYS_RANDOM: + randStP->vtable = pm_randsysrandom_vtable; + break; + case PM_RAND_MERSENNETWISTER: + randStP->vtable = pm_randmersenne_vtable; + break; + default: + pm_error("INTERNAL ERROR: Invalid value of " + "PM_RANDOM_NUMBER_GENERATOR (random number generator " + "engine type): %u", PM_RANDOM_NUMBER_GENERATOR); + } + + randStP->vtable.init(randStP); + + randStP->gaussCacheValid = false; +} + + + +void +pm_randterm(struct pm_randSt * const randStP) { +/*---------------------------------------------------------------------------- + Tear down the random number generator. +-----------------------------------------------------------------------------*/ + if (randStP->stateP) + free(randStP->stateP); +} + + + + diff --git a/lib/util/rand.h b/lib/util/rand.h new file mode 100644 index 00000000..c441890a --- /dev/null +++ b/lib/util/rand.h @@ -0,0 +1,107 @@ +/* Interface header file for random number generator functions in libnetpbm */ + +#ifndef RAND_H_INCLUDED +#define RAND_H_INCLUDED + +#include "netpbm/pm_c_util.h" +#include "netpbm/mallocvar.h" + +/* + Definitions for selecting the random number generator + + The default random number generator is Mersenne Twister. Here we + provide a means to revert to the system rand() generator if need be. + + Glibc provides generators: rand(), random() and drand48(). Each has + its own associated functions. In the Glibc documentation rand() is + called "ISO", random() is called "BSD" and drand48() is called "SVID". + If your system has the glibc documentation installed "info rand" should + get you to the relevant page. The documentation is available online + from: + + https://www.gnu.org/software/libc/manual/html_node/Pseudo_002dRandom-Numbers.html + Pseudo-Random Numbers (The GNU C Library) + + Glibc's choice of name is confusing for what it calls "ISO" rand() + was available in early BSD systems. + + Functions by these names appear on most Unix systems, but generation + formulas and default initial states are known to differ. On systems + which do not use glibc, what is called rand() may have no relation + with the formula of the ISO C standard. Likewise random() may have + no relation with the BSD formula. +*/ + +enum PmRandEngine {PM_RAND_SYS_RAND, /* rand() */ + PM_RAND_SYS_RANDOM, /* random() */ + PM_RAND_SYS_DRAND48, /* drand48() reserved */ + PM_RAND_MERSENNETWISTER /* default */}; + +#ifndef PM_RANDOM_NUMBER_GENERATOR + #define PM_RANDOM_NUMBER_GENERATOR PM_RAND_MERSENNETWISTER +#endif + + +/* Structure to hold random number generator profile and internal state */ + +struct pm_randSt; + +struct pm_rand_vtable { + void + (*init)(struct pm_randSt * const randStP); + + void + (*srand)(struct pm_randSt * const randStP, + unsigned int const seed); + + unsigned long int + (*rand)(struct pm_randSt * const randStP); +}; + +extern struct pm_rand_vtable const pm_randsysrand_vtable; +extern struct pm_rand_vtable const pm_randsysrandom_vtable; +extern struct pm_rand_vtable const pm_randmersenne_vtable; + +struct pm_randSt { + struct pm_rand_vtable vtable; + void * stateP; /* Internal state */ + unsigned int max; + unsigned int seed; + bool gaussCacheValid; + double gaussCache; +}; + +/* Function declarations */ + +extern void +pm_randinit(struct pm_randSt * const randStP); + +extern void +pm_randterm(struct pm_randSt * const randStP); + +extern void +pm_srand(struct pm_randSt * const randStP, + unsigned int const seed); + + +extern void +pm_srand2(struct pm_randSt * const randStP, + bool const withSeed, + unsigned int const seedVal); + +extern unsigned long int +pm_rand(struct pm_randSt * const randStP); + +extern double +pm_drand(struct pm_randSt * const randStP); + +extern void +pm_gaussrand2(struct pm_randSt * const randStP, + double * const r1P, + double * const r2P); + +extern double +pm_gaussrand(struct pm_randSt * const randStP); + + +#endif diff --git a/lib/util/randmersenne.c b/lib/util/randmersenne.c new file mode 100644 index 00000000..34355a23 --- /dev/null +++ b/lib/util/randmersenne.c @@ -0,0 +1,192 @@ +#include "netpbm/pm.h" +#include "netpbm/rand.h" + +/* +++++ Start of Mersenne Twister pseudorandom number generator code +++++ */ + +/* + Original source code from: + http://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/VERSIONS/C-LANG/c-lang.html + + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Any feedback is very welcome. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) + + Above conditions apply in the following code to the line which says: + +++++ End of Mersenne Twister pseudorandom number generator code +++++ +*/ + +/* Period parameters */ + +#define MT_N 624 +#define MT_M 397 +#define MT_MATRIX_A 0x9908b0dfUL /* constant vector a */ + +struct MtState { + uint32_t mt[MT_N]; /* the array for the state vector */ + unsigned int mtIndex; +}; + + + +static void +randMtAlloc(struct MtState ** const statePP) { + + struct MtState * stateP; + + MALLOCVAR_NOFAIL(stateP); + + *statePP = stateP; +} + + + +/* 32 bit masks */ + +static uint32_t const FMASK = 0xffffffffUL; /* all bits */ +static uint32_t const UMASK = 0x80000000UL; /* most significant bit */ +static uint32_t const LMASK = 0x7fffffffUL; /* least significant 31 bits */ + + + +static void +srandMt(struct MtState * const stateP, + unsigned int const seed) { +/*----------------------------------------------------------------------------- + Initialize state array mt[MT_N] with seed +-----------------------------------------------------------------------------*/ + unsigned int mtIndex; + uint32_t * const mt = stateP->mt; + + mt[0]= seed & FMASK; + + for (mtIndex = 1; mtIndex < MT_N; ++mtIndex) { + mt[mtIndex] = (1812433253UL * (mt[mtIndex-1] + ^ (mt[mtIndex-1] >> 30)) + mtIndex); + + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + } + + stateP->mtIndex = mtIndex; +} + + + +static unsigned long int +randMt32(struct MtState * const stateP) { +/*---------------------------------------------------------------------------- + Generate a 32 bit random number interval: [0, 0xffffffff] + ----------------------------------------------------------------------------*/ + unsigned int mtIndex; + uint32_t retval; + + if (stateP->mtIndex >= MT_N) { + /* generate N words at one time */ + uint32_t * const mt = stateP->mt; + uint32_t const mag01[2]={0x0UL, MT_MATRIX_A}; + /* mag01[x] = x * MT_MATRIX_A for x=0, 1 */ + + int k; + uint32_t y; + + if (stateP->mtIndex >= MT_N+1) { + pm_error("Internal error in Mersenne Twister random number" + "generator"); + } + + for (k = 0; k < MT_N-MT_M; ++k) { + y = (mt[k] & UMASK) | (mt[k+1] & LMASK); + mt[k] = mt[k + MT_M] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + for (; k < MT_N-1; ++k) { + y = (mt[k] & UMASK) | (mt[k+1] & LMASK); + mt[k] = mt[k+(MT_M-MT_N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + y = (mt[MT_N - 1] & UMASK) | (mt[0] & LMASK); + mt[MT_N - 1] = mt[MT_M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + mtIndex = 0; + } else + mtIndex = stateP->mtIndex; + + retval = stateP->mt[mtIndex]; + + /* Tempering */ + retval ^= (retval >> 11); + retval ^= (retval << 7) & 0x9d2c5680UL; + retval ^= (retval << 15) & 0xefc60000UL; + retval ^= (retval >> 18); + + stateP->mtIndex = mtIndex + 1; + + return retval; +} + +/* +++++ End of Mersenne Twister pseudorandom number generator code +++++ */ + + +static void +vinit(struct pm_randSt * const randStP) { + + randMtAlloc((struct MtState ** const) &randStP->stateP); + randStP->max = 0xffffffffUL; +} + + + +static void +vsrand(struct pm_randSt * const randStP, + unsigned int const seed) { + + srandMt(randStP->stateP, seed); +} + + + +static unsigned long int +vrand(struct pm_randSt * const randStP) { + + return randMt32(randStP->stateP); +} + + + +struct pm_rand_vtable const pm_randmersenne_vtable = { + &vinit, + &vsrand, + &vrand +}; + + diff --git a/lib/util/randsysrand.c b/lib/util/randsysrand.c new file mode 100644 index 00000000..f97a5d3c --- /dev/null +++ b/lib/util/randsysrand.c @@ -0,0 +1,39 @@ +#include <stdlib.h> + +#include "netpbm/rand.h" + + + +static void +vinit(struct pm_randSt * const randStP) { + + randStP->max = RAND_MAX; + randStP->stateP = NULL; +} + + + +static void +vsrand(struct pm_randSt * const randStP, + unsigned int const seed) { + + srand(seed); +} + + + +static unsigned long int +vrand(struct pm_randSt * const randStP) { + + return rand(); +} + + + +struct pm_rand_vtable const pm_randsysrand_vtable = { + &vinit, + &vsrand, + &vrand +}; + + diff --git a/lib/util/randsysrandom.c b/lib/util/randsysrandom.c new file mode 100644 index 00000000..3e38b0ae --- /dev/null +++ b/lib/util/randsysrandom.c @@ -0,0 +1,40 @@ +#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */ +#define _XOPEN_SOURCE 500 /* Make sure random() is in stdlib.h */ +#define _BSD_SOURCE /* Make sure random() is in stdlib.h */ + +#include <stdlib.h> + +#include "netpbm/rand.h" + +static void +vinit(struct pm_randSt * const randStP) { + + randStP->max = RAND_MAX; + randStP->stateP = NULL; +} + + + +static void +vsrand(struct pm_randSt * const randStP, + unsigned int const seed) { + + srandom(seed); +} + + + +static unsigned long int +vrand(struct pm_randSt * const randStP) { + + return random(); +} + + +struct pm_rand_vtable const pm_randsysrandom_vtable = { + &vinit, + &vsrand, + &vrand +}; + + diff --git a/other/pamexec.c b/other/pamexec.c index c3a1ee78..cbde81ac 100644 --- a/other/pamexec.c +++ b/other/pamexec.c @@ -15,8 +15,11 @@ #define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ #define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ +#include <stdbool.h> #include <string.h> #include <stdio.h> +#include <signal.h> +#include <setjmp.h> #include <errno.h> #include "pm_c_util.h" @@ -25,7 +28,27 @@ #include "mallocvar.h" #include "pam.h" -struct cmdlineInfo { + +/* About SIGPIPE: + + Unix has a strange function where by default, if you write into a pipe when + the reading side of the pipe has been closed, it generates a signal (of + class SIGPIPE), but if you tell the OS to ignore signals of class SIGPIPE, + then instead of generating that signal, the system call to write to the + pipe just fails. + + Pamexec writes to a pipe when it feeds an image to the user's program's + Standard Input. Should the user's program close its end of the pipe (such + as by exiting) before reading the whole image, that would, if we did + nothing to deal with it, cause Pamexec to receive a SIGPIPE signal, which + would make the OS terminate Pamexec. + + We don't want that, so we tell the OS to ignore SIGPIPE signals, so that + instead our attempt to write to the pipe just fails and we fail with a + meaningful error message. +*/ + +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ @@ -40,7 +63,7 @@ struct cmdlineInfo { static void parseCommandLine(int argc, const char ** argv, - struct cmdlineInfo * const cmdlineP) { + struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the pointers we place into *cmdlineP are sometimes to storage in the argv array. @@ -59,13 +82,13 @@ parseCommandLine(int argc, const char ** argv, OPTENT3(0, "check", OPT_FLAG, NULL, &cmdlineP->check, 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 */ + 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 (argc-1 < 1) + if (argc-1 < 1) pm_error("You must specify at least one argument: the shell command " "to execute"); else { @@ -92,7 +115,7 @@ pipeOneImage(FILE * const infileP, struct pam inpam; struct pam outpam; enum pm_check_code checkRetval; - + unsigned int row; tuple * tuplerow; @@ -107,9 +130,25 @@ pipeOneImage(FILE * const infileP, tuplerow = pnm_allocpamrow(&inpam); - for (row = 0; row < inpam.height; ++row) { - pnm_readpamrow(&inpam, tuplerow); - pnm_writepamrow(&outpam, tuplerow); + { + jmp_buf jmpbuf; + int rc; + rc = setjmp(jmpbuf); + if (rc == 0) { + pm_setjmpbuf(&jmpbuf); + + for (row = 0; row < inpam.height; ++row) { + pnm_readpamrow(&inpam, tuplerow); + pnm_writepamrow(&outpam, tuplerow); + } + } else { + pm_setjmpbuf(NULL); + pm_error("Failed to read image and pipe it to program's " + "Standard Input. If previous messages indicate " + "a broken pipe error, that means the program closed " + "its Standard Error (possibly by exiting) before " + "it had read the entire image>"); + } } pnm_freepamrow(tuplerow); @@ -133,7 +172,7 @@ doOneImage(FILE * const ifP, ofP = popen(command, "w"); if (ofP == NULL) - pm_asprintf(errorP, + pm_asprintf(errorP, "Failed to start shell to run command '%s'. " "errno = %d (%s)", command, errno, strerror(errno)); @@ -141,7 +180,7 @@ doOneImage(FILE * const ifP, int rc; pipeOneImage(ifP, ofP); - + rc = pclose(ofP); if (check && rc != 0) @@ -157,7 +196,7 @@ doOneImage(FILE * const ifP, int main(int argc, const char *argv[]) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; FILE * ifP; /* Input file pointer */ int eof; /* No more images in input */ @@ -169,12 +208,17 @@ main(int argc, const char *argv[]) { pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); - + + /* Make write to closed pipe fail rather than generate a signal. + See comments at top of program. + */ + signal(SIGPIPE, SIG_IGN); + ifP = pm_openr(cmdline.inputFileName); - for (eof = FALSE, imageSeq = 0; !eof; ++imageSeq) { + for (eof = false, imageSeq = 0; !eof; ++imageSeq) { const char * error; - + doOneImage(ifP, cmdline.command, cmdline.check, &error); if (error) { @@ -185,9 +229,8 @@ main(int argc, const char *argv[]) { pnm_nextimage(ifP, &eof); } pm_close(ifP); - + return 0; } - diff --git a/test/Execute-Tests b/test/Execute-Tests index 3530d978..7a02fdef 100755 --- a/test/Execute-Tests +++ b/test/Execute-Tests @@ -178,6 +178,7 @@ elif [ $VALGRIND_TESTS = "on" ] mkdir $valdir vg_command_base="valgrind --trace-children=yes"; + # You may want to add --track-origins=yes to the above. for i in awk basename cat cksum cmp comm cp cut date dirname \ egrep fgrep file grep gs head iconv mkdir mktemp perl rm \ @@ -185,6 +186,7 @@ elif [ $VALGRIND_TESTS = "on" ] testrandom Available-Testprog # Tell valgrind not to probe execution of the above programs. + # You may add programs in Netpbm to the above to speed up tests. do vg_skip=$vg_skip"/*/"$i","; done; @@ -238,10 +240,11 @@ if [ $VALGRIND_TESTS = "on" ] fi # Execute a single test and test its result. -# But first check if the .ok file exists. (Some .ok files are -# dynamically created.) Then see if target programs and requirements -# are in place. If either of these conditions are not met, do -# not execute the test and report "Not Testable". +# But first check if the .ok file exists. +# (In past versions certain .ok files were dynamically created.) +# Then see if target programs and requirements are in place. If +# either of these conditions are not met, do not execute the test and +# report "Not Testable". if [ ! -s ${srcdir}/${tname%.test}.ok ] then diff --git a/test/Makefile b/test/Makefile index c640dfff..732113f3 100644 --- a/test/Makefile +++ b/test/Makefile @@ -8,27 +8,14 @@ include $(BUILDDIR)/config.mk MERGE_OBJECTS = -PROGS = testrandom - -OKSTOGENERATE = $(patsubst %.rand-ok, %.ok, $(wildcard *.rand-ok)) - -all: $(PROGS) $(OKSTOGENERATE) - -testrandom.o: testrandom.c - $(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) $< - -testrandom: testrandom.o - $(LD_FOR_BUILD) -o $@ $(LDFLAGS_FOR_BUILD) $< - -RAND_VARIETY ?= $(shell ./testrandom -x) - -$(OKSTOGENERATE): %.ok: %.rand-ok testrandom - sed -n "/^$(RAND_VARIETY)|/s/^$(RAND_VARIETY)|//p" $< > $@ +PROGS = OMIT_TEST_RULE = 1 include $(SRCDIR)/common.mk +all: + distclean clean: cleanlocal .PHONY: cleanlocal cleanlocal: - rm -f $(PROGS) $(patsubst %.rand-ok,%.ok,$(wildcard *.rand-ok)) + diff --git a/test/all-in-place.ok b/test/all-in-place.ok index 4d840102..88626e3d 100644 --- a/test/all-in-place.ok +++ b/test/all-in-place.ok @@ -61,6 +61,7 @@ pamfunc: ok pamgauss: ok pamgetcolor: ok pamgradient: ok +pamhomography: ok pamhue: ok pamlevels: ok pamlookup: ok diff --git a/test/all-in-place.test b/test/all-in-place.test index 78a9346a..e21c2ad8 100755 --- a/test/all-in-place.test +++ b/test/all-in-place.test @@ -103,6 +103,7 @@ ordinary_testprogs="\ pamgauss \ pamgetcolor \ pamgradient \ + pamhomography \ pamhue \ pamlevels \ pamlookup \ diff --git a/test/pamditherbw-random.ok b/test/pamditherbw-random.ok new file mode 100755 index 00000000..d21e3613 --- /dev/null +++ b/test/pamditherbw-random.ok @@ -0,0 +1,6 @@ +Test: Floyd-Steinberg +Should print 3849876047 33894 +3849876047 33894 +Test: Atkinson +Should print 2887295695 33894 +2887295695 33894 diff --git a/test/pamditherbw-random.test b/test/pamditherbw-random.test new file mode 100755 index 00000000..5bff4bac --- /dev/null +++ b/test/pamditherbw-random.test @@ -0,0 +1,23 @@ +#! /bin/bash +# This script tests: pamditherbw +# Also requires: pamchannel pamtopnm + +tmpdir=${tmpdir:-/tmp} +test_red=${tmpdir}/testimg.red + +# Test 1. Floyd-Steinberg +echo "Test: Floyd-Steinberg" +echo "Should print 3849876047 33894" + +pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \ + tee ${test_red} | \ + pamditherbw -fs -randomseed=1 | cksum + + +# Test 2. Atkinson +echo "Test: Atkinson" +echo "Should print 2887295695 33894" + +pamditherbw -atkinson -randomseed=1 ${test_red} | cksum + +rm ${test_red} \ No newline at end of file diff --git a/test/pamexec.test b/test/pamexec.test index 44c11339..ec57f4ae 100755 --- a/test/pamexec.test +++ b/test/pamexec.test @@ -27,7 +27,8 @@ pamexec "pbmtog3 -no | g3topbm" ${combined_pbm} | cksum cat ${combined_pbm} | cksum echo "Invalid command" 1>&2 -echo "Errors message should appear below the line." 1>&2 +echo "Executes quietly." 1>&2 +echo "Errors message should not appear below the line." 1>&2 echo "-----------------------------------------------------------" 1>&2 echo "Test Invalid: Should not print anything" @@ -35,4 +36,4 @@ echo "Test Invalid: Should not print anything" pamexec "false" ${combined_pbm} pamexec "pamfile | false" ${combined_pbm} -rm ${combined_pbm} \ No newline at end of file +rm ${combined_pbm} diff --git a/test/pamrecolor.ok b/test/pamrecolor.ok new file mode 100755 index 00000000..0a6b5413 --- /dev/null +++ b/test/pamrecolor.ok @@ -0,0 +1,11 @@ +Test 1. Should produce 3500040755 101532 +3500040755 101532 +Test 2. Should produce 3500040755 101532 twice +3500040755 101532 +3500040755 101532 +Expected failure 1 1 +Expected failure 2 1 +Expected failure 3 1 +Expected failure 4 1 +Expected failure 5 1 +Expected failure 6 1 diff --git a/test/pamrecolor.test b/test/pamrecolor.test new file mode 100755 index 00000000..cc79a124 --- /dev/null +++ b/test/pamrecolor.test @@ -0,0 +1,43 @@ +#! /bin/bash +# This script tests: pamrecolor +# Also requires: ppmtopgm pgmmake + +tmpdir=${tmpdir:-/tmp} +base_pgm=${tmpdir}/base.pgm + +pgmmake 0.5 230 150 > ${base_pgm} + +echo "Test 1. Should produce 3500040755 101532" + +pamrecolor --colorfile=${base_pgm} testimg.ppm | cksum + +echo "Test 2. Should produce 3500040755 101532 twice" + +pamrecolor --targetcolor=rgb:80/80/80 testimg.ppm | cksum +pamrecolor --colorspace=ntsc --targetcolor=rgb:80/80/80 testimg.ppm | cksum + + +test_out=${tmpdir}/test_out +truncated_file=${tmpdir}/truncated.txt +echo P6 > ${truncated_file} + +echo 1>&2 +echo "Invalid command-line argument combinations." 1>&2 +echo "Error messages should appear below the line." 1>&2 +echo "-----------------------------------------------------------" 1>&2 + +pamrecolor --targetcolor=rgb:00/11/22 --colorfile={base1_pgm} testimg.ppm > ${test_out} || \ + echo -n "Expected failure 1"; test -s ${test_out}; echo " "$? +pamrecolor --rmult=0.3 --gmult=0.3 --bmult=0.3 --colorfile={base1_pgm} testimg.ppm > ${test_out} || \ + echo -n "Expected failure 2"; test -s ${test_out}; echo " "$? +pamrecolor --colorspace=void --targetcolor=rgb:80/80/80 testimg.ppm > ${test_out} || \ + echo -n "Expected failure 3"; test -s ${test_out}; echo " "$? +pamrecolor --targetcolor=vague testimg.ppm > ${test_out} || \ + echo -n "Expected failure 4"; test -s ${test_out}; echo " "$? +pamrecolor --colorfile=${truncated_file} testimg.ppm > ${test_out} || \ + echo -n "Expected failure 5"; test -s ${test_out}; echo " "$? +pamrecolor --rmult=0.2989 --gmult=0.5866 testimg.ppm > ${test_out} || \ + echo -n "Expected failure 6"; test -s ${test_out}; echo " "$? + + +rm ${base_pgm} ${test_out} ${truncated_file} \ No newline at end of file diff --git a/test/pbmlife.ok b/test/pbmlife.ok new file mode 100755 index 00000000..bd214dfb --- /dev/null +++ b/test/pbmlife.ok @@ -0,0 +1,63 @@ +P1 +63 29 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000001111111000111100000000000111000111000000000000000 +000000000000000100001001100110000000001000101001100000000000000 +000000000000000100000001000010000000001000101000100000000000000 +000000000000000100010010000001000010001100101000100000000000000 +000000000000000111110010000001000010000111001100100000000000000 +000000000000000100010010000001001111101001100111100000000000000 +000000000000000100000001000010000010001000100001000000000000000 +000000000000000100000001100110000010001000100011000000000000000 +000000000000001111000000111100000000000111001100000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000 + + tiles: 1722 + x-edges: 1812 + y-edges: 1842 +vertices: 1920 + area: 1722 +perimeter: 420 + eulerchi: -12 + + tiles: 17 + x-edges: 33 + y-edges: 33 +vertices: 64 + area: 17 +perimeter: 64 + eulerchi: 15 + + tiles: 2 + x-edges: 3 + y-edges: 4 +vertices: 6 + area: 2 +perimeter: 6 + eulerchi: 1 + + tiles: 0 + x-edges: 0 + y-edges: 0 +vertices: 0 + area: 0 +perimeter: 0 + eulerchi: 0 diff --git a/test/pbmlife.test b/test/pbmlife.test new file mode 100755 index 00000000..b4d65ec4 --- /dev/null +++ b/test/pbmlife.test @@ -0,0 +1,13 @@ +#! /bin/bash +# This script tests: pbmlife pbmminkowski +# Also requires: pbmtext + +pbmtext FO+89 -plain +echo +pbmtext FO+89 | pbmminkowski +echo +pbmtext FO+89 | pbmlife | pbmminkowski +echo +pbmtext FO+89 | pbmlife | pbmlife | pbmminkowski +echo +pbmtext FO+89 | pbmlife | pbmlife | pbmlife | pbmminkowski diff --git a/test/pgmnoise.rand-ok b/test/pgmnoise.rand-ok deleted file mode 100644 index b69f48e5..00000000 --- a/test/pgmnoise.rand-ok +++ /dev/null @@ -1,3 +0,0 @@ -000|0 -081|2005134911 10015 -082|3516404574 10015 diff --git a/test/pgmnoise.test b/test/pgmnoise.test index 03301ce6..21a2729e 100755 --- a/test/pgmnoise.test +++ b/test/pgmnoise.test @@ -1,7 +1,89 @@ #! /bin/bash # This script tests: pgmnoise -# Also requires: +# Also requires: pgmhist pamvalidate -# Should print: 1663614689 10015 (Glibc) -# 3516404574 10015 (MAC OS) +echo "Test 1." +echo "Should print: 2132901423 10015" # (Mersenne Twister) +# 1663614689 10015 (Glibc rand()) +# 3516404574 10015 (MAC OS rand()) pgmnoise --randomseed=0 100 100 | cksum + + +echo "Test 2." +# Output is similar to that of Test 2. of random-generator.test +# The lowest four decimal digits are printed. + +pgmnoise --randomseed=5489 -maxval=9999 -plain 5 20 + + +echo "Test 3." +for maxval in `seq 16` 255 65535 + do + echo ${maxval} + pgmnoise -maxval=${maxval} -randomseed=1 -plain 16 1 | tr '\n' ' ' + done +echo + +echo "Test 4." +# Check for maxval violation +for maxval in `seq 16` 30 31 32 254 255 256 65534 65535 + do + echo -n ${maxval} " " + pgmnoise -maxval=${maxval} -randomseed=1 -plain ${maxval} 10 | \ + pamvalidate | pamfile + done + +echo "Test 5." +echo "Should print four identical lines" +# width height values do not affect random number sequence +for xysize in "1 10000" "100 100" "250 40" "1000 10" + do pgmnoise --randomseed=0 ${xysize} | pgmhist -mach | cksum + done + + +tmpdir=${tmpdir:-/tmp} +messages=${tmpdir}/messages + +echo "Test 6." +echo "First column should be 2^n - 1" +# The "pool" method of generating pixvals is used iff maxval is +# a power of 2 minus 1: 1, 3, 7, 15, 31 ... +for maxval in `seq 35; seq 60 69; seq 120 129; seq 250 259` + do + pgmnoise -maxval=${maxval} -randomseed=1 -verbose 1 1 > /dev/null \ + 2> ${messages} + awk -v mval=${maxval} '/method/ && /pool/ { print mval, $0 }' ${messages} + done + + rm ${messages} + + +echo "Test Invalid" + +test_out=${tmpdir}/test_out + +echo 1>&2 +echo "Invalid command-line arguments." 1>&2 +echo "Error messages should appear below the line." 1>&2 +echo "-----------------------------------------------------------" 1>&2 + +pgmnoise 0 0 > ${test_out} || \ + echo -n "Expected failure 1"; test -s ${test_out}; echo " "$? +pgmnoise 0 1 > ${test_out} || \ + echo -n "Expected failure 2"; test -s ${test_out}; echo " "$? +pgmnoise 1 0 > ${test_out} || \ + echo -n "Expected failure 3"; test -s ${test_out}; echo " "$? +pgmnoise > ${test_out} || \ + echo -n "Expected failure 4"; test -s ${test_out}; echo " "$? +pgmnoise 1 > ${test_out} || \ + echo -n "Expected failure 5"; test -s ${test_out}; echo " "$? +pgmnoise 100 -1 > ${test_out} || \ + echo -n "Expected failure 6"; test -s ${test_out}; echo " "$? +pgmnoise -randomseed=-1 100 100 > ${test_out} || \ + echo -n "Expected failure 7"; test -s ${test_out}; echo " "$? +pgmnoise -maxval=-1 100 100 > ${test_out} || \ + echo -n "Expected failure 8"; test -s ${test_out}; echo " "$? +pgmnoise -maxval=0 100 100 > ${test_out} || \ + echo -n "Expected failure 9"; test -s ${test_out}; echo " "$? +pgmnoise -maxval=$((256 * 256 * 256 * 256)) 10 10 > ${test_out} || \ + echo -n "Expected failure 10"; test -s ${test_out}; echo " "$? diff --git a/test/ppmforge-parameters.ok b/test/ppmforge-parameters.ok index 37d89ccd..c8edc5dd 100644 --- a/test/ppmforge-parameters.ok +++ b/test/ppmforge-parameters.ok @@ -1,6 +1,8 @@ -Test 1 +Test 1: Should print 256 256 256 256 -100 90 +Test 2: Should print 40 30 +40 30 +Test 3: Should print 90 90 90 90 Test Invalid Expected failure 1 1 @@ -8,3 +10,5 @@ Expected failure 2 1 Expected failure 3 1 Expected failure 4 1 Expected failure 5 1 +Expected failure 6 1 +Expected failure 7 1 diff --git a/test/ppmforge-parameters.test b/test/ppmforge-parameters.test index 28a92916..39f199ea 100755 --- a/test/ppmforge-parameters.test +++ b/test/ppmforge-parameters.test @@ -2,17 +2,21 @@ # This script tests: ppmforge # Also requires: pamfile -echo "Test 1" +echo "Test 1: Should print 256 256" -# Should print 256 256 +# Default size is 256 256 ppmforge -night | pamfile -size +echo "Test 2: Should print 40 30" + # Width is adjusted if not even -# becomes 100 in this case -ppmforge -night -width=99 -height=90 | pamfile -size +# becomes 40 in this case +ppmforge -night -width=39 -height=30 | pamfile -size + +echo "Test 3: Should print 90 90" # Width is adjusted if smaller than height -# brought up to 100 in this case +# brought up to 90 in this case ppmforge -night -width=80 -height=90 | pamfile -size echo "Test Invalid" @@ -27,13 +31,17 @@ echo "-----------------------------------------------------------" 1>&2 ppmforge -night -dimension=0 > ${test_out} || \ echo -n "Expected failure 1"; test -s ${test_out}; echo " "$? -ppmforge -clouds -mesh=1.99 > ${test_out} || \ +ppmforge -dimension=10 > ${test_out} || \ echo -n "Expected failure 2"; test -s ${test_out}; echo " "$? -ppmforge -clouds -power=0 > ${test_out} || \ +ppmforge -dimension=-1 > ${test_out} || \ echo -n "Expected failure 3"; test -s ${test_out}; echo " "$? -ppmforge -ice=0 > ${test_out} || \ +ppmforge -clouds -mesh=1.99 > ${test_out} || \ echo -n "Expected failure 4"; test -s ${test_out}; echo " "$? -ppmforge -glaciers=0 > ${test_out} || \ +ppmforge -clouds -power=0 > ${test_out} || \ echo -n "Expected failure 5"; test -s ${test_out}; echo " "$? +ppmforge -ice=-1 > ${test_out} || \ + echo -n "Expected failure 6"; test -s ${test_out}; echo " "$? +ppmforge -glaciers=-1 > ${test_out} || \ + echo -n "Expected failure 7"; test -s ${test_out}; echo " "$? rm ${test_out} diff --git a/test/ppmforge.rand-ok b/test/ppmforge.rand-ok deleted file mode 100644 index c8b3ac9f..00000000 --- a/test/ppmforge.rand-ok +++ /dev/null @@ -1,3 +0,0 @@ -000|0 -081|3634219838 196623 -082|3262664440 196623 diff --git a/test/ppmpat-random.rand-ok b/test/ppmpat-random.rand-ok deleted file mode 100644 index eb8779ab..00000000 --- a/test/ppmpat-random.rand-ok +++ /dev/null @@ -1,7 +0,0 @@ -000|0 -081|2219119109 36015 -081|3436846137 16813 -081|908097729 16813 -082|3606254242 36015 -082|3615722579 16813 -082|1756684515 16813 diff --git a/test/ppmpat-random.test b/test/ppmpat-random.test index a6daa982..44dd2485 100755 --- a/test/ppmpat-random.test +++ b/test/ppmpat-random.test @@ -7,16 +7,22 @@ # These tests require random numbers. -# Test 1. Should print: 2219119109 36015 (glibc) -# 3606254242 36015 (MAC OS) +echo "Test 1. Should print: 1366170000 36015" # Mersenne Twister +# 2219119109 36015 (glibc rand()) +# 3606254242 36015 (MAC OS rand()) + ppmpat --randomseed=0 -camo 100 120 | cksum -# Test 2. Should print: 3436846137 16813 (glibc) -# 3615722579 16813 (MAC OS) +echo "Test 2. Should print: 4073196212 16813" # Mersenne Twister +# 3436846137 16813 (glibc) +# 3615722579 16813 (MAC OS rand()) + ppmpat --randomseed=0 -anticamo 80 70 | cksum -# Test 3. Should print: 908097729 16813 (glibc) -# 1756684515 16813 (MAC OS) +echo "Test 3. Should print: 2292015301 16813" # Mersenne Twister +# 908097729 16813 (glibc rand()) +# 1756684515 16813 (MAC OS rand()) + ppmpat --randomseed=0 --color \ rgb:55/c0/34,rgb:0/ff/0,rgb:0/ee/0,rgb:0/cd/0,rgb:0/8b/0,rgb:4f/4f/2f \ -camo 80 70 | cksum diff --git a/test/ppmrough.rand-ok b/test/ppmrough.rand-ok deleted file mode 100644 index 216545c7..00000000 --- a/test/ppmrough.rand-ok +++ /dev/null @@ -1,3 +0,0 @@ -000|0 -081|378403602 30015 -082|378403602 30015 diff --git a/test/ppmshift.ok b/test/ppmshift.ok new file mode 100755 index 00000000..e18de8f0 --- /dev/null +++ b/test/ppmshift.ok @@ -0,0 +1,14 @@ +Test 1. Should print: 3705777303 101484 +3705777303 101484 +Test 2. Should print: 202790723 685 +202790723 685 +Test 3. Should print: 0 : 0 +0 : 0 +Test 4. (15) Should print: 0 : 0 +0 : 0 +Test 4. (16) Should print: 0 : 0 +0 : 0 +Test 4. (20) Should print: 0 : 0 +0 : 0 +Test 4. (1000) Should print: 0 : 0 +0 : 0 diff --git a/test/ppmshift.test b/test/ppmshift.test new file mode 100755 index 00000000..cd086d68 --- /dev/null +++ b/test/ppmshift.test @@ -0,0 +1,27 @@ +#! /bin/bash +# This script tests: ppmshift +# Also requires: + +echo "Test 1. Should print: 3705777303 101484" +ppmshift -seed=1 10 testimg.ppm | cksum + +echo "Test 2. Should print: 202790723 685" +ppmshift -seed=1 1 testgrid.pbm | cksum + +echo "Test 3. Should print: 0 : 0" +ppmshift -seed=1 0 testimg.ppm | cmp -s - testimg.ppm | \ + echo ${PIPESTATUS[@]} ":" $? + +tmpdir=${tmpdir:-/tmp} +test_pbm=${tmpdir}/test.pbm + +ppmshift -seed=2 14 testgrid.pbm > ${test_pbm} + +for i in 15 16 20 1000 + do + echo "Test 4. ("$i") Should print: 0 : 0" + ppmshift -seed=2 $i testgrid.pbm > ${test_pbm} | cmp -s - testgrid.ppm | \ + echo ${PIPESTATUS[@]} ":" $? + done + +rm ${test_pbm} \ No newline at end of file diff --git a/test/ppmspread.ok b/test/ppmspread.ok new file mode 100755 index 00000000..7d44ab8e --- /dev/null +++ b/test/ppmspread.ok @@ -0,0 +1,6 @@ +Test 1. Should print 639729144 101484 +639729144 101484 +Test 2. Should print 3278353642 685 +3278353642 685 +Test 3. Should print 2425386270 41 +2425386270 41 diff --git a/test/ppmspread.test b/test/ppmspread.test new file mode 100755 index 00000000..871f7438 --- /dev/null +++ b/test/ppmspread.test @@ -0,0 +1,12 @@ +#! /bin/bash +# This script tests: ppmspread +# Also requires: ppmtopgm pgmtopbm + +echo "Test 1. Should print 639729144 101484" +ppmspread -randomseed=1 10 testimg.ppm | cksum + +echo "Test 2. Should print 3278353642 685" +ppmspread -randomseed=1 1 testgrid.pbm | cksum + +echo "Test 3. Should print 2425386270 41" +ppmspread -randomseed=1 0 testgrid.pbm | ppmtopgm | pgmtopbm | cksum diff --git a/test/random-generator.ok b/test/random-generator.ok new file mode 100644 index 00000000..e137d901 --- /dev/null +++ b/test/random-generator.ok @@ -0,0 +1,219 @@ +Test 1: Should produce: +P2 +12 1 +1023 +720 296 192 858 101 57 298 629 804 1019 64 617 +Above output is for Mersenne Twister +P2 +12 1 +1023 +720 296 192 858 101 57 298 629 804 1019 64 617 + +Test 2: Mersenne Twister random number generator +Should produce: +3499211612 581869302 3890346734 3586334585 545404204 +4161255391 3922919429 949333985 2715962298 1323567403 + ... + 297480282 1101405687 1473439254 2634793792 1341017984 + Total 1000 integers, 200 lines + +3499211612 581869302 3890346734 3586334585 545404204 +4161255391 3922919429 949333985 2715962298 1323567403 + 418932835 2350294565 1196140740 809094426 2348838239 +4264392720 4112460519 4279768804 4144164697 4156218106 + 676943009 3117454609 4168664243 4213834039 4111000746 + 471852626 2084672536 3427838553 3437178460 1275731771 + 609397212 20544909 1811450929 483031418 3933054126 +2747762695 3402504553 3772830893 4120988587 2163214728 +2816384844 3427077306 153380495 1551745920 3646982597 + 910208076 4011470445 2926416934 2915145307 1712568902 +3254469058 3181055693 3191729660 2039073006 1684602222 +1812852786 2815256116 746745227 735241234 1296707006 +3032444839 3424291161 136721026 1359573808 1189375152 +3747053250 198304612 640439652 417177801 4269491673 +3536724425 3530047642 2984266209 537655879 1361931891 +3280281326 4081172609 2107063880 147944788 2850164008 +1884392678 540721923 1638781099 902841100 3287869586 + 219972873 3415357582 156513983 802611720 1755486969 +2103522059 1967048444 1913778154 2094092595 2775893247 +3410096536 3046698742 3955127111 3241354600 3468319344 +1185518681 3031277329 2919300778 12105075 2813624502 +3052449900 698412071 2765791248 511091141 1958646067 +2140457296 3323948758 4122068897 2464257528 1461945556 +3765644424 2513705832 3471087299 961264978 76338300 +3226667454 3527224675 1095625157 3525484323 2173068963 +4037587209 3002511655 1772389185 3826400342 1817480335 +4120125281 2495189930 2350272820 678852156 595387438 +3271610651 641212874 988512770 1105989508 3477783405 +3610853094 4245667946 1092133642 1427854500 3497326703 +1287767370 1045931779 58150106 3991156885 933029415 +1503168825 3897101788 844370145 3644141418 1078396938 +4101769245 2645891717 3345340191 2032760103 4241106803 +1510366103 290319951 3568381791 3408475658 2513690134 +2553373352 2361044915 3147346559 3939316793 2986002498 +1227669233 2919803768 3252150224 1685003584 3237241796 +2411870849 1634002467 893645500 2438775379 2265043167 + 325791709 1736062366 231714000 1515103006 2279758133 +2546159170 3346497776 1530490810 4011545318 4144499009 + 557942923 663307952 2443079012 1696117849 2016017442 +1663423246 51119001 3122246755 1447930741 1668894615 + 696567687 3983551422 3411426125 1873110678 1336658413 +3705174600 2270032533 2664425968 711455903 513451233 +2585492744 2027039028 1129453058 1461232481 2809248324 +2275654012 2960153730 3075629128 3213286615 4245057188 +1935061435 3094495853 360010077 3919490483 983448591 +2171099548 3922754098 2397746050 654458600 2161184684 +3546856898 1986311591 2312163142 2347594600 4278366025 +1922360368 335761339 3669839044 1901288696 2595154464 + 458070173 2141230976 4131320786 4208748424 19903848 + 147391738 3328215103 4196191786 3510290616 1559873971 +3731015357 2918514861 362649214 1487061100 1717053387 +3675955720 1116134897 193529268 3436267940 2835191639 +1852908272 3220971953 3911201640 571213604 781027019 +4219206494 1133024903 409547355 625085180 1214072539 + 584409985 3445042528 3733581611 333104904 2489812253 +2694595213 2361631596 34763086 622576118 2921810672 +3663740744 2293225236 2671706445 1884059696 1507329019 + 857065948 2204390003 592711182 1725752375 1642107460 + 326274448 3274574484 1030432041 173822100 529650788 +1086437636 789877945 2167974914 1030588245 3533061365 +1792148406 4216468704 213264131 3536714075 3877136173 +1296338417 4057830103 205919137 2108245233 1064497347 +2101324080 2336703164 1450493809 3812754708 3865701845 +1476779561 1585902852 142887412 477612192 699530444 +3351157089 3768249319 1673915577 903239649 1038056164 +1171465372 1734789440 2115022236 414269055 959581346 + 566820984 2105828892 4046076449 4101450561 4106566571 +2800184123 2470502098 3253453343 256751188 1869365987 +1008372035 2374606708 1516804538 228288551 3527001547 +1385173098 66157275 1739381798 184785808 3901692666 + 725806641 3475217997 2787929747 1109372433 3142723729 + 557686578 2782047723 2118822689 1936702581 1625646963 +2349385293 3085804937 1272688179 1236112995 3198431244 +2677635414 811555596 3486972196 2949678043 1342211552 + 788174404 1656614077 1582629285 1477167035 2687011245 +3503701453 3351051324 2874557775 348432514 1629591495 +3991682351 1969229192 3331660584 1304012077 2090754125 +3910846836 1871998370 2098597104 1918921592 3246092887 +1315760974 464122393 2184028058 1690455542 2193747147 +3737423698 3511684278 1549884962 3413774919 3938991454 +2767325310 2335626851 1626114941 601913200 3485711542 + 858447440 2288468476 4075602213 1506361431 4252489875 +4032981007 1031118352 3762145731 70955369 2362903502 +1669089455 2673510137 3348740333 2521337794 2047144929 + 892246357 2319875070 1293843163 79245769 2022600352 +3866257397 989939126 835351312 3626278636 3805332945 + 836506264 1895040349 970326679 634920763 733185481 +1028655248 977810701 3434484235 1871311609 2031584214 +1336174158 385787519 3965885375 2768323462 1847726660 +2718987737 793780050 2509902580 3886434164 3120956802 +4207987247 1523159183 1884932179 2922324286 477253416 +3037922812 1108379444 697195677 1755438379 574393398 +2555059183 1930828628 1126190880 180621093 2589191337 +3424652760 3054648512 719646637 952394946 3570038180 + 504304985 1395707758 1274213163 2816553213 1369142370 +1804702100 1821782344 3358274235 2181234724 486158240 + 367287522 4267199121 1127352639 779850007 3440331597 +3276765484 125500149 1142120513 3989398167 1048565860 +3136747194 432668526 2098559576 1478877150 2484746208 +1209580219 1019125185 4160278734 1970740713 918146921 +4136433784 2602441845 2348512686 973030509 2238261365 + 815637919 994690313 1724736366 2099799816 1775069742 +2680317667 730798472 2916864943 1284417767 1698724919 +2733611686 1578128411 651006053 4243350375 3303874296 + 162087183 3796616231 3801767645 4119825424 3922537059 + 77594039 3419583692 2503306160 423966005 3293613218 +1124728190 1407880681 1440346680 554334954 2919409323 +1253962019 586491243 3638308238 3097648541 991125519 + 458538714 2155963569 2807866455 6862945 2122460897 + 53853750 3346001678 1230879976 3071060893 423909157 +3881450262 1652511030 3826483009 1526211009 1435219366 +3092251623 3001090498 281084412 849586749 2207008400 + 131172352 1820973075 3195774605 2962673849 2147580010 +1090677336 2061249893 1724513375 3885752424 1135918139 +2619357288 4012575714 2652856935 2029480458 3691276589 +2623865075 3459550738 2097670126 2477000057 2209844713 + 785646024 1052349661 1030500157 1430246618 3807539761 +2157629976 123154542 2560049331 2104110449 1332109867 + 721241591 4136042859 4203401395 998151922 3060999432 +3207929139 2149509272 1385268511 2023309182 1366796638 + 256061060 4090836236 2929047008 2296609403 182240337 +3744374619 306855912 4014087816 2240468995 2865233169 + 415452309 1244206523 3513921306 281425419 3511338031 + 995954022 3102854413 3026765331 643667197 837979907 +2832983005 1813414171 2227348307 4020325887 4178893912 + 610818241 2787397224 2762441380 3437393657 2030369078 +1949046312 1876612561 1857107382 1049344864 3544695775 +2172907342 358500115 3895295219 571965125 328582064 + 744698407 3066193991 1679065087 2650874932 3570748805 + 812110431 3450423805 1705023874 259721746 1192558045 +1714799045 3685508436 2262914445 3903852862 1790140070 +2651193482 2821191752 776610414 2697125035 2212010032 +1254062056 3541766210 1853927671 1543286708 66516686 +3505195914 4226521519 1260092911 717982876 739240369 + 456195732 2116515161 1599487648 838913496 850912042 +3712172413 2103192411 877020153 1458113119 2646869271 +4087221703 3771198399 3952796001 1685641891 226245966 +4065518354 3169076409 715963611 1155859114 4174181651 +1816065125 2422210778 2353087594 2569974907 4049024520 + 563593555 1794197249 2434290377 4222178191 2381045132 +1294739153 1333544226 3011196239 518183212 2861903570 +3168787443 2315530531 1042490149 2998340365 3534153126 +2862715604 796613230 765073073 1342937225 549817636 +3786981820 4291017601 2895722553 734959362 3175258828 + 140019477 268621172 2410334776 565052604 3787587805 + 386344800 2874086067 35710270 817904650 1960697289 +1584484509 2724312018 1978802819 2275314726 4216102886 +2138332912 671754166 1442240992 3674442465 1085868016 +2769242611 1003628378 1616076847 743729558 820011032 +2559719034 1839332599 3121982280 2070268989 3769147733 + 518022934 3037227899 2531915367 1008310588 971468687 +2052976098 1651926578 78218926 2503907441 3209763057 +1081499040 2812016370 1247433164 335294964 2650385171 +2030527826 1139372809 4279827824 3540669095 2285341455 +4220507154 3863048231 3136394663 3319584205 1476940506 + 875141230 2508558662 3896001866 462864388 1609807693 +3892563868 3642514037 3778083990 1403162576 3512254868 +1403323269 1119818229 2831288053 2552740643 2520136409 + 96690857 210381252 1826474872 3306977352 1343117402 +2112059492 693571694 2096734379 767794921 1843084587 +1816280216 1695342628 404711915 3334843684 2570639553 +4186538211 2022604264 3214805180 2989079529 2725165355 +3005995436 310011850 2742468706 2720274646 144327376 +2271696819 295519962 1272030376 1372670420 1397272558 +2280044719 2710639434 2810822904 4271368265 1750711132 +2216408539 3521792518 3111505866 3085328191 1054735512 +4160317205 1427385632 2282061755 3215251668 1396490078 +2933318719 453673969 2926038256 2624047458 338625410 +3344930154 1971116345 1818716442 2998517928 390083048 + 291563131 1144486353 296954266 659950561 2263631666 +1206908601 1125491020 1890151284 2076080514 2264060846 + 561805191 1964622705 405620012 3759692386 517035386 +2225016848 4165419081 4052828294 3248204933 2738939733 +1151808775 4113264137 3113447491 1033828852 1785686386 +2903923175 2038900010 1241522880 238119113 2885394101 +2636011022 2985605703 2107193353 292026696 3884689974 +1094315383 4016714705 962244585 3943968050 2868319718 +1304919603 3626636694 3393461291 1479454799 971639318 +3352306399 1928233566 2900529135 2190901098 28842068 + 990556577 2586302532 3057504668 1661169605 4228191763 +3934152427 2814119472 4943754 1171095774 1986204006 +2014406505 1822565279 12890078 1979620724 1917376192 +3307810835 4170173371 1385005883 1308519769 3370429606 + 923886311 2024463563 1063369787 153599761 3463680785 + 755374878 2088947962 3099927142 1750207400 2033606872 + 926120766 655932557 2320365045 1465119024 3105365454 +2608716819 1218456091 823539591 2331574954 3171519129 +3246671799 1043031086 1425831588 3940307546 3443545749 +1155610704 3681098065 3287797558 63959365 810297004 +3800799806 1234795257 2547289014 391329364 370300179 +2474800443 3972311925 2935022755 3924395679 2347599539 +4212318274 1828491430 3865565525 2767860661 4078993078 +2781496513 4013741232 2916354756 35752471 2730683119 +3340599926 4059491907 111492530 897368671 2524912702 +3046341697 2790787159 1014602604 1409764839 512802978 + 477082227 2608350570 533747000 1933326657 4182933327 +1970210993 2290203137 2843031053 2844558050 3308351089 +3041943368 1504174920 295229952 2843309586 884572473 +1787387521 1861566286 3616058184 48071792 3577350513 + 297480282 1101405687 1473439254 2634793792 1341017984 diff --git a/test/random-generator.test b/test/random-generator.test new file mode 100755 index 00000000..88c9d31d --- /dev/null +++ b/test/random-generator.test @@ -0,0 +1,68 @@ +#! /bin/bash +# This script tests: pgmnoise +# Also requires: + +echo "Test 1: Should produce:" + +echo "P2" +echo "12 1" +echo "1023" +echo "720 296 192 858 101 57 298 629 804 1019 64 617" +echo "Above output is for Mersenne Twister" + +# GNU libc rand(): 593 252 207 990 507 824 961 805 559 110 167 172 +# MAC OS rand(): 9 782 60 418 364 654 670 172 1022 515 593 903 + +pgmnoise -maxval=1023 -randomseed=3791 -plain 12 1 + +echo +echo "Test 2: Mersenne Twister random number generator" +echo "Should produce:" + +echo "3499211612 581869302 3890346734 3586334585 545404204" +echo "4161255391 3922919429 949333985 2715962298 1323567403" +echo " ... " +echo " 297480282 1101405687 1473439254 2634793792 1341017984" +echo " Total 1000 integers, 200 lines" +echo + +# Use perl to avoid mawk limitation +# (cannot convert 32 bit integers) + +perlPgmProcessorProgram=' + if (($#F+1) == 10) { + for (my $i = 0; $i <= 9; $i += 2) { + my $r = $F[$i + 1] * 65536 + $F[$i]; + printf "%10u ", $r; + } + print ""; + } +' + +pgmnoise -randomseed=5489 -plain -maxval=65535 10 200 | tee /tmp/z | + perl -walne "$perlPgmProcessorProgram" + +# Method to generate output for Test 2 from original +# Mersenne Twister source code + +# Download Mersenne Twister code. See lib/util/randmersenne.c for URL. +# Edit mt19937ar.c: +# In function main() at bottom of file, replace +# init_by_array(init, length); +# with +# init_genrand(5489UL); +# +# We need only the output of genrand_int32(). +# Remove the second loop which produces double-precision floating point +# random numbers with genrand_real2(). +# +# Compile: gcc mt19937ar.c -o mt1000 +# Execute: ./mt1000 + +# 1000 may seem like a large number of samples but there is a reason +# for this. The generator produces random integers in batches of 624. +# The number of samples must be larger than 624 to ensure proper +# generation in batches after the first. + + + diff --git a/test/winicon-roundtrip2.ok b/test/winicon-roundtrip2.ok index bb8c77d8..54ba54e6 100644 --- a/test/winicon-roundtrip2.ok +++ b/test/winicon-roundtrip2.ok @@ -1,3 +1,4 @@ +Should print: former checksum values 16 24 32 48 64 : 1 plane Should print 2588356089 8591 or 3783949470 69390 four times 2588356089 8591 @@ -38,6 +39,7 @@ Should print 2704877198 33359 or 1699833476 276750 four times Should print 2567279592 41655 or 4154838752 345902 twice 2567279592 41655 2567279592 41655 +Should print: latter checksum values 16 32 48 256 : 1 plane Should print 2588356089 8591 or 3783949470 69390 four times 3783949470 69390 diff --git a/test/winicon-roundtrip2.test b/test/winicon-roundtrip2.test index ec074055..9d55ebee 100755 --- a/test/winicon-roundtrip2.test +++ b/test/winicon-roundtrip2.test @@ -7,7 +7,7 @@ tmpdir=${tmpdir:-/tmp} test_pam=${tmpdir}/testimg.pam test1_pam=${tmpdir}/testimg1.pam test2_pam=${tmpdir}/testimg2.pam -#test3_pam=${tmpdir}/testimg3.pam + test4_pam=${tmpdir}/testimg4.pam test5_pam=${tmpdir}/testimg5.pam black_pam=${tmpdir}/black.pam @@ -19,6 +19,10 @@ gray_pam=${tmpdir}/gray.pam for sizes in "16 24 32 48 64" "16 32 48 256" do + if echo $sizes | awk '{exit !($NF==64)}' ; + then echo "Should print: former checksum values"; + else echo "Should print: latter checksum values"; + fi for size in ${sizes} do @@ -65,11 +69,11 @@ for sizes in "16 24 32 48 64" "16 32 48 256" pamstack ${gray_pam} ${gray_pam} | \ pamchannel -tupletype="GRAYSCALE_ALPHA" 0 1 | tee ${test2_pam} | cksum pamtowinicon ${test2_pam} | winicontopam -allimages | \ - pamchannel -tupletype="GRAYSCALE_ALPHA" 0 1 | cksum + pamchannel -tupletype="GRAYSCALE_ALPHA" 0 1 | pamdepth 255 | cksum pamtowinicon -pngthreshold=300 ${test2_pam} | winicontopam -allimages | \ pamchannel -tupletype="GRAYSCALE_ALPHA" 0 1 | cksum pamtowinicon -pngthreshold=1 ${test2_pam} | winicontopam -allimages | \ - pamchannel -tupletype="GRAYSCALE_ALPHA" 0 1 | cksum + pamchannel -tupletype="GRAYSCALE_ALPHA" 0 1 | pamdepth 255 | cksum rm ${test2_pam} diff --git a/version.mk b/version.mk index daa09ece..21aa6e91 100644 --- a/version.mk +++ b/version.mk @@ -1,3 +1,3 @@ NETPBM_MAJOR_RELEASE = 10 -NETPBM_MINOR_RELEASE = 93 -NETPBM_POINT_RELEASE = 3 +NETPBM_MINOR_RELEASE = 94 +NETPBM_POINT_RELEASE = 0 |