Home / Alt manpages / ppmshadow(1)

  • ppmshadow(1)
  • User command
  • linux

Add a Controlled Drop Shadow to a PPM Image with ppmshadow

A flat product photo on a plain background looks better with depth, and ppmshadow adds a simulated drop shadow. It treats the top-left pixel as the background colour, so the result is predictable whenever the image has a clean border, and it writes a new PPM file, leaving the source alone. Give a first run about 15 minutes, including a small test image and a visual check.

This guide uses Netpbm 11.5.2, from package netpbm 2:11.05.02-1.1build1; the installed manual is dated 24 June 2017, so everything below is verified against this local install.

1. Confirm the install and pick a safe output path

This is ordinary user-level image conversion: it reads one PPM, writes PPM to standard output, and needs no sudo. Pick a destination that is not already holding something useful, shell redirection with > truncates an existing file before ppmshadow even starts.

$ command -v ppmshadow
/usr/bin/ppmshadow
$ ppmshadow --version
netpbm 2:11.05.02-1.1build1

If the destination already exists, write to a temporary name in the same directory and swap it in only after checking it:

$ ppmshadow /path/to/source.ppm > /path/to/shadow.ppm.new
$ test -s /path/to/shadow.ppm.new && mv /path/to/shadow.ppm.new /path/to/shadow.ppm

Checkpoint

The source is untouched, and a failed run leaves the previous shadow.ppm exactly where it was. If the new file is incomplete, remove only shadow.ppm.new after inspecting the error; never remove the original output as part of recovery.

2. Run the default black shadow

With no options, ppmshadow makes a black shadow. It calls every pixel whose RGB exactly matches the top-left pixel "background", and treats everything else as foreground. The output keeps the input's width, height and maxval.

$ ppmshadow /path/to/source.ppm > /path/to/shadow.ppm
$ file /path/to/shadow.ppm
/path/to/shadow.ppm: Netpbm image data, size 800 x 600, rawbits, pixmap
$ test -s /path/to/shadow.ppm && echo 'non-empty PPM written'
non-empty PPM written

Your file description may read differently. What matters is a non-empty file with unchanged dimensions. Open the result in a viewer, or hand it to another Netpbm converter; standard output carries the image, so do not expect a progress message.

A black object sitting on a background that is exactly black will not cast a shadow. If part of your object is genuinely black and needs to count as foreground, nudge that region one RGB value away from pure black before processing. The visual change is normally invisible, but keep that edited input separate from the original.

3. Set the light direction and softness

-x and -y describe where the light sits, not where the shadow falls. A positive -x puts the light to the left, so the shadow moves right. A positive -y puts the light above the image, so the shadow moves down.

$ ppmshadow -x 24 -y 18 -b 9 /path/to/source.ppm > /path/to/shadow-right-down.ppm

-b controls the fringe around the shadow; the default is 11 pixels. Larger values look more diffuse, smaller values sharper, it is blur, not physical growth or shrinkage of the shadow shape. Start with the default, then try something like 5 for a crisp graphic or 18 for a softer look.

Leave out -x and -y and each defaults to half the blur size, light to the left and above. For a script you want to reproduce later, set all three explicitly rather than leaning on that default relationship.

Checkpoint

Compare two outputs with different names side by side. If a larger -x appears to move the shadow left instead of right, recheck the sign, the documented convention is that a bigger positive offset moves the shadow right.

4. Reach for a translucent shadow only when the image suits it

The default models an opaque object and always produces a black shadow. Add -t when the foreground should cast a shadow coloured by the object itself:

$ ppmshadow -b 11 -x 16 -y 16 -t /path/to/source.ppm > /path/to/translucent-shadow.ppm

This can look better on translucent artwork, but it can also read as a blurred copy of the image when contrast between foreground and background is low. Treat it as a visual choice, not a quality upgrade, it neither makes the source physically translucent nor changes its dimensions.

5. Fix the two input problems behind most failures

First, make sure the top-left pixel really is background. On a tightly cropped image, add a one-pixel border in the intended background colour, run ppmshadow, then trim the matching border back off the output; there is no separate option to name a background colour.

Second, leave room on the edges in the direction the shadow falls. The output cannot grow past the input dimensions, so the shadow clips at the boundary, and with too little room, internal Netpbm stages can fail outright rather than hand you a partial image. Use pnmmargin to widen a tight border before shadowing, then crop afterwards if the final composition needs it.

Do not shadow a low-maxval image and expect a smooth fringe, the blur needs enough shades to represent its gradient. Raise the depth first for PBM or another low-depth source:

$ pamdepth 255 /path/to/low-depth.ppm > /path/to/source-255.ppm
$ ppmshadow -b 11 /path/to/source-255.ppm > /path/to/shadow-255.ppm

The output keeps the input's maxval, so raising it beforehand is what leaves room for the intermediate shades the blur needs.

6. Diagnose a bad run without guessing

Check the input header and permissions first:

$ file /path/to/source.ppm
$ test -r /path/to/source.ppm && echo readable
$ test -s /path/to/shadow.ppm.new && echo output-present

If the output is empty, treat that run as failed even if a wrapper script reports success, the manual warns that an internal Netpbm error can leave an empty file behind without a non-zero status. Rerun to a fresh temporary name and keep the source available.

For deeper debugging, -k preserves intermediate files in a directory under TMPDIR, or under /tmp if that variable is unset:

$ ppmshadow -k -b 11 /path/to/source.ppm > /path/to/debug-shadow.ppm
$ find "${TMPDIR:-/tmp}" -maxdepth 1 -type d -name 'ppmshadow*' -user "$(id -un)" -print

Use -k only while investigating, it names the directory after the process ID and can fail if that name is taken. The preserved files can include copies of your input, so review and remove that specific debug directory once you are done. Never delete a broad temporary directory, or anything you do not own.

Done means

  • Background confirmed: the source has a deliberate background colour at its top-left pixel and enough border for the offset you chose.
  • Output checked: the result is non-empty and matches the source's dimensions and maxval.
  • Look dialled in: your -b, -x and -y values give the softness and direction you wanted.
  • Translucency deliberate: -t is used only when a coloured, translucent-looking shadow is actually wanted.
  • Cleaned up: any temporary output or -k debug directory has been checked and removed without touching the original source.