/*
* Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* ********************************************************************
**********************************************************************
**********************************************************************
*** COPYRIGHT (c) Eastman Kodak Company, 1997 ***
*** As an unpublished work pursuant to Title 17 of the United ***
*** States Code. All rights reserved. ***
**********************************************************************
**********************************************************************
**********************************************************************/
package java.awt.image.renderable;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.RenderedImage;
import java.awt.RenderingHints;
import java.util.Hashtable;
import java.util.Vector;
/**
* This class handles the renderable aspects of an operation with help
* from its associated instance of a ContextualRenderedImageFactory.
*/
public class RenderableImageOp implements RenderableImage {
/** A ParameterBlock containing source and parameters. */
ParameterBlock paramBlock;
/** The associated ContextualRenderedImageFactory. */
ContextualRenderedImageFactory myCRIF;
/** The bounding box of the results of this RenderableImageOp. */
Rectangle2D boundingBox;
/**
* Constructs a RenderedImageOp given a
* ContextualRenderedImageFactory object, and
* a ParameterBlock containing RenderableImage sources and other
* parameters. Any RenderedImage sources referenced by the
* ParameterBlock will be ignored.
*
* @param CRIF a ContextualRenderedImageFactory object
* @param paramBlock a ParameterBlock containing this operation's source
* images and other parameters necessary for the operation
* to run.
*/
public RenderableImageOp(ContextualRenderedImageFactory CRIF,
ParameterBlock paramBlock) {
this.myCRIF = CRIF;
this.paramBlock = (ParameterBlock) paramBlock.clone();
}
/**
* Returns a vector of RenderableImages that are the sources of
* image data for this RenderableImage. Note that this method may
* return an empty vector, to indicate that the image has no sources,
* or null, to indicate that no information is available.
*
* @return a (possibly empty) Vector of RenderableImages, or null.
*/
public Vector<RenderableImage> getSources() {
return getRenderableSources();
}
private Vector getRenderableSources() {
Vector sources = null;
if (paramBlock.getNumSources() > 0) {
sources = new Vector();
int i = 0;
while (i < paramBlock.getNumSources()) {
Object o = paramBlock.getSource(i);
if (o instanceof RenderableImage) {
sources.add((RenderableImage)o);
i++;
} else {
break;
}
}
}
return sources;
}
/**
* Gets a property from the property set of this image.
* If the property name is not recognized, java.awt.Image.UndefinedProperty
* will be returned.
*
* @param name the name of the property to get, as a String.
* @return a reference to the property Object, or the value
* java.awt.Image.UndefinedProperty.
*/
public Object getProperty(String name) {
return myCRIF.getProperty(paramBlock, name);
}
/**
* Return a list of names recognized by getProperty.
* @return a list of property names.
*/
public String[] getPropertyNames() {
return myCRIF.getPropertyNames();
}
/**
* Returns true if successive renderings (that is, calls to
* createRendering() or createScaledRendering()) with the same arguments
* may produce different results. This method may be used to
* determine whether an existing rendering may be cached and
* reused. The CRIF's isDynamic method will be called.
* @return <code>true</code> if successive renderings with the
* same arguments might produce different results;
* <code>false</code> otherwise.
*/
public boolean isDynamic() {
return myCRIF.isDynamic();
}
/**
* Gets the width in user coordinate space. By convention, the
* usual width of a RenderableImage is equal to the image's aspect
* ratio (width divided by height).
*
* @return the width of the image in user coordinates.
*/
public float getWidth() {
if (boundingBox == null) {
boundingBox = myCRIF.getBounds2D(paramBlock);
}
return (float)boundingBox.getWidth();
}
/**
* Gets the height in user coordinate space. By convention, the
* usual height of a RenderedImage is equal to 1.0F.
*
* @return the height of the image in user coordinates.
*/
public float getHeight() {
if (boundingBox == null) {
boundingBox = myCRIF.getBounds2D(paramBlock);
}
return (float)boundingBox.getHeight();
}
/**
* Gets the minimum X coordinate of the rendering-independent image data.
*/
public float getMinX() {
if (boundingBox == null) {
boundingBox = myCRIF.getBounds2D(paramBlock);
}
return (float)boundingBox.getMinX();
}
/**
* Gets the minimum Y coordinate of the rendering-independent image data.
*/
public float getMinY() {
if (boundingBox == null) {
boundingBox = myCRIF.getBounds2D(paramBlock);
}
return (float)boundingBox.getMinY();
}
/**
* Change the current ParameterBlock of the operation, allowing
* editing of image rendering chains. The effects of such a
* change will be visible when a new rendering is created from
* this RenderableImageOp or any dependent RenderableImageOp.
*
* @param paramBlock the new ParameterBlock.
* @return the old ParameterBlock.
* @see #getParameterBlock
*/
public ParameterBlock setParameterBlock(ParameterBlock paramBlock) {
ParameterBlock oldParamBlock = this.paramBlock;
this.paramBlock = (ParameterBlock)paramBlock.clone();
return oldParamBlock;
}
/**
* Returns a reference to the current parameter block.
* @return the <code>ParameterBlock</code> of this
* <code>RenderableImageOp</code>.
* @see #setParameterBlock(ParameterBlock)
*/
public ParameterBlock getParameterBlock() {
return paramBlock;
}
/**
* Creates a RenderedImage instance of this image with width w, and
* height h in pixels. The RenderContext is built automatically
* with an appropriate usr2dev transform and an area of interest
* of the full image. All the rendering hints come from hints
* passed in.
*
* <p> If w == 0, it will be taken to equal
* Math.round(h*(getWidth()/getHeight())).
* Similarly, if h == 0, it will be taken to equal
* Math.round(w*(getHeight()/getWidth())). One of
* w or h must be non-zero or else an IllegalArgumentException
* will be thrown.
*
* <p> The created RenderedImage may have a property identified
* by the String HINTS_OBSERVED to indicate which RenderingHints
* were used to create the image. In addition any RenderedImages
* that are obtained via the getSources() method on the created
* RenderedImage may have such a property.
*
* @param w the width of rendered image in pixels, or 0.
* @param h the height of rendered image in pixels, or 0.
* @param hints a RenderingHints object containg hints.
* @return a RenderedImage containing the rendered data.
*/
public RenderedImage createScaledRendering(int w, int h,
RenderingHints hints) {
// DSR -- code to try to get a unit scale
double sx = (double)w/getWidth();
double sy = (double)h/getHeight();
if (Math.abs(sx/sy - 1.0) < 0.01) {
sx = sy;
}
AffineTransform usr2dev = AffineTransform.getScaleInstance(sx, sy);
RenderContext newRC = new RenderContext(usr2dev, hints);
return createRendering(newRC);
}
/**
* Gets a RenderedImage instance of this image with a default
* width and height in pixels. The RenderContext is built
* automatically with an appropriate usr2dev transform and an area
* of interest of the full image. All the rendering hints come
* from hints passed in. Implementors of this interface must be
* sure that there is a defined default width and height.
*
* @return a RenderedImage containing the rendered data.
*/
public RenderedImage createDefaultRendering() {
AffineTransform usr2dev = new AffineTransform(); // Identity
RenderContext newRC = new RenderContext(usr2dev);
return createRendering(newRC);
}
/**
* Creates a RenderedImage which represents this
* RenderableImageOp (including its Renderable sources) rendered
* according to the given RenderContext.
*
* <p> This method supports chaining of either Renderable or
* RenderedImage operations. If sources in
* the ParameterBlock used to construct the RenderableImageOp are
* RenderableImages, then a three step process is followed:
*
* <ol>
* <li> mapRenderContext() is called on the associated CRIF for
* each RenderableImage source;
* <li> createRendering() is called on each of the RenderableImage sources
* using the backwards-mapped RenderContexts obtained in step 1,
* resulting in a rendering of each source;
* <li> ContextualRenderedImageFactory.create() is called
* with a new ParameterBlock containing the parameters of
* the RenderableImageOp and the RenderedImages that were created by the
* createRendering() calls.
* </ol>
*
* <p> If the elements of the source Vector of
* the ParameterBlock used to construct the RenderableImageOp are
* instances of RenderedImage, then the CRIF.create() method is
* called immediately using the original ParameterBlock.
* This provides a basis case for the recursion.
*
* <p> The created RenderedImage may have a property identified
* by the String HINTS_OBSERVED to indicate which RenderingHints
* (from the RenderContext) were used to create the image.
* In addition any RenderedImages
* that are obtained via the getSources() method on the created
* RenderedImage may have such a property.
*
* @param renderContext The RenderContext to use to perform the rendering.
* @return a RenderedImage containing the desired output image.
*/
public RenderedImage createRendering(RenderContext renderContext) {
RenderedImage image = null;
RenderContext rcOut = null;
// Clone the original ParameterBlock; if the ParameterBlock
// contains RenderableImage sources, they will be replaced by
// RenderedImages.
ParameterBlock renderedParamBlock = (ParameterBlock)paramBlock.clone();
Vector sources = getRenderableSources();
try {
// This assumes that if there is no renderable source, that there
// is a rendered source in paramBlock
if (sources != null) {
Vector renderedSources = new Vector();
for (int i = 0; i < sources.size(); i++) {
rcOut = myCRIF.mapRenderContext(i, renderContext,
paramBlock, this);
RenderedImage rdrdImage =
((RenderableImage)sources.elementAt(i)).createRendering(rcOut);
if (rdrdImage == null) {
return null;
}
// Add this rendered image to the ParameterBlock's
// list of RenderedImages.
renderedSources.addElement(rdrdImage);
}
if (renderedSources.size() > 0) {
renderedParamBlock.setSources(renderedSources);
}
}
return myCRIF.create(renderContext, renderedParamBlock);
} catch (ArrayIndexOutOfBoundsException e) {
// This should never happen
return null;
}
}
}