1 /* 2 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* ******************************************************************** 27 ********************************************************************** 28 ********************************************************************** 29 *** COPYRIGHT (c) Eastman Kodak Company, 1997 *** 30 *** As an unpublished work pursuant to Title 17 of the United *** 31 *** States Code. All rights reserved. *** 32 ********************************************************************** 33 ********************************************************************** 34 **********************************************************************/ 35 36 package java.awt.image.renderable; 37 import java.awt.geom.AffineTransform; 38 import java.awt.geom.Rectangle2D; 39 import java.awt.image.RenderedImage; 40 import java.awt.RenderingHints; 41 import java.util.Hashtable; 42 import java.util.Vector; 43 44 /** 45 * This class handles the renderable aspects of an operation with help 46 * from its associated instance of a ContextualRenderedImageFactory. 47 */ 48 public class RenderableImageOp implements RenderableImage { 49 50 /** A ParameterBlock containing source and parameters. */ 51 ParameterBlock paramBlock; 52 53 /** The associated ContextualRenderedImageFactory. */ 54 ContextualRenderedImageFactory myCRIF; 55 56 /** The bounding box of the results of this RenderableImageOp. */ 57 Rectangle2D boundingBox; 58 59 60 /** 61 * Constructs a RenderedImageOp given a 62 * ContextualRenderedImageFactory object, and 63 * a ParameterBlock containing RenderableImage sources and other 64 * parameters. Any RenderedImage sources referenced by the 65 * ParameterBlock will be ignored. 66 * 67 * @param CRIF a ContextualRenderedImageFactory object 68 * @param paramBlock a ParameterBlock containing this operation's source 69 * images and other parameters necessary for the operation 70 * to run. 71 */ 72 public RenderableImageOp(ContextualRenderedImageFactory CRIF, 73 ParameterBlock paramBlock) { 74 this.myCRIF = CRIF; 75 this.paramBlock = (ParameterBlock) paramBlock.clone(); 76 } 77 78 /** 79 * Returns a vector of RenderableImages that are the sources of 80 * image data for this RenderableImage. Note that this method may 81 * return an empty vector, to indicate that the image has no sources, 82 * or null, to indicate that no information is available. 83 * 84 * @return a (possibly empty) Vector of RenderableImages, or null. 85 */ 86 public Vector<RenderableImage> getSources() { 87 return getRenderableSources(); 88 } 89 90 private Vector getRenderableSources() { 91 Vector sources = null; 92 93 if (paramBlock.getNumSources() > 0) { 94 sources = new Vector(); 95 int i = 0; 96 while (i < paramBlock.getNumSources()) { 97 Object o = paramBlock.getSource(i); 98 if (o instanceof RenderableImage) { 99 sources.add((RenderableImage)o); 100 i++; 101 } else { 102 break; 103 } 104 } 105 } 106 return sources; 107 } 108 109 /** 110 * Gets a property from the property set of this image. 111 * If the property name is not recognized, java.awt.Image.UndefinedProperty 112 * will be returned. 113 * 114 * @param name the name of the property to get, as a String. 115 * @return a reference to the property Object, or the value 116 * java.awt.Image.UndefinedProperty. 117 */ 118 public Object getProperty(String name) { 119 return myCRIF.getProperty(paramBlock, name); 120 } 121 122 /** 123 * Return a list of names recognized by getProperty. 124 * @return a list of property names. 125 */ 126 public String[] getPropertyNames() { 127 return myCRIF.getPropertyNames(); 128 } 129 130 /** 131 * Returns true if successive renderings (that is, calls to 132 * createRendering() or createScaledRendering()) with the same arguments 133 * may produce different results. This method may be used to 134 * determine whether an existing rendering may be cached and 135 * reused. The CRIF's isDynamic method will be called. 136 * @return <code>true</code> if successive renderings with the 137 * same arguments might produce different results; 138 * <code>false</code> otherwise. 139 */ 140 public boolean isDynamic() { 141 return myCRIF.isDynamic(); 142 } 143 144 /** 145 * Gets the width in user coordinate space. By convention, the 146 * usual width of a RenderableImage is equal to the image's aspect 147 * ratio (width divided by height). 148 * 149 * @return the width of the image in user coordinates. 150 */ 151 public float getWidth() { 152 if (boundingBox == null) { 153 boundingBox = myCRIF.getBounds2D(paramBlock); 154 } 155 return (float)boundingBox.getWidth(); 156 } 157 158 /** 159 * Gets the height in user coordinate space. By convention, the 160 * usual height of a RenderedImage is equal to 1.0F. 161 * 162 * @return the height of the image in user coordinates. 163 */ 164 public float getHeight() { 165 if (boundingBox == null) { 166 boundingBox = myCRIF.getBounds2D(paramBlock); 167 } 168 return (float)boundingBox.getHeight(); 169 } 170 171 /** 172 * Gets the minimum X coordinate of the rendering-independent image data. 173 */ 174 public float getMinX() { 175 if (boundingBox == null) { 176 boundingBox = myCRIF.getBounds2D(paramBlock); 177 } 178 return (float)boundingBox.getMinX(); 179 } 180 181 /** 182 * Gets the minimum Y coordinate of the rendering-independent image data. 183 */ 184 public float getMinY() { 185 if (boundingBox == null) { 186 boundingBox = myCRIF.getBounds2D(paramBlock); 187 } 188 return (float)boundingBox.getMinY(); 189 } 190 191 /** 192 * Change the current ParameterBlock of the operation, allowing 193 * editing of image rendering chains. The effects of such a 194 * change will be visible when a new rendering is created from 195 * this RenderableImageOp or any dependent RenderableImageOp. 196 * 197 * @param paramBlock the new ParameterBlock. 198 * @return the old ParameterBlock. 199 * @see #getParameterBlock 200 */ 201 public ParameterBlock setParameterBlock(ParameterBlock paramBlock) { 202 ParameterBlock oldParamBlock = this.paramBlock; 203 this.paramBlock = (ParameterBlock)paramBlock.clone(); 204 return oldParamBlock; 205 } 206 207 /** 208 * Returns a reference to the current parameter block. 209 * @return the <code>ParameterBlock</code> of this 210 * <code>RenderableImageOp</code>. 211 * @see #setParameterBlock(ParameterBlock) 212 */ 213 public ParameterBlock getParameterBlock() { 214 return paramBlock; 215 } 216 217 /** 218 * Creates a RenderedImage instance of this image with width w, and 219 * height h in pixels. The RenderContext is built automatically 220 * with an appropriate usr2dev transform and an area of interest 221 * of the full image. All the rendering hints come from hints 222 * passed in. 223 * 224 * <p> If w == 0, it will be taken to equal 225 * Math.round(h*(getWidth()/getHeight())). 226 * Similarly, if h == 0, it will be taken to equal 227 * Math.round(w*(getHeight()/getWidth())). One of 228 * w or h must be non-zero or else an IllegalArgumentException 229 * will be thrown. 230 * 231 * <p> The created RenderedImage may have a property identified 232 * by the String HINTS_OBSERVED to indicate which RenderingHints 233 * were used to create the image. In addition any RenderedImages 234 * that are obtained via the getSources() method on the created 235 * RenderedImage may have such a property. 236 * 237 * @param w the width of rendered image in pixels, or 0. 238 * @param h the height of rendered image in pixels, or 0. 239 * @param hints a RenderingHints object containing hints. 240 * @return a RenderedImage containing the rendered data. 241 */ 242 public RenderedImage createScaledRendering(int w, int h, 243 RenderingHints hints) { 244 // DSR -- code to try to get a unit scale 245 double sx = (double)w/getWidth(); 246 double sy = (double)h/getHeight(); 247 if (Math.abs(sx/sy - 1.0) < 0.01) { 248 sx = sy; 249 } 250 AffineTransform usr2dev = AffineTransform.getScaleInstance(sx, sy); 251 RenderContext newRC = new RenderContext(usr2dev, hints); 252 return createRendering(newRC); 253 } 254 255 /** 256 * Gets a RenderedImage instance of this image with a default 257 * width and height in pixels. The RenderContext is built 258 * automatically with an appropriate usr2dev transform and an area 259 * of interest of the full image. All the rendering hints come 260 * from hints passed in. Implementors of this interface must be 261 * sure that there is a defined default width and height. 262 * 263 * @return a RenderedImage containing the rendered data. 264 */ 265 public RenderedImage createDefaultRendering() { 266 AffineTransform usr2dev = new AffineTransform(); // Identity 267 RenderContext newRC = new RenderContext(usr2dev); 268 return createRendering(newRC); 269 } 270 271 /** 272 * Creates a RenderedImage which represents this 273 * RenderableImageOp (including its Renderable sources) rendered 274 * according to the given RenderContext. 275 * 276 * <p> This method supports chaining of either Renderable or 277 * RenderedImage operations. If sources in 278 * the ParameterBlock used to construct the RenderableImageOp are 279 * RenderableImages, then a three step process is followed: 280 * 281 * <ol> 282 * <li> mapRenderContext() is called on the associated CRIF for 283 * each RenderableImage source; 284 * <li> createRendering() is called on each of the RenderableImage sources 285 * using the backwards-mapped RenderContexts obtained in step 1, 286 * resulting in a rendering of each source; 287 * <li> ContextualRenderedImageFactory.create() is called 288 * with a new ParameterBlock containing the parameters of 289 * the RenderableImageOp and the RenderedImages that were created by the 290 * createRendering() calls. 291 * </ol> 292 * 293 * <p> If the elements of the source Vector of 294 * the ParameterBlock used to construct the RenderableImageOp are 295 * instances of RenderedImage, then the CRIF.create() method is 296 * called immediately using the original ParameterBlock. 297 * This provides a basis case for the recursion. 298 * 299 * <p> The created RenderedImage may have a property identified 300 * by the String HINTS_OBSERVED to indicate which RenderingHints 301 * (from the RenderContext) were used to create the image. 302 * In addition any RenderedImages 303 * that are obtained via the getSources() method on the created 304 * RenderedImage may have such a property. 305 * 306 * @param renderContext The RenderContext to use to perform the rendering. 307 * @return a RenderedImage containing the desired output image. 308 */ 309 public RenderedImage createRendering(RenderContext renderContext) { 310 RenderedImage image = null; 311 RenderContext rcOut = null; 312 313 // Clone the original ParameterBlock; if the ParameterBlock 314 // contains RenderableImage sources, they will be replaced by 315 // RenderedImages. 316 ParameterBlock renderedParamBlock = (ParameterBlock)paramBlock.clone(); 317 Vector sources = getRenderableSources(); 318 319 try { 320 // This assumes that if there is no renderable source, that there 321 // is a rendered source in paramBlock 322 323 if (sources != null) { 324 Vector renderedSources = new Vector(); 325 for (int i = 0; i < sources.size(); i++) { 326 rcOut = myCRIF.mapRenderContext(i, renderContext, 327 paramBlock, this); 328 RenderedImage rdrdImage = 329 ((RenderableImage)sources.elementAt(i)).createRendering(rcOut); 330 if (rdrdImage == null) { 331 return null; 332 } 333 334 // Add this rendered image to the ParameterBlock's 335 // list of RenderedImages. 336 renderedSources.addElement(rdrdImage); 337 } 338 339 if (renderedSources.size() > 0) { 340 renderedParamBlock.setSources(renderedSources); 341 } 342 } 343 344 return myCRIF.create(renderContext, renderedParamBlock); 345 } catch (ArrayIndexOutOfBoundsException e) { 346 // This should never happen 347 return null; 348 } 349 } 350 }