Previous | Next | Trail Map | Creating a User Interface | Working with Graphics


How to Use an Image Filter

The following applet uses a filter to rotate an image. The filter is a custom one named RotateFilter that you'll see discussed on the next page. All you need to know about the filter to use it is that its constructor takes a single double argument, the rotation angle in radians.

Here's the source code that uses the filter. (Here's the whole program.)

public class ImageRotator extends Applet {
    . . .
    RotatorCanvas rotator;
    double radiansPerDegree = Math.PI / 180;

    public void init() {
        //Load the image.
        Image image = getImage(getCodeBase(), "../images/pizza.gif");

        ...//Create the component that uses the image filter:
        rotator = new RotatorCanvas(image);
        . . .
        add(rotator);
        . . .
    }

    public boolean action(Event evt, Object arg) {
        int degrees;

        ...//Get the number of degrees to rotate the image by.

        //Convert to radians.
        rotator.rotateImage((double)degrees * radiansPerDegree);

        return true;
    }
}

class RotatorCanvas extends Canvas {
    Image sourceImage;
    Image resultImage;

    public RotatorCanvas(Image image) {
        sourceImage = image;
        resultImage = sourceImage;
    }

    public void rotateImage(double angle) {
        ImageFilter filter = new RotateFilter(angle);
        ImageProducer producer = new FilteredImageSource(
                                        sourceImage.getSource(),
                                        filter);
        resultImage = createImage(producer);
        repaint();
    }

    public void paint(Graphics g) {
        Dimension d = size();
        int x = (d.width - resultImage.getWidth(this)) / 2;
        int y = (d.height - resultImage.getHeight(this)) / 2;

        g.drawImage(resultImage, x, y, this); 
    }
}

How the Code Works

To use an image filter, a program goes through the following steps:
  1. Get an Image object (usually done with a getImage() method).
  2. Get the data source (an ImageProducer) for the Image object (using the getSource() method).
  3. Create an instance of the image filter, initializing the filter as necessary.
  4. Create a FilteredImageSource object, passing the constructor the image source and filter objects.
  5. Create a new Image object (with the Component createImage() method) that has the FilteredImageSource as its image producer.

This might sound complex, but it's actually very easy for you to implement. The real complexity is behind the scenes, as we'll explain a bit later. First, we'll explain the code in the example applet that uses the image filter.

In the example applet, the RotatorCanvas rotateImage() method performs most of the tasks associated with using the image filter. The one exception is the first step, getting the Image object, which is performed by the applet's init() method. This Image object is passed to the RotatorCanvas, which refers to it as sourceImage.

The rotateImage() method instantiates the image filter by calling the filter's constructor. The single argument to the constructor is the angle, in radians, to rotate the image by.

ImageFilter filter = new RotateFilter(angle);
Next, the rotateImage() method creates a FilteredImageSource instance. The first argument to the FilteredImageSource constructor is the image source, obtained with the getSource() method. The second argument is the filter object.
ImageProducer producer = new FilteredImageSource(
                                sourceImage.getSource(),
                                filter);
Finally, the code creates a second Image, resultImage. by invoking the Component createImage() method. The lone argument to createImage() is the FilteredImageSource created in the above step.
resultImage = createImage(producer);

What Happens Behind the Scenes

You might think from the above code that you've inserted the RotateFilter instance between the FilteredImageSource (an image producer) and the Component (the image consumer). However, this is not the case! This section explains what really happens. If you don't care about these implementation details, feel free to skip ahead to Where to Find Image Filters.

The first thing you need to know is that the AWT uses ImageConsumers behind the scenes, in response to drawImage() requests. So the Component that displays the image isn't the image consumer -- some object deep in the AWT is the image consumer.

The createImage() call above sets up an Image (resultImage) that expects to get image data from its producer, the FilteredImageSource instance. Here's what the path for the image data looks like, from the perspective of resultImage:

The dotted line indicates that the image consumer never actually gets data from the FilteredImageSource. Instead, when the image consumer requests image data (in response to g.drawImage(resultImage,...)), the FilteredImageSource performs some sleight of hand and then steps out of the way. Here's the magic that FilteredImageSource performs:

Here is the result:

Where to Find Image Filters

So where can you find existing image filters? The java.awt.image package includes one ready-to-use filter, CropImageFilter.(in the API reference documentation)You can also find several image filters used by applets at our website. All of the following pages include links to the source code for each applet and image filter: Finally, you can use the same image filter the above applet does: RotateFilter.


Previous | Next | Trail Map | Creating a User Interface | Working with Graphics