/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package com.bc.ceres.jai.operator;
import com.bc.ceres.jai.ExpressionCompilerConfig;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptorImpl;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.RenderedOp;
import javax.media.jai.registry.RenderableRegistryMode;
import javax.media.jai.registry.RenderedRegistryMode;
import java.awt.RenderingHints;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* An <code>OperationDescriptor</code> describing the "Expression"
* operation.
* <p/>
* <p> The Expression operation creates a single-banded, tiled rendered
* image, where all the samples are computed from a given mathematical
* (Java) expression.
*/
public class ExpressionDescriptor extends OperationDescriptorImpl {
/**
* The resource strings that provide the general documentation
* and specify the parameter list for this operation.
*/
private static final String[][] resources = {
{"GlobalName", "Expression"},
{"LocalName", "Expression"},
{"Vendor", "com.bc.ceres.jai"},
{"Description", "Computes a single-banded image using a Java expression."},
{"DocURL", ""},
{"Version", "1.0"},
{"arg0Desc", "The type of the destination image."},
{"arg1Desc", "An arbitrary Java expression."},
{"arg2Desc", "The configuration for the Java expression compiler."},
};
private static final String[] supportedModes = {RenderedRegistryMode.MODE_NAME};
private static final String[] sourceNames = {"sources"};
private static final Class[][] sourceTypes = {{Map.class}};
private static final String[] paramNames = {"dataType", "expression", "compilerConfig"};
private static final Class[] paramClasses = {Integer.class, String.class, ExpressionCompilerConfig.class};
private static final Object[] paramDefaults = {NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT};
private static final Set<Integer> validTypeValues = new TreeSet<Integer>(Arrays.asList(DataBuffer.TYPE_BYTE,
DataBuffer.TYPE_SHORT,
DataBuffer.TYPE_USHORT,
DataBuffer.TYPE_INT,
DataBuffer.TYPE_FLOAT,
DataBuffer.TYPE_DOUBLE));
private static final Object[] validParamValues = {null, null, null};
/**
* Constructor.
*/
public ExpressionDescriptor() {
super(resources,
supportedModes,
sourceNames,
sourceTypes,
paramNames,
paramClasses,
paramDefaults,
validParamValues);
}
/**
* Validates the sources.
*/
@Override
protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer message) {
if (!super.validateSources(modeName, args, message)) {
return false;
}
final Map<Object, Object> source = (Map<Object, Object>) args.getSource(0);
if (source.isEmpty()) {
message.append("At least a single source must be given.");
return false;
}
final Map.Entry[] entries = source.entrySet().toArray(new Map.Entry[0]);
RenderedImage image0 = null;
for (Map.Entry entry : entries) {
if (!(entry.getKey() instanceof String)) {
message.append("Invalid key in source map.");
return false;
}
if (!(entry.getValue() instanceof RenderedImage)) {
message.append("Invalid value in source map.");
return false;
}
final RenderedImage image = (RenderedImage) entry.getValue();
if (image.getSampleModel().getNumBands() != 1) {
message.append("All images in the source map must have exactly one band.");
return false;
}
if (image0 != null) {
if (image.getWidth() != image0.getWidth()
|| image.getHeight() != image0.getHeight()) {
message.append("All images in the source map must have the same width x height.");
return false;
}
} else {
image0 = image;
}
}
return true;
}
/**
* Validates the parameters.
*/
@Override
protected boolean validateParameters(String modeName,
ParameterBlock args,
StringBuffer message) {
if (!super.validateParameters(modeName, args, message)) {
return false;
}
final int dataType = args.getIntParameter(0);
if (!validTypeValues.contains(dataType)) {
message.append("Parameter 'dataType' is not valid.");
return false;
}
final String expression = ((String) args.getObjectParameter(1)).trim();
int length = expression.length();
if (length == 0) {
message.append("Parameter 'expression' must not be empty.");
return false;
}
// todo - further validate expression here
return true;
}
/**
* Creates an expression image.
* <p/>
* <p>Creates a <code>ParameterBlockJAI</code> from all
* supplied arguments except <code>hints</code> and invokes
* {@link JAI#create(String,ParameterBlock,RenderingHints)}.
*
* @param sources The name-to-source map.
* @param dataType The destination image's data type.
* @param expression The mathematical expression.
* @param compilerConfig The configuration for the Java expression compiler.
* @param hints The <code>RenderingHints</code> to use.
* May be <code>null</code>.
*
* @return The <code>RenderedOp</code> destination.
*
* @throws IllegalArgumentException if <code>expression</code> is <code>invalid</code>.
*/
public static RenderedOp create(Map<String, RenderedImage> sources,
int dataType,
String expression,
ExpressionCompilerConfig compilerConfig,
RenderingHints hints) {
ParameterBlockJAI pb =
new ParameterBlockJAI("Expression",
RenderedRegistryMode.MODE_NAME);
pb.addSource(sources);
pb.setParameter("dataType", dataType);
pb.setParameter("expression", expression);
pb.setParameter("compilerConfig", compilerConfig);
return JAI.create("Expression", pb, hints);
}
}