0001: /*
0002: * $RCSfile: RenderedOp.java,v $
0003: *
0004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Use is subject to license terms.
0007: *
0008: * $Revision: 1.2 $
0009: * $Date: 2006/06/16 22:52:04 $
0010: * $State: Exp $
0011: */
0012: package javax.media.jai;
0013:
0014: import com.sun.media.jai.util.ImageUtil;
0015: import com.sun.media.jai.util.PropertyUtil;
0016: import java.awt.Point;
0017: import java.awt.Rectangle;
0018: import java.awt.RenderingHints;
0019: import java.awt.Shape;
0020: import java.awt.geom.Area;
0021: import java.awt.geom.GeneralPath;
0022: import java.awt.geom.PathIterator;
0023: import java.awt.geom.Point2D;
0024: import java.awt.image.ColorModel;
0025: import java.awt.image.ImageProducer;
0026: import java.awt.image.Raster;
0027: import java.awt.image.RenderedImage;
0028: import java.awt.image.SampleModel;
0029: import java.awt.image.WritableRaster;
0030: import java.awt.image.renderable.ParameterBlock;
0031: import java.beans.PropertyChangeEvent;
0032: import java.beans.PropertyChangeListener;
0033: import java.io.IOException;
0034: import java.io.ObjectInputStream;
0035: import java.io.ObjectOutputStream;
0036: import java.io.Serializable;
0037: import java.lang.ref.WeakReference;
0038: import java.util.Arrays;
0039: import java.util.ArrayList;
0040: import java.util.Collection;
0041: import java.util.Enumeration;
0042: import java.util.HashSet;
0043: import java.util.Hashtable;
0044: import java.util.Iterator;
0045: import java.util.List;
0046: import java.util.Locale;
0047: import java.util.Set;
0048: import java.util.Vector;
0049: import javax.media.jai.registry.RIFRegistry;
0050: import javax.media.jai.registry.RenderedRegistryMode;
0051: import javax.media.jai.remote.PlanarImageServerProxy;
0052: import javax.media.jai.remote.SerializableRenderedImage;
0053: import javax.media.jai.util.CaselessStringKey;
0054: import javax.media.jai.util.ImagingListener;
0055:
0056: /**
0057: * A node in a rendered imaging chain. A <code>RenderedOp</code> stores
0058: * an operation name, a <code>ParameterBlock</code> containing sources and
0059: * parameters, and a <code>RenderingHints</code> containing hints which
0060: * may be used in rendering the node. A set of nodes may be joined together
0061: * via the source <code>Vector</code>s within their respective
0062: * <code>ParameterBlock</code>s to form a <u>d</u>irected <u>a</u>cyclic
0063: * <u>g</u>raph (DAG). The topology, i.e., connectivity, of the graph may be
0064: * altered by changing the node's sources. The operation name, parameters,
0065: * and rendering hints may also be changed.
0066: *
0067: * <p> Such chains are useful for example as arguments to a
0068: * <code>RemoteImage</code>; they convey the structure of an imaging
0069: * chain in a compact representation and at a suitably high level of
0070: * abstraction to allow the server some leeway in materializing the
0071: * results. They are also useful in that a chain may be manipulated
0072: * dynamically and rendered multiple times. Thus for example the same
0073: * chain of operations may be applied to different images or the parameters
0074: * of certain operations in a chain may be modified interactively.
0075: *
0076: * <p> A <code>RenderedOp</code> may be constructed directly as, for example,
0077: * <pre>
0078: * <code>
0079: * ParameterBlock pb =
0080: * (new ParameterBlock()).add("SomeFile.tif");
0081: * RenderedOp node = new RenderedOp("fileload", pb, null);
0082: * </code>
0083: * </pre>
0084: * or via the <code>create</code> or <code>createNS()</code> methods defined
0085: * in the <code>JAI</code> class. The difference between direct construction
0086: * of a node and creation via a convenience method is that in the latter case:
0087: *
0088: * <ol>
0089: * <li> It is verified that the operation supports the rendered mode.</li>
0090: * <li> Using the <code>validateArguments()</code> method of the associated
0091: * <code>OperationDescriptor</code>, the arguments (sources and parameters)
0092: * are validated as being compatible with the specified operation.</li>
0093: * <li> Global <code>RenderingHints</code> maintained by the <code>JAI</code>
0094: * instance are merged with the local <code>RenderingHints</code> with the
0095: * local hints taking precedence.</li>
0096: * <li> If the operation is defined to be "immediate" (the
0097: * <code>isImmediate()</code> method of the corresponding
0098: * <code>OperationDescriptor</code> returns <code>true</code>)
0099: * then the node is rendered.</li>
0100: * </ol>
0101: *
0102: * <p> When a chain of nodes is rendered by any means a "parallel" chain of
0103: * <code>RenderedImage</code>s is created. Each node in the chain of
0104: * <code>RenderedOp</code>s corresponds to a node in the chain of
0105: * <code>RenderedImage</code>s. <code>RenderedImage</code> methods invoked
0106: * on the <code>RenderedOp</code> are in general forwarded to the associated
0107: * <code>RenderedImage</code> which is referred to as the <i>rendering</i>
0108: * of the node.
0109: *
0110: * <p> The translation between <code>RenderedOp</code> chains and
0111: * <code>RenderedImage</code> (usually <code>OpImage</code>) chains makes
0112: * use of two levels of indirection provided by the
0113: * <code>OperationRegistry</code> and <code>RenderedImageFactory</code>
0114: * (RIF) facilities. First, the local <code>OperationRegistry</code> is
0115: * used to map the operation name into a RIF. This RIF then constructs
0116: * a <code>RenderedImage</code> (usually an <code>OpImage</code>) which
0117: * does the actual image data processing. The local
0118: * <code>OperationRegistry</code> is used in order to take advantage
0119: * of the best possible implementation of the operation, e.g., RIFs that
0120: * provide acceleration for certain cases or RIFs that are known to a server
0121: * without having to burden the client.
0122: *
0123: * <p> A node may be rendered explicitly by invoking the method
0124: * <code>getRendering()</code> which also returns the rendering of the
0125: * node. A node may be rendered implicitly by invoking any method
0126: * defined in the <code>RenderedImage</code> interface. A node may also be
0127: * rendered implicitly by invoking any method the execution of which
0128: * <ul>
0129: * <li> requires some dimensional quantity of the image such as its
0130: * bounds or tile layout;</li>
0131: * <li> retrieves image data by any means;</li>
0132: * </ul>
0133: * The current rendering may be obtained without forcing the rendering of
0134: * an unrendered node via the method <code>getCurrentRendering()</code>.
0135: * A node may also be re-rendered via <code>getNewRendering()</code> which
0136: * regenerates the rendering from the existing set of sources, parameters,
0137: * and hints.
0138: *
0139: * <p> A rendering of a node may also be obtained by means of the
0140: * <code>createInstance()</code> method. This method returns a
0141: * <code>PlanarImage</code> rendering without marking the node as
0142: * having been rendered. If the node is not marked as rendered then it
0143: * will not fire <code>RenderingChangeEvent</code>s as described below.
0144: *
0145: * <p> <code>RenderedOp</code> nodes may participate in Java Bean-style
0146: * events. The <code>PropertyChangeEmitter</code> methods may be used
0147: * to register and unregister <code>PropertyChangeListener</code>s.
0148: * <code>RenderedOp</code>s are also <code>PropertyChangeListener</code>s
0149: * so that they may be registered as listeners of other
0150: * <code>PropertyChangeEmitter</code>s or the equivalent. Each
0151: * <code>RenderedOp</code> also automatically receives any
0152: * <code>RenderingChangeEvent</code>s emitted by any of its sources which
0153: * are also <code>RenderedOp</code>s or any <code>CollectionChangeEvent</code>s
0154: * from any <code>CollectionOp</code> sources.
0155: *
0156: * <p> Certain <code>PropertyChangeEvent</code>s may be emitted by the
0157: * <code>RenderedOp</code>. These include the
0158: * <code>PropertyChangeEventJAI</code>s and
0159: * <code>PropertySourceChangeEvent</code>s required by virtue of implementing
0160: * the <code>OperationNode</code> interface. Additionally a
0161: * <code>RenderingChangeEvent</code> may be emitted if the node has already
0162: * been rendered and both of the following conditions are satisfied:
0163: * <ol>
0164: * <li>A. any of the critical attributes is changed (edited), i.e., the
0165: * operation name, operation registry, node sources, parameters, or rendering
0166: * hints; or
0167: * <br>B. the node receives a <code>RenderingChangeEvent</code> from one of
0168: * its <code>RenderedOp</code> sources or a <code>CollectionChangeEvent</code>
0169: * from one of its <code>CollectionOp</code> sources.</li>
0170: * <li>the old and new renderings differ over some non-empty region.</li>
0171: * </ol>
0172: *
0173: * <p> When a rendered <code>RenderedOp</code> node receives a
0174: * <code>RenderingChangeEvent</code> from a <code>RenderedOp</code> source,
0175: * then if the rendering is an <code>OpImage</code>, the region of
0176: * the current rendering which may be retained will be determined by using
0177: * <code>mapSourceRect()</code> to forward map the bounds of the invalid
0178: * region. A similar procedure is used for "InvalidRegion" events emitted
0179: * by source <code>RenderedImage</code>s such as <code>TiledImage</code>s.
0180: * If a critical attribute of the node is edited, then the
0181: * <code>getInvalidRegion()</code> method of the corresponding
0182: * <code>OperationDescriptor</code> will be used to determine the
0183: * invalid region. If the complement of the invalid region contains any tiles
0184: * of the current rendering and the rendering is an <code>OpImage</code>, a
0185: * new rendering of the node will be generated and the
0186: * identified tiles will be retained from the old rendering insofar as
0187: * possible. This might involve for example adding tiles to a
0188: * <code>TileCache</code> under the ownership of the new rendering. A
0189: * <code>RenderingChangeEvent</code> will then be fired to all
0190: * <code>PropertyChangeListener</code>s of the node, and to any sinks that
0191: * are <code>PropertyChangeListener</code>s. The <code>newRendering</code>
0192: * parameter of the event constructor (which may be retrieved via the
0193: * <code>getNewValue()</code> method of the event) will be set to either
0194: * the new rendering of the node or to <code>null</code> if it was not
0195: * possible to retain any tiles of the previous rendering.
0196: *
0197: * <p> <code>RenderedOp</code> nodes are <code>WritablePropertySource</code>s
0198: * and so manage a name-value database of image meta-data also known as image
0199: * properties. Properties may be set on and requested from a node. The
0200: * value of a property not explicitly set on the node (via
0201: * <code>setProperty()</code>) is obtained from the property environment of
0202: * the node. When a property is derived from the property environment it is
0203: * cached locally to ensure synchronization, i.e., that properties do not
0204: * change spontaneously if for example the same property is modified upstream.
0205: *
0206: * <p> The property environment of a <code>RenderedOp</code> is initially
0207: * derived from that of the corresponding <code>OperationDescriptor</code>
0208: * as maintained by the <code>OperationRegistry</code>. It may be modified
0209: * locally by adding <code>PropertyGenerator</code>s, directives to copy
0210: * certain properties from specific sources, or requests to suppress certain
0211: * properties. These modifications per se cannot be undone directly but
0212: * may be eliminated as a side effect of other changes to the node as
0213: * described below.
0214: *
0215: * <p> The <code>RenderedOp</code> itself synthesizes several property values,
0216: * which may neither be set nor removed. These are: <code>image_width</code>,
0217: * <code>image_height</code>, <code>image_min_x_coord</code>,
0218: * <code>image_min_y_coord</code>, <code>tile_cache</code> and
0219: * <code>tile_cache_key</code>. These properties are referred to as
0220: * <i>synthetic properties</i>. The property <code>tile_cache_key</code>
0221: * has a value of type {@link TileCache} which indicates where the tiles
0222: * of the rendering are cached, if anywhere. The value of the property
0223: * <code>tile_cache_key</code> is a {@link RenderedImage} by which the
0224: * cached tiles are referenced in the indicated cache. If the rendering
0225: * is of type {@link OpImage} or
0226: * {@link javax.media.jai.remote.PlanarImageServerProxy} then the value of
0227: * <code>tile_cache_key</code> will be set to the rendering itself and the
0228: * value of <code>tile_cache</code> to the value returned by invoking
0229: * <code>getTileCache()</code> on the rendering. Otherwise these properties
0230: * will be set to the same values as the properties of the same names set
0231: * on the rendering. It is legal for these properties to have the value
0232: * <code>java.awt.Image.UndefinedProperty</code>.
0233: *
0234: * <p> When a property value is requested an attempt will be made to derive
0235: * it from the several entities in the following order of precedence:
0236: * <ol>
0237: * <li> synthetic properties; </li>
0238: * <li> local properties; </li>
0239: * <li> the rendering of the node; </li>
0240: * <li> any registered <code>PropertyGenerator</code>s, or
0241: * <br> a source specified via a copy-from-source directive;</li>
0242: * <li> the first node source which defines the property. </li>
0243: * </ol>
0244: * Local properties are those which have been cached locally either by virtue
0245: * of direct invocation of <code>setProperty()</code> or due to caching of a
0246: * property derived from the property environment. Note that the properties
0247: * of a node are not copied to its rendering.
0248: *
0249: * <p> All dynamically computed properties of a <code>RenderedOp</code> which
0250: * have been cached locally, i.e., those cached properties which were not set
0251: * by an explicit call to <code>setProperty()</code>, will be cleared when any
0252: * of the critical attributes of the node is edited. By implication these
0253: * properties will also be cleared when a <code>RenderingChangeEvent</code>
0254: * is received from any node source. The property environment or the cached
0255: * properties may also be cleared by invoking <code>resetProperties()</code>.
0256: *
0257: * <p> As mentioned, a <code>RenderedOp</code> chain created on a client
0258: * may be passed to a server via a <code>RemoteImage</code>. Whether the
0259: * node has been previously rendered is irrelevant to its ability to be
0260: * serialized. Any <code>RenderedImage</code> sources which are not
0261: * <code>Serializable</code> will be wrapped in
0262: * <code>SerializableRenderedImage</code>s for serialization. The tile
0263: * transmission parameters will be determined from the
0264: * <code>RenderingHints</code> of the node. All other non-serializable
0265: * objects will attempt to be serialized using
0266: * <code>SerializerFactory</code>. If no <code>Serializer</code> is
0267: * available for a particular object, a
0268: * <code>java.io.NotSerializableException</code> may result. Image
0269: * properties (meta-data) are serialized insofar as they are serializable:
0270: * non-serializable components are simply eliminated from the local cache
0271: * of properties and from the property environment.
0272: *
0273: * <p> Note that <code>RenderedOp</code> nodes used to instantiate
0274: * operations which have a corresponding <code>OperationDescriptor</code>
0275: * the <code>isImmediate()</code> method of which returns
0276: * <code>true</code> are rendered upon deserialization.
0277: *
0278: * <p> <code>RenderedOp</code> represents a single <code>PlanarImage</code>
0279: * as a node in a <code>RenderedImage</code> operation chain. Its companion
0280: * classes, <code>RenderableOp</code> and <code>CollectionOp</code>, represent
0281: * nodes in operation chains of <code>RenderableImage</code>s and
0282: * <code>CollectionImage</code>s, respectively.
0283: *
0284: *
0285: * @see CollectionOp
0286: * @see JAI
0287: * @see OperationDescriptor
0288: * @see OperationRegistry
0289: * @see OpImage
0290: * @see RenderableOp
0291: * @see RenderingChangeEvent
0292: * @see javax.media.jai.remote.SerializableRenderedImage
0293: * @see javax.media.jai.remote.Serializer
0294: * @see javax.media.jai.remote.SerializerFactory
0295: * @see java.awt.RenderingHints
0296: * @see java.awt.image.renderable.ParameterBlock
0297: * @see java.awt.image.renderable.RenderedImageFactory
0298: *
0299: */
0300: public class RenderedOp extends PlanarImage implements OperationNode,
0301: PropertyChangeListener, Serializable {
0302:
0303: /**
0304: * An object to assist in implementing <code>OperationNode</code>.
0305: *
0306: * @since JAI 1.1
0307: */
0308: protected OperationNodeSupport nodeSupport;
0309:
0310: /**
0311: * The <code>PropertySource</code> containing the combined properties
0312: * of all of the node's sources.
0313: */
0314: protected transient PropertySource thePropertySource;
0315:
0316: /** The rendering of the current image, not preserved over RMI. */
0317: protected transient PlanarImage theImage;
0318:
0319: /**
0320: * The RenderingHints when the node was last rendered, i.e., when
0321: * "theImage" was set to its current value.
0322: */
0323: private transient RenderingHints oldHints;
0324:
0325: /** Names of synthesized properties. */
0326: // XXX Synthetic properties should never be inherited. This might imply
0327: // a need for setting non-inheritable in addition to suppressed properties.
0328: private static List synthProps;
0329:
0330: /** Synthesized properties. */
0331: private Hashtable synthProperties = null;
0332:
0333: /** Node event names. */
0334: private static Set nodeEventNames = null;
0335:
0336: /**
0337: * Whether dispose() has been invoked.
0338: */
0339: private boolean isDisposed = false;
0340:
0341: static {
0342: CaselessStringKey[] propKeys = new CaselessStringKey[] {
0343: new CaselessStringKey("image_width"),
0344: new CaselessStringKey("image_height"),
0345: new CaselessStringKey("image_min_x_coord"),
0346: new CaselessStringKey("image_min_y_coord"),
0347: new CaselessStringKey("tile_cache"),
0348: new CaselessStringKey("tile_cache_key") };
0349: synthProps = Arrays.asList(propKeys);
0350:
0351: nodeEventNames = new HashSet();
0352: nodeEventNames.add("operationname");
0353: nodeEventNames.add("operationregistry");
0354: nodeEventNames.add("parameterblock");
0355: nodeEventNames.add("sources");
0356: nodeEventNames.add("parameters");
0357: nodeEventNames.add("renderinghints");
0358: }
0359:
0360: /**
0361: * Constructs a <code>RenderedOp</code> that will be used to
0362: * instantiate a particular rendered operation from the specified
0363: * operation registry, an operation name, a <code>ParameterBlock</code>,
0364: * and a set of rendering hints.
0365: *
0366: * <p> This method does not validate the contents of the supplied
0367: * <code>ParameterBlock</code>. The caller should ensure that
0368: * the sources and parameters in the <code>ParameterBlock</code>
0369: * are suitable for the operation this node represents; otherwise
0370: * some form of error or exception may occur at the time of rendering.
0371: *
0372: * <p> The <code>ParameterBlock</code> may include
0373: * <code>DeferredData</code> parameters. These will not be evaluated
0374: * until their values are actually required, i.e., when the node is
0375: * rendered.
0376: *
0377: * <p> The node is added automatically as a sink of any
0378: * <code>PlanarImage</code> or <code>CollectionImage</code> sources.
0379: *
0380: * @param registry The <code>OperationRegistry</code> to be used for
0381: * instantiation. if <code>null</code>, the default registry
0382: * is used. Saved by reference.
0383: * @param opName The operation name. Saved by reference.
0384: * @param pb The sources and parameters. If <code>null</code>,
0385: * it is assumed that this node has no sources and parameters.
0386: * This parameter is cloned.
0387: * @param hints The rendering hints. If <code>null</code>, it is assumed
0388: * that no hints are associated with the rendering.
0389: * This parameter is cloned.
0390: *
0391: * @throws IllegalArgumentException if <code>opName</code> is
0392: * <code>null</code>.
0393: */
0394: public RenderedOp(OperationRegistry registry, String opName,
0395: ParameterBlock pb, RenderingHints hints) {
0396: super (new ImageLayout(), null, null);
0397:
0398: if (pb == null) {
0399: // Ensure that the PB is non-null.
0400: pb = new ParameterBlock();
0401: } else {
0402: // Clone the PB per the doc.
0403: pb = (ParameterBlock) pb.clone();
0404: }
0405:
0406: if (hints != null) {
0407: // Clone the hints per the doc.
0408: hints = (RenderingHints) hints.clone();
0409: }
0410:
0411: nodeSupport = new OperationNodeSupport(getRegistryModeName(),
0412: opName, registry, pb, hints, eventManager);
0413:
0414: // Add the node as a PropertyChangeListener of itself for
0415: // the critical attributes of the node. Case is ignored
0416: // in the property names but infix caps are used here anyway.
0417: addPropertyChangeListener("OperationName", this );
0418: addPropertyChangeListener("OperationRegistry", this );
0419: addPropertyChangeListener("ParameterBlock", this );
0420: addPropertyChangeListener("Sources", this );
0421: addPropertyChangeListener("Parameters", this );
0422: addPropertyChangeListener("RenderingHints", this );
0423:
0424: // Add self as a sink of any PlanarImage or CollectionImage sources.
0425: Vector nodeSources = pb.getSources();
0426: if (nodeSources != null) {
0427: Iterator it = nodeSources.iterator();
0428: while (it.hasNext()) {
0429: Object src = it.next();
0430: if (src instanceof PlanarImage) {
0431: ((PlanarImage) src).addSink(this );
0432: } else if (src instanceof CollectionImage) {
0433: ((CollectionImage) src).addSink(this );
0434: }
0435: }
0436: }
0437: }
0438:
0439: /**
0440: * Constructs a <code>RenderedOp</code> that will be used to
0441: * instantiate a particular rendered operation from the default
0442: * operation registry, an operation name, a <code>ParameterBlock</code>,
0443: * and a set of rendering hints. The default operation registry
0444: * is used.
0445: *
0446: * <p> This method does not validate the contents of the supplied
0447: * <code>ParameterBlock</code>. The caller should ensure that
0448: * the sources and parameters in the <code>ParameterBlock</code>
0449: * are suitable for the operation this node represents; otherwise
0450: * some form of error or exception may occur at the time of rendering.
0451: *
0452: * <p> The <code>ParameterBlock</code> may include
0453: * <code>DeferredData</code> parameters. These will not be evaluated
0454: * until their values are actually required, i.e., when the node is
0455: * rendered.
0456: *
0457: * <p> The node is added automatically as a sink of any
0458: * <code>PlanarImage</code> or <code>CollectionImage</code> sources.
0459: *
0460: * @param opName The operation name. Saved by reference.
0461: * @param pb The sources and parameters. If <code>null</code>,
0462: * it is assumed that this node has no sources and parameters.
0463: * This parameter is cloned.
0464: * @param hints The rendering hints. If <code>null</code>, it is assumed
0465: * that no hints are associated with the rendering.
0466: * This parameter is cloned.
0467: *
0468: * @throws IllegalArgumentException if <code>opName</code> is
0469: * <code>null</code>.
0470: */
0471: public RenderedOp(String opName, ParameterBlock pb,
0472: RenderingHints hints) {
0473: this (null, opName, pb, hints);
0474: }
0475:
0476: /**
0477: * A <code>TileComputationListener</code> to pass to the
0478: * <code>scheduleTiles()</code> method of the rendering to intercept
0479: * method calls such that the image reference is this
0480: * <code>RenderedOp</code>.
0481: */
0482: private class TCL implements TileComputationListener {
0483: RenderedOp node;
0484:
0485: private TCL(RenderedOp node) {
0486: this .node = node;
0487: }
0488:
0489: public void tileComputed(Object eventSource,
0490: TileRequest[] requests, PlanarImage image, int tileX,
0491: int tileY, Raster tile) {
0492: if (image == theImage) {
0493: // Forward call to all listeners.
0494: TileComputationListener[] listeners = getTileComputationListeners();
0495:
0496: if (listeners != null) {
0497: int numListeners = listeners.length;
0498:
0499: for (int i = 0; i < numListeners; i++) {
0500: listeners[i].tileComputed(node, requests,
0501: image, tileX, tileY, tile);
0502: }
0503: }
0504: }
0505: }
0506:
0507: public void tileCancelled(Object eventSource,
0508: TileRequest[] requests, PlanarImage image, int tileX,
0509: int tileY) {
0510: if (image == theImage) {
0511: // Forward call to all listeners.
0512: TileComputationListener[] listeners = getTileComputationListeners();
0513:
0514: if (listeners != null) {
0515: int numListeners = listeners.length;
0516:
0517: for (int i = 0; i < numListeners; i++) {
0518: listeners[i].tileCancelled(node, requests,
0519: image, tileX, tileY);
0520: }
0521: }
0522: }
0523: }
0524:
0525: public void tileComputationFailure(Object eventSource,
0526: TileRequest[] requests, PlanarImage image, int tileX,
0527: int tileY, Throwable situation) {
0528: if (image == theImage) {
0529: // Forward call to all listeners.
0530: TileComputationListener[] listeners = getTileComputationListeners();
0531:
0532: if (listeners != null) {
0533: int numListeners = listeners.length;
0534:
0535: for (int i = 0; i < numListeners; i++) {
0536: listeners[i].tileComputationFailure(node,
0537: requests, image, tileX, tileY,
0538: situation);
0539: }
0540: }
0541: }
0542: }
0543: }
0544:
0545: /**
0546: * Returns the name of the <code>RegistryMode</code> corresponding to
0547: * this <code>RenderedOp</code>. This method always returns the
0548: * <code>String</code> "rendered".
0549: *
0550: * @since JAI 1.1
0551: */
0552: public String getRegistryModeName() {
0553: return RegistryMode.getMode("rendered").getName();
0554: }
0555:
0556: /* ----- Critical attribute main accessors and mutators. ----- */
0557:
0558: /**
0559: * Returns the <code>OperationRegistry</code> that is used
0560: * by this node. If the registry is not set, the default
0561: * registry is returned.
0562: */
0563: public synchronized OperationRegistry getRegistry() {
0564: return nodeSupport.getRegistry();
0565: }
0566:
0567: /**
0568: * Sets the <code>OperationRegistry</code> that is used by
0569: * this node. If the specified registry is <code>null</code>, the
0570: * default registry is used. The parameter is saved by reference.
0571: *
0572: * <p> If the supplied registry does not equal the current registry, a
0573: * <code>PropertyChangeEventJAI</code> named "OperationRegistry"
0574: * will be fired and a <code>RenderingChangeEvent</code> may be
0575: * fired if the node has already been rendered.
0576: *
0577: * @param registry The new <code>OperationRegistry</code> to be set;
0578: * it may be <code>null</code>.
0579: */
0580: public synchronized void setRegistry(OperationRegistry registry) {
0581: nodeSupport.setRegistry(registry);
0582: }
0583:
0584: /**
0585: * Returns the name of the operation this node represents as
0586: * a <code>String</code>.
0587: */
0588: public synchronized String getOperationName() {
0589: return nodeSupport.getOperationName();
0590: }
0591:
0592: /**
0593: * Sets the name of the operation this node represents.
0594: * The parameter is saved by reference.
0595: *
0596: * <p> If the supplied name does not equal the current operation name, a
0597: * <code>PropertyChangeEventJAI</code> named "OperationName"
0598: * will be fired and a <code>RenderingChangeEvent</code> may be
0599: * fired if the node has already been rendered.
0600: *
0601: * @param opName The new operation name to be set.
0602: *
0603: * @throws IllegalArgumentException if <code>opName</code> is
0604: * <code>null</code>.
0605: */
0606: public synchronized void setOperationName(String opName) {
0607: nodeSupport.setOperationName(opName);
0608: }
0609:
0610: /** Returns a clone of the <code>ParameterBlock</code> of this node. */
0611: public synchronized ParameterBlock getParameterBlock() {
0612: return (ParameterBlock) nodeSupport.getParameterBlock().clone();
0613: }
0614:
0615: /**
0616: * Sets the <code>ParameterBlock</code> of this node.
0617: * If the specified new <code>ParameterBlock</code> is <code>null</code>,
0618: * it is assumed that this node has no input sources and parameters.
0619: * The supplied parameter is cloned.
0620: *
0621: * <p> This method does not validate the content of the supplied
0622: * <code>ParameterBlock</code>. The caller should ensure that
0623: * the sources and parameters in the <code>ParameterBlock</code>
0624: * are suitable for the operation this node represents; otherwise
0625: * some form of error or exception may occur at the time of rendering.
0626: *
0627: * <p> If the supplied <code>ParameterBlock</code> does not equal the
0628: * current <code>ParameterBlock</code>, a
0629: * <code>PropertyChangeEventJAI</code> named "ParameterBlock", "Sources",
0630: * or "Parameters" will be fired. A <code>RenderingChangeEvent</code>
0631: * may also be fired if the node has already been rendered.
0632: *
0633: * <p> The <code>ParameterBlock</code> may include
0634: * <code>DeferredData</code> parameters. These will not be evaluated
0635: * until their values are actually required, i.e., when the node is
0636: * rendered.
0637: *
0638: * <p> The node is registered as a sink of any <code>PlanarImage</code>
0639: * or <code>CollectionImage</code> sources contained in the supplied
0640: * <code>ParameterBlock</code>. The node is also removed as a sink of
0641: * any previous <code>PlanarImage</code> or <code>CollectionImage</code>
0642: * sources if these are not in the new <code>ParameterBlock</code>.
0643: *
0644: * @param pb The new <code>ParameterBlock</code> to be set;
0645: * it may be <code>null</code>.
0646: */
0647: public synchronized void setParameterBlock(ParameterBlock pb) {
0648: Vector nodeSources = nodeSupport.getParameterBlock()
0649: .getSources();
0650: if (nodeSources != null && nodeSources.size() > 0) {
0651: Iterator it = nodeSources.iterator();
0652: while (it.hasNext()) {
0653: Object src = it.next();
0654: if (src instanceof PlanarImage) {
0655: ((PlanarImage) src).removeSink(this );
0656: } else if (src instanceof CollectionImage) {
0657: ((CollectionImage) src).removeSink(this );
0658: }
0659: }
0660: }
0661:
0662: if (pb != null) {
0663: Vector newSources = pb.getSources();
0664: ;
0665: if (newSources != null && newSources.size() > 0) {
0666: Iterator it = newSources.iterator();
0667: while (it.hasNext()) {
0668: Object src = it.next();
0669: if (src instanceof PlanarImage) {
0670: ((PlanarImage) src).addSink(this );
0671: } else if (src instanceof CollectionImage) {
0672: ((CollectionImage) src).addSink(this );
0673: }
0674: }
0675: }
0676: }
0677:
0678: nodeSupport.setParameterBlock(pb == null ? new ParameterBlock()
0679: : (ParameterBlock) pb.clone());
0680: }
0681:
0682: /**
0683: * Returns a clone of the <code>RenderingHints</code> of this node or
0684: * <code>null</code>.
0685: */
0686: public RenderingHints getRenderingHints() {
0687: RenderingHints hints = nodeSupport.getRenderingHints();
0688: return hints == null ? null : (RenderingHints) hints.clone();
0689: }
0690:
0691: /**
0692: * Sets the <code>RenderingHints</code> of this node.
0693: * The supplied parameter is cloned if non-<code>null</code>.
0694: *
0695: * <p> If the supplied <code>RenderingHints</code> does not equal the
0696: * current <code>RenderingHints</code>, a
0697: * <code>PropertyChangeEventJAI</code> named "RenderingHints"
0698: * will be fired and a <code>RenderingChangeEvent</code> may be
0699: * fired if the node has already been rendered.
0700: *
0701: * @param hints The new <code>RenderingHints</code> to be set;
0702: * it may be <code>null</code>.
0703: */
0704: public synchronized void setRenderingHints(RenderingHints hints) {
0705: if (hints != null) {
0706: hints = (RenderingHints) hints.clone();
0707: }
0708: nodeSupport.setRenderingHints(hints);
0709: }
0710:
0711: /* ----- Rendering generation methods. ----- */
0712:
0713: /**
0714: * Instantiate a <code>PlanarImage</code> that computes the result
0715: * of this <code>RenderedOp</code>. The local
0716: * <code>OperationRegistry</code> of this node is used to translate
0717: * the operation name into a <code>RenderedImageFactory</code> and
0718: * eventually an actual <code>RenderedImage</code> (usually an
0719: * <code>OpImage</code>).
0720: *
0721: * <p> During this method, all the sources supplied in the
0722: * <code>ParameterBlock</code> are checked. If any of the sources
0723: * is a <code>RenderedOp</code>, a rendering of that source is
0724: * created. This propagates all the way up to the top of the op
0725: * chain. If any of the sources is a <code>Collection</code>,
0726: * then the collection is passed to the operation as-is. If there
0727: * is a <code>RenderedOp</code> anywhere in the collection, it is
0728: * up to the individual operation to create the rendering for that
0729: * <code>RenderedOp</code>.
0730: *
0731: * <p> This method does not validate the sources and parameters
0732: * stored in the <code>ParameterBlock</code> against the specification
0733: * of the operation this node represents. It is the responsibility
0734: * of the caller to ensure that the data in the
0735: * <code>ParameterBlock</code> are suitable for this operation.
0736: * Otherwise, some kind of exception or error will occur.
0737: *
0738: * <p> Invoking this method will cause any source <code>RenderedOp</code>
0739: * nodes to be rendered using <code>getRendering()</code> and any
0740: * source <code>CollectionOp</code> nodes to be rendered using
0741: * <code>getCollection()</code>. Any <code>DeferredData</code> parameters
0742: * in the <code>ParameterBlock</code> will also be evaluated.
0743: *
0744: * <p> The <code>RenderedImage</code> generated by the selected
0745: * <code>RenderedImageFactory</code> will be converted to a
0746: * <code>PlanarImage</code> by invoking
0747: * <code>PlanarImage.wrapRenderedImage()</code>.
0748: *
0749: * @return The resulting image as a <code>PlanarImage</code>.
0750: *
0751: * @throws RuntimeException if the image factory charged with rendering
0752: * the node is unable to create a rendering.
0753: */
0754: public synchronized PlanarImage createInstance() {
0755: return createInstance(false);
0756: }
0757:
0758: /**
0759: * This method performs the actions described by the documentation of
0760: * <code>createInstance()</code> optionally marking the node as rendered
0761: * according to the parameter.
0762: *
0763: * @throws RuntimeException if the image factory charged with rendering
0764: * the node is unable to create a rendering.
0765: *
0766: * @see #createInstance()
0767: *
0768: * @since JAI 1.1
0769: */
0770: protected synchronized PlanarImage createInstance(
0771: boolean isNodeRendered) {
0772: ParameterBlock pb = new ParameterBlock();
0773: Vector parameters = nodeSupport.getParameterBlock()
0774: .getParameters();
0775:
0776: // Evaluate and DeferredData parameters.
0777: pb.setParameters(ImageUtil.evaluateParameters(parameters));
0778:
0779: int numSources = getNumSources();
0780: for (int i = 0; i < numSources; i++) {
0781: Object source = getNodeSource(i);
0782: Object ai = null;
0783:
0784: if (source instanceof RenderedOp) {
0785: RenderedOp src = (RenderedOp) source;
0786: ai = isNodeRendered ? src.getRendering() : src
0787: .createInstance();
0788: } else if (source instanceof CollectionOp) {
0789: ai = ((CollectionOp) source).getCollection();
0790: } else if ((source instanceof RenderedImage)
0791: || (source instanceof Collection)) {
0792: // XXX: RenderedImageList - bpb 8 dec 2000
0793: // If source is a RenderedImageAdapter which is wrapping a
0794: // RenderedImageList whose primary image is a RenderedOp,
0795: // set ai to the rendering of that RenderedOp.
0796: ai = source;
0797: } else {
0798: // Source is some other type. Pass on (for now).
0799: ai = source;
0800: }
0801: pb.addSource(ai);
0802: }
0803:
0804: // Create the rendering.
0805: RenderedImage rendering = RIFRegistry.create(getRegistry(),
0806: nodeSupport.getOperationName(), pb, nodeSupport
0807: .getRenderingHints());
0808:
0809: // Throw an exception if the rendering is null.
0810: if (rendering == null) {
0811: throw new RuntimeException(JaiI18N.getString("RenderedOp0"));
0812: }
0813:
0814: // XXX: RenderedImageList - bpb 8 dec 2000
0815: // If rendering is a wrapped RenderedImageList whose primary image
0816: // is a RenderedOp, reset the sources of the primary image
0817: // to the source List of this node. That is to say, replace
0818: // the OpImage sources with RenderedOp sources. Also, register
0819: // this node as a PropertyChangeListener of the primary image.
0820: // Somehow this node also needs to be able to identify
0821: // RenderingChangeEvents emitted by the primary image.
0822: // The invalid region would be extracted from such RCEs and used
0823: // in creating a new RCE with this node as its source which would
0824: // be fired as usual to all listeners and sinks.
0825:
0826: // Ensure that the rendering is a PlanarImage.
0827: PlanarImage instance = PlanarImage.wrapRenderedImage(rendering);
0828:
0829: // Save the RenderingHints.
0830: oldHints = nodeSupport.getRenderingHints() == null ? null
0831: : (RenderingHints) nodeSupport.getRenderingHints()
0832: .clone();
0833:
0834: return instance;
0835: }
0836:
0837: /**
0838: * Creates a <code>PlanarImage</code> rendering if none exists
0839: * and sets <code>theImage</code> to the resulting value. This method
0840: * performs the same actions as <code>createInstance()</code> but sets
0841: * <code>theImage</code> to the result.
0842: *
0843: * @throws RuntimeException if the image factory charged with rendering
0844: * the node is unable to create a rendering.
0845: *
0846: * @see #createInstance()
0847: *
0848: * @since JAI 1.1
0849: */
0850: protected synchronized void createRendering() {
0851: if (theImage == null) {
0852: setImageLayout(new ImageLayout(
0853: theImage = createInstance(true)));
0854:
0855: if (theImage != null) {
0856: // Get listeners, wrap, and add to OpImage listener list.
0857: theImage.addTileComputationListener(new TCL(this ));
0858: }
0859: }
0860: }
0861:
0862: /**
0863: * Returns the <code>PlanarImage</code> rendering associated with this
0864: * <code>RenderedOp</code> node. This method performs the same action
0865: * as <code>createRendering()</code> but returns <code>theImage</code>.
0866: *
0867: * @throws RuntimeException if the image factory charged with rendering
0868: * the node is unable to create a rendering.
0869: *
0870: * @see #createRendering()
0871: * @see #createInstance()
0872: */
0873: public PlanarImage getRendering() {
0874: createRendering();
0875: return theImage;
0876: }
0877:
0878: /**
0879: * Returns the value of the protected variable <code>theImage</code>
0880: * which may be <code>null</code> if no rendering has yet been created.
0881: * This method does not force the node to be rendered.
0882: *
0883: * @since JAI 1.1
0884: */
0885: public PlanarImage getCurrentRendering() {
0886: return theImage;
0887: }
0888:
0889: /**
0890: * Forces the node to be re-rendered and returns the new rendering.
0891: *
0892: * <p> If the node has not yet been rendered this method is identical to
0893: * <code>getRendering()</code>.
0894: *
0895: * <p> If the node has already been rendered, then a new rendering will be
0896: * generated. The synthetic and locally cached properties and the property
0897: * environment of the node will all be reset. All registered
0898: * <code>PropertyChangeListener</code>s and any
0899: * <code>PropertyChangeListener</code> sinks will be notifed of the
0900: * change in the rendering via a <code>RenderingChangeEvent</code>
0901: * the invalid region of which will be <code>null</code>.
0902: *
0903: * <p> This method could be used for example to trigger a
0904: * re-rendering of the node in cases where this would not happen
0905: * automatically but is desirable to the application. One
0906: * example occurs if a parameter of the operation is a referent of
0907: * some other entity which changes but the parameter itself does not
0908: * change according to <code>equals()</code>. This could occur for
0909: * example for an image file input operation wherein the path to the
0910: * file remains the same but the content of the file changes.
0911: *
0912: * @return The (possibly regenerated) rendering of the node. This value
0913: * may be ignored if the intent of invoking the method was merely to
0914: * re-render the node and generate events for
0915: * <code>RenderingChangeEvent</code> listeners.
0916: *
0917: * @since JAI 1.1
0918: */
0919: public PlanarImage getNewRendering() {
0920: if (theImage == null) {
0921: return getRendering();
0922: }
0923:
0924: // Save the previous rendering.
0925: PlanarImage theOldImage = theImage;
0926:
0927: // Clear the current rendering.
0928: theImage = null;
0929:
0930: // XXX The rest of this method is effectively duplicated from the
0931: // end of propertyChange(). Should another method be created to be
0932: // called in these two places in order to avoid code duplication?
0933:
0934: // Re-render the node.
0935: createRendering();
0936:
0937: // Clear the synthetic and cached properties and reset the
0938: // property source.
0939: resetProperties(true);
0940:
0941: // Create the event object.
0942: RenderingChangeEvent rcEvent = new RenderingChangeEvent(this ,
0943: theOldImage, theImage, null);
0944:
0945: // Fire to all registered listeners.
0946: eventManager.firePropertyChange(rcEvent);
0947:
0948: // Fire an event to all PropertyChangeListener sinks.
0949: Vector sinks = getSinks();
0950: if (sinks != null) {
0951: int numSinks = sinks.size();
0952: for (int i = 0; i < numSinks; i++) {
0953: Object sink = sinks.get(i);
0954: if (sink instanceof PropertyChangeListener) {
0955: ((PropertyChangeListener) sink)
0956: .propertyChange(rcEvent);
0957: }
0958: }
0959: }
0960:
0961: return theImage;
0962: }
0963:
0964: /* ----- PropertyChangeListener method. ----- */
0965:
0966: /**
0967: * Implementation of <code>PropertyChangeListener</code>.
0968: *
0969: * <p> When invoked with an event which is an instance of
0970: * <code>RenderingChangeEvent</code> or <code>CollectionChangeEvent</code>
0971: * emitted by a <code>RenderedOp</code> or <code>CollectionOp</code>,
0972: * respectively, the node will respond by re-rendering itself while
0973: * retaining any tiles possible. It will respond to an "InvalidRegion"
0974: * event emitted by a source <code>RenderedImage</code> in a manner
0975: * similar to that applied for <code>RenderingChangeEvent</code>s.
0976: *
0977: * @see TiledImage#propertyChange
0978: *
0979: * @since JAI 1.1
0980: */
0981: public synchronized void propertyChange(PropertyChangeEvent evt) {
0982: //
0983: // React if and only if the node has been rendered and
0984: // A: a non-PropertySourceChangeEvent PropertyChangeEventJAI
0985: // was received from this node, or
0986: // B: a RenderingChangeEvent was received from a source RenderedOp, or
0987: // C: a CollectionChangeEvent was received from a source CollectionOp, or
0988: // D: an "InvalidRegion" event was received from a source RenderedImage.
0989: //
0990:
0991: // Cache event and node sources.
0992: Object evtSrc = evt.getSource();
0993: Vector nodeSources = nodeSupport.getParameterBlock()
0994: .getSources();
0995:
0996: // Get the name of the bean property and convert it to lower
0997: // case now for efficiency later.
0998: String propName = evt.getPropertyName().toLowerCase(
0999: Locale.ENGLISH);
1000:
1001: if (theImage != null
1002: && ((evt instanceof PropertyChangeEventJAI
1003: && evtSrc == this
1004: && !(evt instanceof PropertySourceChangeEvent) && nodeEventNames
1005: .contains(propName)) || ((evt instanceof RenderingChangeEvent
1006: || evt instanceof CollectionChangeEvent || (evt instanceof PropertyChangeEventJAI
1007: && evtSrc instanceof RenderedImage && propName
1008: .equals("invalidregion"))) && nodeSources
1009: .contains(evtSrc)))) {
1010:
1011: // Save the previous rendering.
1012: PlanarImage theOldImage = theImage;
1013:
1014: // Initialize the event flag.
1015: boolean fireEvent = false;
1016:
1017: // Set default invalid region to null (the entire image).
1018: Shape invalidRegion = null;
1019:
1020: if (evtSrc == this
1021: && (propName.equals("operationname") || propName
1022: .equals("operationregistry"))) {
1023:
1024: // Operation name or OperationRegistry changed:
1025: // invalidate the entire rendering.
1026: fireEvent = true;
1027: theImage = null;
1028:
1029: } else if (evt instanceof RenderingChangeEvent
1030: || (evtSrc instanceof RenderedImage && propName
1031: .equals("invalidregion"))) {
1032:
1033: // Set the event flag.
1034: fireEvent = true;
1035:
1036: Shape srcInvalidRegion = null;
1037:
1038: if (evt instanceof RenderingChangeEvent) {
1039: // RenderingChangeEvent presumably from a source RenderedOp.
1040: RenderingChangeEvent rcEvent = (RenderingChangeEvent) evt;
1041:
1042: // Get the invalidated region of the source.
1043: srcInvalidRegion = rcEvent.getInvalidRegion();
1044:
1045: // If entire source is invalid replace with source bounds.
1046: if (srcInvalidRegion == null) {
1047: srcInvalidRegion = ((PlanarImage) rcEvent
1048: .getOldValue()).getBounds();
1049: }
1050: } else {
1051: // Get the invalidated region of the source.
1052: srcInvalidRegion = (Shape) evt.getNewValue();
1053:
1054: // If entire source is invalid replace with source bounds.
1055: if (srcInvalidRegion == null) {
1056: RenderedImage rSrc = (RenderedImage) evtSrc;
1057: srcInvalidRegion = new Rectangle(
1058: rSrc.getMinX(), rSrc.getMinY(), rSrc
1059: .getWidth(), rSrc.getHeight());
1060: }
1061: }
1062:
1063: // Only process further if the rendering is an OpImage.
1064: if (!(theImage instanceof OpImage)) {
1065:
1066: // Clear the current rendering.
1067: theImage = null;
1068:
1069: } else {
1070: // Save the previous rendering as an OpImage.
1071: OpImage oldOpImage = (OpImage) theImage;
1072:
1073: // Cache source invalid bounds.
1074: Rectangle srcInvalidBounds = srcInvalidRegion
1075: .getBounds();
1076:
1077: // If bounds are empty, replace srcInvalidRegion with
1078: // the complement of the image bounds within the
1079: // bounds of all tiles.
1080: if (srcInvalidBounds.isEmpty()) {
1081: int x = oldOpImage.tileXToX(oldOpImage
1082: .getMinTileX());
1083: int y = oldOpImage.tileYToY(oldOpImage
1084: .getMinTileY());
1085: int w = oldOpImage.getNumXTiles()
1086: * oldOpImage.getTileWidth();
1087: int h = oldOpImage.getNumYTiles()
1088: * oldOpImage.getTileHeight();
1089: Rectangle tileBounds = new Rectangle(x, y, w, h);
1090: Rectangle imageBounds = oldOpImage.getBounds();
1091: if (!tileBounds.equals(imageBounds)) {
1092: Area tmpArea = new Area(tileBounds);
1093: tmpArea.subtract(new Area(imageBounds));
1094: srcInvalidRegion = tmpArea;
1095: srcInvalidBounds = srcInvalidRegion
1096: .getBounds();
1097: }
1098: }
1099:
1100: // ----- Determine invalid destination region. -----
1101:
1102: boolean saveAllTiles = false;
1103: ArrayList validTiles = null;
1104: if (srcInvalidBounds.isEmpty()) {
1105: invalidRegion = srcInvalidRegion;
1106: saveAllTiles = true;
1107: } else {
1108: // Get index of source which changed.
1109: int idx = nodeSources.indexOf(evtSrc);
1110:
1111: // Determine bounds of invalid destination region.
1112: Rectangle dstRegionBounds = oldOpImage
1113: .mapSourceRect(srcInvalidBounds, idx);
1114:
1115: if (dstRegionBounds == null) {
1116: dstRegionBounds = oldOpImage.getBounds();
1117: }
1118:
1119: // Determine invalid destination region.
1120: Point[] indices = getTileIndices(dstRegionBounds);
1121: int numIndices = indices != null ? indices.length
1122: : 0;
1123: GeneralPath gp = null;
1124:
1125: for (int i = 0; i < numIndices; i++) {
1126: if (i % 1000 == 0 && gp != null)
1127: gp = new GeneralPath(new Area(gp));
1128:
1129: Rectangle dstRect = getTileRect(
1130: indices[i].x, indices[i].y);
1131: Rectangle srcRect = oldOpImage.mapDestRect(
1132: dstRect, idx);
1133: if (srcRect == null) {
1134: gp = null;
1135: break;
1136: }
1137: if (srcInvalidRegion.intersects(srcRect)) {
1138: if (gp == null) {
1139: gp = new GeneralPath(dstRect);
1140: } else {
1141: gp.append(dstRect, false);
1142: }
1143: } else {
1144: if (validTiles == null) {
1145: validTiles = new ArrayList();
1146: }
1147: validTiles.add(indices[i]);
1148: }
1149: }
1150:
1151: invalidRegion = (gp == null) ? null : new Area(
1152: gp);
1153: }
1154:
1155: // Clear the current rendering.
1156: theImage = null;
1157:
1158: // Retrieve the old TileCache.
1159: TileCache oldCache = oldOpImage.getTileCache();
1160:
1161: // Only perform further processing if there is a cache
1162: // and there are tiles to save.
1163: if (oldCache != null
1164: && (saveAllTiles || validTiles != null)) {
1165: // Re-render the node.
1166: createRendering();
1167:
1168: // Only perform further processing if the new
1169: // rendering is an OpImage with a non-null TileCache.
1170: if (theImage instanceof OpImage
1171: && ((OpImage) theImage).getTileCache() != null) {
1172: OpImage newOpImage = (OpImage) theImage;
1173: TileCache newCache = newOpImage
1174: .getTileCache();
1175: Object tileCacheMetric = newOpImage
1176: .getTileCacheMetric();
1177:
1178: if (saveAllTiles) {
1179: Raster[] tiles = oldCache
1180: .getTiles(oldOpImage);
1181: int numTiles = tiles == null ? 0
1182: : tiles.length;
1183: for (int i = 0; i < numTiles; i++) {
1184: Raster tile = tiles[i];
1185: int tx = newOpImage.XToTileX(tile
1186: .getMinX());
1187: int ty = newOpImage.YToTileY(tile
1188: .getMinY());
1189: newCache.add(newOpImage, tx, ty,
1190: tile, tileCacheMetric);
1191: }
1192: } else { // save some, but not all, tiles
1193: int numValidTiles = validTiles.size();
1194: for (int i = 0; i < numValidTiles; i++) {
1195: Point tileIndex = (Point) validTiles
1196: .get(i);
1197: Raster tile = oldCache.getTile(
1198: oldOpImage, tileIndex.x,
1199: tileIndex.y);
1200: if (tile != null) {
1201: newCache.add(newOpImage,
1202: tileIndex.x,
1203: tileIndex.y, tile,
1204: tileCacheMetric);
1205: }
1206: }
1207: }
1208: }
1209: }
1210: }
1211: } else { // not op name or registry change nor RenderingChangeEvent
1212: ParameterBlock oldPB = null;
1213: ParameterBlock newPB = null;
1214:
1215: boolean checkInvalidRegion = false;
1216: if (propName.equals("parameterblock")) {
1217: oldPB = (ParameterBlock) evt.getOldValue();
1218: newPB = (ParameterBlock) evt.getNewValue();
1219: checkInvalidRegion = true;
1220: } else if (propName.equals("sources")) {
1221: // Replace source(s)
1222: Vector params = nodeSupport.getParameterBlock()
1223: .getParameters();
1224: oldPB = new ParameterBlock((Vector) evt
1225: .getOldValue(), params);
1226: newPB = new ParameterBlock((Vector) evt
1227: .getNewValue(), params);
1228: checkInvalidRegion = true;
1229: } else if (propName.equals("parameters")) {
1230: // Replace parameter(s)
1231: oldPB = new ParameterBlock(nodeSources,
1232: (Vector) evt.getOldValue());
1233: newPB = new ParameterBlock(nodeSources,
1234: (Vector) evt.getNewValue());
1235: checkInvalidRegion = true;
1236: } else if (propName.equals("renderinghints")) {
1237: oldPB = newPB = nodeSupport.getParameterBlock();
1238: checkInvalidRegion = true;
1239: } else if (evt instanceof CollectionChangeEvent) {
1240: // Event from a CollectionOp source.
1241:
1242: // Replace appropriate source.
1243: int collectionIndex = nodeSources.indexOf(evtSrc);
1244: Vector oldSources = (Vector) nodeSources.clone();
1245: Vector newSources = (Vector) nodeSources.clone();
1246: oldSources.set(collectionIndex, evt.getOldValue());
1247: newSources.set(collectionIndex, evt.getNewValue());
1248:
1249: Vector params = nodeSupport.getParameterBlock()
1250: .getParameters();
1251:
1252: oldPB = new ParameterBlock(oldSources, params);
1253: newPB = new ParameterBlock(newSources, params);
1254:
1255: checkInvalidRegion = true;
1256: }
1257:
1258: if (checkInvalidRegion) {
1259: // Set event flag.
1260: fireEvent = true;
1261:
1262: // Get the associated OperationDescriptor.
1263: OperationRegistry registry = nodeSupport
1264: .getRegistry();
1265: OperationDescriptor odesc = (OperationDescriptor) registry
1266: .getDescriptor(OperationDescriptor.class,
1267: nodeSupport.getOperationName());
1268:
1269: // Evaluate any DeferredData parameters.
1270: oldPB = ImageUtil.evaluateParameters(oldPB);
1271: newPB = ImageUtil.evaluateParameters(newPB);
1272:
1273: // Determine the invalid region.
1274: invalidRegion = (Shape) odesc.getInvalidRegion(
1275: RenderedRegistryMode.MODE_NAME, oldPB,
1276: oldHints, newPB, nodeSupport
1277: .getRenderingHints(), this );
1278:
1279: if (invalidRegion == null
1280: || !(theImage instanceof OpImage)) {
1281:
1282: // Can't save any tiles; clear the rendering.
1283: theImage = null;
1284:
1285: } else {
1286: // Create a new rendering.
1287: OpImage oldRendering = (OpImage) theImage;
1288: theImage = null;
1289: createRendering();
1290:
1291: // If the new rendering is also an OpImage,
1292: // save some tiles.
1293: if (theImage instanceof OpImage
1294: && oldRendering.getTileCache() != null
1295: && ((OpImage) theImage).getTileCache() != null) {
1296: OpImage newRendering = (OpImage) theImage;
1297:
1298: // Save some values.
1299: TileCache oldCache = oldRendering
1300: .getTileCache();
1301: TileCache newCache = newRendering
1302: .getTileCache();
1303: Object tileCacheMetric = newRendering
1304: .getTileCacheMetric();
1305:
1306: // If bounds are empty, replace invalidRegion with
1307: // the complement of the image bounds within the
1308: // bounds of all tiles.
1309: if (invalidRegion.getBounds().isEmpty()) {
1310: int x = oldRendering
1311: .tileXToX(oldRendering
1312: .getMinTileX());
1313: int y = oldRendering
1314: .tileYToY(oldRendering
1315: .getMinTileY());
1316: int w = oldRendering.getNumXTiles()
1317: * oldRendering.getTileWidth();
1318: int h = oldRendering.getNumYTiles()
1319: * oldRendering.getTileHeight();
1320: Rectangle tileBounds = new Rectangle(x,
1321: y, w, h);
1322: Rectangle imageBounds = oldRendering
1323: .getBounds();
1324: if (!tileBounds.equals(imageBounds)) {
1325: Area tmpArea = new Area(tileBounds);
1326: tmpArea.subtract(new Area(
1327: imageBounds));
1328: invalidRegion = tmpArea;
1329: }
1330: }
1331:
1332: if (invalidRegion.getBounds().isEmpty()) {
1333: // Save all tiles.
1334: Raster[] tiles = oldCache
1335: .getTiles(oldRendering);
1336: int numTiles = tiles == null ? 0
1337: : tiles.length;
1338: for (int i = 0; i < numTiles; i++) {
1339: Raster tile = tiles[i];
1340: int tx = newRendering.XToTileX(tile
1341: .getMinX());
1342: int ty = newRendering.YToTileY(tile
1343: .getMinY());
1344: newCache.add(newRendering, tx, ty,
1345: tile, tileCacheMetric);
1346: }
1347: } else {
1348: // Copy tiles not in invalid region from old
1349: // TileCache to new TileCache.
1350: Raster[] tiles = oldCache
1351: .getTiles(oldRendering);
1352: int numTiles = tiles == null ? 0
1353: : tiles.length;
1354: for (int i = 0; i < numTiles; i++) {
1355: Raster tile = tiles[i];
1356: Rectangle bounds = tile.getBounds();
1357: if (!invalidRegion
1358: .intersects(bounds)) {
1359: newCache
1360: .add(
1361: newRendering,
1362: newRendering
1363: .XToTileX(bounds.x),
1364: newRendering
1365: .YToTileY(bounds.y),
1366: tile,
1367: tileCacheMetric);
1368: }
1369: }
1370: }
1371: }
1372: }
1373: }
1374: }
1375:
1376: // Re-render the node. This will only occur if theImage
1377: // has been set to null above.
1378: createRendering();
1379:
1380: // Fire an event if the flag was set.
1381: if (fireEvent) {
1382: // Clear the synthetic and cached properties and reset the
1383: // property source.
1384: resetProperties(true);
1385:
1386: // Create the event object.
1387: RenderingChangeEvent rcEvent = new RenderingChangeEvent(
1388: this , theOldImage, theImage, invalidRegion);
1389:
1390: // Fire to all registered listeners.
1391: eventManager.firePropertyChange(rcEvent);
1392:
1393: // Fire an event to all PropertyChangeListener sinks.
1394: Vector sinks = getSinks();
1395: if (sinks != null) {
1396: int numSinks = sinks.size();
1397: for (int i = 0; i < numSinks; i++) {
1398: Object sink = sinks.get(i);
1399: if (sink instanceof PropertyChangeListener) {
1400: ((PropertyChangeListener) sink)
1401: .propertyChange(rcEvent);
1402: }
1403: }
1404: }
1405: }
1406: }
1407: }
1408:
1409: /* ----- Node source methods: interact with ParameterBlock sources ----- */
1410:
1411: /**
1412: * Adds a source to the <code>ParameterBlock</code> of this node.
1413: * This is a convenience method that invokes
1414: * <code>setParameterBlock()</code> and so adheres to the same event
1415: * firing behavior.
1416: *
1417: * @param source The source to be added to the
1418: * <code>ParameterBlock</code>
1419: * @deprecated as of JAI 1.1 Use <code>addSource(Object)</code>.
1420: */
1421: public synchronized void addNodeSource(Object source) {
1422: addSource(source);
1423: }
1424:
1425: /**
1426: * Sets the specified source stored in the <code>ParameterBlock</code>
1427: * of this node to a new source object.
1428: * This is a convenience method that invokes
1429: * <code>setParameterBlock()</code> and so adheres to the same event
1430: * firing behavior.
1431: *
1432: * @param source The Source to be set.
1433: * @param index The Index at which it is to be set.
1434: *
1435: * @throws IllegalArgumentException if
1436: * <code>source</code> is <code>null</code>.
1437: * @throws ArrayIndexOutOfBoundsException if
1438: * <code>index</code> is invalid.
1439: * @deprecated as of JAI 1.1 Use <code>setSource(Object,int)</code>.
1440: */
1441: public synchronized void setNodeSource(Object source, int index) {
1442: setSource(source, index);
1443: }
1444:
1445: /**
1446: * Returns the specified source stored in the
1447: * <code>ParameterBlock</code> of this node.
1448: * If there is no source corresponding to the specified index, an
1449: * <code>ArrayIndexOutOfBoundsException</code> will be thrown.
1450: *
1451: * @param index The index of the source.
1452: * @deprecated as of JAI 1.1 Use <code>getSourceObject(int)</code>.
1453: */
1454: public synchronized Object getNodeSource(int index) {
1455: return nodeSupport.getParameterBlock().getSource(index);
1456: }
1457:
1458: /* ----- Parameter methods: interact with ParameterBlock params ----- */
1459:
1460: /**
1461: * Returns the number of parameters stored in the
1462: * <code>ParameterBlock</code> of this node.
1463: */
1464: public synchronized int getNumParameters() {
1465: return nodeSupport.getParameterBlock().getNumParameters();
1466: }
1467:
1468: /**
1469: * Returns a clone of the <code>Vector</code> of parameters stored in the
1470: * <code>ParameterBlock</code> of this node.
1471: */
1472: public synchronized Vector getParameters() {
1473: // In the Sun JDK ParameterBlock the parameter Vector is never null.
1474: Vector params = nodeSupport.getParameterBlock().getParameters();
1475: return params == null ? null : (Vector) params.clone();
1476: }
1477:
1478: /**
1479: * Returns the specified parameter stored in the
1480: * <code>ParameterBlock</code> of this node as a <code>byte</code>.
1481: * An <code>ArrayIndexOutOfBoundsException</code> may occur if an
1482: * invalid index is supplied
1483: *
1484: * @param index The index of the parameter.
1485: *
1486: * @throws ArrayIndexOutOfBoundsException if
1487: * <code>index</code> is invalid.
1488: */
1489: public synchronized byte getByteParameter(int index) {
1490: return nodeSupport.getParameterBlock().getByteParameter(index);
1491: }
1492:
1493: /**
1494: * Returns the specified parameter stored in the
1495: * <code>ParameterBlock</code> of this node as a <code>char</code>.
1496: * An <code>ArrayIndexOutOfBoundsException</code> may occur if an
1497: * invalid index is supplied
1498: *
1499: * @param index The index of the parameter.
1500: *
1501: * @throws ArrayIndexOutOfBoundsException if
1502: * <code>index</code> is invalid.
1503: */
1504: public synchronized char getCharParameter(int index) {
1505: return nodeSupport.getParameterBlock().getCharParameter(index);
1506: }
1507:
1508: /**
1509: * Returns the specified parameter stored in the
1510: * <code>ParameterBlock</code> of this node as a <code>short</code>.
1511: * An <code>ArrayIndexOutOfBoundsException</code> may occur if an
1512: * invalid index is supplied
1513: *
1514: * @param index The index of the parameter.
1515: *
1516: * @throws ArrayIndexOutOfBoundsException if
1517: * <code>index</code> is invalid.
1518: */
1519: public synchronized short getShortParameter(int index) {
1520: return nodeSupport.getParameterBlock().getShortParameter(index);
1521: }
1522:
1523: /**
1524: * Returns the specified parameter stored in the
1525: * <code>ParameterBlock</code> of this node as an <code>int</code>.
1526: * An <code>ArrayIndexOutOfBoundsException</code> may occur if an
1527: * invalid index is supplied
1528: *
1529: * @param index The index of the parameter.
1530: *
1531: * @throws ArrayIndexOutOfBoundsException if
1532: * <code>index</code> is invalid.
1533: */
1534: public synchronized int getIntParameter(int index) {
1535: return nodeSupport.getParameterBlock().getIntParameter(index);
1536:
1537: }
1538:
1539: /**
1540: * Returns the specified parameter stored in the
1541: * <code>ParameterBlock</code> of this node as a <code>long</code>.
1542: * An <code>ArrayIndexOutOfBoundsException</code> may occur if an
1543: * invalid index is supplied
1544: *
1545: * @param index The index of the parameter.
1546: *
1547: * @throws ArrayIndexOutOfBoundsException if
1548: * <code>index</code> is invalid.
1549: */
1550: public synchronized long getLongParameter(int index) {
1551: return nodeSupport.getParameterBlock().getLongParameter(index);
1552: }
1553:
1554: /**
1555: * Returns the specified parameter stored in the
1556: * <code>ParameterBlock</code> of this node as a <code>float</code>.
1557: * An <code>ArrayIndexOutOfBoundsException</code> may occur if an
1558: * invalid index is supplied
1559: *
1560: * @param index The index of the parameter.
1561: *
1562: * @throws ArrayIndexOutOfBoundsException if
1563: * <code>index</code> is invalid.
1564: */
1565: public synchronized float getFloatParameter(int index) {
1566: return nodeSupport.getParameterBlock().getFloatParameter(index);
1567: }
1568:
1569: /**
1570: * Returns the specified parameter stored in the
1571: * <code>ParameterBlock</code> of this node as a <code>double</code>.
1572: * An <code>ArrayIndexOutOfBoundsException</code> may occur if an
1573: * invalid index is supplied
1574: *
1575: * @param index The index of the parameter.
1576: *
1577: * @throws ArrayIndexOutOfBoundsException if
1578: * <code>index</code> is invalid.
1579: */
1580: public synchronized double getDoubleParameter(int index) {
1581: return nodeSupport.getParameterBlock()
1582: .getDoubleParameter(index);
1583: }
1584:
1585: /**
1586: * Returns the specified parameter stored in the
1587: * <code>ParameterBlock</code> of this node as an <code>Object</code>.
1588: * An <code>ArrayIndexOutOfBoundsException</code> may occur if an
1589: * invalid index is supplied
1590: *
1591: * @param index The index of the parameter.
1592: *
1593: * @throws ArrayIndexOutOfBoundsException if
1594: * <code>index</code> is invalid.
1595: */
1596: public synchronized Object getObjectParameter(int index) {
1597: return nodeSupport.getParameterBlock()
1598: .getObjectParameter(index);
1599: }
1600:
1601: /**
1602: * Sets all the parameters of this node.
1603: * This is a convenience method that invokes
1604: * <code>setParameterBlock()</code> and so adheres to the same event
1605: * firing behavior.
1606: *
1607: * <p> The <code>Vector</code> may include
1608: * <code>DeferredData</code> parameters. These will not be evaluated
1609: * until their values are actually required, i.e., when the node is
1610: * rendered.
1611: *
1612: * @since JAI 1.1
1613: */
1614: public synchronized void setParameters(Vector parameters) {
1615: ParameterBlock pb = (ParameterBlock) nodeSupport
1616: .getParameterBlock().clone();
1617: pb.setParameters(parameters);
1618: nodeSupport.setParameterBlock(pb);
1619: }
1620:
1621: /**
1622: * Sets one of the node's parameters to a <code>byte</code>.
1623: * If the <code>index</code> lies beyond the current source list,
1624: * the list is extended with nulls as needed.
1625: * This is a convenience method that invokes
1626: * <code>setParameter(Object,int)</code> and so adheres to the same event
1627: * firing behavior.
1628: *
1629: * @param param The parameter, as a <code>byte</code>.
1630: * @param index The index of the parameter.
1631: */
1632: public synchronized void setParameter(byte param, int index) {
1633: setParameter(new Byte(param), index);
1634: }
1635:
1636: /**
1637: * Sets one of the node's parameters to a <code>char</code>.
1638: * If the <code>index</code> lies beyond the current source list,
1639: * the list is extended with nulls as needed.
1640: * This is a convenience method that invokes
1641: * <code>setParameter(Object,int)</code> and so adheres to the same event
1642: * firing behavior.
1643: *
1644: * @param param The parameter, as a <code>char</code>.
1645: * @param index The index of the parameter.
1646: */
1647: public synchronized void setParameter(char param, int index) {
1648: setParameter(new Character(param), index);
1649: }
1650:
1651: /**
1652: * Sets one of the node's parameters to a <code>short</code>.
1653: * If the <code>index</code> lies beyond the current source list,
1654: * the list is extended with nulls as needed.
1655: *
1656: * @param param The parameter, as a <code>short</code>.
1657: * @param index The index of the parameter.
1658: */
1659: public synchronized void setParameter(short param, int index) {
1660: setParameter(new Short(param), index);
1661: }
1662:
1663: /**
1664: * Sets one of the node's parameters to an <code>in</code>t.
1665: * If the <code>index</code> lies beyond the current source list,
1666: * the list is extended with nulls as needed.
1667: *
1668: * @param param The parameter, as an <code>int</code>.
1669: * @param index The index of the parameter.
1670: */
1671: public synchronized void setParameter(int param, int index) {
1672: setParameter(new Integer(param), index);
1673: }
1674:
1675: /**
1676: * Sets one of the node's parameters to a <code>long</code>.
1677: * If the <code>index</code> lies beyond the current source list,
1678: * the list is extended with nulls as needed.
1679: *
1680: * @param param The parameter, as a <code>long</code>.
1681: * @param index The index of the parameter.
1682: */
1683: public synchronized void setParameter(long param, int index) {
1684: setParameter(new Long(param), index);
1685: }
1686:
1687: /**
1688: * Sets one of the node's parameters to a <code>float</code>.
1689: * If the <code>index</code> lies beyond the current source list,
1690: * the list is extended with nulls as needed.
1691: *
1692: * @param param The parameter, as a <code>float</code>.
1693: * @param index The index of the parameter.
1694: */
1695: public synchronized void setParameter(float param, int index) {
1696: setParameter(new Float(param), index);
1697: }
1698:
1699: /**
1700: * Sets one of the node's parameters to a <code>double</code>.
1701: * If the <code>index</code> lies beyond the current source list,
1702: * the list is extended with nulls as needed.
1703: *
1704: * @param param The parameter, as a <code>double</code>.
1705: * @param index The index of the parameter.
1706: */
1707: public synchronized void setParameter(double param, int index) {
1708: setParameter(new Double(param), index);
1709: }
1710:
1711: /**
1712: * Sets one of the node's parameters to an <code>Object</code>.
1713: * If the <code>index</code> lies beyond the current source list,
1714: * the list is extended with nulls as needed.
1715: * This is a convenience method that invokes
1716: * <code>setParameterBlock()</code> and so adheres to the same event
1717: * firing behavior.
1718: *
1719: * <p> The <code>Object</code> may be a
1720: * <code>DeferredData</code> instance. It will not be evaluated
1721: * until its value is actually required, i.e., when the node is
1722: * rendered.
1723: *
1724: * @param param The parameter, as an <code>Object</code>.
1725: * @param index The index of the parameter.
1726: */
1727: public synchronized void setParameter(Object param, int index) {
1728: ParameterBlock pb = (ParameterBlock) nodeSupport
1729: .getParameterBlock().clone();
1730: pb.set(param, index);
1731: nodeSupport.setParameterBlock(pb);
1732: }
1733:
1734: /* ----- RenderingHints methods. ----- */
1735:
1736: /**
1737: * Sets a hint in the <code>RenderingHints</code> of this node. This
1738: * is a convenience method which calls <code>setRenderingHints()</code>
1739: * and so adheres to the same event firing behavior.
1740: *
1741: * @throws IllegalArgumentException
1742: * if the key or value is <code>null</code>.
1743: * @throws IllegalArgumentException
1744: * value is not appropriate for the specified key.
1745: *
1746: * @since JAI 1.1
1747: */
1748: public synchronized void setRenderingHint(RenderingHints.Key key,
1749: Object value) {
1750:
1751: if (key == null || value == null) {
1752: throw new IllegalArgumentException(JaiI18N
1753: .getString("Generic0"));
1754: }
1755:
1756: RenderingHints rh = nodeSupport.getRenderingHints();
1757: if (rh == null) {
1758: nodeSupport
1759: .setRenderingHints(new RenderingHints(key, value));
1760: } else {
1761: rh.put(key, value);
1762: nodeSupport.setRenderingHints(rh);
1763: }
1764: }
1765:
1766: /**
1767: * Gets a hint from the <code>RenderingHints</code> of this node.
1768: *
1769: * @return the value associated with the specified key or
1770: * <code>null</code> if the key is not mapped to any value.
1771: *
1772: * @since JAI 1.1
1773: */
1774: public synchronized Object getRenderingHint(RenderingHints.Key key) {
1775: RenderingHints rh = nodeSupport.getRenderingHints();
1776: return rh == null ? null : rh.get(key);
1777: }
1778:
1779: /* ----- Property-related methods. ----- */
1780:
1781: /** Creates a <code>PropertySource</code> if none exists. */
1782: private synchronized void createPropertySource() {
1783: if (thePropertySource == null) {
1784: // Create a <code>PropertySource</code> wrapper of the rendering.
1785: PropertySource defaultPS = new PropertySource() {
1786: /**
1787: * Retrieve the names from an instance of the node.
1788: */
1789: public String[] getPropertyNames() {
1790: return getRendering().getPropertyNames();
1791: }
1792:
1793: public String[] getPropertyNames(String prefix) {
1794: return PropertyUtil.getPropertyNames(
1795: getPropertyNames(), prefix);
1796: }
1797:
1798: public Class getPropertyClass(String name) {
1799: return null;
1800: }
1801:
1802: /**
1803: * Retrieve the actual property values from a rendering
1804: * of the node.
1805: */
1806: public Object getProperty(String name) {
1807: return getRendering().getProperty(name);
1808: }
1809: };
1810:
1811: // Create a <code>PropertySource</code> encapsulating the
1812: // property environment of the node.
1813: thePropertySource = nodeSupport.getPropertySource(this ,
1814: defaultPS);
1815:
1816: // Add the <code>PropertySource</code> to the helper object.
1817: properties.addProperties(thePropertySource);
1818: }
1819: }
1820:
1821: /**
1822: * Resets the <code>PropertySource</code>. If the parameter is
1823: * <code>true</code> then the property environment is completely
1824: * reset; if <code>false</code> then only cached properties are
1825: * cleared, i.e., those which were derived from the property
1826: * environment and are now stored in the local cache.
1827: *
1828: * @since JAI 1.1
1829: */
1830: protected synchronized void resetProperties(
1831: boolean resetPropertySource) {
1832: properties.clearCachedProperties();
1833: if (resetPropertySource && thePropertySource != null) {
1834: synthProperties = null;
1835: properties.removePropertySource(thePropertySource);
1836: thePropertySource = null;
1837: }
1838: }
1839:
1840: /**
1841: * Returns the names of properties available from this node.
1842: * These properties are a combination of those derived
1843: * from prior nodes in the imaging chain, those set locally,
1844: * and a number of locally derived, immutable properties
1845: * based on the rendering associated with this node --
1846: * height, width, and so forth.
1847: *
1848: * @return An array of <code>String</code>s containing valid
1849: * property names.
1850: */
1851: public synchronized String[] getPropertyNames() {
1852: createPropertySource();
1853:
1854: // Initialize names to synthetic property names.
1855: Vector names = new Vector(synthProps);
1856:
1857: // Create a dummy key for later use.
1858: CaselessStringKey key = new CaselessStringKey("");
1859:
1860: // Get property names managed by WritablePropertySourceImpl.
1861: // This includes those of thePropertySource.
1862: String[] localNames = properties.getPropertyNames();
1863: if (localNames != null) {
1864: int length = localNames.length;
1865: for (int i = 0; i < length; i++) {
1866: key.setName(localNames[i]);
1867:
1868: // Check for duplicates being inserted
1869: if (!names.contains(key)) {
1870: names.add(key.clone());
1871: }
1872: }
1873: }
1874:
1875: // Return an array.
1876: String[] propertyNames = null;
1877: int numNames = names.size();
1878: if (numNames > 0) {
1879: propertyNames = new String[numNames];
1880: for (int i = 0; i < numNames; i++) {
1881: propertyNames[i] = ((CaselessStringKey) names.get(i))
1882: .getName();
1883: }
1884: }
1885:
1886: return propertyNames;
1887: }
1888:
1889: /**
1890: * Returns the class expected to be returned by a request for
1891: * the property with the specified name. If this information
1892: * is unavailable, <code>null</code> will be returned.
1893: *
1894: * @exception IllegalArgumentException if <code>name</code>
1895: * is <code>null</code>.
1896: *
1897: * @return The <code>Class</code> expected to be return by a
1898: * request for the value of this property or <code>null</code>.
1899: *
1900: * @since JAI 1.1
1901: */
1902: public Class getPropertyClass(String name) {
1903: createPropertySource();
1904: return properties.getPropertyClass(name);
1905: }
1906:
1907: /** Initialize the synthProperties Hashtable if needed. */
1908: private synchronized void createSynthProperties() {
1909: if (synthProperties == null) {
1910: synthProperties = new Hashtable();
1911: synthProperties.put(new CaselessStringKey("image_width"),
1912: new Integer(theImage.getWidth()));
1913: synthProperties.put(new CaselessStringKey("image_height"),
1914: new Integer(theImage.getHeight()));
1915: synthProperties.put(new CaselessStringKey(
1916: "image_min_x_coord"), new Integer(theImage
1917: .getMinX()));
1918: synthProperties.put(new CaselessStringKey(
1919: "image_min_y_coord"), new Integer(theImage
1920: .getMinY()));
1921:
1922: if (theImage instanceof OpImage) {
1923: synthProperties.put(new CaselessStringKey(
1924: "tile_cache_key"), theImage);
1925: Object tileCache = ((OpImage) theImage).getTileCache();
1926: synthProperties
1927: .put(
1928: new CaselessStringKey("tile_cache"),
1929: tileCache == null ? java.awt.Image.UndefinedProperty
1930: : tileCache);
1931: } else if (theImage instanceof PlanarImageServerProxy) {
1932: synthProperties.put(new CaselessStringKey(
1933: "tile_cache_key"), theImage);
1934: Object tileCache = ((PlanarImageServerProxy) theImage)
1935: .getTileCache();
1936: synthProperties
1937: .put(
1938: new CaselessStringKey("tile_cache"),
1939: tileCache == null ? java.awt.Image.UndefinedProperty
1940: : tileCache);
1941: } else {
1942: Object tileCacheKey = theImage
1943: .getProperty("tile_cache_key");
1944: synthProperties
1945: .put(
1946: new CaselessStringKey("tile_cache_key"),
1947: tileCacheKey == null ? java.awt.Image.UndefinedProperty
1948: : tileCacheKey);
1949: Object tileCache = theImage.getProperty("tile_cache");
1950: synthProperties
1951: .put(
1952: new CaselessStringKey("tile_cache"),
1953: tileCache == null ? java.awt.Image.UndefinedProperty
1954: : tileCache);
1955: }
1956: }
1957: }
1958:
1959: /**
1960: * Returns the property associated with the specified property name,
1961: * or <code>java.awt.Image.UndefinedProperty</code> if the specified
1962: * property is not set on the image. If <code>name</code> equals the
1963: * name of any synthetic property, i.e., <code>image_width</code>,
1964: * <code>image_height</code>, <code>image_min_x_coord</code>, or
1965: * <code>image_min_y_coord</code>, then the node will be rendered.
1966: *
1967: * @param name A <code>String</code> naming the property.
1968: *
1969: * @throws IllegalArgumentException if
1970: * <code>name</code> is <code>null</code>.
1971: */
1972: public synchronized Object getProperty(String name) {
1973:
1974: if (name == null)
1975: throw new IllegalArgumentException(JaiI18N
1976: .getString("Generic0"));
1977:
1978: createPropertySource();
1979: CaselessStringKey key = new CaselessStringKey(name);
1980:
1981: // Attempt to retrieve from synthetic properties.
1982: // If present, return the value directly.
1983: if (synthProps.contains(key)) {
1984: createRendering();
1985:
1986: // Create synthProperties Hashtable "just in time."
1987: createSynthProperties();
1988: return synthProperties.get(key);
1989: }
1990:
1991: // Attempt to retrieve from local properties.
1992: Object value = properties.getProperty(name);
1993:
1994: // If still undefined, query the property environment.
1995: if (value == java.awt.Image.UndefinedProperty) {
1996: value = thePropertySource.getProperty(name);
1997: }
1998:
1999: // Special case handling of ROI property: clip to destination bounds.
2000: // XXX Do we really want to do this (clip ROI to dest bounds)?
2001: if (value != java.awt.Image.UndefinedProperty
2002: && name.equalsIgnoreCase("roi") && value instanceof ROI) {
2003: ROI roi = (ROI) value;
2004: Rectangle imageBounds = getBounds();
2005: if (!imageBounds.contains(roi.getBounds())) {
2006: value = roi.intersect(new ROIShape(imageBounds));
2007: }
2008: }
2009:
2010: return value;
2011: }
2012:
2013: /**
2014: * Sets a local property on a node. The synthetic properties
2015: * (containing image width, height, and location) may not be set.
2016: * Local property settings override properties derived from prior
2017: * nodes in the imaging chain.
2018: *
2019: * <p> If the node is serialized then serializable properties will
2020: * also be serialized but non-serializable properties will be lost.
2021: *
2022: * @param name A <code>String</code> representing the property name.
2023: * @param value The property's value, as an <code>Object</code>.
2024: *
2025: * @throws IllegalArgumentException if
2026: * <code>name</code> is <code>null</code>.
2027: * @throws IllegalArgumentException if
2028: * <code>value</code> is <code>null</code>.
2029: * @throws RuntimeException if <code>name</code>
2030: * conflicts with Synthetic property.
2031: */
2032: public synchronized void setProperty(String name, Object value) {
2033: if (name == null)
2034: throw new IllegalArgumentException(JaiI18N
2035: .getString("Generic0"));
2036: if (value == null)
2037: throw new IllegalArgumentException(JaiI18N
2038: .getString("Generic0"));
2039:
2040: // Check whether property conflicts with synthetic properties.
2041: if (synthProps.contains(new CaselessStringKey(name))) {
2042: throw new RuntimeException(JaiI18N.getString("RenderedOp4"));
2043: }
2044:
2045: createPropertySource();
2046: super .setProperty(name, value);
2047: }
2048:
2049: /**
2050: * Removes the named property from the local property
2051: * set of the <code>RenderedOp</code> as well as from its property
2052: * environment. The synthetic properties
2053: * (containing image width, height, and position) may not be removed.
2054: *
2055: * @exception IllegalArgumentException if <code>name</code>
2056: * is <code>null</code>.
2057: * @throws RuntimeException if <code>name</code>
2058: * conflicts with Synthetic property.
2059: *
2060: * @since JAI 1.1
2061: */
2062: public void removeProperty(String name) {
2063: if (name == null)
2064: throw new IllegalArgumentException(JaiI18N
2065: .getString("Generic0"));
2066:
2067: // Check whether property conflicts with synthetic properties.
2068: if (synthProps.contains(new CaselessStringKey(name))) {
2069: throw new RuntimeException(JaiI18N.getString("RenderedOp4"));
2070: }
2071:
2072: createPropertySource();
2073: properties.removeProperty(name);
2074: }
2075:
2076: /**
2077: * Returns the property associated with the specified property name,
2078: * or <code>java.awt.Image.UndefinedProperty</code> if the specified
2079: * property is not set on the image. This method is dynamic in the
2080: * sense that subsequent invocations of this method on the same object
2081: * may return different values as a function of changes in the property
2082: * environment of the node, e.g., a change in which
2083: * <code>PropertyGenerator</code>s are registered or in the values
2084: * associated with properties of node sources. The case of the property
2085: * name passed to this method is ignored.
2086: *
2087: * @param name A <code>String</code> naming the property.
2088: *
2089: * @throws IllegalArgumentException if
2090: * <code>name</code> is <code>null</code>.
2091: *
2092: * @since JAI 1.1
2093: */
2094: public synchronized Object getDynamicProperty(String name) {
2095: createPropertySource();
2096: return thePropertySource.getProperty(name);
2097: }
2098:
2099: /**
2100: * Adds a <code>PropertyGenerator</code> to the node. The property values
2101: * emitted by this property generator override any previous
2102: * definitions.
2103: *
2104: * @param pg A <code>PropertyGenerator</code> to be added to this node's
2105: * property environment.
2106: *
2107: * @throws IllegalArgumentException if
2108: * <code>pg</code> is <code>null</code>.
2109: */
2110: public synchronized void addPropertyGenerator(PropertyGenerator pg) {
2111: nodeSupport.addPropertyGenerator(pg);
2112: }
2113:
2114: /**
2115: * Forces a property to be copied from the specified source node.
2116: * By default, a property is copied from the first source node
2117: * that emits it. The result of specifying an invalid source is
2118: * undefined.
2119: *
2120: * @param propertyName the name of the property to be copied.
2121: * @param sourceIndex the index of the from which to copy the property.
2122: * @throws IllegalArgumentException if <code>propertyName</code> is
2123: * <code>null</code>.
2124: *
2125: * @since JAI 1.1
2126: */
2127: public synchronized void copyPropertyFromSource(
2128: String propertyName, int sourceIndex) {
2129: nodeSupport.copyPropertyFromSource(propertyName, sourceIndex);
2130: }
2131:
2132: /**
2133: * Removes a named property from the property environment of this
2134: * node. Unless the property is stored locally either due
2135: * to having been set explicitly via <code>setProperty()</code>
2136: * or to having been cached for property
2137: * synchronization purposes, subsequent calls to
2138: * <code>getProperty(name)</code> will return
2139: * <code>java.awt.Image.UndefinedProperty</code>, and <code>name</code>
2140: * will not appear on the list of properties emitted by
2141: * <code>getPropertyNames()</code>. To delete the property from the
2142: * local property set of the node, <code>removeProperty()</code> should
2143: * be used.
2144: *
2145: * @param name A <code>String</code> naming the property to be suppressed.
2146: *
2147: * @throws IllegalArgumentException if
2148: * <code>name</code> is <code>null</code>.
2149: * @throws IllegalArgumentException if <code>name</code>
2150: * conflicts with Synthetic property.
2151: */
2152: public synchronized void suppressProperty(String name) {
2153: if (name == null)
2154: throw new IllegalArgumentException(JaiI18N
2155: .getString("Generic0"));
2156:
2157: if (synthProps.contains(new CaselessStringKey(name))) {
2158: throw new IllegalArgumentException(JaiI18N
2159: .getString("RenderedOp5"));
2160: }
2161:
2162: nodeSupport.suppressProperty(name);
2163: }
2164:
2165: /*****************************************************************
2166: * The following methods override public or protected methods in *
2167: * PlanarImage thus causing this node to be rendered. *
2168: ****************************************************************/
2169:
2170: /**
2171: * Renders the node if it has not already been rendered, and returns
2172: * the X coordinate of the leftmost column of the rendered image.
2173: */
2174: public int getMinX() {
2175: createRendering();
2176: return theImage.getMinX();
2177: }
2178:
2179: /**
2180: * Renders the node if it has not already been rendered, and returns
2181: * the X coordinate of the uppermost row of the rendered image.
2182: */
2183: public int getMinY() {
2184: createRendering();
2185: return theImage.getMinY();
2186: }
2187:
2188: /**
2189: * Renders the node if it has not already been rendered,
2190: * and returns the width of the rendered image.
2191: */
2192: public int getWidth() {
2193: createRendering();
2194: return theImage.getWidth();
2195: }
2196:
2197: /**
2198: * Renders the node if it has not already been rendered,
2199: * and returns the height of the rendered image.
2200: */
2201: public int getHeight() {
2202: createRendering();
2203: return theImage.getHeight();
2204: }
2205:
2206: /**
2207: * Renders the node if it has not already been rendered,
2208: * and returns the tile width of the rendered image.
2209: */
2210: public int getTileWidth() {
2211: createRendering();
2212: return theImage.getTileWidth();
2213: }
2214:
2215: /**
2216: * Renders the node if it has not already been rendered,
2217: * and returns the tile height of the rendered image.
2218: */
2219: public int getTileHeight() {
2220: createRendering();
2221: return theImage.getTileHeight();
2222: }
2223:
2224: /**
2225: * Renders the node if it has not already been rendered,
2226: * and returns the tile grid X offset of the rendered image.
2227: */
2228: public int getTileGridXOffset() {
2229: createRendering();
2230: return theImage.getTileGridXOffset();
2231: }
2232:
2233: /**
2234: * Renders the node if it has not already been rendered,
2235: * and returns the tile grid Y offset of the rendered image.
2236: */
2237: public int getTileGridYOffset() {
2238: createRendering();
2239: return theImage.getTileGridYOffset();
2240: }
2241:
2242: /**
2243: * Renders the node if it has not already been rendered, and
2244: * returns the <code>SampleModel</code> of the rendered image.
2245: */
2246: public SampleModel getSampleModel() {
2247: createRendering();
2248: return theImage.getSampleModel();
2249: }
2250:
2251: /**
2252: * Renders the node if it has not already been rendered, and
2253: * returns the <code>ColorModel</code> of the rendered image.
2254: */
2255: public ColorModel getColorModel() {
2256: createRendering();
2257: return theImage.getColorModel();
2258: }
2259:
2260: /**
2261: * Renders the node if it has not already been rendered,
2262: * and returns the specified tile of the rendered image.
2263: *
2264: * @param tileX The X index of the tile.
2265: * @param tileY The Y index of the tile.
2266: *
2267: * @return The requested tile as a <code>Raster</code>.
2268: */
2269: public Raster getTile(int tileX, int tileY) {
2270: createRendering();
2271: return theImage.getTile(tileX, tileY);
2272: }
2273:
2274: /**
2275: * Renders the node if it has not already been rendered, and
2276: * returns the entire rendered image as a <code>Raster</code>.
2277: */
2278: public Raster getData() {
2279: createRendering();
2280: return theImage.getData();
2281: }
2282:
2283: /**
2284: * Renders the node if it has not already been rendered, and
2285: * returns a specified rectangular region of the rendered
2286: * image as a <code>Raster</code>.
2287: */
2288: public Raster getData(Rectangle rect) {
2289: createRendering();
2290: return theImage.getData(rect);
2291: }
2292:
2293: /**
2294: * Renders the node if it has not already been rendered, and
2295: * copies and returns the entire rendered image into a single raster.
2296: */
2297: public WritableRaster copyData() {
2298: createRendering();
2299: return theImage.copyData();
2300: }
2301:
2302: /**
2303: * Renders the node if it has not already been rendered, and
2304: * copies a specified rectangle of the rendered image into
2305: * the given <code>WritableRaster</code>.
2306: *
2307: * @param raster A <code>WritableRaster</code> to be filled with image data.
2308: * @return A reference to the supplied <code>WritableRaster</code>.
2309: *
2310: */
2311: public WritableRaster copyData(WritableRaster raster) {
2312: createRendering();
2313: return theImage.copyData(raster);
2314: }
2315:
2316: /**
2317: * Renders the node if it has not already been rendered, and
2318: * returns the tiles indicated by the <code>tileIndices</code>
2319: * of the rendered image as an array of <code>Raster</code>s.
2320: *
2321: * @param tileIndices An array of Points representing TileIndices.
2322: * @return An array of Raster containing the tiles corresponding
2323: * to the given TileIndices.
2324: *
2325: * @throws IllegalArgumentException If <code>tileIndices</code> is
2326: * <code>null</code>.
2327: */
2328: public Raster[] getTiles(Point tileIndices[]) {
2329: createRendering();
2330: return theImage.getTiles(tileIndices);
2331: }
2332:
2333: /**
2334: * Queues a list of tiles for computation. Registered listeners
2335: * will be notified after each tile has been computed. The event
2336: * source parameter passed to such listeners will be the node itself;
2337: * the image parameter will be the rendering of the node. The
2338: * <code>RenderedOp</code> itself in fact should monitor any
2339: * <code>TileComputationListener</code> events of its rendering and
2340: * forward any such events to any of its registered listeners.
2341: *
2342: * @param tileIndices A list of tile indices indicating which tiles
2343: * to schedule for computation.
2344: * @throws IllegalArgumentException If <code>tileIndices</code> is
2345: * <code>null</code>.
2346: *
2347: * @since JAI 1.1
2348: */
2349: public TileRequest queueTiles(Point[] tileIndices) {
2350: createRendering();
2351: return theImage.queueTiles(tileIndices);
2352: }
2353:
2354: /**
2355: * Issue an advisory cancellation request to nullify processing of
2356: * the indicated tiles.
2357: *
2358: * @param request The request for which tiles are to be cancelled.
2359: * @param tileIndices The tiles to be cancelled; may be <code>null</code>.
2360: * Any tiles not actually in the <code>TileRequest</code> will be
2361: * ignored.
2362: * @throws IllegalArgumentException If <code>request</code> is
2363: * <code>null</code>.
2364: *
2365: * @since JAI 1.1
2366: */
2367: public void cancelTiles(TileRequest request, Point[] tileIndices) {
2368: if (request == null) {
2369: throw new IllegalArgumentException(JaiI18N
2370: .getString("Generic4"));
2371: }
2372: createRendering();
2373: theImage.cancelTiles(request, tileIndices);
2374: }
2375:
2376: /**
2377: * Renders the node if it has not already been rendered.
2378: * Hints that the given tiles of the rendered image might be
2379: * needed in the near future.
2380: *
2381: * @param tileIndices A list of tileIndices indicating which tiles
2382: * to prefetch.
2383: *
2384: * @throws IllegalArgumentException If <code>tileIndices</code> is
2385: * <code>null</code>.
2386: */
2387: public void prefetchTiles(Point tileIndices[]) {
2388: createRendering();
2389: theImage.prefetchTiles(tileIndices);
2390: }
2391:
2392: // ----- Methods dealing with source Vector: forward call to ParamBlock.
2393:
2394: /**
2395: * Adds a <code>PlanarImage</code> source to the
2396: * <code>ParameterBlock</code> of this node.
2397: * This is a convenience method that invokes
2398: * <code>setParameterBlock()</code> and so adheres to the same event
2399: * firing behavior.
2400: *
2401: * <p><i> Note that the behavior of this method has changed as of
2402: * Java Advanced Imaging 1.1. To obtain the previous behavior use
2403: * <code>getRendering().addSource()</code>. The description of the
2404: * previous behavior is as follows:
2405: * <blockquote>
2406: *
2407: * Renders the node if it has not already been rendered, and
2408: * adds a <code>PlanarImage</code> source to the list of sources
2409: * of the rendered image.
2410: *
2411: * </blockquote>
2412: * </i>
2413: *
2414: * @param source The source to be added to the
2415: * <code>ParameterBlock</code>
2416: *
2417: * @throws IllegalArgumentException if <code>source</code> is
2418: * <code>null</code>.
2419: *
2420: * @deprecated as of JAI 1.1. Use <code>addSource(Object)</code>.
2421: */
2422: public synchronized void addSource(PlanarImage source) {
2423: Object sourceObject = source;
2424: addSource(sourceObject);
2425: }
2426:
2427: /**
2428: * Sets the specified source stored in the <code>ParameterBlock</code>
2429: * of this node to a new <code>PlanarImage</code> source.
2430: * This is a convenience method that invokes
2431: * <code>setParameterBlock()</code> and so adheres to the same event
2432: * firing behavior.
2433: *
2434: * <p><i> Note that the behavior of this method has changed as of
2435: * Java Advanced Imaging 1.1. To obtain the previous behavior use
2436: * <code>getRendering().setSource()</code>. The description of the
2437: * previous behavior is as follows:
2438: * <blockquote>
2439: *
2440: * Renders the node if it has not already been rendered, and
2441: * sets the specified source of the rendered image to the
2442: * supplied <code>PlanarImage</code>.
2443: * An <code>ArrayIndexOutOfBoundsException</code> may be thrown if
2444: * an invalid <code>index</code> is supplied.
2445: *
2446: * </blockquote>
2447: * </i>
2448: *
2449: * @param source The source, as a <code>PlanarImage</code>.
2450: * @param index The index of the source.
2451: * @throws IllegalArgumentException if <code>source</code> is
2452: * <code>null</code>.
2453: * @throws ArrayIndexOutOfBoundsException if
2454: * <code>index</code> is invalid.
2455: *
2456: * @deprecated as of JAI 1.1. Use <code>setSource(Object, int)</code>.
2457: */
2458: public synchronized void setSource(PlanarImage source, int index) {
2459: Object sourceObject = source;
2460: setSource(sourceObject, index);
2461: }
2462:
2463: /**
2464: * Returns the specified <code>PlanarImage</code> source stored in the
2465: * <code>ParameterBlock</code> of this node.
2466: * If there is no source corresponding to the specified index, an
2467: * <code>ArrayIndexOutOfBoundsException</code> will be thrown.
2468: *
2469: * <p><i> Note that the behavior of this method has changed as of
2470: * Java Advanced Imaging 1.1. To obtain the previous behavior use
2471: * <code>getRendering().getSource()</code>. The description of the
2472: * previous behavior is as follows:
2473: * <blockquote>
2474: *
2475: * Renders the node if it has not already been rendered, and
2476: * returns the specified <code>PlanarImage</code> source of
2477: * the rendered image. If there is no source corresponding to
2478: * the specified index, this method will throw an
2479: * <code>ArrayIndexOutOfBoundsException</code>.
2480: * The source returned may differ from the source stored in
2481: * the <code>ParameterBlock</code> of this node.
2482: *
2483: * </blockquote>
2484: * </i>
2485: *
2486: * @param index The index of the desired source.
2487: * @return A <code>PlanarImage</code> source.
2488: * @throws ArrayIndexOutOfBoundsException if
2489: * <code>index</code> is invalid.
2490: * @throws ClassCastException if the source at the indicated index is
2491: * not a <code>PlanarImage</code>.
2492: *
2493: * @deprecated as of JAI 1.1. Use <code>getSourceObject()</code>.
2494: */
2495: public PlanarImage getSource(int index) {
2496: return (PlanarImage) nodeSupport.getParameterBlock().getSource(
2497: index);
2498: }
2499:
2500: /**
2501: * Removes the specified <code>PlanarImage</code> source from the
2502: * <code>ParameterBlock</code> of this node.
2503: *
2504: * <p><i> Note that the behavior of this method has changed as of
2505: * Java Advanced Imaging 1.1. To obtain the previous behavior use
2506: * <code>getRendering().removeSource()</code>. The description of the
2507: * previous behavior is as follows:
2508: * <blockquote>
2509: *
2510: * Renders the node if it has not already been rendered, and
2511: * removes a <code>PlanarImage</code> source from the list
2512: * of sources of the rendered image.
2513: *
2514: * </blockquote>
2515: * </i>
2516: *
2517: * @param source A <code>PlanarImage</code> to be removed.
2518: *
2519: * @throws IllegalArgumentException if
2520: * <code>source</code> is <code>null</code>.
2521: * @return <code>true</code> if the element was present, <code>false</code>
2522: * otherwise.
2523: *
2524: * @deprecated as of JAI 1.1. Use <code>removeSource(Object)</code>.
2525: */
2526: public synchronized boolean removeSource(PlanarImage source) {
2527: Object sourceObject = source;
2528: return removeSource(sourceObject);
2529: }
2530:
2531: /**
2532: * Adds a source to the <code>ParameterBlock</code> of this node.
2533: * This is a convenience method that invokes
2534: * <code>setParameterBlock()</code> and so adheres to the same event
2535: * firing behavior.
2536: *
2537: * <p> The node is added automatically as a sink if the source is a
2538: * <code>PlanarImage</code> or a <code>CollectionImage</code>.
2539: *
2540: * @param source The source to be added to the
2541: * <code>ParameterBlock</code>
2542: * @throws IllegalArgumentException if
2543: * <code>source</code> is <code>null</code>.
2544: *
2545: * @since JAI 1.1
2546: */
2547: public synchronized void addSource(Object source) {
2548: if (source == null)
2549: throw new IllegalArgumentException(JaiI18N
2550: .getString("Generic0"));
2551:
2552: ParameterBlock pb = (ParameterBlock) nodeSupport
2553: .getParameterBlock().clone();
2554: pb.addSource(source);
2555: nodeSupport.setParameterBlock(pb);
2556:
2557: if (source instanceof PlanarImage) {
2558: ((PlanarImage) source).addSink(this );
2559: } else if (source instanceof CollectionImage) {
2560: ((CollectionImage) source).addSink(this );
2561: }
2562: }
2563:
2564: /**
2565: * Sets the specified source stored in the <code>ParameterBlock</code>
2566: * of this node to a new source object.
2567: * This is a convenience method that invokes
2568: * <code>setParameterBlock()</code> and so adheres to the same event
2569: * firing behavior.
2570: *
2571: * <p> The node is added automatically as a sink if the source is a
2572: * <code>PlanarImage</code> or a <code>CollectionImage</code>. If
2573: * appropriate the node is removed as a sink of any previous source
2574: * at the same index.
2575: *
2576: * @param source The Source to be set.
2577: * @param index The Index at which it is to be set.
2578: *
2579: * @throws IllegalArgumentException if
2580: * <code>source</code> is <code>null</code>.
2581: * @throws ArrayIndexOutOfBoundsException if
2582: * <code>index</code> is invalid.
2583: *
2584: * @since JAI 1.1
2585: */
2586: public synchronized void setSource(Object source, int index) {
2587: if (source == null)
2588: throw new IllegalArgumentException(JaiI18N
2589: .getString("Generic0"));
2590:
2591: ParameterBlock pb = (ParameterBlock) nodeSupport
2592: .getParameterBlock().clone();
2593:
2594: if (index < pb.getNumSources()) {
2595: Object priorSource = pb.getSource(index);
2596: if (priorSource instanceof PlanarImage) {
2597: ((PlanarImage) priorSource).removeSink(this );
2598: } else if (priorSource instanceof CollectionImage) {
2599: ((CollectionImage) priorSource).removeSink(this );
2600: }
2601: }
2602:
2603: pb.setSource(source, index);
2604: nodeSupport.setParameterBlock(pb);
2605:
2606: if (source instanceof PlanarImage) {
2607: ((PlanarImage) source).addSink(this );
2608: } else if (source instanceof CollectionImage) {
2609: ((CollectionImage) source).addSink(this );
2610: }
2611: }
2612:
2613: /**
2614: * Removes the specified <code>Object</code> source from the
2615: * <code>ParameterBlock</code> of this node.
2616: *
2617: * <p> The node is removed automatically as a sink if the source is a
2618: * <code>PlanarImage</code> or a <code>CollectionImage</code>.
2619: *
2620: * @param source A <code>Object</code> to be removed.
2621: *
2622: * @throws IllegalArgumentException if
2623: * <code>source</code> is <code>null</code>.
2624: * @return <code>true</code> if the element was present, <code>false</code>
2625: * otherwise.
2626: *
2627: * @since JAI 1.1
2628: */
2629: public synchronized boolean removeSource(Object source) {
2630: if (source == null) {
2631: throw new IllegalArgumentException(JaiI18N
2632: .getString("Generic0"));
2633: }
2634:
2635: ParameterBlock pb = (ParameterBlock) nodeSupport
2636: .getParameterBlock().clone();
2637:
2638: Vector nodeSources = pb.getSources();
2639: if (nodeSources.contains(source)) {
2640: if (source instanceof PlanarImage) {
2641: ((PlanarImage) source).removeSink(this );
2642: } else if (source instanceof CollectionImage) {
2643: ((CollectionImage) source).removeSink(this );
2644: }
2645: }
2646:
2647: boolean result = nodeSources.remove(source);
2648: nodeSupport.setParameterBlock(pb);
2649:
2650: return result;
2651: }
2652:
2653: /**
2654: * Returns the specified <code>PlanarImage</code> source stored in the
2655: * <code>ParameterBlock</code> of this node.
2656: * If there is no source corresponding to the specified index, an
2657: * <code>ArrayIndexOutOfBoundsException</code> will be thrown.
2658: *
2659: * @param index The index of the desired source.
2660: * @return A <code>PlanarImage</code> source.
2661: * @throws ArrayIndexOutOfBoundsException if
2662: * <code>index</code> is invalid.
2663: * @throws ClassCastException if the source at the indicated index is
2664: * not a <code>PlanarImage</code>.
2665: *
2666: * @since JAI 1.1
2667: */
2668: public PlanarImage getSourceImage(int index) {
2669: return (PlanarImage) nodeSupport.getParameterBlock().getSource(
2670: index);
2671: }
2672:
2673: /**
2674: * Returns the specified source stored in the
2675: * <code>ParameterBlock</code> of this node.
2676: * If there is no source corresponding to the specified index, an
2677: * <code>ArrayIndexOutOfBoundsException</code> will be thrown.
2678: *
2679: * @param index The index of the source.
2680: * @throws ArrayIndexOutOfBoundsException if
2681: * <code>index</code> is invalid.
2682: *
2683: * @since JAI 1.1
2684: */
2685: public synchronized Object getSourceObject(int index) {
2686: return nodeSupport.getParameterBlock().getSource(index);
2687: }
2688:
2689: /**
2690: * Returns the number of sources stored in the
2691: * <code>ParameterBlock</code> of this node.
2692: * This may differ from the number of sources of the rendered image.
2693: */
2694: public int getNumSources() {
2695: // This method must return the number of sources of this node,
2696: // not the number of sources of the rendered image. Otherwise,
2697: // it'll cause exception in some cases.
2698: return nodeSupport.getParameterBlock().getNumSources();
2699: }
2700:
2701: /**
2702: * Returns a clone of the <code>Vector</code> of sources stored in the
2703: * <code>ParameterBlock</code> of this node.
2704: * This may differ from the source vector of the rendering of the node.
2705: */
2706: public synchronized Vector getSources() {
2707: Vector srcs = nodeSupport.getParameterBlock().getSources();
2708: return srcs == null ? null : (Vector) srcs.clone();
2709: }
2710:
2711: /**
2712: * Replaces the sources in the <code>ParameterBlock</code> of this node
2713: * with a new list of sources.
2714: * This is a convenience method that invokes
2715: * <code>setParameterBlock()</code> and so adheres to the same event
2716: * firing behavior.
2717: *
2718: * <p> The node is added automatically as a sink of any source which is a
2719: * <code>PlanarImage</code> or a <code>CollectionImage</code>. It is
2720: * also automatically removed as a sink of any such prior sources which
2721: * are no longer sources.
2722: *
2723: * @param sourceList A <code>List</code> of sources.
2724: *
2725: * @throws IllegalArgumentException if
2726: * <code>sourceList</code> is <code>null</code>.
2727: */
2728: public synchronized void setSources(List sourceList) {
2729: if (sourceList == null)
2730: throw new IllegalArgumentException(JaiI18N
2731: .getString("Generic0"));
2732:
2733: ParameterBlock pb = (ParameterBlock) nodeSupport
2734: .getParameterBlock().clone();
2735:
2736: Iterator it = pb.getSources().iterator();
2737: while (it.hasNext()) {
2738: Object priorSource = it.next();
2739: if (!sourceList.contains(priorSource)) {
2740: if (priorSource instanceof PlanarImage) {
2741: ((PlanarImage) priorSource).removeSink(this );
2742: } else if (priorSource instanceof CollectionImage) {
2743: ((CollectionImage) priorSource).removeSink(this );
2744: }
2745: }
2746: }
2747:
2748: pb.removeSources();
2749:
2750: int size = sourceList.size();
2751: for (int i = 0; i < size; i++) {
2752: Object src = sourceList.get(i);
2753: pb.addSource(src);
2754: if (src instanceof PlanarImage) {
2755: ((PlanarImage) src).addSink(this );
2756: } else if (src instanceof CollectionImage) {
2757: ((CollectionImage) src).addSink(this );
2758: }
2759: }
2760:
2761: nodeSupport.setParameterBlock(pb);
2762: }
2763:
2764: /**
2765: * Removes all the sources stored in the
2766: * <code>ParameterBlock</code> of this node.
2767: * This is a convenience method that invokes
2768: * <code>setParameterBlock()</code> and so adheres to the same event
2769: * firing behavior.
2770: *
2771: * <p> The node is removed automatically as a sink of any source which
2772: * is a <code>PlanarImage</code> or a <code>CollectionImage</code>.
2773: */
2774: public synchronized void removeSources() {
2775: ParameterBlock pb = (ParameterBlock) nodeSupport
2776: .getParameterBlock().clone();
2777: Iterator it = pb.getSources().iterator();
2778: while (it.hasNext()) {
2779: Object priorSource = it.next();
2780: if (priorSource instanceof PlanarImage) {
2781: ((PlanarImage) priorSource).removeSink(this );
2782: } else if (priorSource instanceof CollectionImage) {
2783: ((CollectionImage) priorSource).removeSink(this );
2784: }
2785: it.remove();
2786: }
2787: nodeSupport.setParameterBlock(pb);
2788: }
2789:
2790: // ----- Methods dealing with sinks Vector.
2791:
2792: /**
2793: * Adds a <code>PlanarImage</code> sink to the list of sinks of the node.
2794: *
2795: * <p><i> Note that the behavior of this method has changed as of
2796: * Java Advanced Imaging 1.1. To obtain the previous behavior use
2797: * <code>getRendering().addSink()</code>. The description of the
2798: * previous behavior is as follows:
2799: * <blockquote>
2800: *
2801: * Renders the node if it has not already been rendered, and
2802: * adds a <code>PlanarImage</code> sink to the list of sinks
2803: * of the rendered image.
2804: *
2805: * </blockquote>
2806: *
2807: * <p> Note also that this class no longer overrides
2808: * <code>getSinks()</code>. To obtain the previous behavior of
2809: * <code>getSinks()</code> use <code>getRendering().getSinks()</code>.
2810: * </i>
2811: *
2812: * @throws IllegalArgumentException if
2813: * <code>sink</code> is <code>null</code>.
2814: */
2815: public synchronized void addSink(PlanarImage sink) {
2816: if (sink == null) {
2817: throw new IllegalArgumentException(JaiI18N
2818: .getString("Generic0"));
2819: }
2820: super .addSink(sink);
2821: }
2822:
2823: /**
2824: * Removes a <code>PlanarImage</code> sink from the list of sinks of
2825: * the node.
2826: *
2827: * <p><i> Note that the behavior of this method has changed as of
2828: * Java Advanced Imaging 1.1. To obtain the previous behavior use
2829: * <code>getRendering().removeSink()</code>. The description of the
2830: * previous behavior is as follows:
2831: * <blockquote>
2832: *
2833: * Renders the node if it has not already been rendered, and
2834: * removes a <code>PlanarImage</code> sink from the list of sinks
2835: * of the rendered image.
2836: *
2837: * </blockquote>
2838: *
2839: * <p> Note also that this class no longer overrides
2840: * <code>getSinks()</code>. To obtain the previous behavior of
2841: * <code>getSinks()</code> use <code>getRendering().getSinks()</code>.
2842: * </i>
2843: *
2844: * @throws IllegalArgumentException if
2845: * <code>sink</code> is <code>null</code>.
2846: */
2847: public synchronized boolean removeSink(PlanarImage sink) {
2848: if (sink == null) {
2849: throw new IllegalArgumentException(JaiI18N
2850: .getString("Generic0"));
2851: }
2852: return super .removeSink(sink);
2853: }
2854:
2855: /**
2856: * Removes all sinks from the list of sinks of the node.
2857: *
2858: * @since JAI 1.1
2859: */
2860: public void removeSinks() {
2861: super .removeSinks();
2862: }
2863:
2864: /**
2865: * Adds a sink to the list of node sinks. If the sink is an
2866: * instance of <code>PropertyChangeListener</code> it will be
2867: * notified in the same manner as registered listeners for the
2868: * changes to the "Rendering" property of this node as long as its
2869: * <code>WeakReference</code> has not yet been cleared.
2870: *
2871: * @throws IllegalArgumentException if
2872: * <code>sink</code> is <code>null</code>.
2873: *
2874: * @since JAI 1.1
2875: */
2876: public boolean addSink(Object sink) {
2877: if (sink == null) {
2878: throw new IllegalArgumentException(JaiI18N
2879: .getString("Generic0"));
2880: }
2881: return super .addSink(sink);
2882: }
2883:
2884: /**
2885: * Removes a sink from the list of node sinks. If the sink
2886: * is a <code>PropertyChangeListener</code> for the "Rendering"
2887: * property of this node it will no longer be eligible for
2888: * notification events indicating a change in this property.
2889: *
2890: * @throws IllegalArgumentException if
2891: * <code>sink</code> is <code>null</code>.
2892: *
2893: * @since JAI 1.1
2894: */
2895: public boolean removeSink(Object sink) {
2896: if (sink == null) {
2897: throw new IllegalArgumentException(JaiI18N
2898: .getString("Generic0"));
2899: }
2900: return super .removeSink(sink);
2901: }
2902:
2903: /* ----- Image coordinate mapping methods ----- */
2904:
2905: /**
2906: * Computes the position in the specified source that best
2907: * matches the supplied destination image position. If it
2908: * is not possible to compute the requested position,
2909: * <code>null</code> will be returned. If the point is mapped
2910: * outside the source bounds, the coordinate value or <code>null</code>
2911: * may be returned at the discretion of the implementation.
2912: *
2913: * <p>If the rendering of the node is an <code>OpImage</code>, the
2914: * call is forwarded to the equivalent method of the rendering.
2915: * Otherwise <code>destPt</code> is returned to indicate the identity
2916: * mapping. In either case if the node had not been rendered it will
2917: * be.</p>
2918: *
2919: * @param destPt the position in destination image coordinates
2920: * to map to source image coordinates.
2921: * @param sourceIndex the index of the source image.
2922: *
2923: * @return a <code>Point2D</code> of the same class as
2924: * <code>destPt</code> or <code>null</code>.
2925: *
2926: * @throws IllegalArgumentException if <code>destPt</code> is
2927: * <code>null</code>.
2928: * @throws IndexOutOfBoundsException if <code>sourceIndex</code> is
2929: * negative or greater than or equal to the number of sources.
2930: *
2931: * @since JAI 1.1.2
2932: */
2933: public Point2D mapDestPoint(Point2D destPt, int sourceIndex) {
2934: if (destPt == null) {
2935: throw new IllegalArgumentException(JaiI18N
2936: .getString("Generic0"));
2937: } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) {
2938: throw new IndexOutOfBoundsException(JaiI18N
2939: .getString("Generic1"));
2940: }
2941:
2942: createRendering();
2943:
2944: if (theImage != null && theImage instanceof OpImage) {
2945: return ((OpImage) theImage).mapDestPoint(destPt,
2946: sourceIndex);
2947: }
2948:
2949: return destPt;
2950: }
2951:
2952: /**
2953: * Computes the position in the destination that best
2954: * matches the supplied source image position. If it
2955: * is not possible to compute the requested position,
2956: * <code>null</code> will be returned. If the point is mapped
2957: * outside the destination bounds, the coordinate value or
2958: * <code>null</code> may be returned at the discretion of the
2959: * implementation.
2960: *
2961: * <p>If the rendering of the node is an <code>OpImage</code>, the
2962: * call is forwarded to the equivalent method of the rendering.
2963: * Otherwise <code>sourcePt</code> is returned to indicate the identity
2964: * mapping. In either case if the node had not been rendered it will
2965: * be.</p>
2966: *
2967: * @param sourcePt the position in source image coordinates
2968: * to map to destination image coordinates.
2969: * @param sourceIndex the index of the source image.
2970: *
2971: * @return a <code>Point2D</code> of the same class as
2972: * <code>sourcePt</code> or <code>null</code>.
2973: *
2974: * @throws IllegalArgumentException if <code>sourcePt</code> is
2975: * <code>null</code>.
2976: * @throws IndexOutOfBoundsException if <code>sourceIndex</code> is
2977: * negative or greater than or equal to the number of sources.
2978: *
2979: * @since JAI 1.1.2
2980: */
2981: public Point2D mapSourcePoint(Point2D sourcePt, int sourceIndex) {
2982: if (sourcePt == null) {
2983: throw new IllegalArgumentException(JaiI18N
2984: .getString("Generic0"));
2985: } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) {
2986: throw new IndexOutOfBoundsException(JaiI18N
2987: .getString("Generic1"));
2988: }
2989:
2990: createRendering();
2991:
2992: if (theImage != null && theImage instanceof OpImage) {
2993: return ((OpImage) theImage).mapSourcePoint(sourcePt,
2994: sourceIndex);
2995: }
2996:
2997: return sourcePt;
2998: }
2999:
3000: /* ----- Object cleanup ----- */
3001:
3002: /**
3003: * Hints that this node and its rendering will no longer be used.
3004: *
3005: * <p>If <code>theImage</code> is non-<code>null</code>, then this
3006: * call is first forwarded to <code>theImage.dispose()</code>.
3007: * Subsequent to this <code>super.dispose()</code> is invoked.</p>
3008: *
3009: * <p> The results of referencing an image after a call to
3010: * <code>dispose()</code> are undefined.</p>
3011: *
3012: * @since JAI 1.1.2
3013: */
3014: public synchronized void dispose() {
3015: if (isDisposed) {
3016: return;
3017: }
3018:
3019: isDisposed = true;
3020:
3021: if (theImage != null) {
3022: theImage.dispose();
3023: }
3024:
3025: super .dispose();
3026: }
3027:
3028: /* ----- [De]serialization methods ----- */
3029:
3030: /** Serializes the <code>RenderedOp</code>. */
3031: private void writeObject(ObjectOutputStream out) throws IOException {
3032: // Write non-static and non-transient fields.
3033: out.defaultWriteObject();
3034:
3035: // Explicitly serialize the required superclass fields.
3036: out.writeObject(eventManager);
3037: out.writeObject(properties);
3038: }
3039:
3040: /**
3041: * Deserialize the <code>RenderedOp</code>.
3042: */
3043: private synchronized void readObject(ObjectInputStream in)
3044: throws IOException, ClassNotFoundException {
3045:
3046: // Read non-static and non-transient fields.
3047: in.defaultReadObject();
3048:
3049: // Explicitly deserialize the required superclass fields.
3050: eventManager = (PropertyChangeSupportJAI) in.readObject();
3051: properties = (WritablePropertySourceImpl) in.readObject();
3052:
3053: // If this operation requires immediate rendering then render it.
3054: OperationDescriptor odesc = (OperationDescriptor) getRegistry()
3055: .getDescriptor("rendered",
3056: nodeSupport.getOperationName());
3057:
3058: if (odesc.isImmediate()) {
3059: createRendering();
3060: }
3061: }
3062:
3063: void sendExceptionToListener(String message, Exception e) {
3064: ImagingListener listener = (ImagingListener) getRenderingHints()
3065: .get(JAI.KEY_IMAGING_LISTENER);
3066:
3067: listener.errorOccurred(message, e, this , false);
3068: }
3069: }
|