/* 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.bandmerge;
import it.geosolutions.jaiext.range.Range;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.awt.image.renderable.RenderableImage;
import java.util.List;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptorImpl;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.ROI;
import javax.media.jai.RenderableOp;
import javax.media.jai.RenderedOp;
import javax.media.jai.registry.RenderableRegistryMode;
import javax.media.jai.registry.RenderedRegistryMode;
/**
* An <code>OperationDescriptor</code> describing the "BandMerge" operation.
*
* <p>
* The BandMerge operation takes two or more rendered or renderable images, and performs the merging of all the image bands inside a singular
* multibanded image. Each source image should be non-null. The number of bands of the destination image is the sum of those of all source images.
*
* <p>
* All the bands of source images are ordered in the order given in a ParameterBlock. If image1 has three bands and image2 has one band, the merged
* image has 4 bands. Three grayscale images can be used to merge into an RGB image in this way.
*
* The destination image bound is the intersection of the source image bounds. If the sources don't intersect, the destination will have a width and
* height of 0.
*
* If No Data values are present, then the user can define an array of {@link Range} objects(each one for an image) for the No Data and a double value
* called "destination" no data which will be set instead of the old No data value. If the array length is smaller than that of the input images, then
* the first Range object is taken.
*
* If the user wants to do a backward mapping from the destination pixels to each source image pixel, then he must add a parameter called "transformations".
* This parameter is a List of the transformations to perform on each image. Note that the list size must be equal to that of the sources, because each
* transformation must be related to only one image. The user should remember that these transformations have to map from the destination image to the source
* image and not the opposite.
*
* <p>
* <table border=1>
* <caption>Resource List</caption>
* <tr>
* <th>Name</th>
* <th>Value</th>
* </tr>
* <tr>
* <td>GlobalName</td>
* <td>BandMerge</td>
* </tr>
* <tr>
* <td>LocalName</td>
* <td>BandMerge</td>
* </tr>
* <tr>
* <td>Vendor</td>
* <td>it.geosolutions.jaiext</td>
* </tr>
* <tr>
* <td>Description</td>
* <td>Operation used for merging multiple images into a single multibanded image.</td>
* </tr>
* <tr>
* <td>DocURL</td>
* <td>Not Defined</td>
* </tr>
* <tr>
* <td>Version</td>
* <td>1.0</td>
* </tr>
* <td>arg0Desc</td>
* <td>NoData values.</td>
* </tr>
* <td>arg1Desc</td>
* <td>Destination No Data value.</td>
* </tr>
* <td>arg2Desc</td>
* <td>Transformations List.</td>
* </tr>
* <td>arg3Desc</td>
* <td>ROI object to use.</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>noData</td>
* <td>Range[]</td>
* <td>null</td>
* <tr>
* <td>destinationNoData</td>
* <td>Double</td>
* <td>0</td>
* <tr>
* <td>transformations</td>
* <td>java.util.List</td>
* <td>null</td>
* <tr>
* <td>roi</td>
* <td>javax.media.jai.ROI</td>
* <td>null</td>
* <tr>
* <tr>
* </table>
* </p>
* <p>
*
* @see javax.media.jai.OperationDescriptor
*
*/
public class BandMergeDescriptor extends OperationDescriptorImpl {
/**
* The resource strings that provide the general documentation and specify the parameter list for this operation.
*/
private static final String[][] resources = {
{ "GlobalName", "BandMerge" },
{ "LocalName", "BandMerge" },
{ "Vendor", "it.geosolutions.jaiext" },
{ "Description",
"Operation used for merging multiple images into a single multibanded image" },
{ "DocURL", "Not Defined" }, { "Version", "1.0" }, { "arg0Desc", "NoData values" },
{ "arg1Desc", "Destination No Data value" },
{ "arg2Desc", "Transformations List" },
{ "arg3Desc", "ROI object to use" },
{ "arg4Desc", "Boolean indicating if the last band is an alpha band" }
};
/**
* Input Parameter name
*/
private static final String[] paramNames = { "noData", "destinationNoData", "transformations", "roi", "setAlpha" };
/**
* Input Parameter class
*/
private static final Class[] paramClasses = { it.geosolutions.jaiext.range.Range[].class,
Double.class, List.class, javax.media.jai.ROI.class, Boolean.class };
/**
* Input Parameter default values
*/
private static final Object[] paramDefaults = { null, 0d, null, null, false };
/** Constructor. */
public BandMergeDescriptor() {
super(resources, paramClasses, paramNames, paramDefaults);
}
/** Returns <code>true</code> since renderable operation is supported. */
public boolean isRenderableSupported() {
return true;
}
/**
* Merge (possibly multi-banded)images into a multibanded image.
*
* <p>
* Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes
* {@link JAI#create(String,ParameterBlock,RenderingHints)}.
*
* @see JAI
* @see ParameterBlockJAI
* @see RenderedOp
*
* @param noData Array of input No Data Ranges.
* @param destinationNoData value used by the RenderedOp for setting the output no data value.
* @param setAlpha boolean used for setting the last band as alpha band
* @param hints The <code>RenderingHints</code> to use. May be <code>null</code>.
* @param sources Array of source <code>RenderedImage</code>.
* @return The <code>RenderedOp</code> destination.
* @throws IllegalArgumentException if <code>sources</code> is <code>null</code>.
* @throws IllegalArgumentException if a <code>source</code> is <code>null</code>.
*/
public static RenderedOp create(Range[] noData, double destinationNoData, boolean setAlpha, RenderingHints hints,
RenderedImage... sources) {
return create(noData, destinationNoData, setAlpha, hints, null, sources);
}
/**
* Merge (possibly multi-banded)images into a multibanded image.
*
* <p>
* Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes
* {@link JAI#create(String,ParameterBlock,RenderingHints)}.
*
* @see JAI
* @see ParameterBlockJAI
* @see RenderedOp
*
* @param noData Array of input No Data Ranges.
* @param destinationNoData value used by the RenderedOp for setting the output no data value.
* @param setAlpha boolean used for setting the last band as alpha band
* @param hints The <code>RenderingHints</code> to use. May be <code>null</code>.
* @param transform A List of AffineTransformation to use for backward mapping each source image. May be <code>null</code>.
* @param sources Array of source <code>RenderedImage</code>.
* @return The <code>RenderedOp</code> destination.
* @throws IllegalArgumentException if <code>sources</code> is <code>null</code>.
* @throws IllegalArgumentException if a <code>source</code> is <code>null</code>.
*/
public static RenderedOp create(Range[] noData, double destinationNoData, boolean setAlpha, RenderingHints hints,
List<AffineTransform> transform, RenderedImage... sources) {
return create(noData, destinationNoData, setAlpha, hints, transform, null, sources);
}
/**
* Merge (possibly multi-banded)images into a multibanded image.
*
* <p>
* Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes
* {@link JAI#create(String,ParameterBlock,RenderingHints)}.
*
* @see JAI
* @see ParameterBlockJAI
* @see RenderedOp
*
* @param noData Array of input No Data Ranges.
* @param destinationNoData value used by the RenderedOp for setting the output no data value.
* @param hints The <code>RenderingHints</code> to use. May be <code>null</code>.
* @param transform A List of AffineTransformation to use for backward mapping each source image. May be <code>null</code>.
* @param roi Input ROI object to use in the bandmerge operation.
* @param sources Array of source <code>RenderedImage</code>.
* @return The <code>RenderedOp</code> destination.
* @throws IllegalArgumentException if <code>sources</code> is <code>null</code>.
* @throws IllegalArgumentException if a <code>source</code> is <code>null</code>.
*/
public static RenderedOp create(Range[] noData, double destinationNoData, boolean setAlpha, RenderingHints hints,
List<AffineTransform> transform, ROI roi, RenderedImage... sources) {
ParameterBlockJAI pb = new ParameterBlockJAI("BandMerge", RenderedRegistryMode.MODE_NAME);
// Source number
int numSources = sources.length;
// Check on the source number
if (numSources <= 0) {
throw new IllegalArgumentException("No resources are present");
}
// Setting of all the sources
for (int index = 0; index < numSources; index++) {
RenderedImage source = sources[index];
if (source == null) {
throw new IllegalArgumentException("This resource is null");
}
pb.setSource(source, index);
}
// Check if the transform object can be used
if(transform != null && !transform.isEmpty() && transform.get(0) instanceof AffineTransform){
pb.setParameter("transformations", transform);
}
// Setting of the parameters
pb.setParameter("noData", noData);
pb.setParameter("destinationNoData", destinationNoData);
pb.setParameter("roi", roi);
pb.setParameter("setAlpha", setAlpha);
// Creation of the RenderedOp
return JAI.create("BandMerge", pb, hints);
}
/**
* Merge (possibly multi-banded)images into a multibanded image.
*
* <p>
* Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes
* {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}.
*
* @see JAI
* @see ParameterBlockJAI
* @see RenderedOp
*
* @param noData Array of input No Data Ranges.
* @param destinationNoData value used by the RenderableOp for setting the output no data value.
* @param hints The <code>RenderingHints</code> to use. May be <code>null</code>.
* @param sources Array of source <code>RenderableImage</code>.
* @return The <code>RenderableOp</code> destination.
* @throws IllegalArgumentException if <code>sources</code> is <code>null</code>.
* @throws IllegalArgumentException if a <code>source</code> is <code>null</code>.
*/
public static RenderableOp createRenderable(Range[] noData, double destinationNoData,
RenderingHints hints, RenderableImage... sources) {
return createRenderable(noData, destinationNoData, hints, null, sources);
}
/**
* Merge (possibly multi-banded)images into a multibanded image.
*
* <p>
* Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes
* {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}.
*
* @see JAI
* @see ParameterBlockJAI
* @see RenderedOp
*
* @param noData Array of input No Data Ranges.
* @param destinationNoData value used by the RenderableOp for setting the output no data value.
* @param hints The <code>RenderingHints</code> to use. May be <code>null</code>.
* @param transform A List of AffineTransformation to use for backward mapping each source image. May be <code>null</code>.
* @param sources Array of source <code>RenderableImage</code>.
* @return The <code>RenderableOp</code> destination.
* @throws IllegalArgumentException if <code>sources</code> is <code>null</code>.
* @throws IllegalArgumentException if a <code>source</code> is <code>null</code>.
*/
public static RenderableOp createRenderable(Range[] noData, double destinationNoData,
RenderingHints hints, List<AffineTransform> transform, RenderableImage... sources) {
return createRenderable(noData, destinationNoData, hints, transform, null, sources);
}
/**
* Merge (possibly multi-banded)images into a multibanded image.
*
* <p>
* Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes
* {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}.
*
* @see JAI
* @see ParameterBlockJAI
* @see RenderedOp
*
* @param noData Array of input No Data Ranges.
* @param destinationNoData value used by the RenderableOp for setting the output no data value.
* @param hints The <code>RenderingHints</code> to use. May be <code>null</code>.
* @param transform A List of AffineTransformation to use for backward mapping each source image. May be <code>null</code>.
* @param roi Input ROI object to use in the bandmerge operation.
* @param sources Array of source <code>RenderableImage</code>.
* @return The <code>RenderableOp</code> destination.
* @throws IllegalArgumentException if <code>sources</code> is <code>null</code>.
* @throws IllegalArgumentException if a <code>source</code> is <code>null</code>.
*/
public static RenderableOp createRenderable(Range[] noData, double destinationNoData,
RenderingHints hints, List<AffineTransform> transform, ROI roi, RenderableImage... sources) {
ParameterBlockJAI pb = new ParameterBlockJAI("BandMerge",
RenderableRegistryMode.MODE_NAME);
// Source number
int numSources = sources.length;
// Check on the source number
if (numSources <= 0) {
throw new IllegalArgumentException("No resources are present");
}
// Setting of all the sources
for (int index = 0; index < numSources; index++) {
RenderableImage source = sources[index];
if (source == null) {
throw new IllegalArgumentException("This resource is null");
}
pb.setSource(source, index);
}
// Check if the transform object can be used
if (transform != null && !transform.isEmpty()
&& transform.get(0) instanceof AffineTransform) {
pb.setParameter("transformations", transform);
}
// Setting of the parameters
pb.setParameter("noData", noData);
pb.setParameter("destinationNoData", destinationNoData);
pb.setParameter("roi", roi);
// Creation of the RenderedOp
return JAI.createRenderable("BandMerge", pb, hints);
}
}