1 /*
2 * Portions Copyright 1998-2000 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 containg 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 }