As I said earlier, you rarely need to write an image consumer. However, there is one kind of image consumer that's worth knowing about. In this final section on images, we'll build a simple image filter. An image filter is simply a class that performs some work on image data before passing the data to another consumer.
The ColorSep applet acquires an image; uses
an image filter to separate the image into red, green, and blue
components; and displays the three resulting images. With this applet
and a few million dollars, you could build your own color separation
plant.
import java.awt.*;
import java.awt.image.*;
public class ColorSep extends java.applet.Applet {
Image img, redImg, greenImg, blueImg;
public void init() {
img = getImage( getClass().getResource( getParameter("img")) );
redImg = createImage(new FilteredImageSource(img.getSource(),
new ColorMaskFilter( Color.red )));
greenImg = createImage(new FilteredImageSource(img.getSource(),
new ColorMaskFilter( Color.green )));
blueImg = createImage(new FilteredImageSource(img.getSource(),
new ColorMaskFilter( Color.blue )));
}
public void paint( Graphics g ) {
int width = getSize().width, height = getSize().height;
g.drawImage( redImg, 0, 0, width/3, height, this );
g.drawImage( greenImg, width/3, 0, width/3, height, this );
g.drawImage( blueImg, 2*width/3, 0, width/3, height, this );
}
}
class ColorMaskFilter extends RGBImageFilter {
Color color;
ColorMaskFilter( Color mask ) {
color = mask;
canFilterIndexColorModel = true;
}
public int filterRGB(int x, int y, int pixel ) {
return
255 << 24 |
(((pixel & 0xff0000) >> 16) * color.getRed()/255) << 16 |
(((pixel & 0xff00) >> 8) * color.getGreen()/255) << 8 |
(pixel & 0xff) * color.getBlue()/255 ;
}
}
The FilteredImageSource and
RGBImageFilter classes form the basis for building
and using image filters. A FilteredImageSource is
an image producer (like MemoryImageSource) that is
constructed from an image and an ImageFilter
object. It fetches pixel data from the image and feeds it through the
image filter before passing the data along. Because
FilteredImageSource is an image producer, we can
use it in our calls to createImage().
But where's the consumer?
FilteredImageSource obviously consumes image data
as well as producing it. The image consumer is still mostly hidden,
but is peeking out from under its rock. Our class
ColorMaskFilter extends
RGBImageFilter, which in turn extends
ImageFilter. And ImageFilter is
(finally!) an image consumer. Of course, we still don't see the
calls to addConsumer(), and we don't see an
implementation of setPixels(); they're hidden
in the ImageFilter sources and inherited by
ColorMaskFilter.
So what does ColorMaskFilter actually do?
Not much. ColorMaskFilter is a simple subclass of
RGBImageFilter that implements one method,
filterRGB(), through which all of the pixel data
are fed. Its constructor saves a mask value we use for
filtering. The filterRGB() method accepts a
pixel value, along with its x
and y coordinates, and returns the filtered version
of the pixel. In ColorMaskFilter, we simply
multiply the color components by the mask color to get the proper
effect. A more complex filter, however, might use the coordinates to
change its behavior based on the pixel's position.
One final detail: the constructor for
ColorMaskFilter sets the flag
canFilterIndexColorModel. This flag is inherited
from RGBImageFilter. It means our filter
doesn't depend on the pixel's position. In turn, this
means it can filter the colors in a color table. If
we were using an indexed color model, filtering the color table would
be much faster than filtering the individual pixels.