001: /*
002: * $RCSfile: ScaleDescriptor.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:43 $
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: * "Scale" dynamically.
039: */
040: class ScalePropertyGenerator extends PropertyGeneratorImpl {
041:
042: /** Constructor. */
043: public ScalePropertyGenerator() {
044: super (new String[] { "ROI" }, new Class[] { ROI.class },
045: new Class[] { RenderedOp.class });
046: }
047:
048: /**
049: * Returns the specified property.
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(4);
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 scale factors and translation values.
099: float sx = pb.getFloatParameter(0);
100: float sy = pb.getFloatParameter(1);
101: float tx = pb.getFloatParameter(2);
102: float ty = pb.getFloatParameter(3);
103:
104: // Create an equivalent transform.
105: AffineTransform transform = new AffineTransform(sx, 0.0,
106: 0.0, sy, tx, ty);
107:
108: // Create the scaled ROI.
109: ROI dstROI = srcROI.transform(transform);
110:
111: // Retrieve the destination bounds.
112: Rectangle dstBounds = op.getBounds();
113:
114: // If necessary, clip the warped ROI to the destination bounds.
115: if (!dstBounds.contains(dstROI.getBounds())) {
116: dstROI = dstROI.intersect(new ROIShape(dstBounds));
117: }
118:
119: // Return the warped and possibly clipped ROI.
120: return dstROI;
121: }
122:
123: return java.awt.Image.UndefinedProperty;
124: }
125: }
126:
127: /**
128: * An <code>OperationDescriptor</code> describing the "Scale" operation.
129: *
130: * <p> The "Scale" operation translates and resizes an image. For each
131: * pixel (x, y) of the destination, the source value at the fractional
132: * subpixel position ((x - xTrans)/xScale, (y - yTrans)/yScale) is
133: * constructed by means of an Interpolation object and written to the
134: * destination.
135: *
136: * <p> When applying scale factors of scaleX, scaleY to a source image
137: * with the upper left pixel at (srcMinX, srcMinY) and width of srcWidth
138: * and height of srcHeight, the resulting image is defined to have the
139: * following bounds:
140: *
141: * <code>
142: * dstMinX = ceil(A), where A = srcMinX * scaleX - 0.5 + transX,
143: * dstMinY = ceil(B), where B = srcMinY * scaleY - 0.5 + transY,
144: * dstMaxX = ceil(C), where C = (srcMaxX + 1) * scaleX - 1.5 + transX
145: * and srcMaxX = srcMinX + srcWidth - 1
146: * dstMaxY = ceil(D), where D = (srcMaxY + 1) * scaleY - 1.5 + transY
147: * and srcMaxY = srcMinY + srcHeight - 1
148: * dstWidth = dstMaxX - dstMinX + 1
149: * dstHeight = dstMaxY - dstMinY + 1
150: * </code>
151: *
152: * <p> In the case where source's upper left pixel is located is (0, 0),
153: * the formulae simplify to
154: *
155: * <code>
156: * dstMinX = 0
157: * dstMinY = 0
158: * dstWidth = ceil (srcWidth * scaleX - 0.5 + transX)
159: * dstHeight = ceil (srcHeight * scaleY - 0.5 + transY)
160: * </code>
161: *
162: * <p> In the case where the source's upper left pixel is located at (0, 0)
163: * and the scaling factors are integers, the formulae further simplify to
164: *
165: * <code>
166: * dstMinX = 0
167: * dstMinY = 0
168: * dstWidth = ceil (srcWidth * scaleX + transX)
169: * dstWidth = ceil (srcHeight * scaleY + transY)
170: * </code>
171: *
172: * <p> When interpolations which require padding the source such as Bilinear
173: * or Bicubic interpolation are specified, the source needs to be extended
174: * such that it has the extra pixels needed to compute all the destination
175: * pixels. This extension is performed via the <code>BorderExtender</code>
176: * class. The type of Border Extension can be specified as a
177: * <code>RenderingHint</code> to the <code>JAI.create</code> method.
178: *
179: * <p> If no Border Extension is specified, the source will not be extended.
180: * The scaled image size is still calculated according to the formula
181: * specified above. However since there isn't enough source to compute all the
182: * destination pixels, only that subset of the destination image's pixels,
183: * which can be computed, will be written in the destination. The rest of the
184: * destination will not be written.
185: *
186: * <p> Specifying a scale factor of greater than 1 increases the size
187: * of the image, specifying a scale factor between 0 and 1 (non-inclusive)
188: * decreases the size of an image. An IllegalArgumentException will be thrown
189: * if the specified scale factors are negative or equal to zero.
190: *
191: * <p> It may be noted that the minX, minY, width and height hints as
192: * specified through the <code>JAI.KEY_IMAGE_LAYOUT</code> hint in the
193: * <code>RenderingHints</code> object are not honored, as this operator
194: * calculates the destination image bounds itself. The other
195: * <code>ImageLayout</code> hints, like tileWidth and tileHeight,
196: * however are honored.
197: *
198: * <p> It should be noted that this operation automatically adds a
199: * value of <code>Boolean.TRUE</code> for the
200: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> to the given
201: * <code>configuration</code> so that the operation is performed
202: * on the pixel values instead of being performed on the indices into
203: * the color map if the source(s) have an <code>IndexColorModel</code>.
204: * This addition will take place only if a value for the
205: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> has not already been
206: * provided by the user. Note that the <code>configuration</code> Map
207: * is cloned before the new hint is added to it. The operation can be
208: * smart about the value of the <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code>
209: * <code>RenderingHints</code>, i.e. while the default value for the
210: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> is
211: * <code>Boolean.TRUE</code>, in some cases the operator could set the
212: * default.
213: *
214: * <p> "Scale" defines a PropertyGenerator that performs an identical
215: * transformation on the "ROI" property of the source image, which can
216: * be retrieved by calling the <code>getProperty</code> method with
217: * "ROI" as the property name.
218: *
219: * <p><table border=1>
220: * <caption>Resource List</caption>
221: * <tr><th>Name</th> <th>Value</th></tr>
222: * <tr><td>GlobalName</td> <td>Scale</td></tr>
223: * <tr><td>LocalName</td> <td>Scale</td></tr>
224: * <tr><td>Vendor</td> <td>com.sun.media.jai</td></tr>
225: * <tr><td>Description</td> <td>Resizes an image.</td></tr>
226: * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ScaleDescriptor.html</td></tr>
227: * <tr><td>Version</td> <td>1.0</td></tr>
228: * <tr><td>arg0Desc</td> <td>The X scale factor.</td></tr>
229: * <tr><td>arg1Desc</td> <td>The Y scale factor.</td></tr>
230: * <tr><td>arg2Desc</td> <td>The X translation.</td></tr>
231: * <tr><td>arg3Desc</td> <td>The Y translation.</td></tr>
232: * <tr><td>arg4Desc</td> <td>The interpolation method for
233: * resampling.</td></tr>
234: * </table></p>
235: *
236: * <p><table border=1>
237: * <caption>Parameter List</caption>
238: * <tr><th>Name</th> <th>Class Type</th>
239: * <th>Default Value</th></tr>
240: * <tr><td>xScale</td> <td>java.lang.Float</td>
241: * <td>1.0F</td>
242: * <tr><td>yScale</td> <td>java.lang.Float</td>
243: * <td>1.0F</td>
244: * <tr><td>xTrans</td> <td>java.lang.Float</td>
245: * <td>0.0F</td>
246: * <tr><td>yTrans</td> <td>java.lang.Float</td>
247: * <td>0.0F</td>
248: * <tr><td>interpolation</td> <td>javax.media.jai.Interpolation</td>
249: * <td>InterpolationNearest</td>
250: * </table></p>
251: *
252: * @see javax.media.jai.Interpolation
253: * @see javax.media.jai.BorderExtender
254: * @see javax.media.jai.OperationDescriptor
255: */
256: public class ScaleDescriptor extends OperationDescriptorImpl {
257:
258: /**
259: * The resource strings that provide the general documentation
260: * and specify the parameter list for this operation.
261: */
262: private static final String[][] resources = {
263: { "GlobalName", "Scale" },
264: { "LocalName", "Scale" },
265: { "Vendor", "com.sun.media.jai" },
266: { "Description", JaiI18N.getString("ScaleDescriptor0") },
267: {
268: "DocURL",
269: "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ScaleDescriptor.html" },
270: { "Version", JaiI18N.getString("DescriptorVersion") },
271: { "arg0Desc", JaiI18N.getString("ScaleDescriptor1") },
272: { "arg1Desc", JaiI18N.getString("ScaleDescriptor2") },
273: { "arg2Desc", JaiI18N.getString("ScaleDescriptor3") },
274: { "arg3Desc", JaiI18N.getString("ScaleDescriptor4") },
275: { "arg4Desc", JaiI18N.getString("ScaleDescriptor5") } };
276:
277: /** The parameter class list for this operation. */
278: private static final Class[] paramClasses = {
279: java.lang.Float.class, java.lang.Float.class,
280: java.lang.Float.class, java.lang.Float.class,
281: javax.media.jai.Interpolation.class };
282:
283: /** The parameter name list for this operation. */
284: private static final String[] paramNames = { "xScale", "yScale",
285: "xTrans", "yTrans", "interpolation" };
286:
287: /** The parameter default value list for this operation. */
288: private static final Object[] paramDefaults = { new Float(1.0F),
289: new Float(1.0F), new Float(0.0F), new Float(0.0F),
290: Interpolation.getInstance(Interpolation.INTERP_NEAREST) };
291:
292: /** Constructor. */
293: public ScaleDescriptor() {
294: super (resources, 1, paramClasses, paramNames, paramDefaults);
295: }
296:
297: /** Returns <code>true</code> since renderable operation is supported. */
298: public boolean isRenderableSupported() {
299: return true;
300: }
301:
302: /**
303: * Returns an array of <code>PropertyGenerators</code> implementing
304: * property inheritance for the "Scale" operation.
305: *
306: * @return An array of property generators.
307: */
308: public PropertyGenerator[] getPropertyGenerators() {
309: PropertyGenerator[] pg = new PropertyGenerator[1];
310: pg[0] = new ScalePropertyGenerator();
311: return pg;
312: }
313:
314: /**
315: * Validates the input parameters.
316: *
317: * <p> In addition to the standard checks performed by the
318: * superclass method, this method checks that "xScale" and "yScale"
319: * are both greater than 0.
320: */
321: protected boolean validateParameters(ParameterBlock args,
322: StringBuffer msg) {
323: if (!super .validateParameters(args, msg)) {
324: return false;
325: }
326:
327: float xScale = args.getFloatParameter(0);
328: float yScale = args.getFloatParameter(1);
329: if (xScale <= 0 || yScale <= 0) {
330: msg.append(getName() + " "
331: + JaiI18N.getString("ScaleDescriptor6"));
332: return false;
333: }
334:
335: return true;
336: }
337:
338: /**
339: * Returns the minimum legal value of a specified numeric parameter
340: * for this operation.
341: *
342: * <p> For the minimum value of "xScale" and "yScale", this method
343: * returns 0. However, the scale factors must be a positive floating
344: * number and can not be 0.
345: */
346: public Number getParamMinValue(int index) {
347: if (index == 0 || index == 1) {
348: return new Float(0.0F);
349: } else if (index == 2 || index == 3) {
350: return new Float(-Float.MAX_VALUE);
351: } else if (index == 4) {
352: return null;
353: } else {
354: throw new ArrayIndexOutOfBoundsException();
355: }
356: }
357:
358: /**
359: * Resizes an image.
360: *
361: * <p>Creates a <code>ParameterBlockJAI</code> from all
362: * supplied arguments except <code>hints</code> and invokes
363: * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
364: *
365: * @see JAI
366: * @see ParameterBlockJAI
367: * @see RenderedOp
368: *
369: * @param source0 <code>RenderedImage</code> source 0.
370: * @param xScale The X scale factor.
371: * May be <code>null</code>.
372: * @param yScale The Y scale factor.
373: * May be <code>null</code>.
374: * @param xTrans The X translation.
375: * May be <code>null</code>.
376: * @param yTrans The Y translation.
377: * May be <code>null</code>.
378: * @param interpolation The interpolation method for resampling.
379: * May be <code>null</code>.
380: * @param hints The <code>RenderingHints</code> to use.
381: * May be <code>null</code>.
382: * @return The <code>RenderedOp</code> destination.
383: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
384: */
385: public static RenderedOp create(RenderedImage source0,
386: Float xScale, Float yScale, Float xTrans, Float yTrans,
387: Interpolation interpolation, RenderingHints hints) {
388: ParameterBlockJAI pb = new ParameterBlockJAI("Scale",
389: RenderedRegistryMode.MODE_NAME);
390:
391: pb.setSource("source0", source0);
392:
393: pb.setParameter("xScale", xScale);
394: pb.setParameter("yScale", yScale);
395: pb.setParameter("xTrans", xTrans);
396: pb.setParameter("yTrans", yTrans);
397: pb.setParameter("interpolation", interpolation);
398:
399: return JAI.create("Scale", pb, hints);
400: }
401:
402: /**
403: * Resizes an image.
404: *
405: * <p>Creates a <code>ParameterBlockJAI</code> from all
406: * supplied arguments except <code>hints</code> and invokes
407: * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}.
408: *
409: * @see JAI
410: * @see ParameterBlockJAI
411: * @see RenderableOp
412: *
413: * @param source0 <code>RenderableImage</code> source 0.
414: * @param xScale The X scale factor.
415: * May be <code>null</code>.
416: * @param yScale The Y scale factor.
417: * May be <code>null</code>.
418: * @param xTrans The X translation.
419: * May be <code>null</code>.
420: * @param yTrans The Y translation.
421: * May be <code>null</code>.
422: * @param interpolation The interpolation method for resampling.
423: * May be <code>null</code>.
424: * @param hints The <code>RenderingHints</code> to use.
425: * May be <code>null</code>.
426: * @return The <code>RenderableOp</code> destination.
427: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
428: */
429: public static RenderableOp createRenderable(
430: RenderableImage source0, Float xScale, Float yScale,
431: Float xTrans, Float yTrans, Interpolation interpolation,
432: RenderingHints hints) {
433: ParameterBlockJAI pb = new ParameterBlockJAI("Scale",
434: RenderableRegistryMode.MODE_NAME);
435:
436: pb.setSource("source0", source0);
437:
438: pb.setParameter("xScale", xScale);
439: pb.setParameter("yScale", yScale);
440: pb.setParameter("xTrans", xTrans);
441: pb.setParameter("yTrans", yTrans);
442: pb.setParameter("interpolation", interpolation);
443:
444: return JAI.createRenderable("Scale", pb, hints);
445: }
446: }
|