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