@(#)NonImage.txt 1.10
Proposal: Non-image Data in Processing Chains
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Brian Burkhalter
Created: 11 July 2000
Revised: 12/18/00
CHANGES DURING COMMUNITY REVIEW
-------------------------------
. Changed specification of DeferredData.setData(). In particular, the old
value of the wrapped data object is passed as the Object parameter of
Observer.update() instead of the new value.
. Corrected verbiage and signature of DeferredProperty.equals(); also added
a hashCode() method.
. OperationDescriptorImpl: DeferredData parameters will no longer be given
special treatment.
. Added sections on ParameterBlockJAI, OperationNodes, and OperationNodeSupport
PROPOSAL
--------
1. Motivation
Non-image data are the most common parameters of imaging operations and
may even be the destination of a non-imaging operation. However there is at
present no way of deferring the computation of non-image source data until
such time as they are required. Non-image destination data may be deferred if
they are defined to be properties of a node in an imaging chain. There is
however no way to define destination non-image data without defining a complex
imaging operation.
This proposal attempts to resolve these problems by:
A) defining a mechanism by which the computation of non-image parameters of
imaging operators may be deferred; and
B) clarifying the procedure by which the existing imaging chain mechanism
may be used to simulate non-image destination data via the use of
minimum overhead imaging operators.
2. Proposal
2.1 Non-image Data Classes
2.1.1 DeferredData
Create an abstract class DeferredData:
package javax.media.jai;
import java.util.Observable;
/**
* Class to be used as a wrapper for data which will be calculated
* at a later time. For example, an instance of a subclass of this
* class may be passed as a parameter in the ParameterBlock
* set on a RenderedOp
node. The data wrapped by the
* DeferredData
object will not however actually be
* requested until the node is rendered.
*
* @see DeferredProperty
* @see RenderedOp
*
* @since Java Advanced Imaging 1.1
*/
public abstract class DeferredData extends Observable implements Serializable {
/**
* The class of the wrapped data.
*/
protected Class dataClass;
/**
* The data wrapped by this class. This field is marked
* transient
so that subclasses are obligated to provide
* specific serialization methods if they desire that this field be
* serialized.
*/
protected transient Object data;
/**
* Creates a DeferredData
wrapper for an object of the
* indicated class. The parameter must be non-null
or an
* IllegalArgumentException
will be thrown.
*
* @throws IllegalArgumentException if dataClass
is
* null
.
*/
protected DeferredData(Class dataClass) {}
/**
* Returns the class of the object wrapped by this DeferredData.
*/
public Class getDataClass() {}
/**
* Whether the data value has already been computed.
*
* @return true
if and inly if the internal data value is
* non-null
, i.e., has already been computed.
*/
public boolean isValid() {}
}
/**
* Computes the value of the data object wrapped by this object.
* The returned object must be an instance of a class assignable
* to the class specified at construction.
*/
protected abstract Object computeData();
/**
* Returns the object wrapped by this DeferredData
. If
* the data have not yet been computed, computeData()
* will be invoked to provide the data. If computeData()
* is invoked, then setData()
will be invoked with
* its parameter set to the value returned by computeData()
.
*/
public synchronized final Object getData() {}
/**
* Sets the instance variable associated with the data to the supplied
* parameter. If the parameter is non-null
and not an
* instance of the class specified at construction, an
* IllegalArgumentException
will be thrown. If the
* supplied parameter differs from the current data value, then
* setChanged()
will be invoked and
* notifyObservers()
will be called with its argument set
* to the previous value of the data object. This implies that the
* update()
method of any registered Observer
s
* will be invoked with the Observable
parameter set to this
* DeferredData
object and its Object
parameter
* set to the previous value of the data
field of this
* DeferredData
instance. The current value of the
* object can be retrieved by an Observer
by casting the
* Observable
parameter of update()
to
* DeferredData
and invoking getData()
. To
* avoid provoking computation of deferred data by calling
* getData()
, isValid()
could first be called
* to determine whether data
is non-null
.
* If an Observer
detects that the data of the observed
* DeferredData
object is invalid, i.e., null
,
* then this indicates that the data should be re-requested by invoking
* getData()
.
*
* @throws IllegalArgumentException if data
is
* non-null
and not an instance of the class
* specified at construction.
*/
protected final void setData(Object data) {}
}
2.1.2 DeferredProperty
Create a concrete subclass DeferredProperty of DeferredData to handle
image properties:
package javax.media.jai;
import java.beans.PropertyChangeListener;
/**
* A subclass of DeferredData
to be used to wrap JAI property
* values which will be computed at a later time. For example, an instance
* of this class could be used to wrap a property emitted by an operation
* node so that the actual computation of the property value was deferred
* until it was actually needed.
*
* @see DeferredData
* @see RenderedOp
*
* @since Java Advanced Imaging 1.1
*/
public class DeferredProperty extends DeferredData
implements PropertyChangeListener {
/**
* The PropertySource
from which the value of the named
* property is to be drawn.
*/
protected transient PropertySource propertySource;
/**
* The name of the property the value of which is to be obtained.
*/
protected String propertyName;
/**
* Creates a DeferredProperty
. If the specified
* PropertySource
is a PropertyChangeEmitter
,
* then this DeferredProperty
object is registered as a
* PropertyChangeListener
of the PropertySource
.
*
* @exception IllegalArgumentException if a parameter is null
* or if the propertyName
is not among those
* emitted by the PropertySource
.
*/
public DeferredProperty(PropertySource propertySource,
String propertyName,
Class propertyClass) {}
/**
* Returns the PropertySource
of the property value.
*/
public PropertySource getPropertySource() {}
/**
* Returns the name of the property the value of which is to be obtained.
*/
public String getPropertyName() {}
/**
* Returns the value of the image property associated with the
* image and property name specified at construction.
*/
protected Object computeData() {}
/**
* Tests whether the parameter equals this object. Equality obtains
* if the parameter object is a DeferredProperty
and
* the respective propertySource
and
* dataClass
variables are equal according to their
* respective equals()
methods and the
* propertyName
s are equal ignoring case. The wrapped
* data object is not tested unless the isValid()
of
* both objects returns true
because requesting it via
* getData()
may provoke computation of a deferred quantity.
*/
public boolean equals() {}
/**
* Returns a hash code value for the object.
*/
public int hashCode() {}
/**
* The implementation of PropertyChangeListener
. This
* method responds to certain PropertyChangeEvent
s generated
* by the PropertySource
used to construct this object.
*
*
If the PropertyChangeEvent
is named "Rendering" and is
* an instance of javax.media.jai.RenderingChangeEvent
, then
* the source of the event is checked to determine whether it equals the
* PropertySource
used to construct this object. If this test
* is passed then the PropertySource
is a
* RenderedOp
the rendering of which has changed. Therefore
* setData() will be invoked with a null argument. This will indicate to
* any registered observers that the property data should be re-requested
* by invoking getData() on this object.
*
*
If the PropertyChangeEvent
was generated by the
* PropertySource
used to construct this object, has name
* equal to the name of the deferred property, and is an instance of
* PropertySourceChangeEvent
, then the value returned by
* the getNewValue()
method of the event object will be
* passed to setData()
. Registered observers will be
* notified according to the specification of setData()
.
*/
public void propertyChange(PropertyChangeEvent evt) {}
}
2.2 Non-image Data Computation Deferral
2.2.1 OperationDescriptorImpl
Parameter validation will not be possible if the parameter Vector contains
DeferredData objects unless the OperationDescriptor itself is aware of this
eventuality.
2.2.2 RenderedOp
Internal modifications would be made such that java.util.Observer update()
events were monitored for each parameter that is an instance of DeferredData.
The actual implementation of the monitoring mechanism would not be part of
the public API. An event invalidating a DeferredData parameter would be
responded to in a manner similar to that used to respond to a change in
value of a non-deferred parameter.
The wrapped data of a DeferredData would not be requested until a rendering
of the node was requested. When the rendering was requested, the getData()
method would be invoked on each DeferredData object and a new ParameterBlock
generated using the computed data objects. This new ParameterBlock would be
passed to the registry create() method and eventually to the RIF.
An exception to the case described in the preceding paragraph would occur when
the DeferredData instance is assignable to an instance of the class defined
as corresponding to the parameter in question in the OperationDescriptor for
the operation associated with the RenderedOp. In this case the getData() method
would not be invoked on the DeferredData object before passing it along to the
RIF. This exception permits DeferredData-aware operator implementations to
postpone computation of deferred data objects as long as possible.
2.2.3 RenderableOp
Internal modifications would be made to request the actual data from each
DeferredData object in the course of the createRendering() invocation. The
same exception for the case of a DeferredData-aware operator would be made
in this class also.
2.3 Non-image Data Sources
The implementation of the foregoing specification should permit the use
of non-image data objects which behave in a deferred mode in the context
of rendered and renderable imaging chains.
2.4 Non-image Data Destinations
2.4.1 CRIFImpl
Move the implementation-private utility class "CRIFImpl" to the javax.media.jai
package. The class specification is included here for convenience.
/**
* A utility class to minimize in most cases the effort required to implement
* the ContextualRenderedImageFactory
(CRIF) of an operation.
* An extender of this class is required to implement only the method
* RenderedImage create(ParameterBlock, RenderingHints)
* defined in the RenderedImageFactory
interface. The remaining
* methods may be overridden insofar as this is necessary to obtain behavior
* different from that provided by default.
*
* @see java.awt.image.renderable.ContextualRenderedImageFactory
* @see java.awt.image.renderable.RenderedImageFactory
* @since EA2
*/
public abstract class CRIFImpl implements ContextualRenderedImageFactory {
/**
* If non-null
, this name will be used as a parameter to
* JAI.create()
in
* create(RenderContext,ParameterBlock)
; otherwise the RIF
* create(ParameterBlock,RenderingHints)
method implemented
* in the extending class will be invoked.
*/
protected String operationName = null;
/**
* Default constructor. The operation name is set to null
.
*/
public CRIFImpl() {}
/**
* Constructor. The operation name is set to the specified value
* which may be null
.
*/
public CRIFImpl(String operationName) {}
/**
* The RenderedImageFactory
create()
method
* which must be implemented by concrete subclasses.
*/
public abstract RenderedImage create(ParameterBlock paramBlock,
RenderingHints renderHints);
/**
* Creates a RenderedImage
from the renderable layer.
*
*
If operationName
is non-null
,
* JAI.create()
will be invoked using the supplied
* ParameterBlock
and the RenderingHints
* contained in the RenderContext
. If
* operationName
is null
, or
* JAI.create()
returns null
, the
* create(ParameterBlock,RenderingHints)
method defined
* in the extending class will be invoked.
*
* @param renderContext The rendering information associated with
* this rendering.
* @param paramBlock The parameters used to create the image.
* @return A RenderedImage
.
*/
public RenderedImage create(RenderContext renderContext,
ParameterBlock paramBlock) {}
/**
* Maps the destination RenderContext
into a
* RenderContext
for each source. The
* implementation in this class simply returns the
* RenderContext
passed in by the caller.
*
* @param i The index of the source image.
* @param renderContext The RenderContext
being applied to
* the operation.
* @param paramBlock A ParameterBlock
containing the
* sources and parameters of the operation.
* @param image The RenderableImage
being rendered.
*
* @return The RenderContext
to be used to render the
* given source.
*/
public RenderContext mapRenderContext(int i,
RenderContext renderContext,
ParameterBlock paramBlock,
RenderableImage image) {}
/**
* Returns the bounding box for the output of the operation. The
* implementation in this class computes the bounding box as the
* intersection the bounding boxes of all the (renderable sources).
*
* @param paramBlock A ParameterBlock
containing the
* sources and parameters of the operation.
* @return A Rectangle2D
specifying the bounding box.
*/
public Rectangle2D getBounds2D(ParameterBlock paramBlock) {}
/**
* Returns the appropriate instance of the property with the indicated
* name.
*
*
The implementation in this class always returns
* java.awt.Image.UndefinedProperty
since
* no properties are defined by default.
*
* @param paramBlock A ParameterBlock
containing the
* sources and parameters of the operation.
* @param name A String
containing the desired property name.
*
* @return the value java.awt.Image.UndefinedProperty
* indicating that the property is undefined.
*/
public Object getProperty(ParameterBlock paramBlock,
String name) {}
/**
* Returns the valid property names for the operation. The
* implementation in this class always returns null
* since no properties are associated with the operation by
* default.
*
* @return null
indicating that no properties are defined.
*/
public String[] getPropertyNames() {}
/**
* Returns true
if successive renderings with the same
* arguments may produce different results. The implementation in this
* class always returns false
so as to enable caching
* of renderings by default. CRIFs that do implement dynamic
* rendering behavior must override this method.
*
* @return false
indicating that the rendering is static.
*/
public boolean isDynamic() {}
}
2.4.2 NullCRIF
Create a ContextualRenderedImageFactory javax.media.jai.operator.NullCRIF
which either returns the first source or, if no source is available, a
global user-specifiable image. The initial value of this default image is
null.
/**
* A ContextualRenderedImageFactory
representing an operation
* which performs no processing of its image source(s) per se, i.e., a no-op.
*
*
The primary use of this image factory is as a utility class in
* implementing operations which generate only non-image data via the
* use of PropertyGenerator
s. A PropertyGenerator
* is defined as always contributing to the property environment of a given
* operation when it is returned by the getPropertyGenerators()
* method of the OperationDescriptor
corresponding to the
* operation.
*
*
The procedure to be followed to register an operation which generates * only non-image data as JAI image properties is as follows: * *
PropertyGenerator
which calculates the
* non-image data given the operation node;
* OperationDescriptor
the
* getPropertyGenerators()
method of which returns the
* PropertyGenerator
defined in the previous step;
* OperationDescriptor
with the
* OperationRegistry
as usual by passing it to
* registerOperationDescriptor()
along with the operation name;
* NullCRIF
as the image factory corresponding to
* this operation.
* PropertyGenerator
(s)
* will then be available by invoking getProperty()
on the node
* returned by JAI.create()
using the registered operation name.
*
* @see CRIFImpl
* @see java.awt.image.renderable.ContextualRenderedImageFactory
*
* @since JAI 1.1
*/
public class NullCRIF extends CRIFImpl {
/**
* Constructs a NullCRIF
. The operationName
* in the superclass is set to null
.
*/
public NullCRIF() {}
/**
* Sets the value of the RenderedImage
to be returned by
* the RenderedImageFactory.create()
method when there are
* no sources in the ParameterBlock
.
*
* @param a RenderedImage
or null
.
*/
public static final synchronized void setSourcelessImage(RenderedImage im) {
sourcelessImage = im;
}
/**
* Gets the value of the RenderedImage to be returned by the RIF.create()
* method when there are no sources in the ParameterBlock
.
*
* @return a RenderedImage
or null
.
*/
public static final synchronized RenderedImage getSourcelessImage() {}
/**
* Returns the first source in the source list in the
* ParameterBlock
or the value returned by
* getSourcelessImage()
if there are no soures.
*
* @throws ClassCastException if there are sources and the source
* at index zero is not a RenderedImage
.
*/
public RenderedImage create(ParameterBlock args,
RenderingHints renderHints) {}
}
2.4.3 Creating a Non-image Data Operator
This documentation is included in the class description of NullCRIF.
Creating an operator the output of which is a non-image data object is
effected simply as follows:
. Create a PropertyGenerator which calculates the non-image data given the
rendered or renderable operation node;
. Create an OperationDescriptor the getPropertyGenerators() method of which
returns the PropertyGenerator defined in the previous step;
. Register the OperationDescriptor with the OperationRegistry as usual by
passing it to registerOperationDescriptor() with the operation name;
. Register NullCRIF as the RIF or CRIF corresponding to this operation using
the registerRIF() or registerCRIF() method of the OperationRegistry.
Although the above does cause the creation of an extra node in the operation
chain, the overhead is very low and the necessity of adding a different
graph construct specifically to handle non-image data is avoided.
2.4.4 "Null" Operator
Create a "Null" operation to act as a placeholder in operation chains and to
which PropertyGenerators may optionally be added. This would enable non-image
data nodes to be present in chains without requiring the implementation of
specific OperationDescriptors for these operations. The required
PropertyGenerators would in this case be added to the nodes locally using the
addPropertyGenerator() method of the node.
The associated OperationDescriptor is specified as followed:
/**
* An OperationDescriptor
describing the "Null" operation.
*
* The "Null" operation performs no processing. It merely propagates its * first source along the operation chain unmodified. There may be an * arbitrary number of sources but only the first one is passed along * so it must have the appropriate class type for the operation mode. * *
This operation may be useful as a placeholder in operation chains
* and in creating nodes to which PropertyGenerator
s may be
* attached. This would enable non-image data nodes to be present in chains
* without requiring that specific OperationDescriptor
s be
* implemented for these operations. The PropertyGenerator
s
* required would in this case be added locally to the nodes using the
* addPropertyGenerator()
method of the node.
*
*
Name | Value |
---|---|
GlobalName | Null |
LocalName | Null |
Vendor | com.sun.media.jai |
Description | An operation which does no processing. |
DocURL | http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/NullDescriptor.html |
Version | 1.0 |
true
since renderable mode is supported. */
public boolean isRenderableSupported() {}
/**
* Returns true
if there is at least one source and the
* first source is a RenderedImage
.
*
* @throws NullPointerException if args
is null
.
* @throws NullPointerException if msg
is null
* and the validation fails.
*/
protected boolean validateSources(ParameterBlock args,
StringBuffer msg) {}
/**
* Returns true
if there is at least one source and the
* first source is a RenderableImage
.
*
* @throws NullPointerException if args
is null
.
* @throws NullPointerException if msg
is null
* and the validation fails.
*/
protected boolean validateRenderableSources(ParameterBlock args,
StringBuffer msg) {}
}
2.5 ParameterBlockJAI
The methods
public ParameterList setParameter(String paramName, Object obj) {}
public void setParameters(Vector parameters) {}
will need to be modified to accept a DeferredData parameter if its wrapped
class is compatible with the class expected for the parameter. In this case
no validation of the actual value of the parameter would be performed unless
the isValid() method of the DeferredData object returned true.
2.6 OperationNodes (RenderedOp/RenderableOp/CollectionOp)
Comments will be added regarding the use of DeferredData in the parameter
Vector of the ParameterBlock. Specifically:
. the DeferredData will not be evaluated until required, i.e., when a
rendering is generated.
. Observable events emitted by the DeferredData will be trapped and
acted upon. This will occur via the intermediary of the utility
OperationNodeSupport field.
2.7 OperationNodeSupport
Comments will be added to describe how DeferredData parameters are handled.
Specifically, any DeferredData parameters will be observed and any Observable
events they generate trapped and acted upon. The action taken will be to emit
a PropertyChangeEventJAI named "Parameters". The old and new Vectors passed
via the PropertyChangeEventJAI will have the DeferredData replaced by the
respective old and new values of the wrapped data object. The DeferredData
object itself will remain in the ParameterBlock of the OperationNodeSupport
object.