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