001: /*
002: * $RCSfile: AffineDescriptor.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:29 $
010: * $State: Exp $
011: */
012: package javax.media.jai.operator;
013:
014: import com.sun.media.jai.util.PropertyGeneratorImpl;
015: import java.awt.Rectangle;
016: import java.awt.RenderingHints;
017: import java.awt.geom.AffineTransform;
018: import java.awt.image.RenderedImage;
019: import java.awt.image.renderable.ParameterBlock;
020: import java.awt.image.renderable.RenderableImage;
021: import javax.media.jai.GeometricOpImage;
022: import javax.media.jai.Interpolation;
023: import javax.media.jai.InterpolationNearest;
024: import javax.media.jai.JAI;
025: import javax.media.jai.OperationDescriptorImpl;
026: import javax.media.jai.ParameterBlockJAI;
027: import javax.media.jai.PlanarImage;
028: import javax.media.jai.PropertyGenerator;
029: import javax.media.jai.ROI;
030: import javax.media.jai.ROIShape;
031: import javax.media.jai.RenderableOp;
032: import javax.media.jai.RenderedOp;
033: import javax.media.jai.registry.RenderableRegistryMode;
034: import javax.media.jai.registry.RenderedRegistryMode;
035:
036: /**
037: * This property generator computes the properties for the operation
038: * "Affine" dynamically.
039: */
040: class AffinePropertyGenerator extends PropertyGeneratorImpl {
041:
042: /** Constructor. */
043: public AffinePropertyGenerator() {
044: super (new String[] { "ROI" }, new Class[] { ROI.class },
045: new Class[] { RenderedOp.class });
046: }
047:
048: /**
049: * Returns the specified property in the rendered layer.
050: *
051: * @param name Property name.
052: * @param opNode Operation node.
053: */
054: public Object getProperty(String name, Object opNode) {
055: validate(name, opNode);
056:
057: if (opNode instanceof RenderedOp
058: && name.equalsIgnoreCase("roi")) {
059: RenderedOp op = (RenderedOp) opNode;
060:
061: ParameterBlock pb = op.getParameterBlock();
062:
063: // Retrieve the rendered source image and its ROI.
064: RenderedImage src = pb.getRenderedSource(0);
065: Object property = src.getProperty("ROI");
066: if (property == null
067: || property
068: .equals(java.awt.Image.UndefinedProperty)
069: || !(property instanceof ROI)) {
070: return java.awt.Image.UndefinedProperty;
071: }
072: ROI srcROI = (ROI) property;
073:
074: // Retrieve the Interpolation object.
075: Interpolation interp = (Interpolation) pb
076: .getObjectParameter(1);
077:
078: // Determine the effective source bounds.
079: Rectangle srcBounds = null;
080: PlanarImage dst = op.getRendering();
081: if (dst instanceof GeometricOpImage
082: && ((GeometricOpImage) dst).getBorderExtender() == null) {
083: srcBounds = new Rectangle(src.getMinX()
084: + interp.getLeftPadding(), src.getMinY()
085: + interp.getTopPadding(), src.getWidth()
086: - interp.getWidth() + 1, src.getHeight()
087: - interp.getHeight() + 1);
088: } else {
089: srcBounds = new Rectangle(src.getMinX(), src.getMinY(),
090: src.getWidth(), src.getHeight());
091: }
092:
093: // If necessary, clip the ROI to the effective source bounds.
094: if (!srcBounds.contains(srcROI.getBounds())) {
095: srcROI = srcROI.intersect(new ROIShape(srcBounds));
096: }
097:
098: // Retrieve the AffineTransform object.
099: AffineTransform transform = (AffineTransform) pb
100: .getObjectParameter(0);
101:
102: // Create the transformed ROI.
103: ROI dstROI = srcROI.transform((AffineTransform) transform);
104:
105: // Retrieve the destination bounds.
106: Rectangle dstBounds = op.getBounds();
107:
108: // If necessary, clip the transformed ROI to the
109: // destination bounds.
110: if (!dstBounds.contains(dstROI.getBounds())) {
111: dstROI = dstROI.intersect(new ROIShape(dstBounds));
112: }
113:
114: // Return the transformed and possibly clipped ROI.
115: return dstROI;
116: }
117:
118: return java.awt.Image.UndefinedProperty;
119: }
120: }
121:
122: /**
123: * An <code>OperationDescriptor</code> describing the "Affine" operation.
124: *
125: * <p> The Affine operation performs (possibly filtered) affine
126: * mapping on a rendered or renderable source image.
127: *
128: * <p> The relationship between the source and the destination pixels
129: * is defined as follows. For each pixel (x, y) of the destination,
130: * the source value at the fractional subpixel position (x', y') is
131: * constructed by means of an Interpolation object and written to the
132: * destination.
133: *
134: * The mapping between the destination pixel (x, y) and the source
135: * position (x', y') is given by:
136: *
137: * <pre>
138: * x' = m[0][0] * x + m[0][1] * y + m[0][2]
139: * y' = m[1][0] * x + m[1][1] * y + m[1][2]
140: * </pre>
141: *
142: * where m is a 3x2 transform matrix that inverts the matrix supplied
143: * as the "transform" argument.
144: *
145: * <p> When interpolations which require padding the source such as Bilinear
146: * or Bicubic interpolation are specified, the source needs to be extended
147: * such that it has the extra pixels needed to compute all the destination
148: * pixels. This extension is performed via the <code>BorderExtender</code>
149: * class. The type of Border Extension can be specified as a
150: * <code>RenderingHint</code> to the <code>JAI.create</code> method.
151: *
152: * <p> The parameter, "backgroundValues", is defined to
153: * fill the background with the user-specified background
154: * values. These background values will be translated into background
155: * colors by the <code>ColorModel</code> when the image is displayed.
156: * With the default value, <code>{0.0}</code>, of this parameter,
157: * the background pixels are filled with 0s. If the provided array
158: * length is smaller than the number of bands, the first element of
159: * the provided array is used for all the bands. If the provided values
160: * are out of the data range of the destination image, they will be clamped
161: * into the proper range.
162: *
163: * <p> If no BorderExtender is specified (is null), the source will
164: * not be extended. The transformed image size is still the same as if
165: * the source had been extended. However, since there is insufficient
166: * source to compute all the destination pixels, only that subset of
167: * the destination image's pixels which can be computed will be
168: * written in the destination. The rest of the destination will be
169: * set to the user-specified background values.
170: *
171: * <p> It may be noted that the minX, minY, width and height hints as
172: * specified through the <code>JAI.KEY_IMAGE_LAYOUT</code> hint in the
173: * <code>RenderingHints</code> object are not honored, as this operator
174: * calculates the destination image bounds itself. The other
175: * <code>ImageLayout</code> hints, like tileWidth and tileHeight,
176: * however are honored.
177: *
178: * <p> It should be noted that this operation automatically adds a
179: * value of <code>Boolean.TRUE</code> for the
180: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> to the given
181: * <code>configuration</code> so that the operation is performed
182: * on the pixel values instead of being performed on the indices into
183: * the color map if the source(s) have an <code>IndexColorModel</code>.
184: * This addition will take place only if a value for the
185: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> has not already been
186: * provided by the user. Note that the <code>configuration</code> Map
187: * is cloned before the new hint is added to it. The operation can be
188: * smart about the value of the <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code>
189: * <code>RenderingHints</code>, i.e. while the default value for the
190: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> is
191: * <code>Boolean.TRUE</code>, in some cases the operator could set the
192: * default.
193: *
194: * <p> "Affine" defines a PropertyGenerator that performs an identical
195: * transformation on the "ROI" property of the source image, which can
196: * be retrieved by calling the <code>getProperty</code> method with
197: * "ROI" as the property name.
198: *
199: * <p><table border=1>
200: * <caption>Resource List</caption>
201: * <tr><th>Name</th> <th>Value</th></tr>
202: * <tr><td>GlobalName</td> <td>Affine</td></tr>
203: * <tr><td>LocalName</td> <td>Affine</td></tr>
204: * <tr><td>Vendor</td> <td>com.sun.media.jai</td></tr>
205: * <tr><td>Description</td> <td>Performs interpolated affine transform on
206: * an image.</td></tr>
207: * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AffineDescriptor.html</td></tr>
208: * <tr><td>Version</td> <td>1.0</td></tr>
209: * <tr><td>arg0Desc</td> <td>The affine transform matrix.</td></tr>
210: * <tr><td>arg1Desc</td> <td>The interpolation method.</td></tr>
211: * </table></p>
212: *
213: * <p><table border=1>
214: * <caption>Parameter List</caption>
215: * <tr><th>Name</th> <th>Class Type</th>
216: * <th>Default Value</th></tr>
217: * <tr><td>transform</td> <td>java.awt.geom.AffineTransform</td>
218: * <td>identity transform</td>
219: * <tr><td>interpolation</td> <td>javax.media.jai.Interpolation</td>
220: * <td>InterpolationNearest</td>
221: * <tr><td>backgroundValues</td> <td>double[]</td>
222: * <td>{0.0}</td>
223: * </table></p>
224: *
225: * @see java.awt.geom.AffineTransform
226: * @see javax.media.jai.Interpolation
227: * @see javax.media.jai.OperationDescriptor
228: */
229: public class AffineDescriptor extends OperationDescriptorImpl {
230:
231: /**
232: * The resource strings that provide the general documentation
233: * and specify the parameter list for this operation.
234: */
235: private static final String[][] resources = {
236: { "GlobalName", "Affine" },
237: { "LocalName", "Affine" },
238: { "Vendor", "com.sun.media.jai" },
239: { "Description", JaiI18N.getString("AffineDescriptor0") },
240: {
241: "DocURL",
242: "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AffineDescriptor.html" },
243: { "Version", JaiI18N.getString("DescriptorVersion") },
244: { "arg0Desc", JaiI18N.getString("AffineDescriptor1") },
245: { "arg1Desc", JaiI18N.getString("AffineDescriptor2") },
246: { "arg2Desc", JaiI18N.getString("AffineDescriptor3") }, };
247:
248: /** The parameter class list for this operation. */
249: private static final Class[] paramClasses = {
250: java.awt.geom.AffineTransform.class,
251: javax.media.jai.Interpolation.class, double[].class };
252:
253: /** The parameter name list for this operation. */
254: private static final String[] paramNames = { "transform",
255: "interpolation", "backgroundValues" };
256:
257: /** The parameter default value list for this operation. */
258: private static final Object[] paramDefaults = {
259: new AffineTransform(),
260: Interpolation.getInstance(Interpolation.INTERP_NEAREST),
261: new double[] { 0.0 } };
262:
263: /** Constructor. */
264: public AffineDescriptor() {
265: super (resources, 1, paramClasses, paramNames, paramDefaults);
266: }
267:
268: /** Returns <code>true</code> since renderable operation is supported. */
269: public boolean isRenderableSupported() {
270: return true;
271: }
272:
273: /**
274: * Returns an array of <code>PropertyGenerators</code> implementing
275: * property inheritance for the "Affine" operation.
276: *
277: * @return An array of property generators.
278: */
279: public PropertyGenerator[] getPropertyGenerators() {
280: PropertyGenerator[] pg = new PropertyGenerator[1];
281: pg[0] = new AffinePropertyGenerator();
282: return pg;
283: }
284:
285: /**
286: * Validates the input parameters.
287: *
288: * <p> In addition to the standard checks performed by the
289: * superclass method, this method checks that "transform" is
290: * invertible.
291: */
292: protected boolean validateParameters(ParameterBlock args,
293: StringBuffer message) {
294: if (!super .validateParameters(args, message)) {
295: return false;
296: }
297:
298: AffineTransform transform = (AffineTransform) args
299: .getObjectParameter(0);
300: try {
301: AffineTransform itransform = transform.createInverse();
302: } catch (java.awt.geom.NoninvertibleTransformException e) {
303: message.append(getName() + " "
304: + JaiI18N.getString("AffineDescriptor4"));
305: return false;
306: }
307:
308: return true;
309: }
310:
311: /**
312: * Performs interpolated affine transform on an image.
313: *
314: * <p>Creates a <code>ParameterBlockJAI</code> from all
315: * supplied arguments except <code>hints</code> and invokes
316: * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
317: *
318: * @see JAI
319: * @see ParameterBlockJAI
320: * @see RenderedOp
321: *
322: * @param source0 <code>RenderedImage</code> source 0.
323: * @param transform The affine transform matrix.
324: * May be <code>null</code>.
325: * @param interpolation The interpolation method.
326: * May be <code>null</code>.
327: * @param backgroundValues The user-specified background values.
328: * May be <code>null</code>.
329: * @param hints The <code>RenderingHints</code> to use.
330: * May be <code>null</code>.
331: * @return The <code>RenderedOp</code> destination.
332: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
333: */
334: public static RenderedOp create(RenderedImage source0,
335: AffineTransform transform, Interpolation interpolation,
336: double[] backgroundValues, RenderingHints hints) {
337: ParameterBlockJAI pb = new ParameterBlockJAI("Affine",
338: RenderedRegistryMode.MODE_NAME);
339:
340: pb.setSource("source0", source0);
341:
342: pb.setParameter("transform", transform);
343: pb.setParameter("interpolation", interpolation);
344: pb.setParameter("backgroundValues", backgroundValues);
345:
346: return JAI.create("Affine", pb, hints);
347: }
348:
349: /**
350: * Performs interpolated affine transform on an image.
351: *
352: * <p>Creates a <code>ParameterBlockJAI</code> from all
353: * supplied arguments except <code>hints</code> and invokes
354: * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}.
355: *
356: * @see JAI
357: * @see ParameterBlockJAI
358: * @see RenderableOp
359: *
360: * @param source0 <code>RenderableImage</code> source 0.
361: * @param transform The affine transform matrix.
362: * May be <code>null</code>.
363: * @param interpolation The interpolation method.
364: * May be <code>null</code>.
365: * @param backgroundValues The user-specified background values.
366: * May be <code>null</code>.
367: * @param hints The <code>RenderingHints</code> to use.
368: * May be <code>null</code>.
369: * @return The <code>RenderableOp</code> destination.
370: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
371: */
372: public static RenderableOp createRenderable(
373: RenderableImage source0, AffineTransform transform,
374: Interpolation interpolation, double[] backgroundValues,
375: RenderingHints hints) {
376: ParameterBlockJAI pb = new ParameterBlockJAI("Affine",
377: RenderableRegistryMode.MODE_NAME);
378:
379: pb.setSource("source0", source0);
380:
381: pb.setParameter("transform", transform);
382: pb.setParameter("interpolation", interpolation);
383: pb.setParameter("backgroundValues", backgroundValues);
384:
385: return JAI.createRenderable("Affine", pb, hints);
386: }
387: }
|