#!/bin/perl
#  Change the path name in the line above to wherever Perl lives
#  on your system.  This script was tested with Perl 4.0, patch
#  level 36.

#                         P P M S H A D O W

#            by John Walker  --  http://www.fourmilab.ch/
                           $version = 1.2;
#   --> with minor changes by Bryan Henderson.  See above web site for
#   the real John Walker work, named pnmshadow.

#   Pnmshadow is a brutal sledgehammer implemented in Perl which
#   adds attractive shadows to images, as often seen in titles
#   of World-Wide Web pages.  This program does not actually
#   *do* any image processing--it simply invokes components of
#   Jef Poskanzer's PBMplus package (which must be present on
#   the path when this script is run) to bludgeon the source
#   image into a plausible result.
#
#               This program is in the public domain.
#
#

    $fname = "/tmp/_PPMshadow$$";          # Temporary filepath prefix

    #   Process command line options

    $convolve = 11;                   # Default blur convolution kernel size
    $purge = 1;                       # Set to 0 to preserve intermediate files
    $translucent = 0;                 # Default not translucent

    while (@ARGV) {
        $arg = shift;
        if ((substr($arg, 0, 1) eq '-') && (length($arg) > 1)) {
            $opt = substr($arg, 1, 1);
            $opt =~ tr/A-Z/a-z/;
            if ($opt eq 'b') {        # -B n  --  Blur size
                if (!defined($convolve = shift)) {
                    die("Argument missing after -b option\n");
                }
                if (($convolve < 11) && (($convolve & 1) == 0)) {
                    $convolve++;      # Round up even kernel specification
                }
            } elsif ($opt eq 'k') {   # -K  --  Keep temporary files
                $purge = 0;
            } elsif ($opt eq 't') {   # -T  --  Translucent image
                $translucent = 1;
            } elsif ($opt eq 'u' || $opt eq '?') {
                print(STDERR "ppmshadow  --  Add simulated shadow to anymap.\n");
                if (defined $version) {
                    print(STDERR "               Version $version.\n"); 
                }
                print(STDERR "Usage: ppmshadow [options] [pnmfile] \n");
                print(STDERR "Options:\n");
                print(STDERR "    -B n       Set blur size to n pixels\n");
                print(STDERR "    -K         Keep intermediate temporary files from run\n");
                print(STDERR "    -T         Cast shadows of translucent objects\n");
                print(STDERR "    -U         Print this message\n");
                print(STDERR "    -X n       Shift shadow n pixels to the right\n");
                print(STDERR "    -Y n       Shift shadow n pixels down\n");
                exit(0);
            } elsif ($opt eq 'x') {   # -X n  --  X offset
                if (!defined($xoffset = shift)) {
                    die("Argument missing after -x option\n");
                }
                if ($xoffset < 0) {
                    $xoffset = -$xoffset;
                }
            } elsif ($opt eq 'y') {   # -Y n  --  Y offset
                if (!defined($yoffset = shift)) {
                    die("Argument missing after -x option\n");
                }
                if ($yoffset < 0) {
                    $yoffset = -$xoffset;
                }
            }
        } else {
            if (defined $ifile) {
                die("Duplicate input file specification.");
            }
            $ifile = $arg;            # Input file name
        }
    }

    #   Apply defaults for arguments not specified

    if (!(defined $xoffset)) {
        #   Xoffset defaults to half the blur distance
        $xoffset = int($convolve / 2);
    }

    if (!(defined $yoffset)) {
        #   Yoffset defaults to Xoffset, however specified
        $yoffset = $xoffset;
    }

    $stdin = 0;
    if ((!(defined $ifile)) || ($ifile eq '-')) {
        #   Input default to standard input
        $stdin = 1;
        $ifile = "$fname-0.ppm";
        system("cat >$fname-0.ppm");
    }

    #   Determine the size of the source image

    $a = `pnmfile $ifile`;
    $a =~ m/.*\sP[BGP]M\s.*,\s*(\d*)\sby\s(\d*)/;
    $xsize = $1;
    $ysize = $2;

    #   Create a blank background bitmap the size of the source bitmap

    system("pnmcut 0 0 1 1 $ifile | pnmscale -xsize $xsize -ysize $ysize >$fname-5.ppm");

    #   Create positive mask file from input image

    system("pnmarith -difference $ifile $fname-5.ppm | pnminvert | ppmtopgm | pgmtopbm -thresh -value 1.0 >$fname-1.ppm");

    #   Create convolution kernel file to generate shadow

    open(OF, ">$fname-2.ppm");
    $ckern = $convolve <= 11 ? $convolve : 11;
    printf(OF "P2\n$ckern $ckern\n%d\n", $ckern * $ckern * 2);
    $a = ($ckern * $ckern) + 1;
    for ($i = 0; $i < $ckern; $i++) {
        for ($j = 0; $j < $ckern; $j++) {
            printf(OF "%d%s", $a, ($j < ($ckern - 1)) ? " " : "\n");
        }
    }
    close(OF);

    if ($translucent) {

        #   Convolve the input colour image with the kernel
        #   to create a translucent shadow image.

        system("pnmconvol $fname-2.ppm $ifile >$fname-10.ppm");
        system("rm $fname-2.ppm") if $purge;
        while ($ckern < $convolve) {
            system("pnmsmooth $fname-10.ppm >$fname-10a.ppm");
            system("mv $fname-10a.ppm $fname-10.ppm");
            $ckern++;
        }
    } else {

        #   Convolve the positive mask with the kernel to create shadow

        system("pnmconvol $fname-2.ppm $fname-1.ppm >$fname-3.ppm");
        system("rm $fname-2.ppm") if $purge;
        while ($ckern < $convolve) {
            system("pnmsmooth $fname-3.ppm >$fname-3a.ppm");
            system("mv $fname-3a.ppm $fname-3.ppm");
            $ckern++;
        }

        #   Multiply the shadow by the background colour

        system("pnmarith -multiply $fname-3.ppm $fname-5.ppm >$fname-10.ppm");
        system("rm $fname-3.ppm") if $purge;
    }

    #   Cut an offset rectangle from the shadow image

    $i = $xsize - $xoffset;
    $j = $ysize - $yoffset;
    system("pnmcut 0 0 $i $j $fname-10.ppm >$fname-4.ppm");
    system("rm $fname-10.ppm") if $purge;

    #   Create offset shadow image

    system("pnmpaste -replace $fname-4.ppm $xoffset $yoffset $fname-5.ppm >$fname-6.ppm");
    system("rm $fname-4.ppm $fname-5.ppm") if $purge;

    #   Create inverse mask

    system("pnminvert $fname-1.ppm >$fname-7.ppm");

    #   Multiply original image by inverse mask

    system("pnmarith -multiply $ifile $fname-7.ppm >$fname-8.ppm");
    system("rm $fname-7.ppm") if $purge;
    system("rm $fname-0.ppm") if ($purge && $stdin);

    #   Multiply shadow by mask

    system("pnmarith -multiply $fname-6.ppm $fname-1.ppm >$fname-9.ppm");
    system("rm $fname-6.ppm $fname-1.ppm") if $purge;

    #   Add masked image and shadow to obtain result, which this
    #   last call on pnmarith sends to standard output.

    system("pnmarith -add $fname-8.ppm $fname-9.ppm");
    system("rm $fname-8.ppm $fname-9.ppm") if $purge;
