001: /*
002: * $RCSfile: SubsampleAverageDescriptor.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:44 $
010: * $State: Exp $
011: */package javax.media.jai.operator;
012:
013: import java.awt.Rectangle;
014: import java.awt.RenderingHints;
015: import java.awt.geom.AffineTransform;
016: import java.awt.image.RenderedImage;
017: import java.awt.image.renderable.ParameterBlock;
018: import java.awt.image.renderable.RenderableImage;
019: import javax.media.jai.GeometricOpImage;
020: import javax.media.jai.Interpolation;
021: import javax.media.jai.InterpolationNearest;
022: import javax.media.jai.JAI;
023: import javax.media.jai.OpImage;
024: import javax.media.jai.OperationDescriptorImpl;
025: import javax.media.jai.ParameterBlockJAI;
026: import javax.media.jai.PlanarImage;
027: import javax.media.jai.PropertyGenerator;
028: import javax.media.jai.ROI;
029: import javax.media.jai.ROIShape;
030: import javax.media.jai.RenderableOp;
031: import javax.media.jai.RenderedOp;
032: import javax.media.jai.registry.RenderableRegistryMode;
033: import javax.media.jai.registry.RenderedRegistryMode;
034: import javax.media.jai.util.Range;
035:
036: /**
037: * This property generator computes the properties for the operation
038: * "SubsampleAverage" dynamically.
039: *
040: * @since JAI 1.1.2
041: */
042: class SubsampleAveragePropertyGenerator implements PropertyGenerator {
043:
044: /** Constructor. */
045: public SubsampleAveragePropertyGenerator() {
046: }
047:
048: /**
049: * Returns the valid property names for the operation "SubsampleAverage".
050: * This is equal to the array <code>{"ROI"}</code>.
051: */
052: public String[] getPropertyNames() {
053: String[] properties = new String[1];
054: properties[0] = "ROI";
055: return (properties);
056: }
057:
058: /**
059: * Returns the expected class which is <code>ROI.class</code> if
060: * <code>propertyName</code> and <code>null</code> otherwise.
061: */
062: public Class getClass(String propertyName) {
063: if (propertyName == null) {
064: throw new IllegalArgumentException(JaiI18N
065: .getString("SubsampleAveragePropertyGenerator0"));
066: } else if (propertyName.equalsIgnoreCase("roi")) {
067: return ROI.class;
068: }
069:
070: return null;
071: }
072:
073: /**
074: * Determines whether properties can be generated from the supplied node.
075: */
076: public boolean canGenerateProperties(Object opNode) {
077: if (opNode == null) {
078: throw new IllegalArgumentException(JaiI18N
079: .getString("SubsampleAveragePropertyGenerator1"));
080: }
081:
082: return opNode instanceof RenderedOp;
083: }
084:
085: /**
086: * Returns the specified property for the supplied node.
087: *
088: * @param name Property name.
089: * @param opNode Operation node.
090: */
091: public Object getProperty(String name, Object opNode) {
092: if (name == null || opNode == null) {
093: throw new IllegalArgumentException(JaiI18N
094: .getString("SubsampleAveragePropertyGenerator2"));
095: } else if (!canGenerateProperties(opNode)) {
096: throw new IllegalArgumentException(
097: opNode.getClass().getName()
098: + JaiI18N
099: .getString("SubsampleAveragePropertyGenerator3"));
100: }
101:
102: return opNode instanceof RenderedOp ? getProperty(name,
103: (RenderedOp) opNode) : null;
104: }
105:
106: /**
107: * Returns the specified property.
108: *
109: * @param name Property name.
110: * @param op Operation node.
111: */
112: public Object getProperty(String name, RenderedOp op) {
113: if (name == null || op == null) {
114: throw new IllegalArgumentException(JaiI18N
115: .getString("SubsampleAveragePropertyGenerator4"));
116: }
117:
118: if (name.equals("roi")) {
119: ParameterBlock pb = op.getParameterBlock();
120:
121: // Retrieve the rendered source image and its ROI.
122: PlanarImage src = (PlanarImage) pb.getRenderedSource(0);
123: Object property = src.getProperty("ROI");
124: if (property == null
125: || property
126: .equals(java.awt.Image.UndefinedProperty)
127: || !(property instanceof ROI)) {
128: return null;
129: }
130: ROI srcROI = (ROI) property;
131:
132: // Determine the effective source bounds.
133: Rectangle srcBounds = null;
134: PlanarImage dst = op.getRendering();
135: if (dst instanceof GeometricOpImage
136: && ((GeometricOpImage) dst).getBorderExtender() == null) {
137: GeometricOpImage geomIm = (GeometricOpImage) dst;
138: Interpolation interp = geomIm.getInterpolation();
139: srcBounds = new Rectangle(src.getMinX()
140: + interp.getLeftPadding(), src.getMinY()
141: + interp.getTopPadding(), src.getWidth()
142: - interp.getWidth() + 1, src.getHeight()
143: - interp.getHeight() + 1);
144: } else {
145: srcBounds = src.getBounds();
146: }
147:
148: // If necessary, clip the ROI to the effective source bounds.
149: if (!srcBounds.contains(srcROI.getBounds())) {
150: srcROI = srcROI.intersect(new ROIShape(srcBounds));
151: }
152:
153: // Retrieve the scale factors and translation values.
154: double sx = pb.getDoubleParameter(0);
155: double sy = pb.getDoubleParameter(1);
156:
157: // Create an equivalent transform.
158: AffineTransform transform = new AffineTransform(sx, 0.0,
159: 0.0, sy, 0, 0);
160:
161: // Create the scaled ROI.
162: ROI dstROI = srcROI.transform(transform);
163:
164: // Retrieve the destination bounds.
165: Rectangle dstBounds = op.getBounds();
166:
167: // If necessary, clip the warped ROI to the destination bounds.
168: if (!dstBounds.contains(dstROI.getBounds())) {
169: dstROI = dstROI.intersect(new ROIShape(dstBounds));
170: }
171:
172: // Return the warped and possibly clipped ROI.
173: return dstROI;
174: } else {
175: return null;
176: }
177: }
178:
179: /**
180: * Returns null.
181: *
182: * @param name Property name.
183: * @param op Operation node.
184: */
185: public Object getProperty(String name, RenderableOp op) {
186: if (name == null || op == null) {
187: throw new IllegalArgumentException(JaiI18N
188: .getString("SubsampleAveragePropertyGenerator2"));
189: }
190:
191: return null;
192: }
193: }
194:
195: /**
196: * An <code>OperationDescriptor</code> describing the "SubsampleAverage"
197: * operation. "SubsampleAverage" supports the rendered and renderable modes.
198: *
199: * <p>
200: * The "SubsampleAverage" operation subsamples an image by averaging
201: * over a moving window. The scale factors supplied to the operation are
202: * forward mapping coefficients representing the geometric transformation
203: * from source to destination image coordinates. For example, if both
204: * scale factors were equal to 0.5, the operation would produce an output
205: * image of half the size of the input image in both dimensions. Both
206: * scale factors must be in the range <code>(0.0, 1.0]</code> or an
207: * exception will be thrown when the operation is created. The default
208: * value of the vertical scale factor is the value of the horizontal scale
209: * factor. If both scale factors equal <core>1.0</code>, the source
210: * image is returned directly.
211: * </p>
212: *
213: * <p>
214: * The size of the moving window or <i>block</i> over which source pixels are
215: * averaged varies as a function of the scale factors and is defined as
216: * <pre>
217: * int blockX = (int)Math.ceil(1.0/scaleX);
218: * int blockY = (int)Math.ceil(1.0/scaleY);
219: * </pre>
220: * </p>
221: *
222: * <p>
223: * For a given destination pixel <code>(dstX, dstY)</code>, the upper
224: * left corner <code>(srcX, srcY)</code> of the source block over which
225: * averaging occurs is defined as
226: * <pre>
227: * int srcX = (int)Math.floor((dstX - dstMinX)/scaleX + 0.5) + srcMinX;
228: * int srcY = (int)Math.floor((dstY - dstMinY)/scaleY + 0.5) + srcMinY;
229: * </pre>
230: * where <code>(srcMinX, srcMinY)</code> are the image coordinates of the
231: * upper left pixel of the source and <code>(dstMinX, dstMinY)</code> are
232: * the image coordinates of the upper left pixel of the destination.
233: * </p>
234: *
235: * <p>
236: * The destination image bounds are defined as
237: * <pre>
238: * int dstMinX = (int)Math.floor(srcMinX*scaleX);
239: * int dstMinY = (int)Math.floor(srcMinY*scaleY);
240: * int dstWidth = (int)(srcWidth*scaleX);
241: * int dstHeight = (int)(srcHeight*scaleY);
242: * </pre>
243: * where <code>(srcWidth, srcHeight)</code> and
244: * <code>(dstWidth, dstHeight)</code> are the source and destination
245: * image dimensions, respectively.
246: * </p>
247: *
248: * <p><table border=1>
249: * <caption>Resource List</caption>
250: * <tr><th>Name</th> <th>Value</th></tr>
251: * <tr><td>GlobalName</td> <td>SubsampleAverage</td></tr>
252: * <tr><td>LocalName</td> <td>SubsampleAverage</td></tr>
253: * <tr><td>Vendor</td> <td>com.sun.media.jai</td></tr>
254: * <tr><td>Description</td> <td>Subsamples an image by averaging over a moving window.</td></tr>
255: * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubsampleAverageDescriptor.html</td></tr>
256: * <tr><td>Version</td> <td>1.0</td></tr>
257: * <tr><td>arg0Desc</td> <td>The X scale factor.</td></tr>
258: * <tr><td>arg1Desc</td> <td>The Y scale factor.</td></tr>
259: * </table></p>
260: *
261: * <p><table border=1>
262: * <caption>Parameter List</caption>
263: * <tr><th>Name</th> <th>Class Type</th>
264: * <th>Default Value</th></tr>
265: * <tr><td>scaleX</td> <td>java.lang.Double</td>
266: * <td>0.5</td>
267: * <tr><td>scaleY</td> <td>java.lang.Double</td>
268: * <td>scaleX</td>
269: * </table></p>
270: *
271: * @see FilteredSubsampleDescriptor
272: * @see SubsampleBinaryToGrayDescriptor
273: * @see ScaleDescriptor
274: *
275: * @since JAI 1.1.2
276: */
277: public class SubsampleAverageDescriptor extends OperationDescriptorImpl {
278:
279: /**
280: * The resource strings that provide the general documentation
281: * and specify the parameter list for this operation.
282: */
283: private static final String[][] resources = {
284: { "GlobalName", "SubsampleAverage" },
285: { "LocalName", "SubsampleAverage" },
286: { "Vendor", "com.sun.media.jai" },
287: { "Description",
288: JaiI18N.getString("SubsampleAverageDescriptor0") },
289: {
290: "DocURL",
291: "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubsampleAverageDescriptor.html" },
292: { "Version", JaiI18N.getString("DescriptorVersion") },
293: { "arg0Desc",
294: JaiI18N.getString("SubsampleAverageDescriptor1") },
295: { "arg1Desc",
296: JaiI18N.getString("SubsampleAverageDescriptor2") } };
297:
298: /** The parameter class list for this operation. */
299: private static final Class[] paramClasses = {
300: java.lang.Double.class, java.lang.Double.class };
301:
302: /** The parameter name list for this operation. */
303: private static final String[] paramNames = { "scaleX", "scaleY" };
304:
305: /** The parameter default value list for this operation. */
306: private static final Object[] paramDefaults = { new Double(0.5),
307: null };
308:
309: /** The allowable <code>Range</code>s of parameter values. */
310: private static final Object[] validParamValues = {
311: new Range(Double.class, new Double(Double.MIN_VALUE),
312: new Double(1.0)),
313: new Range(Double.class, new Double(Double.MIN_VALUE),
314: new Double(1.0)) };
315:
316: /** Constructor. */
317: public SubsampleAverageDescriptor() {
318: super (resources, new String[] { RenderedRegistryMode.MODE_NAME,
319: RenderableRegistryMode.MODE_NAME }, 1, paramNames,
320: paramClasses, paramDefaults, validParamValues);
321: }
322:
323: /**
324: * Returns an array of <code>PropertyGenerators</code> implementing
325: * property inheritance for the "SubsampleAverage" operation.
326: */
327: public PropertyGenerator[] getPropertyGenerators(String modeName) {
328: if (modeName == null) {
329: throw new IllegalArgumentException(JaiI18N
330: .getString("SubsampleAverageDescriptor3"));
331: }
332:
333: if (!RenderedRegistryMode.MODE_NAME.equalsIgnoreCase(modeName)) {
334: PropertyGenerator[] pg = new PropertyGenerator[1];
335: pg[0] = new SubsampleAveragePropertyGenerator();
336: return pg;
337: }
338:
339: return null;
340: }
341:
342: /**
343: * Validates the input parameters.
344: *
345: * <p> In addition to the standard checks performed by the
346: * superclass method, this method sets "scaleX" to "scaleY"
347: * if the latter is not provided in <code>args</code>.
348: */
349: protected boolean validateParameters(String modeName,
350: ParameterBlock args, StringBuffer msg) {
351: if (!super .validateParameters(modeName, args, msg)) {
352: return false;
353: }
354:
355: if (args.getNumParameters() < 2
356: || args.getObjectParameter(1) == null) {
357: args.set(args.getObjectParameter(0), 1);
358: }
359:
360: return true;
361: }
362:
363: /**
364: * Subsamples an image by averaging over a moving window.
365: *
366: * <p>Creates a <code>ParameterBlockJAI</code> from all
367: * supplied arguments except <code>hints</code> and invokes
368: * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
369: *
370: * @see JAI
371: * @see ParameterBlockJAI
372: * @see RenderedOp
373: *
374: * @param source0 <code>RenderedImage</code> source 0.
375: * @param scaleX The X scale factor.
376: * May be <code>null</code>.
377: * @param scaleY The Y scale factor.
378: * May be <code>null</code>.
379: * @param hints The <code>RenderingHints</code> to use.
380: * May be <code>null</code>.
381: * @return The <code>RenderedOp</code> destination.
382: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
383: */
384: public static RenderedOp create(RenderedImage source0,
385: Double scaleX, Double scaleY, RenderingHints hints) {
386: ParameterBlockJAI pb = new ParameterBlockJAI(
387: "SubsampleAverage", RenderedRegistryMode.MODE_NAME);
388:
389: pb.setSource("source0", source0);
390:
391: pb.setParameter("scaleX", scaleX);
392: pb.setParameter("scaleY", scaleY);
393:
394: return JAI.create("SubsampleAverage", pb, hints);
395: }
396:
397: /**
398: * Subsamples an image by averaging over a moving window.
399: *
400: * <p>Creates a <code>ParameterBlockJAI</code> from all
401: * supplied arguments except <code>hints</code> and invokes
402: * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}.
403: *
404: * @see JAI
405: * @see ParameterBlockJAI
406: * @see RenderableOp
407: *
408: * @param source0 <code>RenderableImage</code> source 0.
409: * @param scaleX The X scale factor.
410: * May be <code>null</code>.
411: * @param scaleY The Y scale factor.
412: * May be <code>null</code>.
413: * @param hints The <code>RenderingHints</code> to use.
414: * May be <code>null</code>.
415: * @return The <code>RenderableOp</code> destination.
416: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
417: */
418: public static RenderableOp createRenderable(
419: RenderableImage source0, Double scaleX, Double scaleY,
420: RenderingHints hints) {
421: ParameterBlockJAI pb = new ParameterBlockJAI(
422: "SubsampleAverage", RenderableRegistryMode.MODE_NAME);
423:
424: pb.setSource("source0", source0);
425:
426: pb.setParameter("scaleX", scaleX);
427: pb.setParameter("scaleY", scaleY);
428:
429: return JAI.createRenderable("SubsampleAverage", pb, hints);
430: }
431: }
|