Non Rectangular Shadow

Update: I integrated Sebastien's excellent suggestions and repacked the demo. You can now choose between two different blur algorithms using the setRenderingHint() method. By default it uses the fast version. I'm not sure the high quality version makes a difference but it will be needed in the future when I'll add support for Gaussian blur, etc.

You can run the WebStart demo or, as usual, download its source code.

In some of my demos, Shadowed Splash Window or Drag n' Splash for instance, I created drop shadows by using a simple blur filter. Unfortunately, my code was working only for rectangles and, for a new project, I need more accurate shadows. Basically, I need to do what Photoshop does: to cast shadows for any shape (or picture with transparency). Here is the result:

This demo presents how to use DropShadowPanel to which you give a picture. You can control the appearance of the shadow itself by changing the angle, the distance from the subject, the color, the opacity and the size (or fuzziness). You can also load any picture you want. Beware though, the sliders Size and Opacity regenerate the shadow using a blur filter. To prevent these operations from blocking the UI, they are performed in a separate thread and I didn't do anything to synchronize the threads. This means you might see some strange problems while playing with these sliders.

Here is another example with a different pictures and new parameters values:

To create this effect, we first prepare the image. The idea is to change the canvas size to make room for the shadow, particularily when the blur operation occurs:

private BufferedImage prepareImage(BufferedImage original) {
  BufferedImage subject = new BufferedImage(original.getWidth() + shadowSize * 2,
                                            original.getHeight() + shadowSize * 2,
  Graphics2D g2 = subject.createGraphics();
  g2.drawImage(original, null, shadowSize, shadowSize);
  return subject;

The following step is the generation of the shadow itself. We first create the shadow mask and then blur it:

private BufferedImage createDropShadow(BufferedImage image) {
  if (image != null) {
    BufferedImage shadow = new BufferedImage(image.getWidth(),
    getBlurOp(shadowSize).filter(createShadowMask(image), shadow);
    return shadow;
  return null;

We have a huge performances bottleneck here. We first copy the original picture to a larger one, then create the shadow mask and we filter the whole. It is actually possible to do all these operations in one loop and thus improve the performances. On large pictures, this could make a huge difference but I'll try that later :)

The magic happens in createShadowMask() where Icopy every pixel from the original picture and transform it according to the selected shadow color and opacity:

private BufferedImage createShadowMask(BufferedImage image) {
BufferedImage mask = new BufferedImage(image.getWidth(),

for (int x = 0; x > 24 & 0xFF) * shadowOpacity)
Note I preserve the opacity of the original picture to take into account invisible pixels and anti-aliased edges.

I will try to improve the performances very soon as I go on with my new project :)

Comments are closed.