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