@(#)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 Observers * 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 * propertyNames 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 PropertyChangeEvents 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 PropertyGenerators. 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: * *

* * The properties emitted by the associated 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 PropertyGenerators may be * attached. This would enable non-image data nodes to be present in chains * without requiring that specific OperationDescriptors be * implemented for these operations. The PropertyGenerators * required would in this case be added locally to the nodes using the * addPropertyGenerator() method of the node. * *

* * * * * * * * *
Resource List
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

* * @see javax.media.jai.OperationDescriptor */ public class NullDescriptor extends OperationDescriptorImpl { /** Constructor. */ public NullDescriptor() {} /** Returns 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.