1 /*
2  * $RCSfile: WarpDescriptor.java,v $
3  *
4  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Use is subject to license terms.
7  *
8  * $Revision: 1.1 $
9  * $Date: 2005/02/11 04:57:46 $
10  * $State: Exp $
11  */
12 package com.lightcrafts.mediax.jai.operator;
13 import com.lightcrafts.media.jai.util.PropertyGeneratorImpl;
14 import java.awt.Rectangle;
15 import java.awt.RenderingHints;
16 import java.awt.geom.Point2D;
17 import java.awt.image.RenderedImage;
18 import java.awt.image.renderable.ParameterBlock;
19 import com.lightcrafts.mediax.jai.GeometricOpImage;
20 import com.lightcrafts.mediax.jai.ImageLayout;
21 import com.lightcrafts.mediax.jai.Interpolation;
22 import com.lightcrafts.mediax.jai.InterpolationNearest;
23 import com.lightcrafts.mediax.jai.JAI;
24 import com.lightcrafts.mediax.jai.OperationDescriptorImpl;
25 import com.lightcrafts.mediax.jai.ParameterBlockJAI;
26 import com.lightcrafts.mediax.jai.PlanarImage;
27 import com.lightcrafts.mediax.jai.PropertyGenerator;
28 import com.lightcrafts.mediax.jai.ROI;
29 import com.lightcrafts.mediax.jai.ROIShape;
30 import com.lightcrafts.mediax.jai.RenderedOp;
31 import com.lightcrafts.mediax.jai.Warp;
32 import com.lightcrafts.mediax.jai.registry.RenderedRegistryMode;
33 
34 /**
35  * This property generator computes the properties for the operation
36  * "Warp" dynamically.
37  */
38 class WarpPropertyGenerator extends PropertyGeneratorImpl {
39 
40     /** Constructor. */
WarpPropertyGenerator()41     public WarpPropertyGenerator() {
42         super(new String[] {"ROI"},
43               new Class[] {ROI.class},
44               new Class[] {RenderedOp.class});
45     }
46 
47     /**
48      * Returns the specified property.
49      *
50      * @param name  Property name.
51      * @param opNode Operation node.
52      */
getProperty(String name, Object opNode)53     public Object getProperty(String name,
54                               Object opNode) {
55         validate(name, opNode);
56 
57         if(opNode instanceof RenderedOp &&
58            name.equalsIgnoreCase("roi")) {
59             RenderedOp op = (RenderedOp)opNode;
60 
61             ParameterBlock pb = op.getParameterBlock();
62 
63             // Retrieve the rendered source image and its ROI.
64             RenderedImage src = (RenderedImage)pb.getRenderedSource(0);
65             Object property = src.getProperty("ROI");
66             if (property == null ||
67                 property.equals(java.awt.Image.UndefinedProperty) ||
68                 !(property instanceof ROI)) {
69                 return java.awt.Image.UndefinedProperty;
70             }
71 
72             // Return undefined also if source ROI is empty.
73             ROI srcROI = (ROI)property;
74             if (srcROI.getBounds().isEmpty()) {
75                 return java.awt.Image.UndefinedProperty;
76             }
77 
78             // Retrieve the Interpolation object.
79             Interpolation interp = (Interpolation)pb.getObjectParameter(1);
80 
81             // Determine the effective source bounds.
82             Rectangle srcBounds = null;
83             PlanarImage dst = op.getRendering();
84             if (dst instanceof GeometricOpImage &&
85                 ((GeometricOpImage)dst).getBorderExtender() == null) {
86                 srcBounds =
87                     new Rectangle(src.getMinX() + interp.getLeftPadding(),
88                                   src.getMinY() + interp.getTopPadding(),
89                                   src.getWidth() - interp.getWidth() + 1,
90                                   src.getHeight() - interp.getHeight() + 1);
91             } else {
92                 srcBounds = new Rectangle(src.getMinX(),
93 					  src.getMinY(),
94 					  src.getWidth(),
95 					  src.getHeight());
96             }
97 
98             // If necessary, clip the ROI to the effective source bounds.
99             if(!srcBounds.contains(srcROI.getBounds())) {
100                 srcROI = srcROI.intersect(new ROIShape(srcBounds));
101             }
102 
103             // Set the nearest neighbor interpolation object.
104             Interpolation interpNN = interp instanceof InterpolationNearest ?
105                 interp :
106                 Interpolation.getInstance(Interpolation.INTERP_NEAREST);
107 
108             // Retrieve the Warp object.
109             Warp warp = (Warp)pb.getObjectParameter(0);
110 
111             // Create the warped ROI.
112             ROI dstROI = new ROI(JAI.create("warp", srcROI.getAsImage(),
113                                             warp, interpNN));
114 
115             // Retrieve the destination bounds.
116             Rectangle dstBounds = op.getBounds();
117 
118             // If necessary, clip the warped ROI to the destination bounds.
119             if(!dstBounds.contains(dstROI.getBounds())) {
120                 dstROI = dstROI.intersect(new ROIShape(dstBounds));
121             }
122 
123             // Return the warped and possibly clipped ROI.
124             return dstROI;
125         }
126 
127         return java.awt.Image.UndefinedProperty;
128     }
129 }
130 
131 /**
132  * An <code>OperationDescriptor</code> describing the "Warp" operation.
133  *
134  * <p> The "Warp" operation performs (possibly filtered) general
135  * warping on an image.
136  *
137  * <p> The destination bounds may be specified by an {@link ImageLayout}
138  * hint provided via a {@link RenderingHints} supplied to the operation. If
139  * no bounds are so specified, then the destination bounds will be set to
140  * the minimum bounding rectangle of the forward mapped source bounds
141  * calculated using {@link Warp#mapSourceRect(Rectangle)} or, failing that,
142  * {@link Warp#mapSourcePoint(Point2D)} applied to the vertices of the
143  * source bounds. If forward mapping by both methods is not viable, then
144  * an approximate affine mapping will be created and used to determine the
145  * destination bounds by forward mapping the source bounds. If this approach
146  * also fails, then the destination bounds will be set to the source bounds.
147  *
148  * <p> "Warp" defines a PropertyGenerator that
149  * performs an identical transformation on the "ROI" property of the
150  * source image, which can be retrieved by calling the
151  * <code>getProperty</code> method with "ROI" as the property name.
152  *
153  * <p> The parameter, "backgroundValues", is defined to
154  * fill the background with the user-specified background
155  * values.  These background values will be translated into background
156  * colors by the <code>ColorModel</code> when the image is displayed.
157  * With the default value, <code>{0.0}</code>, of this parameter,
158  * the background pixels are filled with 0s.  If the provided array
159  * length is smaller than the number of bands, the first element of
160  * the provided array is used for all the bands.  If the provided values
161  * are out of the data range of the destination image, they will be clamped
162  * into the proper range.
163  *
164  * <p> It should be noted that this operation automatically adds a
165  * value of <code>Boolean.TRUE</code> for the
166  * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> to the given
167  * <code>configuration</code> so that the operation is performed
168  * on the pixel values instead of being performed on the indices into
169  * the color map if the source(s) have an <code>IndexColorModel</code>.
170  * This addition will take place only if a value for the
171  * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> has not already been
172  * provided by the user. Note that the <code>configuration</code> Map
173  * is cloned before the new hint is added to it. The operation can be
174  * smart about the value of the <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code>
175  * <code>RenderingHints</code>, i.e. while the default value for the
176  * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> is
177  * <code>Boolean.TRUE</code>, in some cases the operator could set the
178  * default.
179  *
180  * <p><table border=1>
181  * <caption>Resource List</caption>
182  * <tr><th>Name</th>        <th>Value</th></tr>
183  * <tr><td>GlobalName</td>  <td>Warp</td></tr>
184  * <tr><td>LocalName</td>   <td>Warp</td></tr>
185  * <tr><td>Vendor</td>      <td>com.lightcrafts.media.jai</td></tr>
186  * <tr><td>Description</td> <td>Warps an image according
187  *                              to a specified Warp object.</td></tr>
188  * <tr><td>DocURL</td>      <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/WarpDescriptor.html</td></tr>
189  * <tr><td>Version</td>     <td>1.0</td></tr>
190  * <tr><td>arg0Desc</td>    <td>The Warp object.</td></tr>
191  * <tr><td>arg1Desc</td>    <td>The interpolation method.</td></tr>
192  * </table></p>
193  *
194  * <p><table border=1>
195  * <caption>Parameter List</caption>
196  * <tr><th>Name</th>          <th>Class Type</th>
197  *                            <th>Default Value</th></tr>
198  * <tr><td>warp</td>          <td>com.lightcrafts.mediax.jai.Warp</td>
199  *                            <td>NO_PARAMETER_DEFAULT</td>
200  * <tr><td>interpolation</td> <td>com.lightcrafts.mediax.jai.Interpolation</td>
201  *                            <td>InterpolationNearest</td>
202  * <tr><td>backgroundValues</td> <td>double[]</td>
203  *                            <td>{0.0}</td>
204  * </table></p>
205  *
206  * @see com.lightcrafts.mediax.jai.Interpolation
207  * @see com.lightcrafts.mediax.jai.Warp
208  * @see com.lightcrafts.mediax.jai.OperationDescriptor
209  */
210 public class WarpDescriptor extends OperationDescriptorImpl {
211 
212     /**
213      * The resource strings that provide the general documentation and
214      * specify the parameter list for the "Warp" operation.
215      */
216     private static final String[][] resources = {
217         {"GlobalName",  "Warp"},
218         {"LocalName",   "Warp"},
219         {"Vendor",      "com.lightcrafts.media.jai"},
220         {"Description", JaiI18N.getString("WarpDescriptor0")},
221         {"DocURL",      "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/WarpDescriptor.html"},
222         {"Version",     JaiI18N.getString("DescriptorVersion")},
223         {"arg0Desc",    JaiI18N.getString("WarpDescriptor1")},
224         {"arg1Desc",    JaiI18N.getString("WarpDescriptor2")},
225         {"arg2Desc",    JaiI18N.getString("WarpDescriptor3")}
226     };
227 
228     /** The parameter names for the "Warp" operation. */
229     private static final String[] paramNames = {
230         "warp", "interpolation", "backgroundValues"
231     };
232 
233     /** The parameter class types for the "Warp" operation. */
234     private static final Class[] paramClasses = {
235         com.lightcrafts.mediax.jai.Warp.class, com.lightcrafts.mediax.jai.Interpolation.class,
236 	double[].class
237     };
238 
239     /** The parameter default values for the "Warp" operation. */
240     private static final Object[] paramDefaults = {
241         NO_PARAMETER_DEFAULT,
242         Interpolation.getInstance(Interpolation.INTERP_NEAREST),
243 	new double[] {0.0}
244     };
245 
246     /** Constructor. */
WarpDescriptor()247     public WarpDescriptor() {
248         super(resources, 1, paramClasses, paramNames, paramDefaults);
249     }
250 
251     /**
252      * Returns an array of <code>PropertyGenerators</code> implementing
253      * property inheritance for the "Warp" operation.
254      *
255      * @return  An array of property generators.
256      */
getPropertyGenerators()257     public PropertyGenerator[] getPropertyGenerators() {
258         PropertyGenerator[] pg = new PropertyGenerator[1];
259         pg[0] = new WarpPropertyGenerator();
260         return pg;
261     }
262 
263 
264     /**
265      * Warps an image according to a specified Warp object.
266      *
267      * <p>Creates a <code>ParameterBlockJAI</code> from all
268      * supplied arguments except <code>hints</code> and invokes
269      * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
270      *
271      * @see JAI
272      * @see ParameterBlockJAI
273      * @see RenderedOp
274      *
275      * @param source0 <code>RenderedImage</code> source 0.
276      * @param warp The warp object.
277      * @param interpolation The interpolation method.
278      * May be <code>null</code>.
279      * @param backgroundValues The user-specified background values.
280      * May be <code>null</code>.
281      * @param hints The <code>RenderingHints</code> to use.
282      * May be <code>null</code>.
283      * @return The <code>RenderedOp</code> destination.
284      * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
285      * @throws IllegalArgumentException if <code>warp</code> is <code>null</code>.
286      */
create(RenderedImage source0, Warp warp, Interpolation interpolation, double[] backgroundValues, RenderingHints hints)287     public static RenderedOp create(RenderedImage source0,
288                                     Warp warp,
289                                     Interpolation interpolation,
290                                     double[] backgroundValues,
291                                     RenderingHints hints)  {
292         ParameterBlockJAI pb =
293             new ParameterBlockJAI("Warp",
294                                   RenderedRegistryMode.MODE_NAME);
295 
296         pb.setSource("source0", source0);
297 
298         pb.setParameter("warp", warp);
299         pb.setParameter("interpolation", interpolation);
300         pb.setParameter("backgroundValues", backgroundValues);
301 
302         return JAI.create("Warp", pb, hints);
303     }
304 }
305