1 /*
2  * $RCSfile: PiecewiseDescriptor.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:43 $
10  * $State: Exp $
11  */
12 package com.lightcrafts.mediax.jai.operator;
13 
14 import java.awt.RenderingHints;
15 import java.awt.image.RenderedImage;
16 import java.awt.image.renderable.ParameterBlock;
17 import java.awt.image.renderable.RenderableImage;
18 import com.lightcrafts.mediax.jai.JAI;
19 import com.lightcrafts.mediax.jai.OperationDescriptorImpl;
20 import com.lightcrafts.mediax.jai.ParameterBlockJAI;
21 import com.lightcrafts.mediax.jai.RenderableOp;
22 import com.lightcrafts.mediax.jai.RenderedOp;
23 import com.lightcrafts.mediax.jai.registry.RenderableRegistryMode;
24 import com.lightcrafts.mediax.jai.registry.RenderedRegistryMode;
25 
26 /**
27  * An <code>OperationDescriptor</code> describing the "Piecewise" operation.
28  *
29  * <p> The "Piecewise" operation performs a piecewise linear mapping of the
30  * pixel values of an image.  The piecewise linear mapping is described by a
31  * set of breakpoints which are provided as an array of the form
32  * <pre>float breakPoints[N][2][numBreakPoints]</pre> where the value of
33  * <i>N</i> may be either unity or the number of bands in the source image.
34  * If <i>N</i> is unity then the same set of breakpoints will be applied to
35  * all bands in the image.  The abscissas of the supplied breakpoints must
36  * be monotonically increasing.
37  *
38  * <p> The pixel values of the destination image are defined by the pseudocode:
39  *
40  * <pre>
41  * if (src[x][y][b] < breakPoints[b][0][0]) {
42  *     dst[x][y][b] = breakPoints[b][1][0]);
43  * } else if (src[x][y][b] > breakPoints[b][0][numBreakPoints-1]) {
44  *     dst[x][y][b] = breakPoints[b][1][numBreakPoints-1]);
45  * } else {
46  *     int i = 0;
47  *     while(breakPoints[b][0][i+1] < src[x][y][b]) {
48  *         i++;
49  *     }
50  *     dst[x][y][b] = breakPoints[b][1][i] +
51  *                        (src[x][y][b] - breakPoints[b][0][i])*
52  *                        (breakPoints[b][1][i+1] - breakPoints[b][1][i])/
53  *                        (breakPoints[b][0][i+1] - breakPoints[b][0][i]);
54  * }
55  * </pre>
56  *
57  * <p><table border=1>
58  * <caption>Resource List</caption>
59  * <tr><th>Name</th>        <th>Value</th></tr>
60  * <tr><td>GlobalName</td>  <td>Piecewise</td></tr>
61  * <tr><td>LocalName</td>   <td>Piecewise</td></tr>
62  * <tr><td>Vendor</td>      <td>com.lightcrafts.media.jai</td></tr>
63  * <tr><td>Description</td> <td>Applies a piecewise pixel value mapping.</td></tr>
64  * <tr><td>DocURL</td>      <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PiecewiseDescriptor.html</td></tr>
65  * <tr><td>Version</td>     <td>1.0</td></tr>
66  * <tr><td>arg0Desc</td>    <td>The breakpoint array.</td></tr>
67  * </table></p>
68  *
69  * <p><table border=1>
70  * <caption>Parameter List</caption>
71  * <tr><th>Name</th>         <th>Class Type</th>
72  *                           <th>Default Value</th></tr>
73  * <tr><td>breakPoints</td>  <td>float[][][]</td>
74  *                           <td>identity mapping on [0, 255]</td>
75  * </table></p>
76  *
77  * @see java.awt.image.DataBuffer
78  * @see com.lightcrafts.mediax.jai.ImageLayout
79  * @see com.lightcrafts.mediax.jai.OperationDescriptor
80  */
81 public class PiecewiseDescriptor extends OperationDescriptorImpl {
82 
83     /**
84      * The resource strings that provide the general documentation
85      * and specify the parameter list for this operation.
86      */
87     private static final String[][] resources = {
88         {"GlobalName",  "Piecewise"},
89         {"LocalName",   "Piecewise"},
90         {"Vendor",      "com.lightcrafts.media.jai"},
91         {"Description", JaiI18N.getString("PiecewiseDescriptor0")},
92         {"DocURL",      "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PiecewiseDescriptor.html"},
93         {"Version",     JaiI18N.getString("DescriptorVersion")},
94         {"arg0Desc",    "The breakpoint array."}
95     };
96 
97     /** The parameter class list for this operation. */
98     private static final Class[] paramClasses = {
99         float[][][].class
100     };
101 
102     /** The parameter name list for this operation. */
103     private static final String[] paramNames = {
104         "breakPoints"
105     };
106 
107     /** The parameter default value list for this operation. */
108     private static final Object[] paramDefaults = {
109         new float[][][]{{{0.0f, 255.0f}, {0.0f, 255.0f}}}
110     };
111 
112     private static final String[] supportedModes = {
113 	"rendered",
114 	"renderable"
115     };
116 
117     /** Constructor. */
PiecewiseDescriptor()118     public PiecewiseDescriptor() {
119         super(resources, supportedModes, 1,
120 		paramNames, paramClasses, paramDefaults, null);
121     }
122 
123     /**
124      * Validates the input source and parameter.
125      *
126      * <p> In addition to the standard checks performed by the
127      * superclass method, this method checks that the number of bands
128      * in "breakPoints" is either 1 or the number of bands in the
129      * source image, the second breakpoint array dimension is 2,
130      * the third dimension is the same for abscissas and ordinates,
131      * and that the absicssas are monotonically increasing.
132      */
validateArguments(String modeName, ParameterBlock args, StringBuffer msg)133     public boolean validateArguments(String modeName,
134 				     ParameterBlock args,
135                                      StringBuffer msg) {
136         if (!super.validateArguments(modeName, args, msg)) {
137             return false;
138         }
139 
140 	if (!modeName.equalsIgnoreCase("rendered"))
141 	    return true;
142 
143 	// Get the source and the breakpoint array.
144         RenderedImage src = args.getRenderedSource(0);
145 
146         float[][][] breakPoints =
147             (float[][][])args.getObjectParameter(0);
148 
149         // Ensure that the number of breakpoint bands is either 1 or
150         // the number of bands in the source image, the second
151         // breakpoint array dimension is 2, the third dimension is
152         // the same for abscissas and ordinates, and that the absicssas
153         // are monotonically increasing.
154         if (breakPoints.length != 1 &&
155             breakPoints.length != src.getSampleModel().getNumBands()) {
156             // Number of breakpoints not 1 nor numBands.
157             msg.append(getName() + " " +
158                        JaiI18N.getString("PiecewiseDescriptor1"));
159             return false;
160         } else {
161 	    int numBands = breakPoints.length;
162 	    for (int b = 0; b < numBands; b++) {
163 	        if (breakPoints[b].length != 2) {
164                     // Second breakpoint dimension not 2.
165 		    msg.append(getName() + " " +
166 			       JaiI18N.getString("PiecewiseDescriptor2"));
167 		    return false;
168 		} else if (breakPoints[b][0].length !=
169                           breakPoints[b][1].length) {
170                     // Differing numbers of abscissas and ordinates.
171 		    msg.append(getName() + " " +
172 			       JaiI18N.getString("PiecewiseDescriptor3"));
173 		    return false;
174                 }
175 	    }
176 	    for (int b = 0; b < numBands; b++) {
177                 int count = breakPoints[b][0].length - 1;
178                 float[] x = breakPoints[b][0];
179                 for (int i = 0; i < count; i++) {
180                     if (x[i] >= x[i+1]) {
181                         // Abscissas not monotonically increasing.
182                         msg.append(getName() + " " +
183                                    JaiI18N.getString("PiecewiseDescriptor4"));
184                         return false;
185                     }
186                 }
187 	    }
188 	}
189 
190         return true;
191     }
192 
193 
194     /**
195      * Applies a piecewise pixel value mapping.
196      *
197      * <p>Creates a <code>ParameterBlockJAI</code> from all
198      * supplied arguments except <code>hints</code> and invokes
199      * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
200      *
201      * @see JAI
202      * @see ParameterBlockJAI
203      * @see RenderedOp
204      *
205      * @param source0 <code>RenderedImage</code> source 0.
206      * @param breakPoints The breakpoint array.
207      * May be <code>null</code>.
208      * @param hints The <code>RenderingHints</code> to use.
209      * May be <code>null</code>.
210      * @return The <code>RenderedOp</code> destination.
211      * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
212      */
create(RenderedImage source0, float[][][] breakPoints, RenderingHints hints)213     public static RenderedOp create(RenderedImage source0,
214                                     float[][][] breakPoints,
215                                     RenderingHints hints)  {
216         ParameterBlockJAI pb =
217             new ParameterBlockJAI("Piecewise",
218                                   RenderedRegistryMode.MODE_NAME);
219 
220         pb.setSource("source0", source0);
221 
222         pb.setParameter("breakPoints", breakPoints);
223 
224         return JAI.create("Piecewise", pb, hints);
225     }
226 
227     /**
228      * Applies a piecewise pixel value mapping.
229      *
230      * <p>Creates a <code>ParameterBlockJAI</code> from all
231      * supplied arguments except <code>hints</code> and invokes
232      * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}.
233      *
234      * @see JAI
235      * @see ParameterBlockJAI
236      * @see RenderableOp
237      *
238      * @param source0 <code>RenderableImage</code> source 0.
239      * @param breakPoints The breakpoint array.
240      * May be <code>null</code>.
241      * @param hints The <code>RenderingHints</code> to use.
242      * May be <code>null</code>.
243      * @return The <code>RenderableOp</code> destination.
244      * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
245      */
createRenderable(RenderableImage source0, float[][][] breakPoints, RenderingHints hints)246     public static RenderableOp createRenderable(RenderableImage source0,
247                                                 float[][][] breakPoints,
248                                                 RenderingHints hints)  {
249         ParameterBlockJAI pb =
250             new ParameterBlockJAI("Piecewise",
251                                   RenderableRegistryMode.MODE_NAME);
252 
253         pb.setSource("source0", source0);
254 
255         pb.setParameter("breakPoints", breakPoints);
256 
257         return JAI.createRenderable("Piecewise", pb, hints);
258     }
259 }
260