1 /* 2 * Copyright (c) 1998, 2014, 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 @SuppressWarnings("unchecked") 87 public Vector<RenderableImage> getSources() { 88 return (Vector<RenderableImage>)(Vector)getRenderableSources(); 89 } 90 91 private Vector<Object> getRenderableSources() { 92 Vector<Object> sources = null; 93 94 if (paramBlock.getNumSources() > 0) { 95 sources = new Vector<>(); 96 int i = 0; 97 while (i < paramBlock.getNumSources()) { 98 Object o = paramBlock.getSource(i); 99 if (o instanceof RenderableImage) { 100 sources.add((RenderableImage)o); 101 i++; 102 } else { 103 break; 104 } 105 } 106 } 107 return sources; 108 } 109 110 /** 111 * Gets a property from the property set of this image. 112 * If the property name is not recognized, java.awt.Image.UndefinedProperty 113 * will be returned. 114 * 115 * @param name the name of the property to get, as a String. 116 * @return a reference to the property Object, or the value 117 * java.awt.Image.UndefinedProperty. 118 */ 119 public Object getProperty(String name) { 120 return myCRIF.getProperty(paramBlock, name); 121 } 122 123 /** 124 * Return a list of names recognized by getProperty. 125 * @return a list of property names. 126 */ 127 public String[] getPropertyNames() { 128 return myCRIF.getPropertyNames(); 129 } 130 131 /** 132 * Returns true if successive renderings (that is, calls to 133 * createRendering() or createScaledRendering()) with the same arguments 134 * may produce different results. This method may be used to 135 * determine whether an existing rendering may be cached and 136 * reused. The CRIF's isDynamic method will be called. 137 * @return <code>true</code> if successive renderings with the 138 * same arguments might produce different results; 139 * <code>false</code> otherwise. 140 */ 141 public boolean isDynamic() { 142 return myCRIF.isDynamic(); 143 } 144 145 /** 146 * Gets the width in user coordinate space. By convention, the 147 * usual width of a RenderableImage is equal to the image's aspect 148 * ratio (width divided by height). 149 * 150 * @return the width of the image in user coordinates. 151 */ 152 public float getWidth() { 153 if (boundingBox == null) { 154 boundingBox = myCRIF.getBounds2D(paramBlock); 155 } 156 return (float)boundingBox.getWidth(); 157 } 158 159 /** 160 * Gets the height in user coordinate space. By convention, the 161 * usual height of a RenderedImage is equal to 1.0F. 162 * 163 * @return the height of the image in user coordinates. 164 */ 165 public float getHeight() { 166 if (boundingBox == null) { 167 boundingBox = myCRIF.getBounds2D(paramBlock); 168 } 169 return (float)boundingBox.getHeight(); 170 } 171 172 /** 173 * Gets the minimum X coordinate of the rendering-independent image data. 174 */ 175 public float getMinX() { 176 if (boundingBox == null) { 177 boundingBox = myCRIF.getBounds2D(paramBlock); 178 } 179 return (float)boundingBox.getMinX(); 180 } 181 182 /** 183 * Gets the minimum Y coordinate of the rendering-independent image data. 184 */ 185 public float getMinY() { 186 if (boundingBox == null) { 187 boundingBox = myCRIF.getBounds2D(paramBlock); 188 } 189 return (float)boundingBox.getMinY(); 190 } 191 192 /** 193 * Change the current ParameterBlock of the operation, allowing 194 * editing of image rendering chains. The effects of such a 195 * change will be visible when a new rendering is created from 196 * this RenderableImageOp or any dependent RenderableImageOp. 197 * 198 * @param paramBlock the new ParameterBlock. 199 * @return the old ParameterBlock. 200 * @see #getParameterBlock 201 */ 202 public ParameterBlock setParameterBlock(ParameterBlock paramBlock) { 203 ParameterBlock oldParamBlock = this.paramBlock; 204 this.paramBlock = (ParameterBlock)paramBlock.clone(); 205 return oldParamBlock; 206 } 207 208 /** 209 * Returns a reference to the current parameter block. 210 * @return the <code>ParameterBlock</code> of this 211 * <code>RenderableImageOp</code>. 212 * @see #setParameterBlock(ParameterBlock) 213 */ 214 public ParameterBlock getParameterBlock() { 215 return paramBlock; 216 } 217 218 /** 219 * Creates a RenderedImage instance of this image with width w, and 220 * height h in pixels. The RenderContext is built automatically 221 * with an appropriate usr2dev transform and an area of interest 222 * of the full image. All the rendering hints come from hints 223 * passed in. 224 * 225 * <p> If w == 0, it will be taken to equal 226 * Math.round(h*(getWidth()/getHeight())). 227 * Similarly, if h == 0, it will be taken to equal 228 * Math.round(w*(getHeight()/getWidth())). One of 229 * w or h must be non-zero or else an IllegalArgumentException 230 * will be thrown. 231 * 232 * <p> The created RenderedImage may have a property identified 233 * by the String HINTS_OBSERVED to indicate which RenderingHints 234 * were used to create the image. In addition any RenderedImages 235 * that are obtained via the getSources() method on the created 236 * RenderedImage may have such a property. 237 * 238 * @param w the width of rendered image in pixels, or 0. 239 * @param h the height of rendered image in pixels, or 0. 240 * @param hints a RenderingHints object containing hints. 241 * @return a RenderedImage containing the rendered data. 242 */ 243 public RenderedImage createScaledRendering(int w, int h, 244 RenderingHints hints) { 245 // DSR -- code to try to get a unit scale 246 double sx = (double)w/getWidth(); 247 double sy = (double)h/getHeight(); 248 if (Math.abs(sx/sy - 1.0) < 0.01) { 249 sx = sy; 250 } 251 AffineTransform usr2dev = AffineTransform.getScaleInstance(sx, sy); 252 RenderContext newRC = new RenderContext(usr2dev, hints); 253 return createRendering(newRC); 254 } 255 256 /** 257 * Gets a RenderedImage instance of this image with a default 258 * width and height in pixels. The RenderContext is built 259 * automatically with an appropriate usr2dev transform and an area 260 * of interest of the full image. All the rendering hints come 261 * from hints passed in. Implementors of this interface must be 262 * sure that there is a defined default width and height. 263 * 264 * @return a RenderedImage containing the rendered data. 265 */ 266 public RenderedImage createDefaultRendering() { 267 AffineTransform usr2dev = new AffineTransform(); // Identity 268 RenderContext newRC = new RenderContext(usr2dev); 269 return createRendering(newRC); 270 } 271 272 /** 273 * Creates a RenderedImage which represents this 274 * RenderableImageOp (including its Renderable sources) rendered 275 * according to the given RenderContext. 276 * 277 * <p> This method supports chaining of either Renderable or 278 * RenderedImage operations. If sources in 279 * the ParameterBlock used to construct the RenderableImageOp are 280 * RenderableImages, then a three step process is followed: 281 * 282 * <ol> 283 * <li> mapRenderContext() is called on the associated CRIF for 284 * each RenderableImage source; 285 * <li> createRendering() is called on each of the RenderableImage sources 286 * using the backwards-mapped RenderContexts obtained in step 1, 287 * resulting in a rendering of each source; 288 * <li> ContextualRenderedImageFactory.create() is called 289 * with a new ParameterBlock containing the parameters of 290 * the RenderableImageOp and the RenderedImages that were created by the 291 * createRendering() calls. 292 * </ol> 293 * 294 * <p> If the elements of the source Vector of 295 * the ParameterBlock used to construct the RenderableImageOp are 296 * instances of RenderedImage, then the CRIF.create() method is 297 * called immediately using the original ParameterBlock. 298 * This provides a basis case for the recursion. 299 * 300 * <p> The created RenderedImage may have a property identified 301 * by the String HINTS_OBSERVED to indicate which RenderingHints 302 * (from the RenderContext) were used to create the image. 303 * In addition any RenderedImages 304 * that are obtained via the getSources() method on the created 305 * RenderedImage may have such a property. 306 * 307 * @param renderContext The RenderContext to use to perform the rendering. 308 * @return a RenderedImage containing the desired output image. 309 */ 310 public RenderedImage createRendering(RenderContext renderContext) { 311 RenderedImage image = null; 312 RenderContext rcOut = null; 313 314 // Clone the original ParameterBlock; if the ParameterBlock 315 // contains RenderableImage sources, they will be replaced by 316 // RenderedImages. 317 ParameterBlock renderedParamBlock = (ParameterBlock)paramBlock.clone(); 318 Vector<Object> sources = getRenderableSources(); 319 320 try { 321 // This assumes that if there is no renderable source, that there 322 // is a rendered source in paramBlock 323 324 if (sources != null) { 325 Vector<Object> renderedSources = new Vector<>(); 326 for (int i = 0; i < sources.size(); i++) { 327 rcOut = myCRIF.mapRenderContext(i, renderContext, 328 paramBlock, this); 329 RenderedImage rdrdImage = 330 ((RenderableImage)sources.elementAt(i)).createRendering(rcOut); 331 if (rdrdImage == null) { 332 return null; 333 } 334 335 // Add this rendered image to the ParameterBlock's 336 // list of RenderedImages. 337 renderedSources.addElement(rdrdImage); 338 } 339 340 if (renderedSources.size() > 0) { 341 renderedParamBlock.setSources(renderedSources); 342 } 343 } 344 345 return myCRIF.create(renderContext, renderedParamBlock); 346 } catch (ArrayIndexOutOfBoundsException e) { 347 // This should never happen 348 return null; 349 } 350 } 351 }