Lets say you have a set of images that you want to add to your website, but you want to make sure that each one has a drop shadow. But you want it to be consistent, and not have to pull each one into Gimp or Photoshop in order to add that drop shadow. Here's what you do.

dropshadow-tree.png

The Script

These instructions require a knowledge of Bash and Unix.

A bash shell script has been prepared to help you with the creation of the drop shadow.

Prerequirements:

This pnmdropshadow.sh bash shell script.

#!/bin/bash

TEMPDIR=".pnmdropshadow"

if [ ! -d "$TEMPDIR" ] ; then
    mkdir -p $TEMPDIR
fi

function pnmWidth() {
    PNM=$1

    pamfile $PNM | \
        sed -e "s/^.* \([0-9][0-9]*\) by [0-9][0-9]* .*$/\1/g"
}

function pnmHeight() {
    PNM=$1

    pamfile $PNM | \
        sed -e "s/^.* [0-9][0-9]* by \([0-9][0-9]*\) .*$/\1/g"
}

function pnmBorder() {
    PNM=$1

    COLOR="#555555"

    # Add Border to PNM
    pnmmargin -color "$COLOR" 1 $PNM > $TEMPDIR/with_border.pnm

    cp $TEMPDIR/with_border.pnm $PNM
}

function createConvolMap() {
    let blurSize=$1;

    if [ `expr $blurSize % 2` -eq 0 ] ; then
        let blurSize=$blurSize+1;
    fi

    let offset=$blurSize*$blurSize;
    let rowvalue=$offset+1;
    let offset=$offset*2;

    echo "P2"
    echo "$blurSize $blurSize"
    echo "$offset"

    for (( y=0; y<$blurSize; y++))
    do
        for (( x=0; x<$blurSize; x++))
        do
            echo -n "$rowvalue"
            if [ $x -ne $blurSize ]
            then
                echo -n " "
            fi
        done
        echo ""
    done
}

function pnmDropShadow() {
    PNM=$1

    BGCOLOR="#ffffff"
    SHADOWCOLOR="#888888"

    let shadowOffset=4;
    let shadowBlur=5;

    let leftPad=$shadowBlur-$shadowOffset;
    if [ $leftPad -lt 0 ] ; then
        let leftPad=0;
    fi
    let rightPad=$shadowBlur+$shadowOffset;
    if [ $rightPad -lt 0 ] ; then
        let rightPad=0;
    fi
    let topPad=$shadowBlur-$shadowOffset;
    if [ $topPad -lt 0 ] ; then
        let topPad=0;
    fi
    let bottomPad=$shadowBlur+$shadowOffset;
    if [ $bottomPad -lt 0 ] ; then
        let bottomPad=0;
    fi

    let pnmWidth=`pnmWidth "$PNM"`
    let pnmHeight=`pnmHeight "$PNM"`
    let shadowWidth=$pnmWidth+$leftPad+$rightPad;
    let shadowHeight=$pnmHeight+$topPad+$bottomPad;

    # Create the shadow background
    ppmmake "$BGCOLOR" $shadowWidth $shadowHeight > \
        $TEMPDIR/shadow_background.pnm

    # Create the mask block
    ppmmake "$SHADOWCOLOR" $pnmWidth $pnmHeight > \
        $TEMPDIR/shadow_mask.pnm

    # Insert mask block into the background block
    pnmpaste -replace \
        $TEMPDIR/shadow_mask.pnm \
        $shadowOffset $shadowOffset \
        $TEMPDIR/shadow_background.pnm > \
            $TEMPDIR/shadow_paste.pnm

    # Create Convolution Matrix File
    createConvolMap "$shadowBlur" > $TEMPDIR/shadow.cnv

    # Blur the shadow
    pnmconvol \
        $TEMPDIR/shadow.cnv \
        $TEMPDIR/shadow_paste.pnm > \
            $TEMPDIR/shadow_blur.pnm

    # Put original image into blur
    pnmpaste -replace $PNM \
        $leftPad $topPad $TEMPDIR/shadow_blur.pnm > \
            $TEMPDIR/with_shadow.pnm

    cp $TEMPDIR/with_shadow.pnm $PNM
}

PNMT="$TEMPDIR/temp.pnm"

if [ -z "$1" ]; then
    pnmtopnm > $PNMT
else
    cp $1 $PNMT
fi

pnmBorder "$PNMT"
pnmDropShadow "$PNMT"

pnmtopnm "$PNMT"

rm -rf $TEMPDIR

Executing the Script

To execute the pnmdropshadow.sh script you have to do it the netpbm way, which is to first convert the original image into the PNM format (netpbm internal image format) so it can be piped thru the other tools and scripts before eventually being converted back into the output format you desire.

How to take a PNG file and add a dropshadow to it.

$ pngtopnm flower-pink.png | ./pnmdropshadow.sh | \
     pnmtopng -compression=9 > flower-pink-with-shadow.png

How to take a directory full of PNG files and add a dropshadow to each.

for PNG in *.png
do
  echo Adding drop shadow to $PNG
  pngtopnm $PNG | ./pnmdropshadow.sh | \
    pnmtopng -compression=9 > ${PNG//.png}-with-shadow.png
done

Examples of output

dropshadow-flower-pink.png

dropshadow-flower-white-pink-azalea.png

dropshadow-cow-tongue.png

What's going on

original input image. flower-ping.png (433x189)

original input image.  flower-ping.png

Create a simple 1 pixel border around the image.

$  pngtoppm flower-pink.png | pnmmargin -color "#555555" 1 > with_border.ppm

with_border.ppm (433x189)

with_border.ppm

Create a new, larger image with the background color desired.

$  ppmmake "#FFFFFF" 445 201 > shadow_background.ppm

shadow_background.pnm (445x201)

shadow_background.pnm

Create a new image of the desired shadow color (same size as with_border.ppm).

$  ppmmake "#FFFFFF" 435 191 > shadow_mask.ppm

shadow_mask.ppm (435x191)

shadow_mask.ppm

Paste mask into background with offset applied.

$  ppmpaste -replace shadow_mask.ppm 4 4 shadow_background.pnm > shadow_paste.ppm

shadow_paste.pnm (445x201)

shadow_paste.pnm

Create a Convolution Map text file.

$ cat shadow.cnv
P2
5 5
50
26 26 26 26 26
26 26 26 26 26
26 26 26 26 26
26 26 26 26 26
26 26 26 26 26

Apply convolution map to blur the shadow_paste.pnm

$ pnmconvol shadow.cnv shadow_paste.pnm > shadow_blur.pnm

shadow_blur.pnm (445x201)

shadow_blur.pnm

Paste image with border into blured shadow image.

$ pnmpaste -replace with_border.pnm 1 1 shadow_blur.pnm > with_shadow.pnm

with_shadow.pnm (445x201)

with_shadow.pnm

Convert PNM image to PNG.

$ pnmtopng -compression=9 with_shadow.pnm > flower-pink-with-shadow.png

flower-ping-with-shadow.png (445x201)

flower-ping-with-shadow.png