/* * Copyright (c) 1998, 2014, 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. */ @SuppressWarnings("unchecked") public Vector getSources() { return (Vector)(Vector)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 true if successive renderings with the * same arguments might produce different results; * false 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 ParameterBlock of this * RenderableImageOp. * @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. * *

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. * *

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 containing 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. * *

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: * *

    *
  1. mapRenderContext() is called on the associated CRIF for * each RenderableImage source; *
  2. 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; *
  3. ContextualRenderedImageFactory.create() is called * with a new ParameterBlock containing the parameters of * the RenderableImageOp and the RenderedImages that were created by the * createRendering() calls. *
* *

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. * *

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; } } }