| Working with Graphics

## How to Write an Image Filter

> All image filters must be subclasses of the ImageFilter class. If your image filter will modify the colors or transparency of an image, then instead of creating a direct subclass of ImageFilter, you should probably create a subclass of RGBImageFilter.

> Before writing an image filter, you first should find others, studying any that are similar to what you plan to write. You should also study the ImageProducer and ImageConsumer interfaces, becoming thoroughly familiar with them.

> #### Finding Examples

> > You can find examples of RGBImageFilter subclasses in the applets mentioned on the previous page (Dynamically Generated Color Bullets, Live Feedback ImageMap, and Image Test).

> > Later in this page, you'll see an example of a direct ImageFilter subclass, RotateFilter.

> #### Creating an ImageFilter Subclass

> > As we mentioned before, image filters implement the ImageConsumer interface. This lets them intercept data intended for the image consumer. ImageConsumer defines the following methods:

> > >     void setDimensions(int width, int height);
> > >     void setProperties(Hashtable props);
> > >     void setColorModel(ColorModel model);
> > >     void setHints(int hintflags);
> > >     void setPixels(int x, int y, int w, int h,
> > >                    ColorModel model, byte pixels[],
> > >                    int off, int scansize);
> > >     void setPixels(int x, int y, int w, int h,
> > >                    ColorModel model, int pixels[],
> > >                    int off, int scansize);
> > >     void imageComplete(int status);

> > The ImageFilter class implements all the above methods so that they forward the method data to the filter's consumer. For example, ImageFilter implements the `setDimensions()` method as follows:

> > >     public void setDimensions(int width, int height) {
> > >         consumer.setDimensions(width, height);
> > >     }

> > Thanks to these ImageFilter methods, your subclass probably doesn't need to implement every ImageConsumer method. You need to implement only the methods that transmit data you want to change.

> > For example, the CropImageFilter class implements four of the ImageConsumer methods: `setDimensions()`, `setProperties()`, and both varieties of `setPixels()`. It also implements a constructor with arguments that specify the rectangle to be cropped. As another example, the RGBImageFilter class implements some helper methods, defines an abstract helper method to perform the actual color modifications of each pixel, and implements the following ImageConsumer methods: `setColorModel()` and both varieties of `setPixels()`.

> > Most, if not all, filters implement the `setPixels()` methods. These methods determine exactly what image data is used to construct the Image. One or both of the `setPixels()` methods may be called multiple times during the construction of a single image. Each call gives the ImageConsumer information about a rectangle of pixels within the image. When the ImageConsumer's `imageComplete()` method is called with any status except SINGLEFRAMEDONE (which implies that data for more frames will appear), then the ImageConsumer can assume that it will receive no further `setPixels()` calls. An `imageComplete()` status of STATICIMAGEDONE specifies that not only is the image data complete, but that no errors have been detected.

> > The following illustration and table describe the arguments to the `setPixels()` methods.

> > `x`, `y`
> >   Specify the location within the image, relative to its upper left corner, at which this rectangle begins.
> > `w`, `h`
> >   Specify the width and height, in pixels, of this rectangle.
> > `model`
> >   Specifies the color model used by the data in the pixels array.
> > `pixels[]`
> >   Specifies an array of pixels. The rectangle of image data is contained in this array, but the array might contain more than `w`*`h` entries, depending on the values of `offset` and `scansize`. Here's the formula for determining what entry in the `pixels` array contains the data for the pixel at (`x`+i, `y`+j), where (0 <= i < `w`) and (0 <= j < `h`):

> >   >         offset + (j * scansize) + i

> >   The above formula assumes that (m,n) is in the rectangle this `setPixels()` call specifies, and that (m,n) is relative to the image origin. Below is an illustration of the `pixels` array to make this clearer. It shows how a specific pixel (for example, (x,y)) maps to an entry in the `pixels` array.

> > `offset`
> >   Specifies the index (in the `pixels` array) of the first pixel in the rectangle.
> > `scansize`
> >   Specifies the width of each row in the `pixels` array. Due to efficiency considerations, this might be greater than `w`.

> #### The RotateFilter Image Filter

> > The RotateFilter class rotates an image by the specified angle. It relies on the following graphics formulas to calculate the new position of each pixel:

> > >     newX = oldX*cos(angle) - oldY*sin(angle)
> > >     newY = oldX*sin(angle) + oldY*cos(angle)

> > RotateFilter implements the following ImageConsumer methods:
> > `setDimensions()`
> >   Records the unfiltered image's width and height for use in the `setPixels()` and `imageComplete()` methods. Calculates the filtered image's final width and height, records it for use in its `imageComplete()` method, creates a buffer to store the image data as it comes in, and calls the consumer's `setDimensions()` method to set the new width and height.
> > `setColorModel()`
> >   Tells the consumer to expect pixels in the default RGB color model.
> > `setHints()`
> >   Tells the consumer to expect the image data in top-down-left-LEFT order (the order in which you're reading this page), in complete scan lines, and with every pixel sent exactly once.
> > `setPixels()` (both varieties of this method)
> >   Converts the pixels to the default RGB model (if necessary) and copies the pixels into a storage buffer. Most image filters would simply modify the pixel data and forward it to the consumer, but because the sides of a rotated rectangle are no longer horizontal and vertical (for most angles), this filter can not efficiently forward pixels from its `setPixels()` method. Instead, RotateFilter stores all the pixel data until it receives an `imageComplete()` message.
> > `imageComplete()`
> >   Rotates the image and then invokes `consumer.setPixels()` repeatedly to send each line of the image to the consumer. After sending the whole image, this method invokes `consumer.imageComplete()`.

| Working with Graphics