/* JAI-Ext - OpenSource Java Advanced Image Extensions Library
* http://www.geo-solutions.it/
* Copyright 2014 GeoSolutions
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.geosolutions.jaiext.rescale;
import it.geosolutions.jaiext.range.Range;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.awt.image.renderable.RenderableImage;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptorImpl;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.PropertyGenerator;
import javax.media.jai.ROI;
import javax.media.jai.ROIShape;
import javax.media.jai.RenderableOp;
import javax.media.jai.RenderedOp;
import javax.media.jai.registry.RenderableRegistryMode;
import javax.media.jai.registry.RenderedRegistryMode;
import com.sun.media.jai.util.PropertyGeneratorImpl;
/**
* This class is used for retrieving an eventual ROI object passed to the source image by calling the getProperty() method. This method checks if the
* ROI is present and if so, its bounds are intersected with the source images bounds, and then passed as a result. If no property was found an
* Undefined Property object is returned.
*/
class RescalePropertyGenerator extends PropertyGeneratorImpl {
/** Constructor. */
public RescalePropertyGenerator() {
super(new String[] { "ROI" }, new Class[] { ROI.class }, new Class[] { RenderedOp.class });
}
/**
* Returns the ROI saved as a property.
*/
public Object getProperty(String name, Object opNode) {
validate(name, opNode);
if (opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) {
RenderedOp op = (RenderedOp) opNode;
ParameterBlock pb = op.getParameterBlock();
// Retrieve the rendered source image and its ROI.
RenderedImage src = pb.getRenderedSource(0);
Object property = src.getProperty("ROI");
if (property == null || property.equals(java.awt.Image.UndefinedProperty)
|| !(property instanceof ROI)) {
return java.awt.Image.UndefinedProperty;
}
ROI srcROI = (ROI) property;
// Determine the effective source bounds.
Rectangle srcBounds = null;
srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight());
// If necessary, clip the ROI to the effective source bounds.
if (!srcBounds.contains(srcROI.getBounds())) {
srcROI = srcROI.intersect(new ROIShape(srcBounds));
}
// Saves the destination ROI.
ROI dstROI = srcROI;
// Return the clipped ROI.
return dstROI;
}
return java.awt.Image.UndefinedProperty;
}
}
/**
* An <code>OperationDescriptor</code> describing the "Rescale" operation.
*
* <p>
* The "Rescale" operation takes a rendered or renderable source image and changes the image dynamics by multiplying each pixel value by a constant
* and then adding another constant to the result of the multiplication. Each constant value is associated to a band. If the number of constants
* supplied is less than the number of bands of the destination, then the constant from entry 0 is applied to all the bands. Otherwise, a constant
* from a different entry is applied to each band. The optional presence of NoData or ROI is taken into account by replacing each value out of ROI or
* each NoData, with the supplied DestinationNoData value.
*
* <p>
* The destination pixel values are defined by the following pseudocode:
*
* <pre>
* dst = destination pixel array
* src = source pixel array
*
* dst[x][y][b] = src[x][y][b] * constant + offset;
* </pre>
*
* <p>
* The pixel arithmetic is performed using the data type of the destination image. By default, the destination will have the same data type as the
* source image unless an <code>ImageLayout</code> containing a <code>SampleModel</code> with a different data type is supplied as a rendering hint.
*
* <p>
* <table border=1>
* <caption>Resource List</caption>
* <tr>
* <th>Name</th>
* <th>Value</th>
* </tr>
* <tr>
* <td>GlobalName</td>
* <td>Rescale</td>
* </tr>
* <tr>
* <td>LocalName</td>
* <td>Rescale</td>
* </tr>
* <tr>
* <td>Vendor</td>
* <td>it.geosolutions.jaiext</td>
* </tr>
* <tr>
* <td>Description</td>
* <td>Operation which converts the image dynamic to a new dynamic.</td>
* </tr>
* <tr>
* <td>DocURL</td>
* <td>Not Defined</td>
* </tr>
* <tr>
* <td>Version</td>
* <td>1.0</td>
* </tr>
* <tr>
* <td>arg0Desc</td>
* <td>Scale factors used for rescaling values.</td>
* </tr>
* <tr>
* <td>arg1Desc</td>
* <td>Offset factors used for rescaling values.</td>
* </tr>
* <tr>
* <td>arg2Desc</td>
* <td>ROI object used.</td>
* </tr>
* <tr>
* <td>arg3Desc</td>
* <td>No Data Range used.</td>
* </tr>
* <tr>
* <td>arg4Desc</td>
* <td>Boolean checking if ROI RasterAccessor is used.</td>
* </tr>
* <tr>
* <td>arg5Desc</td>
* <td>Destination No Data value.</td>
* </tr>
* </table>
* </p>
*
* <p>
* <table border=1>
* <caption>Parameter List</caption>
* <tr>
* <th>Name</th>
* <th>Class Type</th>
* <th>Default Value</th>
* </tr>
* <tr>
* <td>scale</td>
* <td>double[]</td>
* <td>{1.0}</td>
* <tr>
* <td>offset</td>
* <td>double[]</td>
* <td>{0.0}</td>
* <tr>
* <td>ROI</td>
* <td>javax.media.jai.ROI</td>
* <td>null</td>
* <tr>
* <td>noData</td>
* <td>it.geosolutions.jaiext.range.Range</td>
* <td>null</td>
* <tr>
* <td>useRoiAccessor</td>
* <td>Boolean</td>
* <td>false</td>
* <tr>
* <td>destNoData</td>
* <td>Double</td>
* <td>0.0d</td>
* </table>
* </p>
*/
@SuppressWarnings("serial")
public class RescaleDescriptor extends OperationDescriptorImpl {
/**
* The resource strings that provide the general documentation and specify the parameter list for this operation.
*/
private static final String[][] resources = { { "GlobalName", "Rescale" },
{ "LocalName", "Rescale" }, { "Vendor", "it.geosolutions.jaiext" },
{ "Description", "Operation which converts the image dynamic to a new dynamic" },
{ "DocURL", "Not Defined" }, { "Version", "1.0" },
{ "arg0Desc", "Scale factors used for rescaling values" },
{ "arg1Desc", "Offset factors used for rescaling values" },
{ "arg2Desc", "ROI object used" }, { "arg3Desc", "No Data Range used" },
{ "arg4Desc", "Boolean checking if ROI RasterAccessor is used" },
{ "arg5Desc", "Destination No Data value" } };
/** The parameter class list for this operation. */
private static final Class[] paramClasses = { double[].class, double[].class,
javax.media.jai.ROI.class, it.geosolutions.jaiext.range.Range.class, Boolean.class,
Double.class };
/** The parameter name list for this operation. */
private static final String[] paramNames = { "constants", "offsets", "ROI", "noData",
"useRoiAccessor", "destNoData" };
/** The parameter default value list for this operation. */
private static final Object[] paramDefaults = { new double[] { 1.0 }, new double[] { 0.0 },
null, null, false, 0.0d };
/** Constructor. */
public RescaleDescriptor() {
super(resources, 1, paramClasses, paramNames, paramDefaults);
}
/** Returns <code>true</code> since renderable operation is supported. */
public boolean isRenderableSupported() {
return true;
}
/**
* Returns an array of <code>PropertyGenerators</code> implementing property inheritance for the "Rescale" operation
*
* @return An array of property generators.
*/
public PropertyGenerator[] getPropertyGenerators() {
PropertyGenerator[] pg = new PropertyGenerator[1];
pg[0] = new RescalePropertyGenerator();
return pg;
}
/**
* Maps the pixels values of an image from one range to another range.
*
* <p>
* Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes
* {@link JAI#create(String,ParameterBlock,RenderingHints)}.
*
* @param source0 <code>RenderedImage</code> source 0.
* @param scales The per-band scale factors to multiply by.
* @param offsets The per-band offsets to be added.
* @param roi Optional ROI used for computations.
* @param noData Optional No Data range used for computations.
* @param useROIAccessor Boolean indicating if ROI RasterAccessor must be used.
* @param destinationNoData Destination value for No Data.
* @param hints The <code>RenderingHints</code> to use.
* @return The <code>RenderedOp</code> destination.
* @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
*/
public static RenderedOp create(RenderedImage source0, double[] constants, double[] offsets,
ROI roi, Range rangeND, boolean useRoiAccessor, double destNoData, RenderingHints hints) {
// Creation of the parameterBlock object associated to the operation
ParameterBlockJAI pb = new ParameterBlockJAI("Rescale", RenderedRegistryMode.MODE_NAME);
// Setting of the source
pb.setSource("source0", source0);
// Setting of the parameters
pb.setParameter("constants", constants);
pb.setParameter("offsets", offsets);
pb.setParameter("ROI", roi);
pb.setParameter("noData", rangeND);
pb.setParameter("useRoiAccessor", useRoiAccessor);
pb.setParameter("destNoData", destNoData);
return JAI.create("Rescale", pb, hints);
}
/**
* Maps the pixels values of an image from one range to another range.
*
* <p>
* Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes
* {@link JAI#createRenderable(String, ParameterBlock, RenderingHints)}.
*
* @param source0 <code>RenderedImage</code> source 0.
* @param scales The per-band scale factors to multiply by.
* @param offsets The per-band offsets to be added.
* @param roi Optional ROI used for computations.
* @param noData Optional No Data range used for computations.
* @param useROIAccessor Boolean indicating if ROI RasterAccessor must be used.
* @param destinationNoData Destination value for No Data.
* @param hints The <code>RenderingHints</code> to use.
* @return The <code>RenderedOp</code> destination.
* @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
*/
public static RenderableOp createRenderable(RenderableImage source0, double[] constants,
double[] offsets, ROI roi, Range rangeND, boolean useRoiAccessor, double destNoData,
RenderingHints hints) {
// Creation of the parameterBlock object associated to the operation
ParameterBlockJAI pb = new ParameterBlockJAI("Rescale", RenderableRegistryMode.MODE_NAME);
// Setting of the source
pb.setSource("source0", source0);
// Setting of the parameters
pb.setParameter("constants", constants);
pb.setParameter("offsets", offsets);
pb.setParameter("ROI", roi);
pb.setParameter("noData", rangeND);
pb.setParameter("useRoiAccessor", useRoiAccessor);
pb.setParameter("destNoData", destNoData);
return JAI.createRenderable("Rescale", pb, hints);
}
}