Programming in Java Advanced Imaging
C H A P T E R3 |
Programming in Java Advanced Imaging |
THIS chapter describes how to get started programming with the Java Advanced Imaging (JAI) API.
3.1 Introduction
An imaging operation within JAI is summarized in the following four steps:1. Obtain the source image or images. Images may be obtained in one of three ways (see Chapter 4, "Image Acquisition and Display"):
a. Load from an image file such as GIF, TIFF, or JPEG
2. Define the imaging graph. This is a two part process:b. Fetch the image from another data source, such as a remote server
c. Generate the image internally
a. Define the image operators (see Section 3.6, "JAI API Operators")
3. Evaluate the graph using one of three execution models:b. Define the parent/child relationship between sources and sinks
a. Rendered execution model (Immediate mode - see Section 3.3.1, "Rendered Graphs")
4. Process the result. There are four possible destinations:b. Renderable execution model (Deferred mode - see Section 3.3.2, "Renderable Graphs")
c. Remote execution model (Remote mode - see Section 3.4, "Remote Execution")
a. Save the image in a file
b. Display the image on the screen
c. Print the image on a printer or other output device
d. Send the image to another API, such as Swing
3.2 An Overview of Graphs
In JAI, any operation is defined as an object. An operator object is instantiated with zero or more image sources and other parameters that define the operation. Two or more operators may be strung together so that the first operator becomes an image source to the next operator. By linking one operator to another, we create an imaging graph or chain.In its simplest form, the imaging graph is a chain of operator objects with one or more image sources at one end and an image sinc (or "user") at the other end. The graph that is created is commonly known as a directed acyclic graph (DAG), where each object is a node in the graph and object references form the edges (see Figure 3-1).
Most APIs simply leave the DAG structure of images and operators implicit. However, JAI makes the notion of a processing graph explicit and allows such graphs to be considered as entities in their own right. Rather than thinking only of performing a series of operations in sequence, you can consider the graph structure produced by the operations. The graph form makes it easier to visualize the operations.
A directed acyclic graph is a graph containing no cycles. This means that if there is a route from node A to node B then there should be no way back. Normally, when creating a graph by instantiating new nodes one at a time, cycles are easily avoided. However, when reconfiguring a graph, you must be careful not to introduce cycles into the graph.
3.3 Processing Graphs
JAI extends rendering independence, which was introduced in the Java 2D API. With rendering independence, you have the ability to describe an image as you want it to appear, independent of any specific instance of it.In most imaging APIs, the application must know the exact resolution and size of the source image before it can begin any imaging operations on the image. The application must also know the resolution of the output device (computer monitor or color printer) and the color and tonal quality of the original image. A rendering-independent description is concerned with none of these. Rendering-independent sources and operations permit operations to be specified in resolution-independent coordinates.
Think of rendering independence a bit like how a PostScript file is handled in a computer. To display a PostScript file on a monitor or to print the file to a high-resolution phototypesetter, you don't need to know the resolution of the output device. The PostScript file is essentially rendering independent in that it displays properly no matter what the resolution of the output device is.
JAI has a "renderable" mode in which it treats all image sources as rendering independent. You can set up a graph (or chain) of renderable operations without any concern for the source image resolution or size; JAI takes care of the details of the operations.
JAI introduces two different types of graphs: rendered and renderable.
Note: The following two sections, "Rendered Graphs" and "Renderable Graphs," are for advanced JAI users. Most programmers will use JAI's Rendered mode and don't really need to know about the Renderable mode.
3.3.1 Rendered Graphs
Rendered graphs are the simplest form of rendering in JAI. Although Renderable graphs have the advantage of rendering-independence, eliminating the need to deal directly with pixels, Rendered graphs are useful when it is necessary to work directly with the pixels.A Rendered graph processes images in immediate mode. For any node in the graph, the image source is considered to have been evaluated at the moment it is instantiated and added to the graph. Or, put another way, as a new operation is added to the chain, it appears to compute its results immediately.
A Rendered graph is composed of Rendered object nodes. These nodes are usually instances of the
RenderedOp
class, but could belong to any subclass ofPlanarImage
, JAI's version ofRenderedImage
.Image sources are objects that implement the
RenderedImage
interface. These sources are specified as parameters in the construction of new image objects.Let's take a look at an example of a rendered graph in Listing 3-1. This example, which is a code fragment rather than an entire class definition, creates two constant images and then adds them together.
Listing 3-1 Rendered Chain Example
import javax.jai.*; import javax.jai.widget.*; import java.awt.Frame; public class AddExample extends Frame { // ScrollingImagePanel is a utility widget that // contains a Graphics2D (i.e., is an image sink). ScrollingImagePanel imagePanel1; // For simplicity, we just do all the work in the // class constructor. public AddExample(ParameterBlock param1, ParameterBlock param2) { // Create a constant image RenderedOp im0 = JAI.create("constant", param1); // Create another constant image. RenderedOp im1 = JAI.create("constant", param2); // Add the two images together. RenderedOp im2 = JAI.create("add", im0, im1); // Display the original in a scrolling window imagePanel1 = new ScrollingImagePanel(im2, 100, 100); // Add the display widget to our frame. add(imagePanel1); } }
The first three lines of the example code specify which classes to import. The classes prefixed with
javax.jai
are the Java Advanced Imaging classes. Thejava.awt
prefix specifies the core Java API classes.import javax.jai.*; import javax.jai.widget.*; import java.awt.Frame;The next line declares the name of the program and that it runs in aFrame
, a window with a title and border.public class AddExample extends Frame {The next line of code creates aScrollingImagePanel
, which is the ultimate destination of our image:ScrollingImagePanel imagePanel1;Next, aParameterBlock
for each source image is defined. The parameters specify the image height, width, origin, tile size, and so on.public AddExample(ParameterBlock param1, ParameterBlock param2) {The next two lines define two operations that create the two "constant" images that will be added together to create the destination image (see Section 4.7, "Creating a Constant Image").RenderedOp im0 = JAI.create("constant", param1); RenderedOp im1 = JAI.create("constant", param2);Next, our example adds the two images together (see Section 6.5.1, "Adding Two Source Images").RenderedOp im2 = JAI.create("add", im0, im1);Finally, we display the destination image in a scrolling window and add the display widget to our frame.imagePanel1 = new ScrollingImagePanel(im2, 100, 100); add(imagePanel1);Once pixels start flowing, the graph will look like Figure 3-2. The display widget drives the process. We mention this because the source images are not loaded and no pixels are produced until the display widget actually requests them.
Figure 3-2 Rendered Chain Example
3.3.2 Renderable Graphs
A renderable graph is a graph that is not evaluated at the time it is specified. The evaluation is deferred until there is a specific request for a rendering. This is known as deferred execution; evaluation is deferred until there is a specific request for rendering.In a renderable graph, if a source image should change before there is a request for rendering, the changes will be reflected in the output. This process can be thought of as a "pull" model, in which the requestor pulls the image through the chain, which is the opposite of the AWT imaging push model.
A renderable graph is made up of nodes implementing the
RenderableImage
interface, which are usually instances of theRenderableOp
class. As the renderable graph is constructed, the sources of each node are specified to form the graph topology. The source of a renderable graph is a Renderable image object.Let's take a look at an example of a renderable graph in Listing 3-2. This example reads a TIFF file, inverts its pixel values, then adds a constant value to the pixels. Once again, this example is a code fragment rather than an entire class definition.
Listing 3-2 Renderable Chain Example
// Get renderable source object from a TIFF source. // The ParameterBlock `pb0' contains the name // of the source (file, URL, etc.). The objects `hints0', // `hints1', and `hints2' contain rendering hints and are // assumed to be created outside of this code fragment. RenderableOp sourceImg = JAI.createRenderable("TIFF", pb0); // Set up the parameter block for the first op. ParameterBlock pb1 = new ParameterBlock(); pb1.addSource(sourceImage); // Make first Op in Renderable chain an invert. RenderableOp Op1 = JAI.createRenderable("invert", pb1); // Set up the parameter block for the second Op. // The constant to be added is "2". ParameterBlock pb2 = new ParameterBlock(); pb2.addSource(Op1); // Op1 as the source pb2.add(2.0f); // 2.0f as the constant // Make a second Op a constant add operation. RenderableOp Op2 = JAI.createRenderable("addconst", pb2); // Set up a rendering context. AffineTransform screenResolution = ...; RenderContext rc = new RenderContext(screenResolution); // Get a rendering. RenderedImage rndImg1 = Op2.createRendering(rc); // Display the rendering onscreen using screenResolution. imagePanel1 = new ScrollingImagePanel(rndImg1, 100, 100);
In this example, the image source is a rendering-independent TIFF image. A TIFF
RenderableOp
is created as a source for the subsequent operations:RenderableOp sourceImg = JAI.createRenderable("TIFF", pb0);Next, aParameterBlock
is set up for the first operation. The parameter block contains sources for the operation and parameters or other objects that the operator may require.ParameterBlock pb1 = new ParameterBlock(); pb1.addSource(sourceImage);An "invert"RenderableOp
is then created with the TIFF image as the source. Theinvert
operation inverts the pixel values of the source image and creates aRenderableImage
as the result of applying the operation to a tuple (source and parameters).RenderableOp Op1 = JAI.createRenderable("invert", pb1);The next part of the code example sets up aParameterBlock
for the next operation. TheParameterBlock
defines the previous operation (Op1) as the source of the next operation and sets a constant with a value of 2.0, which will be used in the next "add constant" operation.ParameterBlock pb2 = new ParameterBlock(); pb2.addSource(Op1); // Op1 as the source pb2.add(2.0f); // 2.0f as the constantThe second operation (Op2
) is an add constant (addconst
), which adds the constant value (2.0) to the pixel values of a source image on a per-band basis. Thepb2
parameter is theParameterBlock
set up in the previous step.RenderableOp Op2 = JAI.createRenderable("addconst", pb2);AfterOp2
is created, the renderable chain thus far is shown in Figure 3-3.
Figure 3-3 Renderable Chain Example Next, a
RenderContext
is created using anAffineTransform
that will produce a screen-size rendering.AffineTransform screenResolution = ...; RenderContext rc = new RenderContext(screenResolution);This rendering is created by calling theRenderableImage.createRendering
method onOp2
. ThecreateRendering
method does not actually compute any pixels, bit it does instantiate aRenderedOp
chain that will produce a rendering at the appropriate pixel dimensions.RenderedImage rndImg1 = Op2.createRendering(rc);The Renderable graph can be thought of as a template that, when rendered, causes the instantiation of a parallel Rendered graph to accomplish the actual processing. Now let's take a look at what happens back up the rendering chain in our example:
- When the
Op2.createRendering
method is called, it recursively calls theOp1.createRendering
method with theRenderContext
rc
as the argument.
- The
Op1
operation then calls thesourceImg.getImage
method, again withrc
as the argument.sourceImg
creates a newRenderedImage
to hold its source pixels at the required resolution and inserts it into the chain. It then returns a handle to this object toOp1
.
Op1
then uses theOperationRegistry
to find aContextualRenderedImageFactory
(CRIF) that can perform the "invert" operation. The resultingRenderedOp
object returned by the CRIF is inserted into the chain with the handle returned bysourceImg
as its source.
- The handle to the "invert"
RenderedImage
is returned toOp2
, which repeats the process, creating an "addconst"RenderedOp
, inserting it into the chain and returning a handle torndImg1
.After the creation of the
- Finally,
rndImg1
is used in the call to theScrollingImagePanel
to display the result on the screen.ScrollingImagePanel
, the Renderable and Rendered chains look like Figure 3-4.
Figure 3-4 Renderable and Rendered Graphs after the getImage Call At this point in the chain, no pixels have been processed and no
OpImages
, which actually calculate the results, have been created. Only when theScrollingImagePanel
needs to put pixels on the screen are theOpImages
created and pixels pulled through the Rendered chain, as done in the final line of code.imagePanel1 = new ScrollingImagePanel(rndImg1, 100, 100);3.3.3 Reusing Graphs
Many times, it is more desirable to make changes to an existing graph and reuse it than to create another nearly identical graph. Both Rendered and Renderable graphs are editable, with certain limitations.
3.3.3.1 Editing Rendered Graphs
Initially, a node in a Rendered graph is mutable; it may be assigned new sources, which are considered to be evaluated as soon as they are assigned, and its parameter values may be altered. However, once rendering takes place at a node, it becomes frozen and its sources and parameters cannot be changed.A chain of Rendered nodes may be cloned without freezing any of its nodes by means of the
RenderedOp.createInstance
method. Using thecreateInstance
method, a Rendered graph may be configured and reused at will, as well as serialized and transmitted over a network.The
RenderedOp
class provides several methods for reconfiguring a Rendered node. ThesetParameter
methods can be used to set the node's parameters to abyte
,char
,short
,int
,long
,float
,double
, or anObject
. ThesetOperationName
method can be used to change the operation name. ThesetParameterBlock
method can be used to change the nodes'sParameterBlock
.
3.3.3.2 Editing Renderable Graphs
Since Renderable graphs are not evaluated until there is a specific request for a rendering, the nodes may be edited at any time. The main concern with editing Renderable graphs is the introduction of cycles, which must be avoided.The
RenderableOp
class provides several methods for reconfiguring a Renderable node. ThesetParameter
methods can be used to set the node's parameters to abyte
,char
,short
,int
,long
,float
,double
, or anObject
. ThesetParameterBlock
method can be used to change the nodes'sParameterBlock
. ThesetProperty
method can be used to change a node's local property. ThesetSource
method can be used to set one of the node's sources to anObject
.
3.4 Remote Execution
Up to this point, we have been talking about standalone image processing. JAI also provides for client-server image processing through what is called the Remote Execution model.Remote execution is based on Java RMI (remote method invocation). Java RMI allows Java code on a client to invoke method calls on objects that reside on another computer without having to move those objects to the client. The advantages of remote execution become obvious if you think of several clients wanting to access the same objects on a server. To learn more about remote method invocation, refer to one of the books on Java described in "Related Documentation" on page xv.
To do remote method invocation in JAI, a
RemoteImage
is set up on the server and aRenderedImage
chain is set up on the client. For more information, see Chapter 12, "Client-Server Imaging."
3.5 Basic JAI API Classes
JAI consists of several classes grouped into five packages:
javax.media.jai
- contains the "core" JAI interfaces and classes
javax.media.jai.iterator
- contains special iterator interfaces and classes, which are useful for writing extension operations
javax.media.jai.operator
- contains classes that describe all of the image operatorsNow, let's take a look at the most common classes in the JAI class hierarchy.
javax.media.jai.widget
- contains interfaces and classes for creating simple image canvases and scrolling windows for image display
3.5.1 The JAI Class
TheJAI
class cannot be instantiated; it is simply a placeholder for static methods that provide a simple syntax for creating Renderable and Rendered graphs. The majority of the methods in this class are used to create aRenderedImage
, taking an operation name, aParameterBlock
, andRenderingHints
as arguments. There is one method to create aRenderableImage
, taking an operation name, aParameterBlock
, andRenderingHints
as arguments.There are several variations of the
create
method, all of which take sources and parameters directly and construct aParameterBlock
automatically.
3.5.2 The PlanarImage Class
ThePlanarImage
class is the main class for describing two-dimensional images in JAI.PlanarImage
implements theRenderedImage
interface from the Java 2D API.TiledImage
andOpImage
, described later, are subclasses ofPlanarImage
.
The
RenderedImage
interface describes a tiled, read-only image with a pixel layout described by aSampleModel
and aDataBuffer
. Each tile is a rectangle of identical dimensions, laid out on a regular grid pattern. All tiles share a commonSampleModel
.In addition to the capabilities offered by
RenderedImage
,PlanarImage
maintains source and sink connections between the nodes of rendered graphs. Since graph nodes are connected bidirectionally, the garbage collector requires assistance to detect when a portion of a graph is no longer referenced from user code and may be discarded.PlanarImage
takes care of this by using the Weak References API of Java 2.Any
RenderedImage
s from outside the API are "wrapped" to produce an instance ofPlanarImage
. This allows the API to make use of the extra functionality ofPlanarImage
for all images.
3.5.3 The CollectionImage Class
CollectionImage
is the abstract superclass for four classes representing collections ofPlanarImage
s:
ImageStack
- represents a set of two-dimensional images lying in a common three-dimensional space, such as CT scans or seismic volumes. The images need not lie parallel to one another.
ImageSequence
- represents a sequence of images with associated time stamps and camera positions. This class can be used to represent video or time-lapse photography.
ImagePyramid
- represents a series of images of progressively lesser resolution, each derived from the last by means of an imaging operator.
ImageMIPMap
- represents a stack of images with a fixed operational relationship between adjacent slices.3.5.4 The TiledImage Class
TheTiledImage
class represents images containing multiple tiles arranged into a grid. The tiles form a regular grid, which may occupy any rectangular region of the plane.
TiledImage
implements theWritableRenderedImage
interface from the Java 2D API, as well as extendingPlanarImage
. ATiledImage
allows its tiles to be checked out for writing, after which their pixel data may be accessed directly.TiledImage
also has acreateGraphics
method that allows its contents to be altered using Java 2D API drawing calls.A
TiledImage
contains a tile grid that is initially empty. As each tile is requested, it is initialized with data from aPlanarImage
source. Once a tile has been initialized, its contents can be altered. The source image may also be changed for all or part of theTiledImage
using itsset
methods. In particular, an arbitrary region of interest (ROI) may be filled with data copied from aPlanarImage
source.The
TiledImage
class includes a method that allows you to paint aGraphics2D
onto theTiledImage
. This is useful for adding text, lines, and other simple graphics objects to an image for annotating the image. For more on the TiledImage class, see Section 4.2.2, "Tiled Image."
3.5.5 The OpImage Class
The OpImage class is the parent class for all imaging operations, such as:
AreaOpImage
- for image operators that require only a fixed rectangular source region around a source pixel to compute each destination pixel
PointOpImage
- for image operators that require only a single source pixel to compute each destination pixel
SourcelessOpImage
- for image operators that have no image sources
StatisticsOpImage
- for image operators that compute statistics on a given region of an image, and with a given sampling rate
UntiledOpimage
- for single-source operations in which the values of all pixels in the source image contribute to the value of each pixel in the destination image
WarpOpImage
- for image operators that perform an image warpThe
ScaleOpImage
- for extension operators that perform image scaling requiring rectilinear backwards mapping and padding by the resampling filter dimensionsOpImage
is able to determine what source areas are sufficient for the computation of a given area of the destination by means of a user-suppliedmapDestRect
method. For most operations, this method as well as a suitable implementation ofgetTile
is supplied by a standard subclass ofOpImage
, such asPointOpImage
orAreaOpImage
.An
OpImage
is effectively aPlanarImage
that is defined computationally. InPlanarImage
, thegetTile
method ofRenderedImage
is left abstract, andOpImage
subclasses override it to perform their operation. Since it may be awkward to produce a tile of output at a time, due to the fact that source tile boundaries may need to be crossed, theOpImage
class defines agetTile
method to cobble (copy) source data as needed and to call a user-suppliedcomputeRect
method. This method then receives contiguous sourceRasters
that are guaranteed to contain sufficient data to produce the desired results. By callingcomputeRect
on subareas of the desired tile,OpImage
is able to minimize the amount of data that must be cobbled.A second version of the
computeRect
method that is called with uncobbled sources is available to extenders. This interface is useful for operations that are implemented using iterators (see Section 14.4, "Iterators"), which abstract away the notion of tile boundaries.
3.5.6 The RenderableOp Class
TheRenderableOp
class provides a lightweight representation of an operation in the Renderable space (see Section 3.3.2, "Renderable Graphs").RenderableOp
s are typically created using thecreateRenderable
method of theJAI
class, and may be edited at will.RenderableOp
implements theRenderableImage
interface, and so may be queried for its rendering-independent dimensions.When a
RenderableOp
is to be rendered, it makes use of theOperationRegistry
(described in Chapter 14) to locate an appropriateContextualRenderedImageFactory
object to perform the conversion from the Renderable space into aRenderedImage
.
3.5.7 The RenderedOp Class
TheRenderedOp
is a lightweight object similar toRenderableOp
that stores an operation name,ParameterBlock
, andRenderingHints
, and can be joined into a Rendered graph (see Section 3.3.1, "Rendered Graphs"). There are two ways of producing a rendering of aRenderedOp
:
- Implicit - Any call to a
RenderedImage
method on aRenderedOp
causes a rendering to be created. This rendering will usually consist of a chain ofOpImage
s with a similar geometry to theRenderedOp
chain. It may have more or fewer nodes, however, since the rendering process may both collapse nodes together by recognizing patterns, and expand nodes by the use of theRenderedImageFactory
interface. TheOperationRegistry
(described in Chapter 14) is used to guide theRenderedImageFactory
selection process.
- Explicit - A call to
createInstance
effectively clones theRenderedOp
and its sourceRenderedOp
s, resulting in an entirely new Rendered chain with the same non-RenderedOp
sources (such asTiledImage
s) as the original chain. The bottom node of the cloned chain is then returned to the caller. This node will then usually be implicitly rendered by callingRenderedImage
methods on it.RenderedOp
s that have not been rendered may have their sources and parameters altered. Sources are considered evaluated as soon as they are connected to aRenderedOp
.
3.6 JAI API Operators
The JAI API specifies a core set of image processing operators. These operators provide a common ground for applications programmers, since they can then make assumptions about what operators are guaranteed to be present on all platforms.The general categories of image processing operators supported include:
The JAI API also supports abstractions for many common types of image collections, such as time-sequential data and image pyramids. These are intended to simplify operations on image collections and allow the development of operators that work directly on these abstractions.
3.6.1 Point Operators
Point operators allow you to modify the way in which the image data fills the available range of gray levels. This affects the image's appearance when displayed. Point operations transform an input image into an output image in such a way that each output pixel depends only on the corresponding input pixel. Point operations do not modify the spatial relationships within an image.Table 3-1 lists the JAI point operators.
3.6.2 Area Operators
The area operators perform geometric transformations, which result in the repositioning of pixels within an image. Using a mathematical transformation, pixels are located from their x and y spatial coordinates in the input image to new coordinates in the output image.There are two basic types of area operations: linear and nonlinear. Linear operations include translation, rotation, and scaling. Non-linear operations, also known as warping transformations, introduce curvatures and bends to the processed image.
Table 3-2 lists the JAI area operators.
3.6.3 Geometric Operators
Geometric operators allow you to modify the orientation, size, and shape of an image. Table 3-3 lists the JAI geometric operators.
3.6.4 Color Quantization Operators
Color quantization, also known as dithering, is often used to reduce the appearance of amplitude contouring on monochrome frame buffers with fewer than eight bits of depth or color frame buffers with fewer than 24 bits of depth. Table 3-4 lists the JAI color quantization operators.
3.6.5 File Operators
The file operators are used to read or write image files. Table 3-5 lists the JAI file operators.
3.6.6 Frequency Operators
Frequency operators are used to decompose an image from its spatial-domain form into a frequency-domain form of fundamental frequency components. Operators also are available to perform an inverse frequency transform, in which the image is converted from the frequency form back into the spatial form.JAI supports several frequency transform types. The most common frequency transform type is the Fourier transform. JAI uses the discrete form known as the discrete Fourier transform. The inverse discrete Fourier transform can be used to convert the image back to a spatial image. JAI also supports the discrete cosine transform and its opposite, the inverse discrete cosine transform.
Table 3-6 lists the JAI frequency operators.
3.6.7 Statistical Operators
Statistical operators provide the means to analyze the content of an image. Table 3-7 lists the JAI statistical operators.
3.6.8 Edge Extraction Operators
The edge extraction operators allow image edge enhancement. Edge enhancement reduces an image to show only its edge details. Edge enhancement is implemented through spatial filters that detect a specific pixel brightness slope within a group of pixels in an image. A steep brightness slope indicates the presence of an edge.Table 3-8 lists the JAI edge extraction operators.
Table 3-8 Edge Extraction Operators Operator Description Reference GradientMagnitude Computes the magnitude of the image gradient vector in two orthogonal directions. page 305
3.6.9 Miscellaneous Operators
The miscellaneous operators do not fall conveniently into any of the previous categories. Table 3-9 lists the JAI miscellaneous operators.
Table 3-9 Miscellaneous Operators Operator Description Reference Renderable Produces a RenderableImage from a RenderedImage. page 117
3.7 Creating Operations
Most image operation objects are created with some variation on the following methods:
For a renderable graph:
There are four variations on methods for creating operations in the Renderable mode, as listed in Table 3-10.
For example:
RenderableOp im = JAI.createRenderable("operationName",paramBlock);TheJAI.createRenderable
method creates a renderable node operation that takes two parameters:
- An operation name (see Section 3.7.1, "Operation Name")
- A source and a set of parameters for the operation contained in a parameter block (see Section 3.7.2, "Parameter Blocks")
For a rendered graph:
There are a great many more variations on methods for creating operations in the Rendered mode, as listed in Table 3-11. The first five methods in the table take sources and parameters specified in aParameterBlock
. The remaining methods are convenience methods that take various numbers of sources and parameters directly.
Two versions of the
create
method are non-static. These methods may be used with a specific instance of theJAI
class. These methods method should only be used when the final result returned is a singleRenderedImage
. However, the source(s) supplied may be a collection of images or a collection of collections.
RenderedOp im = JAI.createNS("operationName", source, param1, param2)
The rendering hints associated with this instance of
JAI
are overlaid with the hints passed to this method. That is, the set of keys will be the union of the keys from the instance's hints and the hints parameter. If the same key exists in both places, the value from the hints parameter will be used.Many of the JAI operations have default values for some of the parameters. If you wish to use any of the default values in an operation, you do not have to specify that particular parameter in the
ParameterBlock
. The default value is automatically used in the operation. Parameters that do not have default values are required; failure to supply a required parameter results in aNullPointerException
.
3.7.1 Operation Name
The operation name describes the operator to be created. The operation name is a string, such as"add"
for the operation to add two images. See Section 3.6, "JAI API Operators," for a list of the operator names.The operation name is always enclosed in quotation marks. For example:
"Mean" "BoxFilter" "UnsharpMask"The operation name parsing is case-insensitive. All of the following variations are legal:"OrConst" "orConst" "ORconst" "ORCONST" "orconst"3.7.2 Parameter Blocks
The parameter block contains the source of the operation and a set parameters used by the operation. The contents of the parameter block depend on the operation being created and may be as simple as the name of the source image or may contain all of the operator parameters (such as the x and y displacement and interpolation type for thetranslate
operation).Parameter blocks encapsulate all the information about sources and parameters (Objects) required by the operation. The parameters specified by a parameter block are objects.
These controlling parameters and sources can be edited through the
setParameterBlock
method to affect specific operations or even the structure of the rendering chain itself. The modifications affect futureRenderedImages
derived from points in the chain below where the change took place.There are two separate classes for specifying parameter blocks:
java.awt.image.renderable.ParameterBlock
- the main class for specifying and changing parameter blocks.The parameter block must contain the same number of sources and parameters as required by the operation. For example, the
javax.media.jai.ParameterBlockJAI
- extendsParameterBlock
by allowing the use of default parameter values and the use of parameter names.Add
operation requires two source images and no parameters. TheaddConst
operator requires one source and a parameter specifying the constant value. If the sources and parameters do not match the operation requirements, an exception is thrown. However, when theParameterBlockJAI
class is used, if the required parameter values are not specified, default parameter values are automatically inserted when available.
3.7.2.1 Adding Sources to a Parameter Block
Sources are added to a parameter block with theaddSource()
method. The following example creates a newParameterBlock
namedpb
and then theaddSource()
method is used to add the source image (im0
) to theParameterBlock
.
ParameterBlock pb = new ParameterBlock(); pb.addSource(im0);
To add two sources to a parameter block, use two
addSource()
methods.
ParameterBlock pb = new ParameterBlock(); pb.addSource(im0); pb.addSource(im1);
3.7.2.2 Adding or Setting Parameters
As described before, there are two separate classes for specifying parameter blocks:ParameterBlock
andParameterBlockJAI
. Both classes work very much alike, except for two differences:ParameterBlockJAI
automatically provides default parameter values and allows setting parameters by name;ParameterBlock
does not.
ParameterBlock
The operation parameters are added to aParameterBlock
with theParameterBlock.add()
method. The following example adds two values (150
and200
) to theParameterBlock
namedpb
, which was created in the previous example.
pb.add(150); pb.add(200);
The
add()
method can be used with all of the supported data types: byte, short, integer, long, float, and double. When using theParameterBlock
object, all parameters that an operation requires must be added, else the operation will fail.
API:java.awt.image.renderable.ParameterBlock
- ParameterBlock addSource(Object source)
- adds an image to end of the list of sources. The image is stored as an object to allow new node types in the future.
- ParameterBlock add(byte b)
- adds a Byte to the list of parameters.
- ParameterBlock add(short s)
- adds a Short to the list of parameters.
- ParameterBlock add(int i)
- adds a Integer to the list of parameters.
- ParameterBlock add(long l)
- adds a Long to the list of parameters.
- ParameterBlock add(float f)
- adds a Float to the list of parameters.
- ParameterBlock add(double d)
- adds a Double to the list of parameters.
ParameterBlockJAI
Since theParameterBlockJAI
object already contains default parameter values at the time of construction, the parameters must be changed (or set) with theParameterBlockJAI.set(value, index)
methods rather than theadd()
method. Theadd()
methods should not be used since the parameter list is already long enough to hold all of the parameters required by theOperationDescriptor
.Listing 3-3 shows the creation of a
ParameterBlockJAI
intended to be passed to a rotate operation. The rotate operation takes four parameters:xOrigin
,yOrigin
,angle
, andinterpolation
. The default values forxOrigin
andyOrigin
are 0.0F (for both). In this example, these two values are not set, as the default values are sufficient for the operation. The other two parameters (angle
andinterpolation
) have default values ofnull
and must therefore be set. The source image must also be specified.
Listing 3-3 Example ParameterBlockJAI
// Specify the interpolation method to be used interp = Interpolation.create(Interpolation.INTERP_NEAREST); // Create the ParameterBlockJAI and add the interpolation to it ParameterBlockJAI pb = new ParameterBlockJAI(); pb.addSource(im); // The source image pb.set(1.2F, "angle"); // The rotation angle in radians pb.set(interp, "interpolation"); // The interpolation method
API:javax.media.jai.ParameterBlockJAI
- ParameterBlock set(byte b, String paramName)
- sets a named parameter to a byte value.
- ParameterBlock set(char c, String paramName)
- sets a named parameter to a char value.
- ParameterBlock set(int i, String paramName)
- sets a named parameter to an int value.
- ParameterBlock set(short s, String paramName)
- sets a named parameter to a short value.
- ParameterBlock set(long l, String paramName)
- sets a named parameter to a long value.
- ParameterBlock set(float f, String paramName)
- sets a named parameter to a float value.
- ParameterBlock set(double d, String paramName)
- sets a named parameter to a double value.
- ParameterBlock set(java.lang.Object obj, String paramName)
- sets a named parameter to an Object value.
3.7.3 Rendering Hints
The rendering hints contain a set of hints that describe how objects are to be rendered. The rendering hints are always optional in any operation.Rendering hints specify different rendering algorithms for such things as antialiasing, alpha interpolation, and dithering. Many of the hints allow a choice between rendering quality or speed. Other hints turn off or on certain rendering options, such as antialiasing and fractional metrics.
There are two separate classes for specifying rendering hints:
java.awt.RenderingHints
- contains rendering hints that can be used by theGraphics2D
class, and classes that implementBufferedImageOp
andRaster
.
javax.media.jai.JAI
- provides methods to define the RenderingHints keys specific to JAI.3.7.3.1 Java AWT Rendering Hints
Table 3-12 lists the rendering hints inherited fromjava.awt.RenderingHints
.
To set the rendering hints, create a
RenderingHints
object and pass it to theJAI.create
method you want to affect. Setting a rendering hint does not guarantee that a particular rendering algorithm, will be used; not all platforms support modification of the rendering code.In the following example, the rendering preference is set to quality.
qualityHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
Now that a
RenderingHints
object,qualityHints
, has been created, the hints can be used in an operation using aJAI.create
method.
3.7.3.2 JAI Rendering Hints
Each instance of aJAI
object contains a set of rendering hints that will be used for all image or collection creations. These hints are merged with any hints supplied to theJAI.create
method; directly supplied hints take precedence over the common hints. When a newJAI
instance is constructed, its hints are initialized to a copy of the hints associated with the default instance. The hints associated with any instance, including the default instance, may be manipulated using thegetRenderingHint
,setRenderingHints
, andclearRenderingHints
methods. As a convenience,getRenderingHint
,setRenderingHint
, andremoveRenderingHint
methods are provided that allow individual hints to be manipulated. Table 3-13 lists the JAI rendering hints.
Listing 3-4 shows an example of image layout rendering hints being specified for a Scale operation. The image layout rendering hint specifies that the origin of the destination opimage is set to 200 x 200.
Listing 3-4 Example of JAI Rendering Hints
// Create the parameter block for the scale operation. ParameterBlock pb = new ParameterBlock(); pb.addSource(im0); // The source image pb.add(4.0F); // The x scale factor pb.add(4.0F); // The y scale factor pb.add(interp); // The interpolation method // Specify the rendering hints. layout = new ImageLayout(); layout.setMinX(200); layout.setMinY(200); RenderingHints rh = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); // Create the scale operation. PlanarImage im2 = (PlanarImage)JAI.create("scale", pb, layout)
Programming in Java Advanced Imaging
Copyright © 1999, Sun Microsystems, Inc. All rights reserved.
Casa de Bender