Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml

AspAutocadCDot netExcelFox proHtmlJava
LinuxMathcadPhotoshopPhpSqlVisual studioWindowsXml

AWT Image Processing

java



+ Font mai mare | - Font mai mic



AWT Image Processing

Several utility classes and interfaces are included in the Java API for image processing. Using these APIs, Image objects can be created from raw data, the raw data of an existing Image object can be examined, and filters can be used to make modified versions of existing images. Image objects that you create from raw data or using filters can be used in exactly the same ways as Image objects created by the Java runtime system.

In the Java language, Image data is presented in a consistent format by objects which implement the ImageProducer interface. Image data is used by objects which implement the ImageConsumer interface. The ImageProducer and ImageConsumer interfaces are the basis for image processing in the Abstract Windows Toolkit (AWT) of Java.



This chapter details how ImageProducers, ImageConsumers, and ImageFilters work, and how you use them to create and modify images in Java. First, we will see how image pixel data is represented using arrays of bytes or integers in Java, and how the ColorModel object is used to interpret image pixel data. Next, we will see how the ImageProducer interface passes image pixel and ColorModel information to ImageConsumers. Finally, this chapter explains the ImageFilter, which is really both an ImageConsumer and an ImageProducer.

The project for this chapter, "MultiFilter," is an extensible image filtering application. It allows the user to apply filters to arbitrary sections of Images. MultiFilter is designed to be extensible-after you see how it is designed and built, you will be able to drop in and test any ImageFilter you create.

Image Data in Java

As with most computer representation of images, images in AWT are conceptually a two-dimensional array of pixels. Each pixel has color data associated with it. According to the color storage model for the Image, this data can be stored in either a single 8-bit byte, or in a 32-bit integer.

The default color model stores eight bits each of red, green, blue, and "alpha" or "transparency" information packed into a single integer. This is the RGBα color model. Figure 8-1 illustrates the packing of RGBα data into a single integer. All Image objects have an associated ColorModel object capable of converting the specific Image's color data into an RGBα representation. The ColorModel class encapulates the methods necessary to convert the Image pixel data for single pixels into the default RGBα packed bits format. Whenever raw Image pixel data is passed to a method, the ColorModel for that pixel data accompanies it. Sometimes the ColorModel is assumed to be the default RGBα model, such as in the RGBImageFilter.filterRGB method, which is passed RGBα pixel data but no ColorModel.


Figure 8-1  RGBα color model

The ColorModel class is an abstract class that defines the methods which must be implemented by actual ColorModels. The two ColorModel classes included with the AWT are IndexColorModel and the DirectColorModel, both of which are also summarized in this chapter.

For example, an Image may use an IndexColorModel, where each pixel's single byte of data is actually an index into a color table of 256 RGBα integers. Given a pixel's 8-bit index into its color table, the IndexColorModel for the Image can supply the specific RGBα data for that pixel. All ColorModels implement getRGB to supply RGBα information for all pixels in the Image. This listing uses getRGB to retrieve RGBα information associated with pixel value 0.

URL urlImage;


// urlImage is loaded with the URL for an image.


Image img = Toolkit.getDefaultToolkit().getImage( urlImage );


// Image data is allowed to be downloaded, for instance by a
// MediaTracker object.


// Pass to getRBG() the native color data for a particular
// pixel. Below, native data '0' is passed in, which could
// refer to the first entry in an indexed color table.
ColorModel cm = img.getColorModel();
int rgb = cm.getRGB(0);

The other ColorModel included in AWT is the DirectColorModel. The DirectColorModel can store packed bits of red, green, blue, and alpha values using arbitrary bitmasks for each of the color component fields in an integer. The width of the bitfields for each color component is completely configurable. The DirectColorModel internally computes the method for converting data stored using arbitrary bitfields into default RGB data. Like all ColorModel-derived classes, the DirectColorModel implements getRed, getGreen, getBlue, getAlpha, and getRGB.

For example, you can define a DirectColorModel that stores 12 bits of red data in the 12 highest bits, 4 bits of green data in the bottom 4 bits, 16 bits of blue data in the middle 16 bits, and no alpha data at all. The DirectColorModel can automatically convert this kind of pixel data into the default RGBα representation.

There are many other ColorModels which could be implemented, each best suited to a particular real-world application. For example, television images are encoded in the so-called "YIQ" or "luminescence/chromaticity" color model. One could define a YIQColorModel so that Images storing pixel data using the YIQ schema could be rendered on display surfaces by Java.

Passing Image Data: The ImageProducer and ImageConsumer Interfaces

The AWT creates Image objects from data provided by ImageProducers. An ImageProducer delivers Image pixel data to ImageConsumers through the member methods of that interface. There are three types of ImageProducers implemented in the Java API:

.  The Toolkit has built-in ImageProducers which can create Images from files stored on the local file system, or accessed through the network. The Toolkit's createImage method takes Image data and an associated ColorModel from an ImageProducer, and transfers it into structures appropriate for rendering the Image in a particular operating system.

.  Any Image object can provide an ImageProducer which will deliver the Image's data to ImageConsumers. Image.getSource returns an ImageProducer that delivers the pixel data to ImageConsumer objects.

.  The MemoryImageSource class is an ImageProducer which can deliver image data taken from an array of pixel data in memory.

You can create your own ImageProducers to accomplish additional Image creation tasks. For example, you may want to implement an ImageProducer to create images from lesser-known or unsupported graphic format files, like PostScript. To do so, just create a class which implements the ImageProducer interface and delivers pixel data to consumers through the member methods of the ImageConsumer interface.

ImageProducers require one or more ImageConsumer objects to accept image data. Figure 8-2 illustrates this relationship as a faucet and one or more buckets. The ImageProducer funnels its image data to ImageConsumers which have been associated with the producer.


Figure 8-2  The flow of Image pixel data from ImageProducer to multiple ImageConsumers

The ImageConsumer interface is implemented by objects wishing to process pixel data. The ImageProducer passes pixel data in one or, more usually, multiple calls to the ImageConsumer's setPixels:

class myConsumer extends ImageConsumer

public void setPixels(int x, int y, int width, int height,
ColorModel model, byte pixels[], int off, int scansize)

Two overloaded versions of setPixels must be implemented, one to accept 8-bit pixel data, and the other to accept 32-bit pixel data. A rectangle of pixels from the Image is passed to setPixels. This rectangle might be the entire Image's two-dimensional array of pixels, a single row or "scanline" of the Image, or even a single pixel of data. Multiple calls to setPixels may even define data for overlapping regions of the Image, in which case only the last piece of Image data for a particular region should be used. Images may also contain multiple frames, such as with a video sequence of animation frames.

The ImageProducer calls the ImageConsumer's setHints method to tell the consumer a little about how the Image data will be passed. These hints can be used by a consumer to optimize its processing of the pixel data. Always before the first call to setPixels, setHints is called. A bitwise ORing of ImageConsumer flags is passed to setHints. See the API summary of the setHints method later in this chapter for a comprehensive listing of the ImageConsumer flags.

Producer hints should be used by a consumer to optimize its processing of image data. For example, one custom ImageConsumer might be an average color calculator which calculates the average red, green, and blue intensities of an image. For non-SINGLEPASS images, such a consumer would have to keep a history of data sufficient to subtract overlapping regions passed to setPixels.

Several other important pieces of information about an image are presented to a consumer before individual pixel data is sent to setPixels. The ImageConsumer methods used to pass this additional information are

.  setDimensions is called to inform the consumer of the Image's width and height.

.  setProperties is called to pass the consumer an extensible list of Image properties. These properties are all stored in a Hashtable as text. The individual properties might include the name and configuration of any filters used to produce the Image, any copyright on the Image, etc.

.  setColorModel is passed a copy of the ColorModel used predominantly by the producer. Similar to the hints passed to setHints, the ColorModel may be used by the consumer to optimize processing of Image color data.

When the producer has finished sending the consumer all information about the image (or a single frame of the image), the imageComplete method is invoked. Similar to setHints, imageComplete is passed a bitwise ORing of ImageConsumer flags indicating the status of the completed image. Here are the ImageConsumer flags that may be passed to imageComplete:

Flag

Meaning


IMAGEERROR

An error was encountered while producing the Image data. The error is unrecoverable, and the image data received so far is not necessarily viable.

SINGLEFRAMEDONE

A single frame in a multiframe Image has been completed.

STATICIMAGEDONE

The Image data for a complete single-frame image has been sent to the consumer. For multi-frame images, this flag implies all frames have been delivered to the consumer.

IMAGEABORTED

The production of the Image has been deliberately aborted.


As you can see, the interpretation of most of these flags is somewhat vague. For example, the IMAGEERROR flag simply indicates that "some error" has occurred: There is no way to discover more about what that error is.

The CountFramesConsumer (see the Example section of the ImageConsumer API Summary below) counts the number of frames in a multiframe Image. The individual pixel data for the frames is actually ignored by the methods in this class. About the only information CountFramesConsumer is interested in is the SINGLEFRAME flag to setHints and the SINGLEFRAMEDONE or STATICIMAGEDONE flags to imageComplete. The CountFramesConsumer's constructor is passed an Image object, whose getSource method returns a Producer to pass the Image's pixel data to a consumer. See the API summary of the ImageConsumer interface for an example of an ImageConsumer.

ImageFilters

An ImageFilter is a special type of ImageConsumer which passes image data on to another ImageConsumer after suitable processing. Figure 8-3 illustrates ImageFilters as intermediate compartments in the flow of image data from its producer to its ultimate consumer. You can see that multiple filters can be chained together.


Figure 8-3  ImageFilters are consumers able to pass modified pixel data on to other consumers (including other ImageFilters)

The default implementation of ImageFilter is a null filter. That is, it actually does nothing to the image data, but simply passes it through to another consumer. The filter keeps track of the ImageConsumer to which it is passing data. All other image filters are derived from the ImageFilter class, and override its default implementations of the ImageConsumer interface to actually perform some action on Image data.

Filtering an image involves creating a FilteredImageSource. The FilteredImageSource requires two parameters to its constructor: an ImageProducer and an ImageFilter. The FilteredImageSource simply makes the ImageFilter a consumer of the ImageProducer's data, and funnels the output of the ImageFilter to a new Image object. This code snippet shows how to use an ImageFilter to create a filtered version of a particular image.

Image img;
ImageFilter filter;

// img and filter are set to refer to an Image and ImageFilter.

Image imgFiltered = Toolkit.getDefaultToolkit().createImage(
new FilteredImageSource(img.getSource(), filter));

The simplest image filters to create are ones which modify each pixel's RGBα data independant of the pixel data for other pixels in the Image. An example of this type of filter would be a TintFilter, which modifies the tint of each pixel in an Image uniformly, much the same as the tint dial on a television set. AWT provides an RGBImageFilter class to make the creation of such filters much easier.

To create an RGBImageFilter, you need only implement the RGBImageFilter.filterRGB method in your derived RGBImageFilter class. The filterRGB method is passed the coordinates of a single pixel and the color data for that pixel (before any filtering) as an RGBα integer. This method returns a replacement RGBα integer for that particular pixel. In all RGBImageFilters, each individual pixel in an image is passed through filterRGB. The following listing is the code for a simple Color2BWFilter. Color2BWFilter converts a color image to a black-and-white image by making the red, green, and blue components of each individual pixel equal, essentially converting the image to grayscale.

class Color2BWFilter extends RGBImageFilter

// RGB-alpha data passed to filterRGB is converted
// to grayscale by setting each of the color components
// to be equal to the average of the r,g and b values.
public int filterRGB(int x, int y, int rgb)

Notice that the constructor of the Color2BWFilter sets a canFilterIndexColorModel member to true. That flag tells the RGBImageFilter implementation that if the image being filtered is based on an IndexColorModel, then passed the IndexColorModel's 256 entries through the filter, not the actual pixel data. In the case of the Color2BWFilter, this means the filter will actually change the values associated with each color index to be a grayscale color. The pixel data doesn't change at all. This feature makes RGBImageFilters remarkably efficient at filtering images based on an IndexColorModel, since only 256 values need to be modified no matter how big the input image is (or, at most the number of color entries in the IndexColorModel, which is usually 256).

More complex filtering than RGBImageFiltering, filtering which is not uniform for all pixels in an image, requires an ImageFilter-derived class which overrides the default implementations of ImageFilter's ImageConsumer methods. An example of such a filtering operation is an image convolution, also known as blurring or sharpening an image. In convolution, the resultant color data for a particular pixel is dependent on the color values of the pixels adjacent to it. To perform this type of operation, your ImageFilter would have to remember the pixel data around the border of regions given to setPixels for filtering.

For example, let's say you need the pixel data of nine pixels from the input image (3 x 3 square of pixels) in order to figure out the pixel data of the center pixel in the output image. You would not be able to calculate the output pixel values for pixels within one pixel of the edge of a rectangle sent to setPixels. You would not be able to calculate these pixel values until the adjacent region was sent to setPixels. In this case, you would have to keep track of the pixel values for these border pixels until you get all adjacent regions.

AWT Image Processing API Summaries

Table 8-1 lists the classes related to AWT Image processing, which are described in this chapter. Table 8-2 lists the methods associated with the AWT Image processing classes and interfaces, including a short description of each method.

Table 8-1 The AWT Image processing classes and interfaces summarized


Class

Description


ColorModel

Defines a storage schema for pixel color data. Can convert this data to the default RGBα schema.

DirectColorModel

An arbitrary, packed bit storage schema for pixel color data. You can create your own instances with different color component bit widths and masks.

IndexColorModel

A storage schema in which each pixel's data is actually an index to a lookup table of colors.

ImageConsumer

An Interface implemented by any class whose job is to collect Image pixel data for whatever reason.

ImageProducer

An Interface implemented by objects that supply Image pixel data to ImageConsumers.

Table 8-2 Methods of the Image processing classes and interfaces


Class

Method

Description


ColorModel

getRGBDefault

Gets a ColorModel describing the default RGBα bit packing scheme.

getPixelSize

Gets number of bits of pixel data for the ColorModel.

getRed

Gets red component value given a pixel's value.

getGreen

Gets green component value given a pixel's value.

getBlue

Gets blue component value given a pixel's value.

getAlpha

Gets transparency value given a pixel's value.

getRGB

Gets RGBα version of a pixel's value.

DirectColorModel

getRedMask

Gets bitmask of red component data for pixels based on this DirectColorModel.

getGreenMask

Gets bitmask of green component data for pixels based on this DirectColorModel.

getBlueMask

Gets bitmask of blue component data for pixels based on this DirectColorModel.

getAlphaMask

Gets bitmask of transparency component data for pixels based on this DirectColorModel.

IndexColorModel

getMapSize

Gets number of indexed colors in the IndexColorModel.

getTransparentPixel

Gets pixel value reserved for full transparency.

getReds

Fills array with indexed red component values.

getGreens

Fills array with indexed green component values.

getBlues

Fills array with indexed blue component values.

getAlphas

Fills array with indexed transparency values.

ImageConsumer

setDimensions

Called to indicate the size of the image.

setProperties

Called to pass the consumer the image's properties.

setColorModel

Indicates the ColorModel for the majority of pixel data being passed to the consumer.

setHints

Indicates some information about the order that pixels will be passed to the consumer.

setPixels

Called to give the consumer image pixel values.

imageComplete

Indicates status of the completed image.

ImageProducer

addConsumer

Registers an ImageConsumer with the producer.

isConsumer

Checks whether an ImageConsumer is registered with this producer.

removeConsumer

Unregisters an ImageConsumer with this producer.

startProduction

Kick-starts the image production process.

requestTopDownLeftRight Resend

After image production has completed, asks the producer to re-send the data in a single pass, in top-down, left-right order.

ColorModel

Purpose

Encapsulate methods for interpreting raw pixel data.

Syntax

public abstract class ColorModel

Description

Encapsulates methods for interpreting raw pixel data. Image data is associated with a particular ColorModel. The ColorModel can then be used to convert the raw pixel data in RGBα data for the pixel. Figure 8-4 shows the hierarchy of the ColorModel class.

Package

java.awt.image

Imports

None.

Constructors

public ColorModel(int pixel_bits);
Since this is an abstract class, this constructor is only called from the constructors of derived classes. Specify the number of bits used to store a single pixel's data; this is the same value returned by getPixelSize.

Parameters

int pixel_bits

This member stores the number of bits required to store a single pixel's data. It is protected, so it is available to derived classes.


Figure 8-4  The hierarchy of the ColorModel class

getRGBDefault

ClassName

ColorModel

Purpose

Gets a ColorModel object which describes the default RGBα color storage scheme.

Syntax

public static ColorModel getRGBDefault();

Parameters

None.

Imports

None.

Description

Returns a DirectColorModel object that defines the default storage schema for RGBα data in an integer. You can use the default RGB ColorModel to unpack red, green, and blue color components from RGBα data. But since the packing schema of the default RGB ColorModel is so well known, it is easier just to unpack the bits yourself. Default RGB packed color data is packed like this: 0xααrrggbb.

Returns

The default RGB ColorModel object.

Example

The example below actually unpacks default RGB color data using two methods: directly unpacking the data using bitshift operators, and unpacking the data using the default RGB ColorModel:

class MyFilter extends RGBImageFilter

getPixelSize

ClassName

ColorModel

Purpose

Gets the width of pixel data described by this ColorModel.

Syntax

public int getPixelSize();

Parameters

None.

Imports

None.

Description

Gets the number of bits required to store a single pixel's color data. This will be either 8 or 32 bits. IndexColorModels use 8 bits to store the index into a color table. Most other schemas use 32 bits.

Returns

The number of bits required to store the color data for a single pixel encoded using this ColorModel.

Example

You can use the results of the getPixelSize method to allocate storage for pixel data using a particular ColorModel, as in this example:

ColorModel cm;

// Initialize the ColorModel cm.

// Now, allocate an array of 100 elements of the correct
// size to store pixel data.
int[] anPixels = null;
byte[] abPixels = null;

if(cm.getPixelSize() == 32)
anPixels = new int[100];
else
abPixels = new byte[100];

getRed

ClassName

ColorModel

Purpose

Gets value of the red color component for a pixel.

Syntax

public int getRed(int pixel);

Parameters

int pixel

The value of a single pixel's data. If original pixel data is a byte, then it must be cast to an integer.

Imports

None.

Description

Gets the value of the red color component from an integer of pixel data. This is an abstract method which must be implemented by all ColorModel-derived classes.

Returns

The red color component from the pixel data.

Example

See the example under the getAlpha method of the ColorModel class, later is this section.

getGreen

ClassName

ColorModel

Purpose

Gets value of the green color component for a pixel.

Syntax

public int getGreen(int pixel);

Parameters

int pixel

The value of a single pixel's data. If original pixel data is a byte, then it must be cast to an integer.

Imports

None.

Description

Gets the value of the green color component from an integer of pixel data. This is an abstract method which must be implemented by all ColorModel-derived classes.

Returns

The green color component from the pixel data.

Example

See the example under the getAlpha method of the ColorModel class, later in this section.

getBlue

ClassName

ColorModel

Purpose

Gets value of the blue color component for a pixel.

Syntax

public int getBlue(int pixel);

Parameters

int pixel

The value of a single pixel's data. If original pixel data is a byte, then it must be cast to an integer.

Imports

None.

Description

Gets the value of the blue color component from an integer of pixel data. This is an abstract method which must be implemented by all ColorModel-derived classes.

Returns

The blue color component from the pixel data.

Example

See the example under the getAlpha method of the ColorModel class, later is this section.

getAlpha

ClassName

ColorModel

Purpose

Gets the transparency value from a single single's data.

Syntax

public int getAlpha(int pixel);

Parameters

int pixel

The value of a single pixel's data. If original pixel data is a byte, then it must be cast to an integer.

Imports

None.

Description

Gets the value of the alpha transparency value from an integer of pixel data. Transparency is generally measured from 0-255, where 255 is fully opaque, and 0 is fully transparent. Note also that the IndexColorModel allows you to specify a single index as being fully transparent, similar to the GIF format's transparent color indicator. getAlpha is an abstract method which must be implemented by all ColorModel-derived classes.

Returns

The alpha transparency value from the pixel data.

Example

In this example, the ColorModel passed to an ImageFilter's setPixels method is used to get the red, green, and blue color components from individual pixels.

public MyFilter extends ImageFilter
}
}

getRGB

ClassName

ColorModel

Purpose

Converts pixel data to RGBα schema.

Syntax

public int getRGB(int pixel);

Parameters

int pixel

Raw pixel data stored in the ColorModel's storage schema.

Description

Converts raw pixel color data to the default RGBα storage schema. The output pixel data is equivalent in color and transparency to the raw color data.

Returns

A packed integer of red, green, blue, and alpha color values packed using the default RGB storage schema.

Example

This example demonstrates the technique used by the RGBImageFilter to pass all pixel data through the filterRGB method. That is, all pixels passed to RGBImageFilters are converted to the default RGBα storage schema using getRGB:

class MyRGBConverter implements ImageConsumer
}
}

// processRGB does something with the default RGB-alpha
// data for each pixel. processRGB is very similar to
// RGBImageFilter.filterRGB.
public void processRGB(int x, int y, int rgb)

DirectColorModel

Purpose

Encapsulates methods for interpreting image pixel data based on packed bitfields of red, green, blue, and alpha color components.

Syntax

public class DirectColorModel extends ColorModel

Description

The DirectColorModel encapsulates methods for interpreting image pixel data based on packed bitfields of red, green, blue, and alpha color components. The size and presence of the bitfields for each of these components is completely configurable. You could, for example, create a DirectColorModel object which can interpret 5-6-5 16-bit RGB pixel data (16 bits of each pixel's data are used, top 5 bits are red, next 6 are green, and bottom 5 are blue). The example below demonstrates how to create such a DirectColorModel. Figure 8-5 shows the hierarchy of the DirectColorModel class.

Package

java.awt.image

Imports

java.awt.AWTException

Constructors

public DirectColorModel(int pixel_bits, int redmask, int greenmask, int bluemask);
public DirectColorModel(int pixel_bits, int redmask, int greenmask, int bluemask, int alphamask);
The pixel_bits parameter can be 8 or 32 bit, and it specifies how many bits of pixel data are used per pixel. Specify bitmasks for each of the color components in the mask parameters. If it is left out, the alphamask is assumed to be 0x00000000.

Parameters

None.

Example

This example builds a DirectColorModel that interprets color data using the default RGBα storage schema: 0xααrrggbb.


Figure 8-5  The hierarchy of the DirectColorModel class



DirectColorModel dcm =
new DirectColorModel(32, // 32 bits/pixel
0x00ff0000, // the red mask
0x0000ff00, // the green mask
0x000000ff, // the blue mask
0xff000000); // the alpha mask


This example creates a DirectColorModel that interprets color data using a 5-6-5 16-bit RGB shema:

DirectColorModel dcm =
new DirectColorModel(32, // 32 bits/pixel
0x0000F800, // red mask
0x000007E0, // green mask
0x0000001F, // green mask
0x00000000); .// no alpha data

getRedMask

ClassName

DirectColorModel

Purpose

Gets bitmask of packed red component values.

Syntax

public int getRedMask();

Parameters

None.

Imports

None.

Description

Gets the bitmask defining the bitfield in which red color data is stored in packed integers using this DirectColorModel.

Returns

The red bitmask passed to the DirectColorModel's constructor.

Example

See the example under the getAlphaMask method of the DirectColorModel, later in this section.

getGreenMask

ClassName

DirectColorModel

Purpose

Gets bitmask of packed green component values.

Syntax

public int getGreenMask();

Parameters

None.

Imports

None.

Description

Gets the bitmask defining the bitfield in which green color data is stored in packed integers using this DirectColorModel.

Returns

The green bitmask passed to the DirectColorModel's constructor.

Example

See the example under the getAlphaMask method of the DirectColorModel, later in this section.

getBlueMask

ClassName

DirectColorModel

Purpose

Gets bitmask of packed blue component values.

Syntax

public int getBlueMask();

Parameters

None.

Imports

None.

Description

Gets the bitmask defining the bitfield in which blue color data is stored in packed integers using this DirectColorModel.

Returns

The blue bitmask passed to the DirectColorModel's constructor.

Example

See the example under the getAlphaMask method of the DirectColorModel, later in this section.

getAlphaMask

ClassName

DirectColorModel

Purpose

Gets bitmask of packed alpha or transparency component values.

Syntax

public int getAlphaMask();

Parameters

None.

Imports

None.

Description

Gets the bitmask defining the bitfield in which alpha color data is stored in packed integers using this DirectColorModel.

Returns

The alpha bitmask passed to the DirectColorModel's constructor.

Example

Given an Image which uses a DirectColorModel, this example method displays how many bits are used to store each color component in the packed raw image data.

public void displayBitSizes(Image img)

System.out.println("red field width is " + redbits);
System.out.println("green field width is " + greenbits);
System.out.println("blue field width is " + bluebits);
System.out.println("alpha field width is " + alphabits);

IndexColorModel

Purpose

Encapsulates methods for interpreting image pixel data based on an indexed color palette. Most IndexColorModels describe a 256-color palette, although the IndexColorModel class constructor allows you to specify any number up to 4 billion.

Syntax

public class IndexColorModel extends ColorModel

Description

For some images, the image pixel data is really an index into a color table. The IndexColorModel represents the color table. The class provides methods for converting pixel data into actual red, green, blue, and alpha color component values. Figure 8-6 shows the hierarchy of the IndexColorModel class.

Package

java.awt.image

Imports

None.

Constructors

public IndexColorModel(int bits, int size, byte[] r, byte[] g, byte[] b);
public IndexColorModel(int bits, int size, byte[] r, byte[] g, byte[] b, int trans);
public IndexColorModel(int bits, int size, byte[] r, byte[] g, byte[] b, byte[] a);
public IndexColorModel(int bits, int size, byte[] cmap, int offset, boolean hasalpha);
public IndexColorModel(int bits, int size, byte[] cmap, int offset, boolean hasalpha, int trans);
The bits parameter specifies the number of bits of pixel data associated with each pixel. The size parameter indicates the size of the color map, and the color map is described by the r, g, b, and a arrays, or by the cmap array.
The cmap array is assumed to have the pattern [r|g|b|a|r|g|b|a|.] or [r|g|b|r|g|b|.]. That is, every third or fourth byte specifies the red color component value for index entry (byte number/4). Every third or fourth starting at index 1 encodes the green color component value for successive index entries. Every third or fourth starting at index 2 encodes the blue component value for successive entries, and so on.
If the hasalpha parameter is true, then every fourth cmap byte, starting at index 3, encodes the alpha values for successive index entries.
The trans parameter indicates the index which is to be interpreted as "transparent." For example, GIF images allow you to specify a "transparent color." All pixels of that color are rendered as transparent when the image is drawn.

Parameters

None.

Example

This example builds a 256-entry IndexColorModel with colors distributed evenly around the color wheel. A black color is defined, a white color and a transparent color, which leaves room for 253 more colors. Even distribution using 6 increments of red, 6 increments or blue, and 7 increments of green creates 252 more color entries. That leaves a single entry which will be defined as gray:



byte r[256];
byte g[256];
byte b[256];

// Create the white, black, gray, and transparent entries as the
// last four entries in each array.
r[255] = g[255] = b[255] = 0; // black
r[254] = g[254] = b[254] = 255; // white
r[253] = g[253] = b[253] = 128; // gray
r[252] = g[252] = b[252] = 0; // used for transparency

// Create evenly distributed sets of RGB entries by allowing
// 6 even increments of red, 6 even increments of blue, and
// 7 even increments of green = 6*6*7 = 252 entries.
for( int iR=0 ; iR<6 ; iR++ )
}


// Create the IndexColorModel
IndexColorModel icm =
new IndexColorModel(8, // pixels are 8 bit each
256, // size of color arrays
r, // the red entries
g, // the green entries
b, .. the blue entries
252); // transparent index



Figure 8-6  The hierarchy of the IndexColorModel class

getMapSize

ClassName

IndexColorModel

Purpose

Gets the number of color entries in the IndexColorModel.

Syntax

public int getMapSize();

Parameters

None.

Imports

None.

Description

Gets the number of color entries in the IndexColorModel. This is the same value as the size parameter to the IndexColorModel's constructor. The arrays you pass to the getReds, getGreens, getBlues, or getAlphas methods must be at least this long.

Returns

Returns the number of color entries in the IndexColorModel.

Example

This code creates a two-dimensional array to hold the indexed color entries.

public byte[][] getRGBAs(IndexColorModel icm)

getTransparentPixel

Class

IndexColorModel

Purpose

Gets the index of the "transparent color" of the IndexColorModel.

Syntax

public int getTransparentPixel();

Parameters

None.

Imports

None.

Description

Gets the index of the "transparent color" of the IndexColorModel. This is the same as the trans parameter to the IndexColorModel constructor.

Returns

The value of the "transparent index" is returned. If there is no transparent color, then -1 is returned.

Example

In this example ImageFilter, that assumes the ColorModel is an IndexColorModel, all pixels of a particular color are converted to the transparent color.

public class TransColorFilter extends ImageFilter

public void setPixels(int x, int y, int w, int h,
ColorModel model, byte[] pixels, int off,
int scansize)

IndexColorModel icm = (IndexColorModel)model;
int trans = icm.getTransparentPixel();
if(-1 == trans)

for(int xx=x ; xx<x+w ; xx++)
for(int yy=y ; yy<y+h ; yy++)
if(pixels[xx * scansize + yy + off] ==
substColor)
pixels[xx * scansize + yy + off] =
(byte)trans;
consumer.setPixels(x, y, w, h, model, pixels,
off, scansize);
}



getReds

ClassName

IndexColorModel

Purpose

Gets index of red color component values.

Syntax

public void getReds(byte[] r);

Parameters

byte[] r

Array of bytes. This array must have as many elements as indicated by the getMapSize method.

Imports

None.

Description

Gets an array of red color components. The values of the internal array of red color values is copied to the passed byte array.

Returns

None.

Example

See the example for the getAlphas method.

getGreens

ClassName

IndexColorModel

Purpose

Gets index of green color component values.

Syntax

public void getGreens(byte[] g);

Parameters

byte[] g

Array of bytes. This array must have as many elements as indicated by the getMapSize method.

Imports

None.

Description

Gets an array of green color components. The values of the internal array of green color values is copied to the passed byte array.

Returns

None.

Example

See the example for the getAlphas method.

getBlues

ClassName

IndexColorModel

Purpose

Gets index of blue color component values.

Syntax

public void getBlues(byte[] b);

Parameters

byte[] b

Array of bytes. This array must have as many elements as indicated by the getMapSize method.

Imports

None.

Description

Gets an array of blue color components. The values of the internal array of blue color values is copied to the passed byte array.

Returns

None.

Example

See the example for the getAlphas method.

getAlphas

ClassName

IndexColorModel

Purpose

Gets index of alpha transparency component values.

Syntax

public void getAlphas(byte[] a);

Parameters

byte[] a

Array of bytes. This array must have as many elements as indicated by the getMapSize method.

Imports

None.

Description

Gets an array of transparency components. The values of the internal array of transparency values is copied to the passed byte array.

Returns

None.

Example

This example writes any IndexColorModel's full palette of colors to System.out.

public void displayPalette(IndexColorModel icm)

ImageConsumer

Purpose

Implement the ImageConsumer interface in your object to receive image pixel data from an ImageProducer.

Syntax

interface ImageConsumer

Description

The ImageConsumer interface is implemented by objects wishing to process image data. The ImageConsumer is associated with an ImageProducer, and the producer sends image pixel data to the consumer through the setPixels method. Figure 8-7 shows the hierarchy of the ImageConsumer interface.

Package

java.awt.image

Imports

None.

Example

This ImageConsumer counts the number of frames in an image.


Figure 8-7  The hierarchy of the ImageConsumer interface

class CountFramesConsumer implements ImageConsumer

// Get the frame count using getFrameCount().
public int getFrameCount()

// constructor requires an Image to count the frames for.
public CountFramesConsumer(Image img)

// To increment the frame counter, the _fComplete flag
// must not be set.
public void incrementFrameCount()

// Ignore the dimensions, though an implementation of this
// method is required.
public void setDimensions(int width, int height)

// Ignore properties, though implementation of this method
// is required.
public void setPropertires(Hashtable props)

// Ignore ColorModel, though implementation of this method
// is required.
public void setColorModel(ColorModel model)

// Hints might be useful, especially if the SINGLEFRAME hint
// is passed, indicating calculation is complete.
public void setHints(int flags)
}

// Pixel data can actually be ignored, though both
// overloaded versions of setPixels() must be implemented.
public void setPixels(int x, int y, int width, int height,
byte[] pixels, int off, int scansize)

public void setPixels(int x, int y, int width, int height,
int[] pixels, int off, int scansize)

// imageComplete is called whenever an image has been
// completed. Look out for the IMAGEERROR and IMAGEABORTED
// flags.
public void imageComplete(int flags)

setDimensions

Interface

ImageConsumer

Purpose

Called to indicate the dimensions of the image being processed by the ImageConsumer.

Syntax

public void setDimensions(int width, int height);

Parameters

int width

The width of the Image being presented to the consumer.

int height

The height of the Image being presented to the consumer.

Imports

None.

Description

The consumer is passed the dimensions of the Image that is being passed to it by the consumer through this method. The setDimensions method is called by the producer exactly once. This call is guaranteed to occur before any call to setPixels.

Example

This example allocates storage for processing an Image based on the size of the Image. The setColorModel method is also implemented so that the ImageConsumer knows the size of each pixel's data.

class MyConsumer implements ImageConsumer

public void setColorModel(ColorModel cm)

public void allocate() else
}

// Other consumer methods implemented.

setProperties

Interface

ImageConsumer

Purpose

Called to give the ImageConsumer a list of the image's properties.

Syntax

public void setProperties(Hashtable props);

Parameters

Hashtable props

Image properties are stored under String keys in the props Hashtable. Properties may include the filters used to create the Image data, the source of the Image data, etc.

Returns

None.

Description

An extensible list of Image properties is passed to the consumer by the producer. The properties are stored in a Hashtable object. Currently, there are only two documented image properties: comments and filter. The comments property has a textual description of the image. The filter property describes which filters were applied to the original Image to get the pixel data being received by this ImageConsumer. setProperties is guaranteed to be called before any calls to setPixels.

Example

This example displays all the Image properties on System.out.

class MyConsumer extends ImageConsumer

// Other consumer methods implemented.

setColorModel

Interface

ImageConsumer

Purpose

Sets the ColorModel for the majority of the image pixel data.

Syntax

public void setColorModel(ColorModel model);

Parameters

ColorModel model

The ColorModel, which will be used in the majority of calls to setPixels().

Imports

None.

Description

The producer passes to the consumer a ColorModel that will be used in the majority of calls to setPixels. No assumption should be made by the ImageConsumer that model will be the only ColorModel passed to the consumer's setPixels method, since each set of pixels can be sent with a unique ColorModel object. For example, an ImageFilter may pass a majority of pixels on to the consumer without modification. For a minority of those pixels, the filter may use a ColorModel conducive to the ImageFilter's filtering process. In that case, the original producer's ColorModel will be passed to the consumer through the filter in the majority of calls to setPixels, and it will be passed to setColorModel. Note also that setColorModel will be called before the first call to setPixels.

Returns

None.

Example

See the example under the setDimensions method of the ImageConsumer interface.

setHints

Interface

ImageConsumer

Purpose

Provides the consumer with some practical information about how pixel data will be sent to it.

Syntax

public void setHints(int flags);

Parameters

int flags

A set of ImageConsumer flags OR together bitwise. The valid flags, and a description of each, is presented in the following table:

Flag

Meaning


RANDOMPIXELORDER

No assumption can be made about the order in which pixels will be delivered to the consumer.

TOPDOWNLEFTRIGHT

Pixels will be delivered to the consumer in top-down, left-right order.

COMPLETESCANLINES

Pixels will be delivered to the consumer in multiples of complete scanlines. That is, all calls to setPixels() will have a width parameter equal to the width of the Image.

SINGLEPASS

Each pixel's data will appear in exactly one call to setPixels().

SINGLEFRAME

The Image includes only a single frame. A JPEG image, for example, includes only a single frame. An MPG Image, on the other hand, includes multiple frames of an animation sequence.


Imports

None.

Description

The producer can pass to the consumer some hints about the order that pixels will be passed to the consumer through setPixels. Those hints are flags passed to the setHints method.
The hints passed to the consumer in setHints can be used by the consumer to optimize its processing of image data. For example, a Fourier Transform consumer could realize significant optimizations in its storage and processing using the so-called "fast-Fourier transform" algorithm if the Image data is passed as complete scanlines, indicated by the COMPLETESCANLINES flag being passed to this method.

Returns

None.

Example

This ImageConsumer calculates the average red, green, and blue color component values for all the pixels it is passed. The consumer is pretty simple, however, and requires that each pixel's value be sent to it only once. This consumer waits for the SINGLEPASS flag to be passed to its setHints method. If this flag is not passed, then the consumer doesn't even try to accomplish its task, since it would be too hard to keep track of the number of times each pixel was re-sent to the consumer.

public class MyConsumer implements ImageConsumer

public void setHints(int flags)



public void setPixels(int x, int y, int w, int h,
ColorModel model, byte[] pixels, int offset,
int scansize)
}

public void imageComplete(int status)

public float getAverageGreen()

public float getAverageBlue()

// This method returns true when the consumer
// is finished with its processing.
public boolean isComplete()

setPixels

Interface

ImageConsumer

Purpose

All pixel data sent to a consumer is sent through one or both of the overloaded setPixels methods.

Syntax

public void setPixels(int x, int y, int width, int height, ColorModel model, byte[] pixels, int offset, int scansize);public void setPixels(int x, int y, int width, int height, ColorModel model, int[] pixels, int offset, int scansize);

Parameters

int x, int y

The origin or upper-left corner of the rectangle of pixels being passed to the consumer.

int width, int height

The dimensions of the rectangle of pixels being passed to the consumer.

ColorModel model

The ColorModel defining the storage schema of color and transparency data for the pixels[] array.

byte pixels[]

An array of 8-bit pixel data. The x, y, width, height, offset and scansize parameters must be used to determine which elements of this array contain valid pixel data for the Image.

int pixels[]

An array of 32-bit pixel data. The x, y, width, height, offset and scansize parameters must be used to determine which elements of this array contain valid pixel data for the Image.

int offset

Used along with scansize to determine the valid elements of the pixels[] array.

int scansize

Used along with offset to determine the valid elements of the pixels[] array.

Imports

java.awt.image.ColorModel

Description

Image pixels are passed from the producer to the consumer in multiple calls to setPixels. Each call defines the pixel data for a rectangle of the image. Two overloaded versions of this method exist, one to accept 8-bit pixel data, and the other to accept 32-bit pixel data.
Use this formula to find the pixels array element for pixel (M, N):

p(M,N) = pixels[(n * scansize) + M + offset];

Returns

None.

Example

The following example, which assumes the SINGLEPASS flag has been passed to setHints, calculates the average pixel values of red, green, and blue for all pixel data passed to MyConsumer:

class MyConsumer implements ImageConsumer

// For the sake of brevity, only one of the overloaded
// versions of setPixels() is implemented here, although
// your ImageConsumer must implement both the 8-bit and
// 32-bit versions.
public void setPixels(int x, int y, int width, int height,
ColorModel model, int[] pixels, int offset,
int scansize)
}
}

public void imageComplete(int flags)
}

imageComplete

Interface

ImageConsumer

Purpose

Called to notify the consumer that all image pixel data has been sent.

Syntax

public void imageComplete(int flags);

Parameters

int flags

A bitwise ORing of ImageConsumer flags indicating the state of the production sequence upon termination. The following table lists the ImageConsumer flags which can be passed to imageComplete.

Flag

Meaning


IMAGEERROR

The producer encountered an error while processing the Image data. No more image data will be passed to the consumer for this Image.

SINGLEFRAMEDONE

A single frame in a multiframe Image has been completed.

STATICIMAGEDONE

The entire image, whether a single-frame or a multiframe Image, has been completely sent to the consumer.

IMAGEABORTED

The Image creation process was deliberately aborted.


Imports

None.

Description

This method is called by the producer to notify the consumer that all image pixel data has been sent to the consumer. The reason for the completion can be an error, a single-frame of a multiframe Image has been completed, or the entire Image has been completed. The flags passed to this method indicate the nature of the completion.
Unfortunately, more specific information about the interpretation of IMAGEERROR or IMAGEABORTED flags is not available to the consumer, unless you implement custom methods in your consumers and producers to provide this information.

Example

See the Example for the setPixels method.

ImageProducer

Purpose

An ImageProducer generates image pixel data and sends it to one or more ImageConsumers.

Syntax

interface ImageProducer

Description

To have your object generate images for display, have it implement the ImageProducer interface. The interface sends image pixel data based on a ColorModel. The image pixel data is sent in potentially overlapping rectangles of image data. Figure 8-8 shows the hierarchy of the ImageProducer interface.

Package

java.awt.image

Imports

None.

Example

This example ImageProducer generates a color gradient. Specify to the constructor the size of the image you want produced and the colors at the top and bottom of the image.


Figure 8-8  The hierarchy of the ImageProducer interface

public class GradientProducer implements ImageProducer

public void addConsumer(ImageConsumer ic)

public boolean isConsumer(ImageConsumer ic)

public void removeConsumer(ImageConsumer ic)

// To produce the image, create each scanline
// of the next color in the gradient and give
// it to the ImageConsumer through setPixels.
// We are going to use default RGBα encoding.
public void startProduction(ImageConsumer ic)

ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
}

// Ignore requests for re-sends.
public void requestTopDownLeftRightResend()

addConsumer

Interface

ImageProducer

Purpose

Registers an ImageConsumer with the ImageProducer. Only registered consumers can receive pixel data.

Syntax

public void addConsumer(ImageConsumer ic)

Parameters

ImageConsumer ic

A new ImageConsumer that the producer is to send pixel data to. Note that the producer should take care not to allow the same consumer to be added multiple times.

Imports

None.

Description

The producer is asked to send pixel data to a new ImageConsumer. The producer must keep track of more than one consumer, making sure to send each of its consumers all pixel data for an Image.
The producer may wait for an explicit call to startProduction, or it may begin sending pixel data to the consumer as soon as it is ready.

Returns

None.

Example

This code stores the list of currently active consumers in a Vector. This code is suitable for use in your own ImageProducers to keep track of ImageConsumer objects.

class MyProducer implements ImageProducer

public boolean isConsumer(ImageConsumer ic)

public void RemoveConsumer(ImageConsumer ic)

// startProduction and requestTopDownLeftRightResend
// must be implemented to create a complete producer.

isConsumer

Interface

ImageProducer

Purpose

Checks to see if an ImageConsumer is registered with this ImageProducer.

Syntax

public boolean isConsumer(ImageConsumer ic)

Parameters

ImageConsumer ic

The ImageConsumer to check.

Imports

None.

Description

Tells whether or not an ImageConsumer is currently registered with this producer.

Returns

True if the ImageConsumer is currently registered with the ImageProducer. Otherwise, false.

Example

See the example listed for addConsumer.

removeConsumer

Interface

ImageProducer

Syntax

public void removeConsumer(ImageConsumer ic)

Parameters

ImageConsumer ic

The ImageConsumer to remove as a consumer of this producer's pixel data.

Imports

None.

Description

Removes the ImageConsumer as a consumer of pixel data from this producer. If the consumer is not currently registered with this producer, this method should return benignly.

Example

See the example listed for addConsumer.

startProduction

Interface

ImageProducer

Purpose

Tells the ImageProducer to start sending pixel data to a specific registered ImageConsumer.

Syntax

public void startProduction(ImageConsumer ic)

Parameters

ImageConsumer ic

The consumer to begin sending pixel data to.

Imports

None.

Description

This method can be called by any external object to kick-start an image production process. An ImageProducer may start sending image pixel data to an ImageConsumer as soon as the consumer gets registered with the producer using addConsumer. The ImageProducer is not required to send any pixel data to a consumer until startProduction is called.
If the consumer has not been added to this producer yet, then this method should return benignly. The minimum implementation of startProduction should call these ImageConsumer methods:

ic.setHints(<hints>);
ic.setPixels(.); // as many times as needed.
ic.imageComplete(STATICIMAGEDONE);

Returns

None.

Example

See the example under the description of the ImageProducer interface.

requestTopDownLeftRightResend

Interface

ImageProducer

Purpose

To force the producer to re-send all image data in a single pass, in top-down-left-right order.

Syntax

public void requestTopDownLeftRightResend(ImageConsumer ic)

Parameters

ImageConsumer ic

The consumer to re-send the Image data to. If the consumer has not been added to this producer yet, then this method should return benignly.

Imports

None.

Description

A request to re-send Image pixel data in TOPDOWNLEFTRIGHT order. The producer can choose whether or not to grant or ignore this request.
Producers, which present Image data from sources which are too diffucult to deliver in TOPDOWNLEFTRIGHT order, need not grant this re-send request.

Returns

None.

The AWT Image Processing Project: The MultiFilter Application

The MultiFilter application is a generic application for testing ImageFilters. It is able to load and display Images from the Internet, or the local file system. MultiFilter's user-interaction involves selecting filters to apply to an image as a whole, or to a selected rectangle of the image. MultiFilter is extensible, so you can add your own ImageFilters to it easily. Once you understand the workings of MultiFilter, you are encouraged to try adding some of the sample filters presented earlier in this chapter, or filters of your own.

Two new ImageFilters are included in the MultiFilter application: The ContrastFilter demonstrates how to build RGBImageFilters. The InvertFilter is a more complex image filter, capable of flipping an Image horizontally or vertically, and is thus directly derived from ImageFilter.

In addition to the AWT Image Processing techniques demonstrated by MultiFilter, this project also demonstrates a user-interface model using modeless dialogs to configure the individual ImageFilters. In this model, user interactions with floating modeless dialogs are reported to the application's main Frame window using the Event delivery pipeline inherited by all Component objects.

Figure 8-9 is a screenshot of the MultiFilter application running. You can see the image that is loaded has had both the contrast and invert filters applied to it. Figures 8-10 and 8-11 are before and after pictures of this image, illustrating how image filtering can be applied to an image.


Figure 8-9  Screenshot of the MultiFilter application running in Windows


Figure 8-10  Before an image is processed by MultiFilter


Figure 8-11  After an image is processed by MultiFilter

Assembling the Project

Note that all the files listed here are also available on the CD packaged with this book in the directory "PROJECTSGOHEREMultiFilter". Please feel free to copy those files directly.

1.  Create a new directory called MultiFilter.

2.  Create a new file in the MultiFilter directory called "MultiFilter.java". Start by importing the necessary packages, declaring our application object, and implementing the static main() method.

import java.awt.*;
import java.awt.image.*;
import java.net.URL;
import java.util.Hashtable;

public class MultiFilter


3.  The application's main window is a MultiFilterFrame object. Its job is to display a filtered image (if one has been loaded), and to react to user menu selections. Here we declare the MultiFilterFrame class, its internal member variables, and its constructor:

class MultiFilterFrame extends Frame

4.  The ImageLoaderThread class is responsible for loading images from the local file system or from the Internet. The Thread keeps a reference to the MultiFilter application's main window and posts custom Events to that window to indicate the progress of image loading. Here is the code for the ImageLoaderThread class:

public class ImageLoaderThread extends Thread

public void run() catch (Exception e)

if( null == _img )

// Use a MediaTracker to wait for the Image to be
// fully loaded.
MediaTracker mt = new MediaTracker( _compDeliver );
mt.addImage( _img, 0 );
try catch(Exception e)

// Deliver completed image to the component.
_compDeliver.deliverEvent(new Event(
_img, IMAGE_COMPLETED, _strLoc));

return;
}

5.  The application's main window will receive the custom Event notifications from ImageLoaderThreads. In its handleEvent implementation, the MultiFilterFrame must process these messages, as well as any WINDOW_DESTROY messages. Here is the code for the Event handling (i.e., MultiFilterFrame.handleEvent):

public boolean handleEvent(Event evt)
break;

case WorkspaceCanvas.SELECTION_CLEARED:
_fActiveSelection = false;
updateMenu();
return true;

case WorkspaceCanvas.SELECTION_CHANGED:
_fActiveSelection = true;
updateMenu();
return true;

case OpenObjectDialog.OPENOBJECTDIALOG_INPUT:
ImageLoaderThread t =
new ImageLoaderThread((String)evt.arg,
this);
t.start();
return true;

case ConfiguredFilterFactory.
CONFIGURATION_UPDATE:
System.out.println( 'Update to config: ' +
evt.arg );
_hashFactoryToConfig.put(_factoryActive,
evt.arg);
return true;

default:
return super.handleEvent(evt);
}

return super.handleEvent(evt);

6.  The MultiFilterFrame's action method is where menu click Events are handled. Here is the code:

// action() is where action events are funneled.
public boolean action(Event evt, Object target)

// Handle 'Exit' menu action by posting a
// WINDOW_DESTROY to this Frame.
if( 'Exit' == (String)evt.arg )

// 'Refresh' simply redraws the base image to the
// Workspace, getting rid of all previous
// filterings to the image or any region of the
// image. Also gets rid of any selection rectangle.
if( 'Refresh' == (String)evt.arg )

// 'Apply To Image' causes the entire image to be
// sent through the current active filter.
if( 'Apply to Image' == (String)evt.arg )

// 'Apply to Region' causes a filtered version
// of the selection rectangle to be created and drawn
// over the selection rectangle on the display image.
if( 'Apply to Region' == (String)evt.arg )

// Attempt to look up a filter factory in the hashes
// matching this menu item, and create a
// configuration for that factory.
ConfiguredFilterFactory factory =
(ConfiguredFilterFactory)
_hashMonikerToFactory.get((String)evt.arg);
if( null != factory )

// Unrecognized menu item, just let it go.
return false;

7.  The main frame must insure that only appropriate menu items are enabled. For example, the "Applet to Image" menu item would be inappropriate if no images were loaded into the application. Whenever a user action is detected which may affect the state of one or more menu items, MultiFilterFrame.updateMenu is called to update the state of the various menu items. Here is the code for that method:

// updateMenu() is called whenever the program state
// has changed in such a way that any of the menu
// items may become enabled/disabled, etc.
private synchronized void updateMenu()

8.  The Workspace image, which is the filtered copy of the loaded image, is displayed by a WorkspaceCanvas class object. This Canvas is responsible for displaying the image, and for tracking user mouse actions so that the user can select a rectangle of the image for filtering. Here is the declaration of the WorkspaceCanvas class, its member variables, and its constructor:

class WorkspaceCanvas extends Canvas

9.  The WorkspaceCanvas class implements methods to get, set, and delete the Image object it is supposed to display:

private synchronized Image getDisplayImage()

public synchronized void clearImage()

public synchronized void setImage(Image img)

10.  The WorkspaceCanvas' update and paint methods are implemented to avoid flicker and to draw the image to display:

// Avoid flicker in paint() by overriding update
// not to erase background.
public void update(Graphics g)

// Paint draws the display image and the active
// selection, if there is any.
public synchronized void paint(Graphics g)

11.  The WorkspaceCanvas uses XOR mode to draw the selection rectangle. This ensures that no matter what color the display image is, the boundaries of the selection rectangle are always visible to the user. Here's the code for the drawSelRect method:

// Draw the selection rectangle. Use XOR mode so the selection
// rectangle is an XOR drawing.
private void drawSelRect(Graphics g)
return;

12.  The user defines the selection rectangle using mouse clicks and drags. The WorkspaceCanvas keeps track of these mouse Events through mouseDown and mouseDrag. Here's the code for those methods:

// mouseDown() and mouseDrag() notification methods
// are used to track the selection region.
public synchronized boolean mouseDown(Event evt,
int x, int y)

public synchronized boolean mouseDrag(Event evt,
int x, int y)

13.  The WorkspaceCanvas also defines helper methods to get, set, and clear the boundaries of the selection rectangle. Here is the code:

public synchronized Rectangle getSelRect()

public synchronized void clearSelRect()

public synchronized void setSelRect(Rectangle rectNewSel)

14.  The OpenObjectDialog simply gathers a string from the user. This string can be either a filename on the local file system or a URL pointing to a file somewhere on the Internet. An OK and a Cancel Button are also provided. The MultiFilterFrame uses an OpenObjectDialog whenever the user clicks on the "Open." menu item. Here is the code for that class:

class OpenObjectDialog extends Dialog

// Button events are caught using action().
// Only the OK button has any actions associated with
// it, and then only if the input field is not empty.
// Cancel results in the window being destroyed.
public boolean action(Event evt, Object what)
return false;
}
if( (null == _tfInput.getText()) ||
('' == _tfInput.getText()) )
return false;

String strInput = _tfInput.getText();
deliverEvent(new Event(this,
OPENOBJECTDIALOG_INPUT, strInput));
dispose();
return true;
}

15.  Create a new file named "ConfiguredFilterFactory.java". This file holds the declaration of the ConfiguredFilterFactory class. The ConfiguredFilterFactory class is a base class. In the MultiFilter application, both the ContrastFilterFactory and the InvertFilterFactory are based on this class. In general, a ConfiguredFilterFactory should be able to create a new ImageFilter that should be configured according to the value of an Object. Here is the declaration:

import java.awt.image.ImageFilter;
import java.awt.Frame;

public class ConfiguredFilterFactory

public synchronized ImageFilter createFilter(
Object objConfiguration)

16.  Create a new file called "ContrastFilterFactory.java". Start off by declaring the ContrastFilterFactory class, and importing the necessary packages into this file:

import java.awt.image.ImageFilter;
import java.awt.image.ColorModel;
import java.awt.image.RGBImageFilter;
import java.awt.*;

public class ContrastFilterFactory extends
ConfiguredFilterFactory

public ImageFilter createFilter(Object objConfig)

17.  A ContrastFilter is a type of RGBImageFilter. In its filterRGB implementation, it increases or decreases the contrast of a particular pixel, based on its constructor parameters. Here is the code:

class ContrastFilter extends RGBImageFilter

public int filterRGB(int x, int y, int rgb)

18.  The ContrastFilterConfigurationDialog is used to make the user select a contrast parameter for a new ContrastImageFilter. It uses a Scrollbar as its principal input device. A ContrastDisplayCanvas is used in the dialog to display the contrast function as indicated by the state of the Scrollbar. Here is the code for those two classes:

class ContrastFilterConfigurationDialog extends Dialog

public int getContrast()

// Implementation of handleEvent so dialog can handle
// scrollbar updates.
public boolean handleEvent(Event evt)

// all other events passed on.
return false;
}


class ContrastFunctionDisplayCanvas extends Canvas

public void setContrast(int nContrast)

public void paint(Graphics g)
else
g.drawLine(0, r.height/2, r.width, r.height/2);
}

19.  Create a new file named "InvertFilterFactory.java". Start off by declaring the InvertFilterFactory. This is very similar to the ContrastFilterFactory, except that it creates InvertFilters, and gets its configuration from InvertFilterConfigurationDialogs. Here is the code for the InvertFilterFactory:

import java.awt.image.ImageFilter;
import java.awt.image.ColorModel;
import java.awt.*;

public class InvertFilterFactory extends
ConfiguredFilterFactory

public ImageFilter createFilter(Object objConfig)

20.  The InvertFilter can be configured to invert an input image along either the X or Y axis, or both. Its setPixels implementation works by swapping individual pixel values to invert the image. Here is the code for that class:

class InvertFilter extends ImageFilter

public void setDimensions(int width, int height)

public void setPixels(int x, int y, int w, int h,
ColorModel cm, byte[] pixels, int off,
int scansize)
}
}

if( 0 != (_nFlags & VERTICAL) )
}
}

// send the transformed pixels on to the consumer.
consumer.setPixels(xNew, yNew, w, h, cm, pixels,
off, scansize);
}

public void setPixels(int x, int y, int w, int h,
ColorModel cm, int[] pixels, int off,
int scansize)

21.  The InvertFilterConfigurationDialog is a simple dialog with two checkboxes, one each of horizontal and vertical invert. Here is the code:

class InvertFilterConfigurationDialog extends Dialog

public int getFlags()

// Capture the checkbox events, and change the
// configuration as the user wishes. Report all
// configuration changes to parent window as
// ConfiguredFilterFactory.CONFIGURATION_UPDATE Events.
public boolean action(Event evt, Object what)

if( (Checkbox)evt.target == _cbVert )

// all other actions passed along.
return false;
}

22.  Compile the project. In the MultiFilter directory, execute this command to compile the MultiFilter project using the JDK's javac compiler:

> javac MultiFilter.java


Correct any errors in typing, which will be reported as errors on the command line.

23.  Run the MultiFilter application. Provide an image's full pathname on your local system, or an image URL as the first and only argument to this program. Use this command line:

> java MultiFilter <your-image-file-or-URL>

How It Works

A lot of MultiFilter's code implements the user-interface of the application. You are encouraged to study the MultiFilter application's user-interface model and techniques, but this discussion will be restricted to the AWT Image Processing functionalities demonstrated by MultiFilter and how you can extend MultiFilter by adding your own ImageFilter objects its interface.

Loading and Storing the Base Image

At the heart of MultiFilter are two Image objects: MultiFilterFrame._imgBase is a pristine copy of an Image as it was loaded from a local file system file, or from a network URL. MultiFilterFrame._imgWorkspace is a modified version of the base image. Whenever a filter is applied to either the entire displayed image, or to a region of it, it is _imgWorkspace which is modified.

The base image is loaded and prepared for use by the ImageLoaderThread object. The ImageLoaderThread is designed to load Image objects from a local file system file, or from a network URL, on behalf of a Component. All downloading and preparing of the Image are done by a background Thread. When the Image has been fully downloaded, the ImageLoaderThread delivers the Image, as the arg member of a custom Event, to the Component. The Event ID is ImageLoaderThread.IMAGE_COMPLETE.

ConfiguredFilterFactory

To make MultiFilter as extensible as possible, the ConfiguredFilterFactory was developed to make creating and configuring generic ImageFilter objects a simple operation for the MultiFilter application. The jobs of a ConfiguredFilterFactory are

.  To perform user-interaction in a modeless dialog, allowing the user to dynamically modify the configuration of an ImageFilter. createConfiguration is overridden by classes derived from ConfiguredFilterFactory to create such a modeless dialog. Enough information to describe a filter configuration must be able to be stored in a single Object instance, though since all objects in Java ultimately derive from Object, this is not a constraint on the amount of information you can cull from the user to configure a filter.

.  To create configured ImageFilter objects, the configuration reflecting the user-input to the modeless dialog is passed to createFilter. This method should return an ImageFilter object initialized using the configuration information the user input into the modeless dialog.

The default implementation of ConfiguredFilterFactory creates objects of class ImageFilter. These objects are null-filters. That is, when applied to an Image, the filtered output looks exactly the same as the unfiltered input. Furthermore, no user-interaction is necessary for such an object, since the ImageFilter class takes no parameters. Thus the default implementations of createConfiguration and createFilter are pretty simple.

The ContrastFilter

The ContrastFilter is an RGBImageFilter. The filter is created with a "contrast" parameter, which defines how much contrast the output Image should have in it. This contrast parameter is interpreted as a degree which defines the slope of the linear relationship between input and output RGB color. If the angle is low, then the slope is near-horizontal, and the output RGB color components are right around 128. If, on the other hand, the angle is high then the slope of the linear relationship is high, and slight differences in the brightness between pixels is magnified in the output image. Figure 8-12 shows the ContrastFilterConfigurationDialog, which is the modeless dialog the user uses to modify the contrast settings.


Figure 8-12  The ContrastFilterConfigurationDialog

The InvertFilter

The InvertFilter is an ImageFilter-derived class. Because the filtered output of each pixel of this filter is dependent on other pixels in the image, the InvertFilter could not be derived from RGBImageFilter.

The internal state of an InvertFilter object is stored as two flags: HORIZONTAL and VERTICAL, which are both set when the filter is created by parameters to the InvertFilter constructor. The InvertFilter.setPixels() method flips each individual rectangle of the input image in the filtered version of the image. Two separate flipping operations may be necessary: HORIZONTAL flipping, and VERTICAL flipping, according to the internal state of the InvertFilter.

"Flipping" of a rectangle involves tranforming the coordinates of the rectangle to the output Image. This transformation ensures the width and height of the rectangle are preserved in the output image, but the origin of the rectangle in the Image is moved. The individual pixel values of the rectangle are flipped symmetrically about either the X or Y axis, according to the internal state of the InvertFilter object. Figure 8-13 illustrates how a rectangle of pixel data is transformed for the filtered output.


Figure 8-13  InvertFilter translates rectangles so the rectangle covers a symmetrical area with respect to the horizontal or vertical axis

Once the pixel data has been flipped in the pixels[] array, and the origin of the rectangle has been transformed for the output Image, the pixel data is passed through to the filter's consumer.

Applying Filters to the Workspace Image

A FilteredImageSource object is used to apply the currently selected filter to the whole Workspace image, or to a selected region of it. This is handled in the MultiFilterFrame.action() method, where the MenuItem selection action is detected by the MultiFilterFrame.

In response to the "Apply to Image" menu item being selected, MultiFilter creates a new Image object which is the output of applying the currently selected filter to the entire Workspace image. This new Image object then paints over the contents of the Workspace image, thus essentially replacing the Workspace image with itself.

The WorkspaceCanvas displays the current contents of the Workspace image, so that as soon as the filter is applied and the Workspace is filled with a copy of the filtered image, the Workspace is displayed on the screen.

In response to the "Apply to Region" menu item being selected, MultiFilter first creates a copy of just the selected region using a CropImageFilter and a FilteredImageSource. A new image is created which is the result of sending the cropped image selection through the currently selected filter. This filtered version of the selected region is then painted onto the Workspace image at precisely the same location as the selected region. When the Workspace is redisplayed, the filtered version of the selected region replaces the original pixel data there.

Adding Your Own ImageFilters to MultiFilter

MultiFilter was designed to make adding new filters as easy as possible. Follow these steps to add a new filter to MultiFilter:

1.  Create your ImageFilter.

2.  Create a modeless Dialog so that the user of MultiFilter can configure your ImageFilter. For example, if your filter requires, say, a "zoom factor" in its constructor, then create a Dialog that allows the user to pick a "zoom factor". This Dialog object should deliver a ConfiguredFilterFactory.CONFIGURATION_UPDATE Event to its parent (result of getParent()) frame whenever the user modifies the values in the Dialog. See ContrastFilterConfigurationDialog in the MultiFilter application (Step 18) for an example.

3.  Create a ConfiguredFilterFactory for your ImageFilter class. The overridden version of createConfiguration should create and show a new instance of the Dialog you defined in Step 2. The overridden version of createFilter should create and return a new instance of the ImageFilter you defined in Step 1, configured using the configuration Object passed as a parameter. See ContrastFilterFactory of the MultiFilter application (Step 16) for an example.

4.  Add code to MultiFilterFrame constructor to add a menu item for your new filter. In the same constructor method, you should also add an entry to Hashtable _hashMonikerToFactory. For example, if I created a filter class called MakeImagePretty and corresponding factory and configuration objects called MakeImagePrettyFactory and MakeImagePrettyConfigurationDialog, then I would change MultiFilterFrame's constructor like so:

public MultiFilterFrame(String strTitle)

That's it! Recompile MultiFilter and try out your filter on a test image.



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 898
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved