0001: /*
0002: * $RCSfile: RenderableOp.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.1 $
0009: * $Date: 2005/02/11 04:57:20 $
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.Dimension;
0017: import java.awt.RenderingHints;
0018: import java.awt.geom.AffineTransform;
0019: import java.awt.geom.Rectangle2D;
0020: import java.awt.image.Raster;
0021: import java.awt.image.RenderedImage;
0022: import java.awt.image.renderable.ContextualRenderedImageFactory;
0023: import java.awt.image.renderable.ParameterBlock;
0024: import java.awt.image.renderable.RenderContext;
0025: import java.awt.image.renderable.RenderableImage;
0026: import java.awt.image.renderable.RenderableImageOp;
0027: import java.beans.PropertyChangeListener;
0028: import java.io.IOException;
0029: import java.io.ObjectInputStream;
0030: import java.io.ObjectOutputStream;
0031: import java.io.Serializable;
0032: import java.util.Enumeration;
0033: import java.util.Hashtable;
0034: import java.util.HashSet;
0035: import java.util.Vector;
0036: import javax.media.jai.util.CaselessStringKey;
0037: import javax.media.jai.registry.CRIFRegistry;
0038: import javax.media.jai.remote.SerializableRenderedImage;
0039:
0040: /**
0041: * A node in a renderable imaging chain. This is the Java Advanced
0042: * Imaging version of the Java2D class <code>RenderableImageOp</code>.
0043: * Instead of an explicit <code>ContextualRenderedImageFactory</code>,
0044: * the indirection of the <code>OperationRegistry</code> is used.
0045: *
0046: * <p> A <code>RenderableOp</code> stores an operation name and a
0047: * <code>ParameterBlock</code> containing sources and parameters. A set of
0048: * nodes may be joined together via the source <code>Vector</code>s within
0049: * their respective <code>ParameterBlock</code>s to form a <u>d</u>irected
0050: * <u>a</u>cyclic <u>g</u>raph (DAG). The topology, i.e., connectivity, of
0051: * the graph may be altered by changing the node's sources. The operation
0052: * name and parameters may also be changed.
0053: *
0054: * <p> Such chains provide a framework for resolution- and rendering-
0055: * independent imaging. They are useful in that a chain may be manipulated
0056: * dynamically and rendered multiple times. Thus for example the same
0057: * chain of operations may be applied to different images or the parameters
0058: * of certain operations in a chain may be modified interactively.
0059: *
0060: * <p> A <code>RenderableOp</code> may be constructed directly as, for example,
0061: * <pre>
0062: * <code>
0063: * RenderableImage addend1;
0064: * RenderableImage addend2;
0065: * ParameterBlock pb =
0066: * (new ParameterBlock()).addSource(addend1).addSource(addend2);
0067: * RenderableOp node = new RenderableOp("add", pb);
0068: * </code>
0069: * </pre>
0070: * or via the <code>createRenderable()</code> or
0071: * <code>createRenderableNS()</code> methods defined in the <code>JAI</code>
0072: * class. The difference between direct construction of a node and creation
0073: * via a convenience method is that in the latter case:
0074: *
0075: * <ol>
0076: * <li> It is verified that the operation supports the renderable mode.</li>
0077: * <li> Using the <code>validateArguments()</code> method of the associated
0078: * <code>OperationDescriptor</code>, the arguments (sources and parameters)
0079: * are validated as being compatible with the specified operation.</li>
0080: * <li> Global <code>RenderingHints</code> maintained by the <code>JAI</code>
0081: * instance are set on the <code>RenderableOp</code> using
0082: * <code>setRenderingHints()</code>.</li>
0083: * </ol>
0084: *
0085: * <p> When a chain of nodes is rendered by any any of the
0086: * <code>createRendering()</code> methods, a "parallel" chain of
0087: * <code>RenderedImage</code>s is created. Each node in the chain of
0088: * <code>RenderableOp</code>s corresponds to a node in the chain of
0089: * <code>RenderedImage</code>s. A <code>RenderedImage</code> associated
0090: * with a given node is referred to as a <i>rendering</i> of the node.
0091: *
0092: * <p> The translation between <code>RenderableOp</code> chains and
0093: * <code>RenderedImage</code> (usually <code>OpImage</code>) chains makes
0094: * use of three levels of indirection provided by the
0095: * <code>OperationRegistry</code>, <code>ContextualRenderedImageFactory</code>,
0096: * (CRIF), and <code>RenderedImageFactory</code> (RIF) facilities. First, the
0097: * <code>OperationRegistry</code> is used to map the operation name into a
0098: * CRIF. This CRIF then constructs a <code>RenderedImage</code> via its
0099: * <code>create(RenderContext, ParameterBlock)</code> method. The third
0100: * level of indirection results from the operation name being mapped within
0101: * <code>create()</code> into the optimum RIF which actually creates the
0102: * <code>RenderedImage</code>. (Note that this third level of indirection is a
0103: * function of the CRIF implementation of the renderable <code>create()</code>
0104: * method: that provided by the convenience class <code>CRIFImpl</code>
0105: * provides this indirection.) If the <code>RenderedImage</code> returned by
0106: * the CRIF <code>create()</code> invocation is a <code>RenderedOp</code>, it
0107: * is replaced with the rendering of the <code>RenderedOp</code> (usually an
0108: * <code>OpImage</code>).
0109: *
0110: * <p> <code>RenderingHints</code> may be set on a <code>RenderableOp</code>
0111: * to provide a set of common hints to be used in all invocations of the
0112: * various <code>createRendering()</code> methods on the node. These hints
0113: * are merged with any hints supplied to <code>createRendering()</code>
0114: * either explicitly or via a <code>RenderContext</code>. Directly
0115: * supplied hints take precedence over the common hints.
0116: *
0117: * <p> <code>RenderableOp</code> nodes may participate in Java Bean-style
0118: * events. The <code>PropertyChangeEmitter</code> methods may be used
0119: * to register and unregister <code>PropertyChangeListener</code>s.
0120: * Certain <code>PropertyChangeEvent</code>s may be emitted by the
0121: * <code>RenderableOp</code>. These include the
0122: * <code>PropertyChangeEventJAI</code>s and
0123: * <code>PropertySourceChangeEvent</code>s required by virtue of implementing
0124: * the <code>OperationNode</code> interface.
0125: *
0126: * <p> <code>RenderableOp</code> nodes are <code>WritablePropertySource</code>s
0127: * and so manage a name-value database of image meta-data also known as image
0128: * properties. Properties may be set on and requested from a node. The
0129: * value of a property not explicitly set on the node (via
0130: * <code>setProperty()</code>) is obtained from the property environment of
0131: * the node. When a property is derived from the property environment it is
0132: * cached locally to ensure synchronization, i.e., that properties do not
0133: * change spontaneously if for example the same property is modified upstream.
0134: *
0135: * <p> The property environment of the <code>RenderableOp</code> is initially
0136: * derived from that of the corresponding <code>OperationDescriptor</code>
0137: * as maintained by the <code>OperationRegistry</code>. It may be modified
0138: * locally by adding a <code>PropertyGenerator</code> or by suppressing a
0139: * specific property. These modifications cannot be undone.
0140: *
0141: * <p> When a property value is requested an attempt will be made to derive
0142: * it from the several entities in the following order of precedence:
0143: * <ol>
0144: * <li> local properties; </li>
0145: * <li> any registered <code>PropertyGenerator</code>s, or
0146: * <br> a source specified via a copy-from-source directive;</li>
0147: * <li> the first source which defines the property. </li>
0148: * </ol>
0149: * Local properties are those which have been cached locally either by virtue
0150: * of direct invocation of <code>setProperty()</code> or due to caching of a
0151: * property derived from the property environment.
0152: *
0153: * <p> The properties of a <code>RenderableOp</code> node are copied to each
0154: * rendering generated by any of the <code>createRendering()</code> methods.
0155: * Properties already set on the rendering are not copied, i.e., those of the
0156: * rendering take precedence.
0157: *
0158: * <p> A <code>RenderableOp</code> chain created on a client may be passed
0159: * to a server via a <code>RemoteImage</code>. Any <code>RenderedImage</code>
0160: * sources which are not <code>Serializable</code> will be wrapped in
0161: * <code>SerializableRenderedImage</code>s for serialization. The tile
0162: * transmission parameters will be determined from the common
0163: * <code>RenderingHints</code> of the node. All other non-serializable
0164: * objects will attempt to be serialized using
0165: * <code>SerializerFactory</code>. If no <code>Serializer</code> is
0166: * available for a particular object, a
0167: * <code>java.io.NotSerializableException</code> may result. Image
0168: * properties (meta-data) are serialized insofar as they are serializable:
0169: * non-serializable components are simply eliminated from the local cache
0170: * of properties and from the property environment.
0171: *
0172: * @see CRIFImpl
0173: * @see CollectionOp
0174: * @see OperationRegistry
0175: * @see RenderedOp
0176: * @see java.awt.RenderingHints
0177: * @see java.awt.image.renderable.ContextualRenderedImageFactory
0178: * @see java.awt.image.renderable.RenderableImageOp
0179: * @see java.awt.image.renderable.RenderContext
0180: */
0181: public class RenderableOp implements RenderableImage, OperationNode,
0182: WritablePropertySource, Serializable {
0183:
0184: /**
0185: * A helper object to manage firing events.
0186: *
0187: * @since JAI 1.1
0188: */
0189: protected PropertyChangeSupportJAI eventManager = null;
0190:
0191: /**
0192: * A helper object to manage the image properties.
0193: *
0194: * @since JAI 1.1
0195: */
0196: protected WritablePropertySourceImpl properties = null;
0197:
0198: /**
0199: * An object to assist in implementing <code>OperationNode</code>.
0200: *
0201: * @since JAI 1.1
0202: */
0203: protected OperationNodeSupport nodeSupport;
0204:
0205: /**
0206: * The <code>PropertySource</code> containing the combined properties
0207: * of all of the node's sources.
0208: */
0209: protected transient PropertySource thePropertySource;
0210:
0211: /**
0212: * The <code>ContextualRenderedImageFactory</code> used to
0213: * generate renderings.
0214: */
0215: protected transient ContextualRenderedImageFactory crif = null;
0216:
0217: /**
0218: * Constructs a RenderableOp given the name of the operation to be
0219: * performed and a ParameterBlock containing RenderableImage sources
0220: * and other parameters. Any RenderedImage sources referenced by the
0221: * ParameterBlock will be ignored.
0222: *
0223: * <p> The <code>ParameterBlock</code> may include
0224: * <code>DeferredData</code> parameters. These will not be evaluated
0225: * until their values are actually required, i.e., when a rendering of
0226: * the node is requested or the renderable dimensions are queried.
0227: *
0228: * @param registry The <code>OperationRegistry</code> to be used for
0229: * instantiation. if <code>null</code>, the default registry
0230: * is used. Saved by reference.
0231: * @param opName The operation name. Saved by reference.
0232: * @param pb The sources and other parameters. If <code>null</code>,
0233: * it is assumed that this node has no sources and parameters.
0234: * This parameter is cloned.
0235: * @param hints The common node <code>RenderingHints</code> to be set;
0236: * it may be <code>null</code>.
0237: * This parameter is cloned.
0238: *
0239: * @throws IllegalArgumentException if <code>opName</code> is
0240: * <code>null</code>.
0241: *
0242: * @since JAI 1.1
0243: */
0244: public RenderableOp(OperationRegistry registry, String opName,
0245: ParameterBlock pb, RenderingHints hints) {
0246:
0247: if (pb == null) {
0248: // Ensure that the PB is non-null.
0249: pb = new ParameterBlock();
0250: } else {
0251: // Clone the PB.
0252: pb = (ParameterBlock) pb.clone();
0253: }
0254:
0255: // Clone the hints if non-null.
0256: if (hints != null) {
0257: hints = (RenderingHints) hints.clone();
0258: }
0259:
0260: // Initialize the various helper objects.
0261: eventManager = new PropertyChangeSupportJAI(this );
0262:
0263: properties = new WritablePropertySourceImpl(null, null,
0264: eventManager);
0265:
0266: nodeSupport = new OperationNodeSupport(getRegistryModeName(),
0267: opName, registry, pb, hints, eventManager);
0268: }
0269:
0270: /**
0271: * Constructs a RenderableOp given the name of the operation to be
0272: * performed and a ParameterBlock containing RenderableImage sources
0273: * and other parameters. Any RenderedImage sources referenced by the
0274: * ParameterBlock will be ignored.
0275: *
0276: * <p> The <code>ParameterBlock</code> may include
0277: * <code>DeferredData</code> parameters. These will not be evaluated
0278: * until their values are actually required, i.e., when a rendering of
0279: * the node is requested or the renderable dimensions are queried.
0280: *
0281: * @param registry The <code>OperationRegistry</code> to be used for
0282: * instantiation. if <code>null</code>, the default registry
0283: * is used. Saved by reference.
0284: * @param opName The operation name. Saved by reference.
0285: * @param pb The sources and other parameters. If <code>null</code>,
0286: * it is assumed that this node has no sources and parameters.
0287: * This parameter is cloned.
0288: *
0289: * @throws IllegalArgumentException if <code>opName</code> is
0290: * <code>null</code>.
0291: */
0292: public RenderableOp(OperationRegistry registry, String opName,
0293: ParameterBlock pb) {
0294: this (registry, opName, pb, null);
0295: }
0296:
0297: /**
0298: * Constructs a RenderableOp given the name of the operation to be
0299: * performed and a ParameterBlock containing RenderableImage sources
0300: * and other parameters. The default operation registry
0301: * is used. Any RenderedImage sources referenced by the
0302: * ParameterBlock will be ignored.
0303: *
0304: * <p> The <code>ParameterBlock</code> may include
0305: * <code>DeferredData</code> parameters. These will not be evaluated
0306: * until their values are actually required, i.e., when a rendering of
0307: * the node is requested or the renderable dimensions are queried.
0308: *
0309: * @param opName The operation name. Saved by reference.
0310: * @param pb The sources and other parameters. If <code>null</code>,
0311: * it is assumed that this node has no sources and parameters.
0312: * This parameter is cloned.
0313: *
0314: * @throws IllegalArgumentException if <code>opName</code> is <code>null</code>.
0315: */
0316: public RenderableOp(String opName, ParameterBlock pb) {
0317: this (null, opName, pb);
0318: }
0319:
0320: /**
0321: * Returns the name of the <code>RegistryMode</code> corresponding to
0322: * this <code>RenderableOp</code>. This method always returns the
0323: * <code>String</code> "renderable".
0324: *
0325: * @since JAI 1.1
0326: */
0327: public String getRegistryModeName() {
0328: return RegistryMode.getMode("renderable").getName();
0329: }
0330:
0331: /* ----- Critical attribute main accessors and mutators. ----- */
0332:
0333: /**
0334: * Returns the <code>OperationRegistry</code> that is used
0335: * by this node. If the registry had not been set, the default
0336: * registry is returned.
0337: */
0338: public synchronized OperationRegistry getRegistry() {
0339: return nodeSupport.getRegistry();
0340: }
0341:
0342: /**
0343: * Sets the <code>OperationRegistry</code> that is used by
0344: * this node. If the specified registry is <code>null</code>, the
0345: * default registry is used.
0346: *
0347: * <p> If the supplied registry does not equal the current registry, a
0348: * <code>PropertyChangeEventJAI</code> named "OperationRegistry"
0349: * will be fired
0350: */
0351: public synchronized void setRegistry(OperationRegistry registry) {
0352: nodeSupport.setRegistry(registry);
0353: }
0354:
0355: /**
0356: * Returns the name of the operation this node represents as
0357: * a <code>String</code>.
0358: */
0359: public String getOperationName() {
0360: return nodeSupport.getOperationName();
0361: }
0362:
0363: /**
0364: * Sets the name of the operation this node represents.
0365: * The parameter is saved by reference.
0366: *
0367: * <p> If the supplied name does not equal the current operation name, a
0368: * <code>PropertyChangeEventJAI</code> named "OperationName"
0369: * will be fired.
0370: *
0371: * @param opName The new operation name to be set.
0372: *
0373: * @throws IllegalArgumentException if <code>opName</code> is
0374: * <code>null</code>.
0375: */
0376: public synchronized void setOperationName(String opName) {
0377: nodeSupport.setOperationName(opName);
0378: }
0379:
0380: /** Returns a clone of the <code>ParameterBlock</code> of this node. */
0381: public ParameterBlock getParameterBlock() {
0382: return (ParameterBlock) nodeSupport.getParameterBlock().clone();
0383: }
0384:
0385: /**
0386: * Sets the <code>ParameterBlock</code> of this node.
0387: * If the specified new <code>ParameterBlock</code> is <code>null</code>,
0388: * it is assumed that this node has no input sources and parameters.
0389: * The supplied parameter is cloned.
0390: *
0391: * <p> This method does not validate the content of the supplied
0392: * <code>ParameterBlock</code>. The caller should ensure that
0393: * the sources and parameters in the <code>ParameterBlock</code>
0394: * are suitable for the operation this node represents; otherwise
0395: * some form of error or exception may occur at the time of rendering.
0396: *
0397: * <p> If the supplied <code>ParameterBlock</code> does not equal the
0398: * current <code>ParameterBlock</code>, a
0399: * <code>PropertyChangeEventJAI</code> named "ParameterBlock", "Sources",
0400: * or "Parameters" will be fired.
0401: *
0402: * <p> The <code>ParameterBlock</code> may include
0403: * <code>DeferredData</code> parameters. These will not be evaluated
0404: * until their values are actually required, i.e., when a rendering of
0405: * the node is requested or the renderable dimensions are queried.
0406: *
0407: * @param pb The new <code>ParameterBlock</code> to be set;
0408: * it may be <code>null</code>.
0409: */
0410: public synchronized void setParameterBlock(ParameterBlock pb) {
0411: nodeSupport.setParameterBlock(pb == null ? new ParameterBlock()
0412: : (ParameterBlock) pb.clone());
0413: }
0414:
0415: /**
0416: * Returns a clone of the common <code>RenderingHints</code> of this node
0417: * or <code>null</code>.
0418: *
0419: * @since JAI 1.1
0420: */
0421: public RenderingHints getRenderingHints() {
0422: RenderingHints hints = nodeSupport.getRenderingHints();
0423: return hints == null ? null : (RenderingHints) hints.clone();
0424: }
0425:
0426: /**
0427: * Sets the common <code>RenderingHints</code> of this node.
0428: * The supplied parameter is cloned if non-<code>null</code>.
0429: *
0430: * <p> If the supplied <code>RenderingHints</code> does not equal the
0431: * current <code>RenderingHints</code>, a
0432: * <code>PropertyChangeEventJAI</code> named "RenderingHints"
0433: * will be fired.
0434: *
0435: * @param hints The new <code>RenderingHints</code> to be set;
0436: * it may be <code>null</code>.
0437: *
0438: * @since JAI 1.1
0439: */
0440: public synchronized void setRenderingHints(RenderingHints hints) {
0441: if (hints != null) {
0442: hints = (RenderingHints) hints.clone();
0443: }
0444: nodeSupport.setRenderingHints(hints);
0445: }
0446:
0447: /* ----- RenderableImage methods (except properties). ----- */
0448:
0449: private Vector getRenderableSources() {
0450: Vector sources = null;
0451:
0452: int numSrcs = nodeSupport.getParameterBlock().getNumSources();
0453: if (numSrcs > 0) {
0454: sources = new Vector();
0455: for (int i = 0; i < numSrcs; i++) {
0456: Object o = nodeSupport.getParameterBlock().getSource(i);
0457: if (o instanceof RenderableImage) {
0458: sources.add(o);
0459: }
0460: }
0461: }
0462: return sources;
0463: }
0464:
0465: /**
0466: * Returns a vector of RenderableImages that are the sources of
0467: * image data for this RenderableImage. Note that this method may
0468: * return an empty vector, to indicate that the image has sources
0469: * but none of them is a RenderableImage, or null to indicate the
0470: * image has no source of any type.
0471: *
0472: * @return a (possibly empty) Vector of RenderableImages, or null.
0473: */
0474: public Vector getSources() {
0475: return getRenderableSources();
0476: }
0477:
0478: /** Use registry to find an appropriate CRIF */
0479: private synchronized ContextualRenderedImageFactory findCRIF() {
0480: if (crif == null) {
0481: // find the CRIF(JAI) from the registry.
0482: crif = CRIFRegistry.get(getRegistry(), getOperationName());
0483: }
0484: if (crif == null) {
0485: throw new RuntimeException(JaiI18N
0486: .getString("RenderableOp2"));
0487: }
0488:
0489: return crif;
0490: }
0491:
0492: /**
0493: * Return the rendering-independent width of the image.
0494: *
0495: * @return the image width as a float.
0496: */
0497: public float getWidth() {
0498: findCRIF();
0499: ParameterBlock paramBlock = ImageUtil
0500: .evaluateParameters(nodeSupport.getParameterBlock());
0501: Rectangle2D boundingBox = crif.getBounds2D(paramBlock);
0502: return (float) boundingBox.getWidth();
0503: }
0504:
0505: /**
0506: * Return the rendering-independent height of the image.
0507: *
0508: * @return the image height as a float.
0509: */
0510: public float getHeight() {
0511: findCRIF();
0512: ParameterBlock paramBlock = ImageUtil
0513: .evaluateParameters(nodeSupport.getParameterBlock());
0514: Rectangle2D boundingBox = crif.getBounds2D(paramBlock);
0515: return (float) boundingBox.getHeight();
0516: }
0517:
0518: /**
0519: * Gets the minimum X coordinate of the rendering-independent image data.
0520: */
0521: public float getMinX() {
0522: findCRIF();
0523: ParameterBlock paramBlock = ImageUtil
0524: .evaluateParameters(nodeSupport.getParameterBlock());
0525: Rectangle2D boundingBox = crif.getBounds2D(paramBlock);
0526: return (float) boundingBox.getX();
0527: }
0528:
0529: /**
0530: * Gets the minimum Y coordinate of the rendering-independent image data.
0531: */
0532: public float getMinY() {
0533: findCRIF();
0534: ParameterBlock paramBlock = ImageUtil
0535: .evaluateParameters(nodeSupport.getParameterBlock());
0536: Rectangle2D boundingBox = crif.getBounds2D(paramBlock);
0537: return (float) boundingBox.getY();
0538: }
0539:
0540: /**
0541: * Returns a default rendering of this <code>RenderableImage</code>.
0542: * In all cases the area of interest will equal the image bounds.
0543: * Any hints set on the node via <code>setRenderingHints()</code> will
0544: * be used.
0545: *
0546: * <p> The dimensions of the created <code>RenderedImage</code> are
0547: * determined in the following order of precedence:
0548: * <ol>
0549: * <li>If a <code>JAI.KEY_DEFAULT_RENDERING_SIZE</code> hint is set on
0550: * the node it is used unless both its dimensions are non-positive.</li>
0551: * <li>The value returned by <code>JAI.getDefaultRenderingSize()</code>
0552: * is used unless it is <code>null</code>.
0553: * <li>An identity transform from renderable to rendered coordinates
0554: * is applied.
0555: * </ol>
0556: * Either dimension of the default rendering size set in the hints or on
0557: * the default <code>JAI</code> instance may be non-positive in which case
0558: * the other dimension and the renderable aspect ratio will be used to
0559: * compute the rendered image size.
0560: *
0561: * <p> This method does not validate sources and parameters supplied
0562: * in the <code>ParameterBlock</code> supplied at construction against
0563: * the specification of the operation this node represents. It is the
0564: * caller's responsibility to ensure that the data in the
0565: * <code>ParameterBlock</code> are suitable for this operation.
0566: * Otherwise, some kind of exception or error will occur. Invoking this
0567: * method will cause any <code>DeferredData</code> parameters to be
0568: * evaluated.
0569: *
0570: * @return The default RenderedImage.
0571: */
0572: public RenderedImage createDefaultRendering() {
0573: // Get the default dimensions.
0574: Dimension defaultDimension = null;
0575: RenderingHints hints = nodeSupport.getRenderingHints();
0576: if (hints != null
0577: && hints.containsKey(JAI.KEY_DEFAULT_RENDERING_SIZE)) {
0578: defaultDimension = (Dimension) hints
0579: .get(JAI.KEY_DEFAULT_RENDERING_SIZE);
0580: }
0581: if (defaultDimension == null
0582: || (defaultDimension.width <= 0 && defaultDimension.height <= 0)) {
0583: defaultDimension = JAI.getDefaultRenderingSize();
0584: }
0585:
0586: // Initialize scale factors to represent the identify transform.
0587: double sx = 1.0;
0588: double sy = 1.0;
0589:
0590: // Reset the scale factors if a default dimension is set.
0591: if (defaultDimension != null
0592: && (defaultDimension.width > 0 || defaultDimension.height > 0)) {
0593: if (defaultDimension.width > 0
0594: && defaultDimension.height > 0) {
0595: sx = defaultDimension.width / getWidth();
0596: sy = defaultDimension.height / getHeight();
0597: } else if (defaultDimension.width > 0) {
0598: sx = sy = defaultDimension.width / getWidth();
0599: } else { // defaultDimension.height > 0
0600: sx = sy = defaultDimension.height / getHeight();
0601: }
0602: }
0603:
0604: // Create the renderable-to-rendered scaling.
0605: AffineTransform transform = AffineTransform.getScaleInstance(
0606: sx, sy);
0607:
0608: // Return the rendering applying the computed transform.
0609: return createRendering(new RenderContext(transform));
0610: }
0611:
0612: /**
0613: * Gets a RenderedImage instance of this image with width w, and
0614: * height h in pixels. The RenderContext is built automatically
0615: * with an appropriate usr2dev transform and an area of interest
0616: * of the full image. The rendering hints come from hints
0617: * passed in. These hints will be merged with any set on the node
0618: * via <code>setRenderingHints()</code> with the hints passed in taking
0619: * precedence.
0620: *
0621: * <p> If w == 0, it will be taken to equal
0622: * Math.round(h*(getWidth()/getHeight())).
0623: * Similarly, if h == 0, it will be taken to equal
0624: * Math.round(w*(getHeight()/getWidth())). One of
0625: * w or h must be non-zero or else an IllegalArgumentException
0626: * will be thrown.
0627: *
0628: * <p> This method does not validate sources and parameters supplied
0629: * in the <code>ParameterBlock</code> supplied at construction against
0630: * the specification of the operation this node represents. It is the
0631: * caller's responsibility to ensure that the data in the
0632: * <code>ParameterBlock</code> are suitable for this operation.
0633: * Otherwise, some kind of exception or error will occur. Invoking this
0634: * method will cause any <code>DeferredData</code> parameters to be
0635: * evaluated.
0636: *
0637: * @param w the width of rendered image in pixels, or 0.
0638: * @param h the height of rendered image in pixels, or 0.
0639: * @param hints a RenderingHints object containg hints.
0640: * @return a RenderedImage containing the rendered data.
0641: *
0642: * @throws IllegalArgumentException if both w and h are zero.
0643: */
0644: public RenderedImage createScaledRendering(int w, int h,
0645: RenderingHints hints) {
0646: if ((w == 0) && (h == 0)) {
0647: throw new IllegalArgumentException(JaiI18N
0648: .getString("RenderableOp3"));
0649: }
0650:
0651: if (w == 0) {
0652: w = Math.round(h * (getWidth() / getHeight()));
0653: } else if (h == 0) {
0654: h = Math.round(w * (getHeight() / getWidth()));
0655: }
0656: double sx = (double) w / getWidth();
0657: double sy = (double) h / getHeight();
0658:
0659: AffineTransform usr2dev = AffineTransform.getScaleInstance(sx,
0660: sy);
0661: RenderContext renderContext = new RenderContext(usr2dev, hints);
0662: return createRendering(renderContext);
0663: }
0664:
0665: /**
0666: * Gets a RenderedImage that represents a rendering of this image
0667: * using a given RenderContext. This is the most general way to obtain a
0668: * rendering of a RenderableImage.
0669: *
0670: * <p> This method does not validate sources and parameters supplied
0671: * in the <code>ParameterBlock</code> supplied at construction against
0672: * the specification of the operation this node represents. It is the
0673: * caller's responsibility to ensure that the data in the
0674: * <code>ParameterBlock</code> are suitable for this operation.
0675: * Otherwise, some kind of exception or error will occur. Invoking this
0676: * method will cause any <code>DeferredData</code> parameters to be
0677: * evaluated.
0678: *
0679: * <p> The <code>RenderContext</code> may contain a <code>Shape</code>
0680: * that represents the area-of-interest (aoi). If the aoi is specifed,
0681: * it is still legal to return an image that's larger than this aoi.
0682: * Therefore, by default, the aoi, if specified, is ignored at the
0683: * rendering.
0684: *
0685: * <p> Any hints in the <code>RenderContext</code> will be merged with any
0686: * set on the node via <code>setRenderingHints()</code> with the hints
0687: * in the <code>RenderContext</code> taking precedence.
0688: *
0689: * @param renderContext the RenderContext to use to produce the rendering.
0690: * @return a RenderedImage containing the rendered data.
0691: */
0692: public RenderedImage createRendering(RenderContext renderContext) {
0693: findCRIF();
0694:
0695: // Clone the original ParameterBlock; if the ParameterBlock
0696: // contains RenderableImage sources, they will be replaced by
0697: // RenderedImages.
0698: ParameterBlock nodePB = nodeSupport.getParameterBlock();
0699: Vector nodeParams = ImageUtil.evaluateParameters(nodePB
0700: .getParameters());
0701: ParameterBlock renderedPB = new ParameterBlock((Vector) nodePB
0702: .getSources().clone(), nodeParams);
0703: Vector sources = getRenderableSources();
0704:
0705: try {
0706: // This assumes that if there is no renderable source, that there
0707: // is a rendered source in the ParameterBlock.
0708:
0709: // If there are any hints set on the node, create a new
0710: // RenderContext which merges them with those in the RenderContext
0711: // passed in with the passed in hints taking precedence.
0712: RenderContext rcIn = renderContext;
0713: RenderingHints nodeHints = nodeSupport.getRenderingHints();
0714: if (nodeHints != null) {
0715: RenderingHints hints = renderContext
0716: .getRenderingHints();
0717: RenderingHints mergedHints = JAI.mergeRenderingHints(
0718: nodeHints, hints);
0719: if (mergedHints != hints) {
0720: rcIn = new RenderContext(renderContext
0721: .getTransform(), renderContext
0722: .getAreaOfInterest(), mergedHints);
0723: }
0724: }
0725:
0726: if (sources != null) {
0727: Vector renderedSources = new Vector();
0728: for (int i = 0; i < sources.size(); i++) {
0729: RenderContext rcOut = crif.mapRenderContext(i,
0730: rcIn, renderedPB, this );
0731: RenderableImage src = (RenderableImage) sources
0732: .elementAt(i);
0733: RenderedImage renderedImage = src
0734: .createRendering(rcOut);
0735: if (renderedImage == null) {
0736: return null;
0737: }
0738:
0739: // Add this rendered image to the ParameterBlock's
0740: // list of RenderedImages.
0741: renderedSources.addElement(renderedImage);
0742: }
0743:
0744: if (renderedSources.size() > 0) {
0745: renderedPB.setSources(renderedSources);
0746: }
0747: }
0748:
0749: RenderedImage rendering = crif.create(rcIn, renderedPB);
0750:
0751: // Replace with the actual rendering if a RenderedOp.
0752: if (rendering instanceof RenderedOp) {
0753: rendering = ((RenderedOp) rendering).getRendering();
0754: }
0755:
0756: // Copy properties to the rendered node.
0757: if (rendering != null
0758: && rendering instanceof WritablePropertySource) {
0759: String[] propertyNames = getPropertyNames();
0760: if (propertyNames != null) {
0761: WritablePropertySource wps = (WritablePropertySource) rendering;
0762:
0763: // Save the names of rendered properties.
0764: HashSet wpsNameSet = null;
0765: String[] wpsNames = wps.getPropertyNames();
0766: if (wpsNames != null) {
0767: wpsNameSet = new HashSet();
0768: for (int j = 0; j < wpsNames.length; j++) {
0769: wpsNameSet.add(new CaselessStringKey(
0770: wpsNames[j]));
0771: }
0772: }
0773:
0774: // Copy any properties not already defined by the rendering.
0775: for (int j = 0; j < propertyNames.length; j++) {
0776: String name = propertyNames[j];
0777: if (wpsNameSet == null
0778: || !wpsNameSet
0779: .contains(new CaselessStringKey(
0780: name))) {
0781: Object value = getProperty(name);
0782: if (value != null
0783: && value != java.awt.Image.UndefinedProperty) {
0784: wps.setProperty(name, value);
0785: }
0786: }
0787: }
0788: }
0789: }
0790:
0791: return rendering;
0792: } catch (ArrayIndexOutOfBoundsException e) {
0793: // This should never happen
0794: return null;
0795: }
0796: }
0797:
0798: /**
0799: * Returns false, i.e., successive renderings with the same arguments
0800: * will produce identical results.
0801: */
0802: public boolean isDynamic() {
0803: return false;
0804: }
0805:
0806: /* ----- Property-related methods. ----- */
0807:
0808: /** Creates a <code>PropertySource</code> if none exists. */
0809: private synchronized void createPropertySource() {
0810: if (thePropertySource == null) {
0811: // Create a <code>PropertySource</code> encapsulating the
0812: // property environment of the node.
0813: thePropertySource = nodeSupport.getPropertySource(this ,
0814: null);
0815:
0816: // Add the <code>PropertySource</code> to the helper object.
0817: properties.addProperties(thePropertySource);
0818: }
0819: }
0820:
0821: /**
0822: * Returns the names of properties available from this node.
0823: * These properties are a combination of those derived
0824: * from prior nodes in the imaging chain and those set locally.
0825: *
0826: * @return An array of <code>String</code>s containing valid
0827: * property names or <code>null</code> if there are none.
0828: */
0829: public String[] getPropertyNames() {
0830: createPropertySource();
0831: return properties.getPropertyNames();
0832: }
0833:
0834: /**
0835: * Returns an array of <code>String</code>s recognized as names by
0836: * this property source that begin with the supplied prefix. If
0837: * no property names match, <code>null</code> will be returned.
0838: * The comparison is done in a case-independent manner.
0839: *
0840: * @throws IllegalArgumentException if prefix is null.
0841: *
0842: * @return an array of <code>String</code>s giving the valid
0843: * property names.
0844: */
0845: public String[] getPropertyNames(String prefix) {
0846: // This gives us a list of all non-suppressed properties
0847: return PropertyUtil
0848: .getPropertyNames(getPropertyNames(), prefix);
0849: }
0850:
0851: /**
0852: * Returns the class expected to be returned by a request for
0853: * the property with the specified name. If this information
0854: * is unavailable, <code>null</code> will be returned.
0855: *
0856: * @return The <code>Class</code> expected to be return by a
0857: * request for the value of this property or <code>null</code>.
0858: * @exception IllegalArgumentException if <code>name</code>
0859: * is <code>null</code>.
0860: *
0861: * @since JAI 1.1
0862: */
0863: public Class getPropertyClass(String name) {
0864: createPropertySource();
0865: return properties.getPropertyClass(name);
0866: }
0867:
0868: /**
0869: * Gets a property from the property set of this image.
0870: * If the property name is not recognized,
0871: * <code>java.awt.Image.UndefinedProperty</code> will be returned.
0872: *
0873: * @param name the name of the property to get, as a String.
0874: * @return a reference to the property Object, or the value
0875: * java.awt.Image.UndefinedProperty.
0876: * @exception IllegalArgumentException if <code>name</code>
0877: * is <code>null</code>.
0878: */
0879: public Object getProperty(String name) {
0880: createPropertySource();
0881: return properties.getProperty(name);
0882: }
0883:
0884: /**
0885: * Sets a local property on a node. Local property settings override
0886: * properties derived from prior nodes in the imaging chain.
0887: *
0888: * <p> If the node is serialized then serializable properties will
0889: * also be serialized but non-serializable properties will be lost.
0890: *
0891: * @param name a String representing the property name.
0892: * @param value the property's value, as an Object.
0893: * @exception IllegalArgumentException if <code>name</code>
0894: * or <code>value</code>
0895: * is <code>null</code>.
0896: */
0897: public void setProperty(String name, Object value) {
0898: createPropertySource();
0899: properties.setProperty(name, value);
0900: }
0901:
0902: /**
0903: * Removes the named property from the local property
0904: * set of the <code>RenderableOp</code> as well as from its property
0905: * environment.
0906: *
0907: * @exception IllegalArgumentException if <code>name</code>
0908: * is <code>null</code>.
0909: *
0910: * @since JAI 1.1
0911: */
0912: public void removeProperty(String name) {
0913: createPropertySource();
0914: properties.removeProperty(name);
0915: }
0916:
0917: /**
0918: * Returns the property associated with the specified property name,
0919: * or <code>java.awt.Image.UndefinedProperty</code> if the specified
0920: * property is not set on the image. This method is dynamic in the
0921: * sense that subsequent invocations of this method on the same object
0922: * may return different values as a function of changes in the property
0923: * environment of the node, e.g., a change in which
0924: * <code>PropertyGenerator</code>s are registered or in the values
0925: * associated with properties of node sources. The case of the property
0926: * name passed to this method is ignored.
0927: *
0928: * @param name A <code>String</code> naming the property.
0929: *
0930: * @throws IllegalArgumentException if
0931: * <code>name</code> is <code>null</code>.
0932: *
0933: * @since JAI 1.1
0934: */
0935: public synchronized Object getDynamicProperty(String name) {
0936: createPropertySource();
0937: return thePropertySource.getProperty(name);
0938: }
0939:
0940: /**
0941: * Adds a PropertyGenerator to the node. The property values
0942: * emitted by this property generator override any previous
0943: * definitions.
0944: *
0945: * @param pg a PropertyGenerator to be added to this node's
0946: * property environment.
0947: */
0948: public void addPropertyGenerator(PropertyGenerator pg) {
0949: nodeSupport.addPropertyGenerator(pg);
0950: }
0951:
0952: /**
0953: * Forces a property to be copied from the specified source node.
0954: * By default, a property is copied from the first source node
0955: * that emits it. The result of specifying an invalid source is
0956: * undefined.
0957: *
0958: * @param propertyName the name of the property to be copied.
0959: * @param sourceIndex the index of the from which to copy the property.
0960: * @throws IllegalArgumentException if <code>propertyName</code> is
0961: * <code>null</code>.
0962: *
0963: * @since JAI 1.1
0964: */
0965: public synchronized void copyPropertyFromSource(
0966: String propertyName, int sourceIndex) {
0967: nodeSupport.copyPropertyFromSource(propertyName, sourceIndex);
0968: }
0969:
0970: /**
0971: * Removes a named property from the property environment of this
0972: * node. Unless the property is stored locally either due
0973: * to having been set explicitly via <code>setProperty()</code>
0974: * or to having been cached for property
0975: * synchronization purposes, subsequent calls to
0976: * <code>getProperty(name)</code> will return
0977: * <code>java.awt.Image.UndefinedProperty</code>, and <code>name</code>
0978: * will not appear on the list of properties emitted by
0979: * <code>getPropertyNames()</code>. To delete the property from the
0980: * local property set of the node, <code>removeProperty()</code> should
0981: * be used.
0982: *
0983: * @param name a String naming the property to be suppressed.
0984: * @throws <code>IllegalArgumentException</code> if
0985: * <code>name</code> is <code>null</code>.
0986: */
0987: public void suppressProperty(String name) {
0988: nodeSupport.suppressProperty(name);
0989: }
0990:
0991: /* ----- PropertyChangeEmitter methods. ----- */
0992:
0993: /**
0994: * Add a PropertyChangeListener to the listener list. The
0995: * listener is registered for all properties.
0996: *
0997: * @since JAI 1.1
0998: */
0999: public void addPropertyChangeListener(
1000: PropertyChangeListener listener) {
1001: eventManager.addPropertyChangeListener(listener);
1002: }
1003:
1004: /**
1005: * Add a PropertyChangeListener for a specific property. The
1006: * listener will be invoked only when a call on
1007: * firePropertyChange names that specific property. The case of
1008: * the name is ignored.
1009: *
1010: * @since JAI 1.1
1011: */
1012: public void addPropertyChangeListener(String propertyName,
1013: PropertyChangeListener listener) {
1014: eventManager.addPropertyChangeListener(propertyName, listener);
1015: }
1016:
1017: /**
1018: * Remove a PropertyChangeListener from the listener list. This
1019: * removes a PropertyChangeListener that was registered for all
1020: * properties.
1021: *
1022: * @since JAI 1.1
1023: */
1024: public void removePropertyChangeListener(
1025: PropertyChangeListener listener) {
1026: eventManager.removePropertyChangeListener(listener);
1027: }
1028:
1029: /**
1030: * Remove a PropertyChangeListener for a specific property. The case
1031: * of the name is ignored.
1032: *
1033: * @since JAI 1.1
1034: */
1035: public void removePropertyChangeListener(String propertyName,
1036: PropertyChangeListener listener) {
1037: eventManager.removePropertyChangeListener(propertyName,
1038: listener);
1039: }
1040:
1041: /* ----- Source Vector convenience methods. ----- */
1042:
1043: /**
1044: * Returns one of the node's sources as an Object.
1045: *
1046: * @param index the index of the source.
1047: */
1048: public Object getSource(int index) {
1049: Vector sources = nodeSupport.getParameterBlock().getSources();
1050: return sources.elementAt(index);
1051: }
1052:
1053: /**
1054: * Sets one of the node's sources to an Object.
1055: * This is a convenience method that invokes
1056: * <code>setParameterBlock()</code> and so adheres to the same event
1057: * firing behavior.
1058: *
1059: * @param source the source, as an Object.
1060: * @param index the index of the source.
1061: * @throws IllegalArgumentException if <code>source</code> is
1062: * <code>null</code>.
1063: */
1064: public void setSource(Object source, int index) {
1065: if (source == null)
1066: throw new IllegalArgumentException(JaiI18N
1067: .getString("Generic0"));
1068:
1069: ParameterBlock pb = (ParameterBlock) nodeSupport
1070: .getParameterBlock().clone();
1071: pb.setSource(source, index);
1072: nodeSupport.setParameterBlock(pb);
1073: }
1074:
1075: /**
1076: * Removes all the node's sources.
1077: * This is a convenience method that invokes
1078: * <code>setParameterBlock()</code> and so adheres to the same event
1079: * firing behavior.
1080: *
1081: * @since JAI 1.1
1082: */
1083: public void removeSources() {
1084: ParameterBlock pb = (ParameterBlock) nodeSupport
1085: .getParameterBlock().clone();
1086: pb.removeSources();
1087: nodeSupport.setParameterBlock(pb);
1088: }
1089:
1090: /* ----- Parameter Vector convenience methods. ----- */
1091:
1092: /**
1093: * Returns one of the node's parameters, as a byte.
1094: *
1095: * @param index the index of the parameter.
1096: */
1097: public byte getByteParameter(int index) {
1098: return nodeSupport.getParameterBlock().getByteParameter(index);
1099: }
1100:
1101: /**
1102: * Returns one of the node's parameters, as a char.
1103: *
1104: * @param index the index of the parameter.
1105: */
1106: public char getCharParameter(int index) {
1107: return nodeSupport.getParameterBlock().getCharParameter(index);
1108: }
1109:
1110: /**
1111: * Returns one of the node's parameters, as a short.
1112: *
1113: * @param index the index of the parameter.
1114: */
1115: public short getShortParameter(int index) {
1116: return nodeSupport.getParameterBlock().getShortParameter(index);
1117: }
1118:
1119: /**
1120: * Returns one of the node's parameters, as an int.
1121: *
1122: * @param index the index of the parameter.
1123: */
1124: public int getIntParameter(int index) {
1125: return nodeSupport.getParameterBlock().getIntParameter(index);
1126: }
1127:
1128: /**
1129: * Returns one of the node's parameters, as a long.
1130: *
1131: * @param index the index of the parameter.
1132: */
1133: public long getLongParameter(int index) {
1134: return nodeSupport.getParameterBlock().getLongParameter(index);
1135: }
1136:
1137: /**
1138: * Returns one of the node's parameters, as a float.
1139: *
1140: * @param index the index of the parameter.
1141: */
1142: public float getFloatParameter(int index) {
1143: return nodeSupport.getParameterBlock().getFloatParameter(index);
1144: }
1145:
1146: /**
1147: * Returns one of the node's parameters, as a double.
1148: *
1149: * @param index the index of the parameter.
1150: */
1151: public double getDoubleParameter(int index) {
1152: return nodeSupport.getParameterBlock()
1153: .getDoubleParameter(index);
1154: }
1155:
1156: /**
1157: * Returns one of the node's parameters, as an Object.
1158: *
1159: * @param index the index of the parameter.
1160: */
1161: public Object getObjectParameter(int index) {
1162: return nodeSupport.getParameterBlock()
1163: .getObjectParameter(index);
1164: }
1165:
1166: /**
1167: * Sets one of the node's parameters to a byte.
1168: * This is a convenience method that invokes
1169: * <code>setParameter(Object,int)</code> and so adheres to the same event
1170: * firing behavior.
1171: *
1172: * @param param the parameter, as a byte.
1173: * @param index the index of the parameter.
1174: */
1175: public void setParameter(byte param, int index) {
1176: setParameter(new Byte(param), index);
1177: }
1178:
1179: /**
1180: * Sets one of the node's parameters to a char.
1181: * This is a convenience method that invokes
1182: * <code>setParameter(Object,int)</code> and so adheres to the same event
1183: * firing behavior.
1184: *
1185: * @param param the parameter, as a char.
1186: * @param index the index of the parameter.
1187: */
1188: public void setParameter(char param, int index) {
1189: setParameter(new Character(param), index);
1190: }
1191:
1192: /**
1193: * Sets one of the node's parameters to a short.
1194: * This is a convenience method that invokes
1195: * <code>setParameter(Object,int)</code> and so adheres to the same event
1196: * firing behavior.
1197: *
1198: * @param param the parameter, as a short.
1199: * @param index the index of the parameter.
1200: */
1201: public void setParameter(short param, int index) {
1202: setParameter(new Short(param), index);
1203: }
1204:
1205: /**
1206: * Sets one of the node's parameters to an int.
1207: * This is a convenience method that invokes
1208: * <code>setParameter(Object,int)</code> and so adheres to the same event
1209: * firing behavior.
1210: *
1211: * @param param the parameter, as an int.
1212: * @param index the index of the parameter.
1213: */
1214: public void setParameter(int param, int index) {
1215: setParameter(new Integer(param), index);
1216: }
1217:
1218: /**
1219: * Sets one of the node's parameters to a long.
1220: * This is a convenience method that invokes
1221: * <code>setParameter(Object,int)</code> and so adheres to the same event
1222: * firing behavior.
1223: *
1224: * @param param the parameter, as a long.
1225: * @param index the index of the parameter.
1226: */
1227: public void setParameter(long param, int index) {
1228: setParameter(new Long(param), index);
1229: }
1230:
1231: /**
1232: * Sets one of the node's parameters to a float.
1233: * This is a convenience method that invokes
1234: * <code>setParameter(Object,int)</code> and so adheres to the same event
1235: * firing behavior.
1236: *
1237: * @param param the parameter, as a float.
1238: * @param index the index of the parameter.
1239: */
1240: public void setParameter(float param, int index) {
1241: setParameter(new Float(param), index);
1242: }
1243:
1244: /**
1245: * Sets one of the node's parameters to a double.
1246: * This is a convenience method that invokes
1247: * <code>setParameter(Object,int)</code> and so adheres to the same event
1248: * firing behavior.
1249: *
1250: * @param param the parameter, as a double.
1251: * @param index the index of the parameter.
1252: */
1253: public void setParameter(double param, int index) {
1254: setParameter(new Double(param), index);
1255: }
1256:
1257: /**
1258: * Sets one of the node's parameters to an Object.
1259: * This is a convenience method that invokes
1260: * <code>setParameterBlock()</code> and so adheres to the same event
1261: * firing behavior.
1262: *
1263: * <p> The <code>Object</code> may be a
1264: * <code>DeferredData</code> instance. It will not be evaluated
1265: * until its value is actually required, i.e., when a rendering of
1266: * the node is requested or the renderable dimensions are queried.
1267: *
1268: * @param param the parameter, as an Object.
1269: * @param index the index of the parameter.
1270: */
1271: public void setParameter(Object param, int index) {
1272: ParameterBlock pb = (ParameterBlock) nodeSupport
1273: .getParameterBlock().clone();
1274: pb.set(param, index);
1275: nodeSupport.setParameterBlock(pb);
1276: }
1277: }
|